https://github.com/bolshakov-a updated https://github.com/llvm/llvm-project/pull/156329
>From 2e0a5fa07d1a52205ee37b98b5f9000522f3a8f0 Mon Sep 17 00:00:00 2001 From: Bolshakov <bolsh.and...@yandex.ru> 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/docs/ReleaseNotes.rst | 3 ++ 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 ++++++++++++ 7 files changed, 56 insertions(+), 25 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 4ad562f2579c6..6189a69a4fa8b 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -84,6 +84,9 @@ C++ Specific Potentially Breaking Changes static_assert((b.*mp)() == 1); // newly rejected static_assert((c.*mp)() == 1); // accepted +- ``VarTemplateSpecializationDecl::getTemplateArgsAsWritten()`` method now + returns ``nullptr`` for implicitly instantiated declarations. + ABI Changes in This Version --------------------------- 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 cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits