Skip to content

[clang] Preserve Qualifiers and type sugar in TemplateNames #93433

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,8 @@ Bug Fixes to AST Handling
- Clang now properly preserves ``FoundDecls`` within a ``ConceptReference``. (#GH82628)
- The presence of the ``typename`` keyword is now stored in ``TemplateTemplateParmDecl``.
- Fixed malformed AST generated for anonymous union access in templates. (#GH90842)
- Improved preservation of qualifiers and sugar in `TemplateNames`, including
template keyword.

Miscellaneous Bug Fixes
^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
19 changes: 10 additions & 9 deletions clang/include/clang/AST/TemplateName.h
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ class TemplateName {
/// unexpanded parameter pack (for C++0x variadic templates).
bool containsUnexpandedParameterPack() const;

enum class Qualified { None, AsWritten, Fully };
enum class Qualified { None, AsWritten };
/// Print the template name.
///
/// \param OS the output stream to which the template name will be
Expand Down Expand Up @@ -417,17 +417,18 @@ inline TemplateName TemplateName::getUnderlying() const {
return *this;
}

/// Represents a template name that was expressed as a
/// qualified name.
/// Represents a template name as written in source code.
///
/// This kind of template name refers to a template name that was
/// This kind of template name may refer to a template name that was
/// preceded by a nested name specifier, e.g., \c std::vector. Here,
/// the nested name specifier is "std::" and the template name is the
/// declaration for "vector". The QualifiedTemplateName class is only
/// used to provide "sugar" for template names that were expressed
/// with a qualified name, and has no semantic meaning. In this
/// manner, it is to TemplateName what ElaboratedType is to Type,
/// providing extra syntactic sugar for downstream clients.
/// declaration for "vector". It may also have been written with the
/// 'template' keyword. The QualifiedTemplateName class is only
/// used to provide "sugar" for template names, so that they can
/// be differentiated from canonical template names. and has no
/// semantic meaning. In this manner, it is to TemplateName what
/// ElaboratedType is to Type, providing extra syntactic sugar
/// for downstream clients.
class QualifiedTemplateName : public llvm::FoldingSetNode {
friend class ASTContext;

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -8988,6 +8988,9 @@ class Sema final : public SemaBase {
const TemplateArgumentListInfo *TemplateArgs);

void diagnoseMissingTemplateArguments(TemplateName Name, SourceLocation Loc);
void diagnoseMissingTemplateArguments(const CXXScopeSpec &SS,
bool TemplateKeyword, TemplateDecl *TD,
SourceLocation Loc);

ExprResult BuildTemplateIdExpr(const CXXScopeSpec &SS,
SourceLocation TemplateKWLoc, LookupResult &R,
Expand Down
16 changes: 6 additions & 10 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5006,9 +5006,6 @@ ASTContext::getTemplateSpecializationType(TemplateName Template,
QualType Underlying) const {
assert(!Template.getAsDependentTemplateName() &&
"No dependent template names here!");
// Look through qualified template names.
if (QualifiedTemplateName *QTN = Template.getAsQualifiedTemplateName())
Template = QTN->getUnderlyingTemplate();

const auto *TD = Template.getAsTemplateDecl();
bool IsTypeAlias = TD && TD->isTypeAlias();
Expand Down Expand Up @@ -5044,10 +5041,6 @@ QualType ASTContext::getCanonicalTemplateSpecializationType(
assert(!Template.getAsDependentTemplateName() &&
"No dependent template names here!");

// Look through qualified template names.
if (QualifiedTemplateName *QTN = Template.getAsQualifiedTemplateName())
Template = TemplateName(QTN->getUnderlyingTemplate());

// Build the canonical template specialization type.
TemplateName CanonTemplate = getCanonicalTemplateName(Template);
bool AnyNonCanonArgs = false;
Expand Down Expand Up @@ -5262,10 +5255,12 @@ TemplateArgument ASTContext::getInjectedTemplateArg(NamedDecl *Param) {
Arg = TemplateArgument(E);
} else {
auto *TTP = cast<TemplateTemplateParmDecl>(Param);
TemplateName Name = getQualifiedTemplateName(
nullptr, /*TemplateKeyword=*/false, TemplateName(TTP));
if (TTP->isParameterPack())
Arg = TemplateArgument(TemplateName(TTP), std::optional<unsigned>());
Arg = TemplateArgument(Name, std::optional<unsigned>());
else
Arg = TemplateArgument(TemplateName(TTP));
Arg = TemplateArgument(Name);
}

if (Param->isTemplateParameterPack())
Expand Down Expand Up @@ -9304,7 +9299,8 @@ TemplateName ASTContext::getAssumedTemplateName(DeclarationName Name) const {
TemplateName ASTContext::getQualifiedTemplateName(NestedNameSpecifier *NNS,
bool TemplateKeyword,
TemplateName Template) const {
assert(NNS && "Missing nested-name-specifier in qualified template name");
assert(Template.getKind() == TemplateName::Template ||
Template.getKind() == TemplateName::UsingTemplate);

// FIXME: Canonicalization?
llvm::FoldingSetNodeID ID;
Expand Down
7 changes: 4 additions & 3 deletions clang/lib/AST/DeclTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -627,9 +627,10 @@ ClassTemplateDecl::getInjectedClassNameSpecialization() {
TemplateParameterList *Params = getTemplateParameters();
SmallVector<TemplateArgument, 16> TemplateArgs;
Context.getInjectedTemplateArgs(Params, TemplateArgs);
CommonPtr->InjectedClassNameType
= Context.getTemplateSpecializationType(TemplateName(this),
TemplateArgs);
TemplateName Name = Context.getQualifiedTemplateName(
/*NNS=*/nullptr, /*TemplateKeyword=*/false, TemplateName(this));
CommonPtr->InjectedClassNameType =
Context.getTemplateSpecializationType(Name, TemplateArgs);
return CommonPtr->InjectedClassNameType;
}

Expand Down
9 changes: 8 additions & 1 deletion clang/lib/AST/ODRHash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,17 @@ void ODRHash::AddTemplateName(TemplateName Name) {
case TemplateName::Template:
AddDecl(Name.getAsTemplateDecl());
break;
case TemplateName::QualifiedTemplate: {
QualifiedTemplateName *QTN = Name.getAsQualifiedTemplateName();
if (NestedNameSpecifier *NNS = QTN->getQualifier())
AddNestedNameSpecifier(NNS);
AddBoolean(QTN->hasTemplateKeyword());
AddTemplateName(QTN->getUnderlyingTemplate());
break;
}
// TODO: Support these cases.
case TemplateName::OverloadedTemplate:
case TemplateName::AssumedTemplate:
case TemplateName::QualifiedTemplate:
case TemplateName::DependentTemplate:
case TemplateName::SubstTemplateTemplateParm:
case TemplateName::SubstTemplateTemplateParmPack:
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/TemplateBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ void TemplateArgument::print(const PrintingPolicy &Policy, raw_ostream &Out,
const auto *TTP = cast<TemplateTemplateParmDecl>(TD);
Out << "template-parameter-" << TTP->getDepth() << "-" << TTP->getIndex();
} else {
TN.print(Out, Policy, TemplateName::Qualified::Fully);
TN.print(Out, Policy);
}
break;
}
Expand Down
64 changes: 30 additions & 34 deletions clang/lib/AST/TemplateName.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,8 @@ TemplateNameDependence TemplateName::getDependence() const {
auto D = TemplateNameDependence::None;
switch (getKind()) {
case TemplateName::NameKind::QualifiedTemplate:
D |= toTemplateNameDependence(
getAsQualifiedTemplateName()->getQualifier()->getDependence());
if (NestedNameSpecifier *NNS = getAsQualifiedTemplateName()->getQualifier())
D |= toTemplateNameDependence(NNS->getDependence());
break;
case TemplateName::NameKind::DependentTemplate:
D |= toTemplateNameDependence(
Expand Down Expand Up @@ -292,9 +292,8 @@ void TemplateName::Profile(llvm::FoldingSetNodeID &ID) {

void TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy,
Qualified Qual) const {
auto Kind = getKind();
TemplateDecl *Template = nullptr;
if (Kind == TemplateName::Template || Kind == TemplateName::UsingTemplate) {
if (NameKind Kind = getKind();
Kind == TemplateName::Template || Kind == TemplateName::UsingTemplate) {
// After `namespace ns { using std::vector }`, what is the fully-qualified
// name of the UsingTemplateName `vector` within ns?
//
Expand All @@ -304,46 +303,43 @@ void TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy,
// Similar to the UsingType behavior, using declarations are used to import
// names more often than to export them, thus using the original name is
// most useful in this case.
Template = getAsTemplateDecl();
}

if (Template)
if (Policy.CleanUglifiedParameters &&
isa<TemplateTemplateParmDecl>(Template) && Template->getIdentifier())
OS << Template->getIdentifier()->deuglifiedName();
else if (Qual == Qualified::Fully &&
getDependence() !=
TemplateNameDependenceScope::DependentInstantiation)
Template->printQualifiedName(OS, Policy);
else
TemplateDecl *Template = getAsTemplateDecl();
if (Qual == Qualified::None)
OS << *Template;
else if (QualifiedTemplateName *QTN = getAsQualifiedTemplateName()) {
if (Qual == Qualified::Fully &&
getDependence() !=
TemplateNameDependenceScope::DependentInstantiation) {
QTN->getUnderlyingTemplate().getAsTemplateDecl()->printQualifiedName(
OS, Policy);
return;
}
if (Qual == Qualified::AsWritten)
QTN->getQualifier()->print(OS, Policy);
else
Template->printQualifiedName(OS, Policy);
} else if (QualifiedTemplateName *QTN = getAsQualifiedTemplateName()) {
if (NestedNameSpecifier *NNS = QTN->getQualifier();
Qual != Qualified::None && NNS)
NNS->print(OS, Policy);
if (QTN->hasTemplateKeyword())
OS << "template ";
OS << *QTN->getUnderlyingTemplate().getAsTemplateDecl();

TemplateName Underlying = QTN->getUnderlyingTemplate();
assert(Underlying.getKind() == TemplateName::Template ||
Underlying.getKind() == TemplateName::UsingTemplate);

TemplateDecl *UTD = Underlying.getAsTemplateDecl();
if (IdentifierInfo *II = UTD->getIdentifier();
Policy.CleanUglifiedParameters && II &&
isa<TemplateTemplateParmDecl>(UTD))
OS << II->deuglifiedName();
else
OS << *UTD;
} else if (DependentTemplateName *DTN = getAsDependentTemplateName()) {
if (Qual == Qualified::AsWritten && DTN->getQualifier())
DTN->getQualifier()->print(OS, Policy);
if (NestedNameSpecifier *NNS = DTN->getQualifier())
NNS->print(OS, Policy);
OS << "template ";

if (DTN->isIdentifier())
OS << DTN->getIdentifier()->getName();
else
OS << "operator " << getOperatorSpelling(DTN->getOperator());
} else if (SubstTemplateTemplateParmStorage *subst
= getAsSubstTemplateTemplateParm()) {
} else if (SubstTemplateTemplateParmStorage *subst =
getAsSubstTemplateTemplateParm()) {
subst->getReplacement().print(OS, Policy, Qual);
} else if (SubstTemplateTemplateParmPackStorage *SubstPack
= getAsSubstTemplateTemplateParmPack())
} else if (SubstTemplateTemplateParmPackStorage *SubstPack =
getAsSubstTemplateTemplateParmPack())
OS << *SubstPack->getParameterPack();
else if (AssumedTemplateStorage *Assumed = getAsAssumedTemplateName()) {
Assumed->getDeclName().print(OS, Policy);
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/AST/TextNodeDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1989,15 +1989,15 @@ void TextNodeDumper::VisitAutoType(const AutoType *T) {

void TextNodeDumper::VisitDeducedTemplateSpecializationType(
const DeducedTemplateSpecializationType *T) {
if (T->getTemplateName().getKind() == TemplateName::UsingTemplate)
if (T->getTemplateName().getAsUsingShadowDecl())
OS << " using";
}

void TextNodeDumper::VisitTemplateSpecializationType(
const TemplateSpecializationType *T) {
if (T->isTypeAlias())
OS << " alias";
if (T->getTemplateName().getKind() == TemplateName::UsingTemplate)
if (T->getTemplateName().getAsUsingShadowDecl())
OS << " using";
OS << " ";
T->getTemplateName().dump(OS);
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4251,7 +4251,8 @@ TemplateSpecializationType::TemplateSpecializationType(
assert((T.getKind() == TemplateName::Template ||
T.getKind() == TemplateName::SubstTemplateTemplateParm ||
T.getKind() == TemplateName::SubstTemplateTemplateParmPack ||
T.getKind() == TemplateName::UsingTemplate) &&
T.getKind() == TemplateName::UsingTemplate ||
T.getKind() == TemplateName::QualifiedTemplate) &&
"Unexpected template name for TemplateSpecializationType");

auto *TemplateArgs = reinterpret_cast<TemplateArgument *>(this + 1);
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/AST/TypePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1586,14 +1586,14 @@ void TypePrinter::printTemplateId(const TemplateSpecializationType *T,
IncludeStrongLifetimeRAII Strong(Policy);

TemplateDecl *TD = T->getTemplateName().getAsTemplateDecl();
// FIXME: Null TD never excercised in test suite.
// FIXME: Null TD never exercised in test suite.
if (FullyQualify && TD) {
if (!Policy.SuppressScope)
AppendScope(TD->getDeclContext(), OS, TD->getDeclName());

OS << TD->getName();
} else {
T->getTemplateName().print(OS, Policy);
T->getTemplateName().print(OS, Policy, TemplateName::Qualified::None);
}

DefaultTemplateArgsPolicyRAII TemplateArgs(Policy);
Expand Down
15 changes: 7 additions & 8 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -538,8 +538,9 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc,
} else if (AllowDeducedTemplate) {
if (auto *TD = getAsTypeTemplateDecl(IIDecl)) {
assert(!FoundUsingShadow || FoundUsingShadow->getTargetDecl() == TD);
TemplateName Template =
FoundUsingShadow ? TemplateName(FoundUsingShadow) : TemplateName(TD);
TemplateName Template = Context.getQualifiedTemplateName(
SS ? SS->getScopeRep() : nullptr, /*TemplateKeyword=*/false,
FoundUsingShadow ? TemplateName(FoundUsingShadow) : TemplateName(TD));
T = Context.getDeducedTemplateSpecializationType(Template, QualType(),
false);
// Don't wrap in a further UsingType.
Expand Down Expand Up @@ -1137,12 +1138,10 @@ Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS,
dyn_cast<UsingShadowDecl>(*Result.begin());
assert(!FoundUsingShadow ||
TD == cast<TemplateDecl>(FoundUsingShadow->getTargetDecl()));
Template =
FoundUsingShadow ? TemplateName(FoundUsingShadow) : TemplateName(TD);
if (SS.isNotEmpty())
Template = Context.getQualifiedTemplateName(SS.getScopeRep(),
/*TemplateKeyword=*/false,
Template);
Template = Context.getQualifiedTemplateName(
SS.getScopeRep(),
/*TemplateKeyword=*/false,
FoundUsingShadow ? TemplateName(FoundUsingShadow) : TemplateName(TD));
} else {
// All results were non-template functions. This is a function template
// name.
Expand Down
12 changes: 6 additions & 6 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11547,12 +11547,12 @@ bool Sema::CheckDeductionGuideDeclarator(Declarator &D, QualType &R,
TemplateName SpecifiedName = RetTST.getTypePtr()->getTemplateName();
bool TemplateMatches =
Context.hasSameTemplateName(SpecifiedName, GuidedTemplate);
auto TKind = SpecifiedName.getKind();
// A Using TemplateName can't actually be valid (either it's qualified, or
// we're in the wrong scope). But we have diagnosed these problems
// already.
bool SimplyWritten = TKind == TemplateName::Template ||
TKind == TemplateName::UsingTemplate;

const QualifiedTemplateName *Qualifiers =
SpecifiedName.getAsQualifiedTemplateName();
assert(Qualifiers && "expected QualifiedTemplate");
bool SimplyWritten = !Qualifiers->hasTemplateKeyword() &&
Qualifiers->getQualifier() == nullptr;
if (SimplyWritten && TemplateMatches)
AcceptableReturnType = true;
else {
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3284,10 +3284,10 @@ ExprResult Sema::BuildDeclarationNameExpr(
return CreateRecoveryExpr(NameInfo.getBeginLoc(), NameInfo.getEndLoc(), {});
}

if (TemplateDecl *Template = dyn_cast<TemplateDecl>(D)) {
if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D)) {
// Specifically diagnose references to class templates that are missing
// a template argument list.
diagnoseMissingTemplateArguments(TemplateName(Template), Loc);
diagnoseMissingTemplateArguments(SS, /*TemplateKeyword=*/false, TD, Loc);
return ExprError();
}

Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/SemaExprMember.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1194,7 +1194,8 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,

if (VarTemplateDecl *VarTempl = dyn_cast<VarTemplateDecl>(MemberDecl)) {
if (!TemplateArgs) {
diagnoseMissingTemplateArguments(TemplateName(VarTempl), MemberLoc);
diagnoseMissingTemplateArguments(
SS, /*TemplateKeyword=*/TemplateKWLoc.isValid(), VarTempl, MemberLoc);
return ExprError();
}

Expand Down
Loading
Loading