[clang] Remove written template args from implicit var tpl spec (#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](13357e8a12/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](13357e8a12/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`](4c91627304/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.
This commit is contained in:
Andrey Ali Khan Bolshakov
2025-09-04 18:32:12 +03:00
committed by GitHub
parent f6e8b26eab
commit 1cb47c19f8
7 changed files with 56 additions and 25 deletions

View File

@@ -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
---------------------------

View File

@@ -11668,7 +11668,8 @@ public:
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
@@ -14028,7 +14029,6 @@ public:
VarTemplateSpecializationDecl *BuildVarTemplateInstantiation(
VarTemplateDecl *VarTemplate, VarDecl *FromVar,
const TemplateArgumentList *PartialSpecArgs,
const TemplateArgumentListInfo &TemplateArgsInfo,
SmallVectorImpl<TemplateArgument> &Converted,
SourceLocation PointOfInstantiation,
LateInstantiatedAttrVec *LateAttrs = nullptr,

View File

@@ -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);

View File

@@ -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();

View File

@@ -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;

View File

@@ -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();

View File

@@ -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);
}