-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[clang] Remove written template args from implicit var tpl spec #156329
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang] Remove written template args from implicit var tpl spec #156329
Conversation
|
@llvm/pr-subscribers-clang Author: Andrey Ali Khan Bolshakov (bolshakov-a) Changes
template <typename>
int VarTpl;
template <typename T>
void tplFn() {
(void)VarTpl<T>; // (1)
}
void fn() {
tplFn<char>();
}Clang treats the Moreover, "template args as written" are stored inside Moreover, it is assumed in That said, Full diff: https://github.com/llvm/llvm-project/pull/156329.diff 6 Files Affected:
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);
+}
|
zyn0217
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need a release note, maybe 'Potential AST breaking changes' would be a good fit.
|
I always forget about release notes, thanks! However, there isn't "Potential AST breaking changes" section but there is "AST Dumping Potentially Breaking Changes" one. But this PR doesn't change AST dumps. Maybe, "C++ Specific Potentially Breaking Changes"? |
801d872 to
69887b3
Compare
clang/docs/ReleaseNotes.rst
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't really a useful release note for someone who uses our compiler. We have to explain WHAT is happening from a user perspective.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there is any change from user's perspective. I didn't succeeded in finding any related user-observable bug. In fact, this change fixes a bug in the IWYU tool. However, those who use Clang as a library (like IWYU) are users as well, aren't they? And this PR fixes the bug in the Clang library interface (or at least makes it more consistent).
There is a related bug that RecursiveASTVisitor traverses (by default) implicitly instantiated variable template specializations and calls VisitVarTemplateSpecializationDecl for them, which manifests itself in the ExtractAPI, AFAIU. Namely, for such code:
template <typename>
int var_tpl;
inline void Fn(){
(void)var_tpl<double>;
}Clang writes "Global Variable Template Specialization" record in the JSON file which looks like a bug because the specialization is implicit. CC @zixu-w, @daniel-grumberg.
I don't mind if you suggest just to remove the relnote or move it into some other section.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also CC @QuietMisdreavus for the ExtractAPI impact. At a glance I believe this is also fine for ExtractAPI (that the template specialization record is not expected in the symbol graph output for this one).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is also fine for ExtractAPI
Do you mean this PR? It doesn't fix this bug. (But fixing it would also fix the problem with IWYU, tbh)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably be: now returns nullptr. Also, I THINK this probably belongs in teh ABI changes section then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@erichkeane, I think that ABI is about name mangling and other codegen-related stuff? Maybe, such a small change in the internal AST representation just don't need mentioning in the relnotes?
clang/include/clang/Sema/Sema.h
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than adding a new bool parameter, I think we better move the responsibility of setting the written args entirely out of CheckVarTemplateId, which would make this more similar to CheckTypeTemplateId.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Setting the args to the CheckVarTemplateId result may change some previous declaration stored in the ASTContext when CheckVarTemplateId returns it and not a new one.
I cannot find CheckTypeTemplateId in the project (typo?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Err I meant CheckTemplateIdType, pretty unfortunate inconsistency in naming formulation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, that's another inconsistency with how CheckTemplateIdType operates and is used.
This may need some though, I don't want to propose a large refactoring to support such a simple change, but the bool parameter does not feel right.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like the difference is because CheckTemplateIdType returns template specialization type and not the specialization declaration.
mizvekov
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
I don't love the new bool parameter, but I don't see a simple way to avoid it, and this change is too simple to be worth asking for such a change.
|
Ok, thanks! Can you merge it please if you don't think that something should be done with #156329 (comment)? |
|
I'd suggest wait for the other reviewers, or until next week if no response. |
erichkeane
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 nit, else lgtm. Fix that and re-ping and I'll merge you.
clang/docs/ReleaseNotes.rst
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably be: now returns nullptr. Also, I THINK this probably belongs in teh ABI changes section then?
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.
69887b3 to
2e0a5fa
Compare
VarTemplateSpecializationDecl::getTemplateArgsAsWritten()function should returnnullptrin the case of implicit instantiation, as itsClassTemplateSpecializationDeclcounterpart does, and not the arguments written inDeclRefExprreferencing the specialization in the first place. Otherwise, for such code:Clang treats the
charargument of theVarTplspecialization 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
ExplicitInfofield ofVarTemplateSpecializationDecl, but it is documented that it is not for implicit instantiations.Moreover, it is assumed in
TraverseVarTemplateSpecializationDeclmethod ofRecursiveASTVisitorthatgetTemplateArgsAsWritten()returnsnullptrfor implicit instantiations, as it is stated in the comment there.That said,
setTemplateArgsAsWrittenshould be called only for variable template explicit specializations (it is already done insideSema::ActOnVarTemplateSpecialization) and explicit instantiations (hencetrueis passed to the newSetWrittenArgsparameter ofCheckVarTemplateIdfunction insideSema::ActOnExplicitInstantiation, but not when handling expressions referencing a variable template specialization).InstantiateVariableDefinitionfunction 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 ofTemplateDeclInstantiator::VisitVarTemplateSpecializationDeclas it was.