https://github.com/bolshakov-a created
https://github.com/llvm/llvm-project/pull/156329
`VarTemplateSpecializationDecl::getTemplateArgsAsWritten()` function should
return `nullptr` in the case of implicit instantiation, as its
`ClassTemplateSpecializationDecl` counterpart does, and not the arguments
written in `DeclRefExpr` referencing the specialization in the first place.
Otherwise, for such code:
```cpp
template <typename>
int VarTpl;
template <typename T>
void tplFn() {
(void)VarTpl<T>; // (1)
}
void fn() {
tplFn<char>();
}
```
Clang treats the `char` argument of the `VarTpl` specialization as if it were
written in the line marked as (1), which is misleading and hardly makes sense.
Moreover, "template args as written" are stored inside `ExplicitInfo` field of
`VarTemplateSpecializationDecl`, but it is
[documented](https://github.com/llvm/llvm-project/blob/13357e8a12c1a45364a0c4d3137b6d21ee6ac40c/clang/include/clang/AST/DeclTemplate.h#L2653)
that it is not for implicit instantiations.
Moreover, it is assumed in `TraverseVarTemplateSpecializationDecl` method of
`RecursiveASTVisitor` that `getTemplateArgsAsWritten()` returns `nullptr` for
implicit instantiations, as it is stated in the comment
[there](https://github.com/llvm/llvm-project/blob/13357e8a12c1a45364a0c4d3137b6d21ee6ac40c/clang/include/clang/AST/RecursiveASTVisitor.h#L2196).
That said, `setTemplateArgsAsWritten` should be called only for variable
template explicit specializations (it is [already done inside
`Sema::ActOnVarTemplateSpecialization`](https://github.com/llvm/llvm-project/blob/4c916273041ff5ed7b2af20bec787ffc42871c9f/clang/lib/Sema/SemaTemplate.cpp#L4459))
and explicit instantiations (hence `true` is passed to the new
`SetWrittenArgs` parameter of `CheckVarTemplateId` function inside
`Sema::ActOnExplicitInstantiation`, but not when handling expressions
referencing a variable template specialization).
`InstantiateVariableDefinition` function just passes the arguments from the
corresponding declaration. I'm not sure about instantiating a class template
containing a variable template explicit specialization and thus have tried to
leave the logic of the first overload of
`TemplateDeclInstantiator::VisitVarTemplateSpecializationDecl` as it was.
>From 801d872538893065507c30859b69331772549ed5 Mon Sep 17 00:00:00 2001
From: Bolshakov <[email protected]>
Date: Mon, 1 Sep 2025 12:38:24 +0300
Subject: [PATCH] [clang] Remove written template args from implicit var tpl
spec
VarTemplateSpecializationDecl::getTemplateArgsAsWritten() function
should return nullptr in the case of implicit instantiation, as its
ClassTemplateSpecializationDecl counterpart does, and not the arguments
written in DeclRefExpr referencing the specialization in the first
place. Otherwise, for such code:
template <typename>
int VarTpl;
template <typename T>
void tplFn() {
(void)VarTpl<T>; // (1)
}
void fn() {
tplFn<char>();
}
Clang treats the 'char' argument of the VarTpl specialization as if it
were written in the line marked as (1), which is misleading and hardly
makes sense.
Moreover, "template args as written" are stored inside ExplicitInfo
field of VarTemplateSpecializationDecl, but it is documented that it
is not for implicit instantiations.
Moreover, it is assumed in TraverseVarTemplateSpecializationDecl method
of RecursiveASTVisitor that getTemplateArgsAsWritten() returns nullptr
for implicit instantiations, as it is stated in the comment there.
That said, setTemplateArgsAsWritten should be called only for variable
template explicit specializations (it is already done inside
Sema::ActOnVarTemplateSpecialization) and explicit instantiations (hence
'true' is passed to the new SetWrittenArgs parameter
of CheckVarTemplateId function inside Sema::ActOnExplicitInstantiation,
but not when handling expressions referencing a variable template
specialization). InstantiateVariableDefinition function just passes
the arguments from the corresponding declaration. I'm not sure about
instantiating a class template containing a variable template explicit
specialization and thus have tried to leave the logic of the first
overload of TemplateDeclInstantiator::VisitVarTemplateSpecializationDecl
as it was.
---
clang/include/clang/Sema/Sema.h | 4 +--
clang/include/clang/Sema/Template.h | 3 +-
clang/lib/Sema/SemaExprMember.cpp | 5 +--
clang/lib/Sema/SemaTemplate.cpp | 16 ++++++----
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 31 +++++++++++--------
clang/unittests/AST/DeclTest.cpp | 19 ++++++++++++
6 files changed, 53 insertions(+), 25 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index c3fb57774c8dc..224095070cbe3 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11662,7 +11662,8 @@ class Sema final : public SemaBase {
DeclResult CheckVarTemplateId(VarTemplateDecl *Template,
SourceLocation TemplateLoc,
SourceLocation TemplateNameLoc,
- const TemplateArgumentListInfo &TemplateArgs);
+ const TemplateArgumentListInfo &TemplateArgs,
+ bool SetWrittenArgs);
/// Form a reference to the specialization of the given variable template
/// corresponding to the specified argument list, or a null-but-valid result
@@ -14022,7 +14023,6 @@ class Sema final : public SemaBase {
VarTemplateSpecializationDecl *BuildVarTemplateInstantiation(
VarTemplateDecl *VarTemplate, VarDecl *FromVar,
const TemplateArgumentList *PartialSpecArgs,
- const TemplateArgumentListInfo &TemplateArgsInfo,
SmallVectorImpl<TemplateArgument> &Converted,
SourceLocation PointOfInstantiation,
LateInstantiatedAttrVec *LateAttrs = nullptr,
diff --git a/clang/include/clang/Sema/Template.h
b/clang/include/clang/Sema/Template.h
index fe907078af275..115c19d4f1540 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -723,9 +723,8 @@ enum class TemplateSubstitutionKind : char {
bool SubstQualifier(const TagDecl *OldDecl,
TagDecl *NewDecl);
- Decl *VisitVarTemplateSpecializationDecl(
+ VarTemplateSpecializationDecl *VisitVarTemplateSpecializationDecl(
VarTemplateDecl *VarTemplate, VarDecl *FromVar,
- const TemplateArgumentListInfo &TemplateArgsInfo,
ArrayRef<TemplateArgument> Converted,
VarTemplateSpecializationDecl *PrevDecl = nullptr);
diff --git a/clang/lib/Sema/SemaExprMember.cpp
b/clang/lib/Sema/SemaExprMember.cpp
index 4a31a139eaf4f..aedfc5e88b1c6 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -1126,8 +1126,9 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType
BaseExprType,
return ExprError();
}
- DeclResult VDecl = CheckVarTemplateId(VarTempl, TemplateKWLoc,
- MemberNameInfo.getLoc(),
*TemplateArgs);
+ DeclResult VDecl =
+ CheckVarTemplateId(VarTempl, TemplateKWLoc, MemberNameInfo.getLoc(),
+ *TemplateArgs, /*SetWrittenArgs=*/false);
if (VDecl.isInvalid())
return ExprError();
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 3d8416ac7dc1b..3533871ad4dad 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4542,7 +4542,8 @@ static bool IsLibstdcxxStdFormatKind(Preprocessor &PP,
VarDecl *Var) {
DeclResult
Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc,
SourceLocation TemplateNameLoc,
- const TemplateArgumentListInfo &TemplateArgs) {
+ const TemplateArgumentListInfo &TemplateArgs,
+ bool SetWrittenArgs) {
assert(Template && "A variable template id without template?");
// Check that the template argument list is well-formed for this template.
@@ -4725,10 +4726,12 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template,
SourceLocation TemplateLoc,
// in DoMarkVarDeclReferenced().
// FIXME: LateAttrs et al.?
VarTemplateSpecializationDecl *Decl = BuildVarTemplateInstantiation(
- Template, InstantiationPattern, PartialSpecArgs, TemplateArgs,
- CTAI.CanonicalConverted, TemplateNameLoc /*, LateAttrs, StartingScope*/);
+ Template, InstantiationPattern, PartialSpecArgs, CTAI.CanonicalConverted,
+ TemplateNameLoc /*, LateAttrs, StartingScope*/);
if (!Decl)
return true;
+ if (SetWrittenArgs)
+ Decl->setTemplateArgsAsWritten(TemplateArgs);
if (AmbiguousPartialSpec) {
// Partial ordering did not produce a clear winner. Complain.
@@ -4760,7 +4763,7 @@ ExprResult Sema::CheckVarTemplateId(
const TemplateArgumentListInfo *TemplateArgs) {
DeclResult Decl = CheckVarTemplateId(Template, TemplateLoc,
NameInfo.getLoc(),
- *TemplateArgs);
+ *TemplateArgs,
/*SetWrittenArgs=*/false);
if (Decl.isInvalid())
return ExprError();
@@ -10707,8 +10710,9 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S,
TemplateArgumentListInfo TemplateArgs =
makeTemplateArgumentListInfo(*this, *D.getName().TemplateId);
- DeclResult Res = CheckVarTemplateId(PrevTemplate, TemplateLoc,
- D.getIdentifierLoc(), TemplateArgs);
+ DeclResult Res =
+ CheckVarTemplateId(PrevTemplate, TemplateLoc, D.getIdentifierLoc(),
+ TemplateArgs, /*SetWrittenArgs=*/true);
if (Res.isInvalid())
return true;
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index ee1b520fa46e9..910f99d3eb160 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4542,14 +4542,17 @@ Decl
*TemplateDeclInstantiator::VisitVarTemplateSpecializationDecl(
PrevDecl->getPointOfInstantiation(), Ignored))
return nullptr;
- return VisitVarTemplateSpecializationDecl(InstVarTemplate, D,
- VarTemplateArgsInfo,
- CTAI.CanonicalConverted, PrevDecl);
+ if (VarTemplateSpecializationDecl *VTSD = VisitVarTemplateSpecializationDecl(
+ InstVarTemplate, D, CTAI.CanonicalConverted, PrevDecl)) {
+ VTSD->setTemplateArgsAsWritten(VarTemplateArgsInfo);
+ return VTSD;
+ }
+ return nullptr;
}
-Decl *TemplateDeclInstantiator::VisitVarTemplateSpecializationDecl(
+VarTemplateSpecializationDecl *
+TemplateDeclInstantiator::VisitVarTemplateSpecializationDecl(
VarTemplateDecl *VarTemplate, VarDecl *D,
- const TemplateArgumentListInfo &TemplateArgsInfo,
ArrayRef<TemplateArgument> Converted,
VarTemplateSpecializationDecl *PrevDecl) {
@@ -4570,7 +4573,6 @@ Decl
*TemplateDeclInstantiator::VisitVarTemplateSpecializationDecl(
VarTemplateSpecializationDecl *Var = VarTemplateSpecializationDecl::Create(
SemaRef.Context, Owner, D->getInnerLocStart(), D->getLocation(),
VarTemplate, DI->getType(), DI, D->getStorageClass(), Converted);
- Var->setTemplateArgsAsWritten(TemplateArgsInfo);
if (!PrevDecl) {
void *InsertPos = nullptr;
VarTemplate->findSpecialization(Converted, InsertPos);
@@ -5880,7 +5882,6 @@ void Sema::InstantiateFunctionDefinition(SourceLocation
PointOfInstantiation,
VarTemplateSpecializationDecl *Sema::BuildVarTemplateInstantiation(
VarTemplateDecl *VarTemplate, VarDecl *FromVar,
const TemplateArgumentList *PartialSpecArgs,
- const TemplateArgumentListInfo &TemplateArgsInfo,
SmallVectorImpl<TemplateArgument> &Converted,
SourceLocation PointOfInstantiation, LateInstantiatedAttrVec *LateAttrs,
LocalInstantiationScope *StartingScope) {
@@ -5922,9 +5923,8 @@ VarTemplateSpecializationDecl
*Sema::BuildVarTemplateInstantiation(
// TODO: Set LateAttrs and StartingScope ...
- return cast_or_null<VarTemplateSpecializationDecl>(
- Instantiator.VisitVarTemplateSpecializationDecl(
- VarTemplate, FromVar, TemplateArgsInfo, Converted));
+ return Instantiator.VisitVarTemplateSpecializationDecl(VarTemplate, FromVar,
+ Converted);
}
VarTemplateSpecializationDecl *Sema::CompleteVarTemplateSpecializationDecl(
@@ -6340,10 +6340,15 @@ void Sema::InstantiateVariableDefinition(SourceLocation
PointOfInstantiation,
TemplateArgInfo.addArgument(Arg);
}
- Var =
cast_or_null<VarDecl>(Instantiator.VisitVarTemplateSpecializationDecl(
- VarSpec->getSpecializedTemplate(), Def, TemplateArgInfo,
- VarSpec->getTemplateArgs().asArray(), VarSpec));
+ VarTemplateSpecializationDecl *VTSD =
+ Instantiator.VisitVarTemplateSpecializationDecl(
+ VarSpec->getSpecializedTemplate(), Def,
+ VarSpec->getTemplateArgs().asArray(), VarSpec);
+ Var = VTSD;
+
if (Var) {
+ VTSD->setTemplateArgsAsWritten(TemplateArgInfo);
+
llvm::PointerUnion<VarTemplateDecl *,
VarTemplatePartialSpecializationDecl *> PatternPtr =
VarSpec->getSpecializedTemplateOrPartial();
diff --git a/clang/unittests/AST/DeclTest.cpp b/clang/unittests/AST/DeclTest.cpp
index 4c83ff9142e87..e76edbfe95b68 100644
--- a/clang/unittests/AST/DeclTest.cpp
+++ b/clang/unittests/AST/DeclTest.cpp
@@ -586,3 +586,22 @@ namespace x::y {
ASSERT_NE(FD, nullptr);
ASSERT_EQ(FD->getQualifiedNameAsString(), "x::y::Foo::Foo<T>");
}
+
+TEST(Decl, NoWrittenArgsInImplicitlyInstantiatedVarSpec) {
+ const char *Code = R"cpp(
+ template <typename>
+ int VarTpl;
+
+ void fn() {
+ (void)VarTpl<char>;
+ }
+ )cpp";
+
+ auto AST = tooling::buildASTFromCode(Code);
+ ASTContext &Ctx = AST->getASTContext();
+
+ const auto *VTSD = selectFirst<VarTemplateSpecializationDecl>(
+ "id", match(varDecl(isTemplateInstantiation()).bind("id"), Ctx));
+ ASSERT_NE(VTSD, nullptr);
+ EXPECT_EQ(VTSD->getTemplateArgsAsWritten(), nullptr);
+}
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits