Skip to content

Commit fe94f11

Browse files
authored
[clang] Fix elaborated keyword canonicalization (#135916)
1 parent 4903a7b commit fe94f11

File tree

15 files changed

+234
-77
lines changed

15 files changed

+234
-77
lines changed

clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,10 @@ static std::optional<TemplateSpecializationTypeLoc>
5454
matchEnableIfSpecializationImplTypename(TypeLoc TheType) {
5555
if (const auto Dep = TheType.getAs<DependentNameTypeLoc>()) {
5656
const IdentifierInfo *Identifier = Dep.getTypePtr()->getIdentifier();
57+
ElaboratedTypeKeyword Keyword = Dep.getTypePtr()->getKeyword();
5758
if (!Identifier || Identifier->getName() != "type" ||
58-
Dep.getTypePtr()->getKeyword() != ElaboratedTypeKeyword::Typename) {
59+
(Keyword != ElaboratedTypeKeyword::Typename &&
60+
Keyword != ElaboratedTypeKeyword::None)) {
5961
return std::nullopt;
6062
}
6163
TheType = Dep.getQualifierLoc().getTypeLoc();
@@ -108,8 +110,10 @@ matchEnableIfSpecializationImplTrait(TypeLoc TheType) {
108110

109111
if (const auto *AliasedType =
110112
dyn_cast<DependentNameType>(Specialization->getAliasedType())) {
113+
ElaboratedTypeKeyword Keyword = AliasedType->getKeyword();
111114
if (AliasedType->getIdentifier()->getName() != "type" ||
112-
AliasedType->getKeyword() != ElaboratedTypeKeyword::Typename) {
115+
(Keyword != ElaboratedTypeKeyword::Typename &&
116+
Keyword != ElaboratedTypeKeyword::None)) {
113117
return std::nullopt;
114118
}
115119
} else {

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,10 @@ Bug Fixes to C++ Support
486486
- Fixes matching of nested template template parameters. (#GH130362)
487487
- Correctly diagnoses template template paramters which have a pack parameter
488488
not in the last position.
489+
- Disallow overloading on struct vs class on dependent types, which is IFNDR, as
490+
this makes the problem diagnosable.
491+
- Improved preservation of the presence or abscence of typename specifier when
492+
printing types in diagnostics.
489493
- Clang now correctly parses ``if constexpr`` expressions in immediate function context. (#GH123524)
490494
- Fixed an assertion failure affecting code that uses C++23 "deducing this". (#GH130272)
491495
- Clang now properly instantiates destructors for initialized members within non-delegating constructors. (#GH93251)

clang/include/clang/AST/Type.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2838,6 +2838,20 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
28382838
/// immediately following this class.
28392839
template <typename T> const T *getAs() const;
28402840

2841+
/// Look through sugar for an instance of TemplateSpecializationType which
2842+
/// is not a type alias, or null if there is no such type.
2843+
/// This is used when you want as-written template arguments or the template
2844+
/// name for a class template specialization.
2845+
const TemplateSpecializationType *
2846+
getAsNonAliasTemplateSpecializationType() const;
2847+
2848+
const TemplateSpecializationType *
2849+
castAsNonAliasTemplateSpecializationType() const {
2850+
const auto *TST = getAsNonAliasTemplateSpecializationType();
2851+
assert(TST && "not a TemplateSpecializationType");
2852+
return TST;
2853+
}
2854+
28412855
/// Member-template getAsAdjusted<specific type>. Look through specific kinds
28422856
/// of sugar (parens, attributes, etc) for an instance of \<specific type>.
28432857
/// This is used when you need to walk over sugar nodes that represent some

clang/lib/AST/ASTContext.cpp

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5747,6 +5747,30 @@ ASTContext::getMacroQualifiedType(QualType UnderlyingTy,
57475747
return QualType(newType, 0);
57485748
}
57495749

5750+
static ElaboratedTypeKeyword
5751+
getCanonicalElaboratedTypeKeyword(ElaboratedTypeKeyword Keyword) {
5752+
switch (Keyword) {
5753+
// These are just themselves.
5754+
case ElaboratedTypeKeyword::None:
5755+
case ElaboratedTypeKeyword::Struct:
5756+
case ElaboratedTypeKeyword::Union:
5757+
case ElaboratedTypeKeyword::Enum:
5758+
case ElaboratedTypeKeyword::Interface:
5759+
return Keyword;
5760+
5761+
// These are equivalent.
5762+
case ElaboratedTypeKeyword::Typename:
5763+
return ElaboratedTypeKeyword::None;
5764+
5765+
// These are functionally equivalent, so relying on their equivalence is
5766+
// IFNDR. By making them equivalent, we disallow overloading, which at least
5767+
// can produce a diagnostic.
5768+
case ElaboratedTypeKeyword::Class:
5769+
return ElaboratedTypeKeyword::Struct;
5770+
}
5771+
llvm_unreachable("unexpected keyword kind");
5772+
}
5773+
57505774
QualType ASTContext::getDependentNameType(ElaboratedTypeKeyword Keyword,
57515775
NestedNameSpecifier *NNS,
57525776
const IdentifierInfo *Name) const {
@@ -5758,10 +5782,13 @@ QualType ASTContext::getDependentNameType(ElaboratedTypeKeyword Keyword,
57585782
DependentNameTypes.FindNodeOrInsertPos(ID, InsertPos))
57595783
return QualType(T, 0);
57605784

5785+
ElaboratedTypeKeyword CanonKeyword =
5786+
getCanonicalElaboratedTypeKeyword(Keyword);
5787+
NestedNameSpecifier *CanonNNS = getCanonicalNestedNameSpecifier(NNS);
5788+
57615789
QualType Canon;
5762-
if (NestedNameSpecifier *CanonNNS = getCanonicalNestedNameSpecifier(NNS);
5763-
CanonNNS != NNS) {
5764-
Canon = getDependentNameType(Keyword, CanonNNS, Name);
5790+
if (CanonKeyword != Keyword || CanonNNS != NNS) {
5791+
Canon = getDependentNameType(CanonKeyword, CanonNNS, Name);
57655792
[[maybe_unused]] DependentNameType *T =
57665793
DependentNameTypes.FindNodeOrInsertPos(ID, InsertPos);
57675794
assert(!T && "broken canonicalization");
@@ -5800,27 +5827,27 @@ QualType ASTContext::getDependentTemplateSpecializationType(
58005827

58015828
QualType Canon;
58025829
if (!IsCanonical) {
5803-
ElaboratedTypeKeyword CanonKeyword = Keyword != ElaboratedTypeKeyword::None
5804-
? Keyword
5805-
: ElaboratedTypeKeyword::Typename;
5830+
ElaboratedTypeKeyword CanonKeyword =
5831+
getCanonicalElaboratedTypeKeyword(Keyword);
58065832
NestedNameSpecifier *CanonNNS = getCanonicalNestedNameSpecifier(NNS);
58075833
bool AnyNonCanonArgs = false;
58085834
auto CanonArgs =
58095835
::getCanonicalTemplateArguments(*this, Args, AnyNonCanonArgs);
58105836

5811-
if (AnyNonCanonArgs || CanonNNS != NNS || !Name.hasTemplateKeyword() ||
5812-
CanonKeyword != Keyword) {
5837+
if (CanonKeyword != Keyword || AnyNonCanonArgs || CanonNNS != NNS ||
5838+
!Name.hasTemplateKeyword()) {
58135839
Canon = getDependentTemplateSpecializationType(
58145840
CanonKeyword, {CanonNNS, Name.getName(), /*HasTemplateKeyword=*/true},
5815-
CanonArgs, /*IsCanonical=*/true);
5841+
CanonArgs,
5842+
/*IsCanonical=*/true);
58165843
// Find the insert position again.
58175844
[[maybe_unused]] auto *Nothing =
58185845
DependentTemplateSpecializationTypes.FindNodeOrInsertPos(ID,
58195846
InsertPos);
58205847
assert(!Nothing && "canonical type broken");
58215848
}
58225849
} else {
5823-
assert(Keyword != ElaboratedTypeKeyword::None);
5850+
assert(Keyword == getCanonicalElaboratedTypeKeyword(Keyword));
58245851
assert(Name.hasTemplateKeyword());
58255852
assert(NNS == getCanonicalNestedNameSpecifier(NNS));
58265853
#ifndef NDEBUG
@@ -7657,7 +7684,7 @@ ASTContext::getCanonicalNestedNameSpecifier(NestedNameSpecifier *NNS) const {
76577684
if (const auto *DTST = T->getAs<DependentTemplateSpecializationType>()) {
76587685
const DependentTemplateStorage &DTN = DTST->getDependentTemplateName();
76597686
QualType NewT = getDependentTemplateSpecializationType(
7660-
ElaboratedTypeKeyword::Typename,
7687+
ElaboratedTypeKeyword::None,
76617688
{/*NNS=*/nullptr, DTN.getName(), /*HasTemplateKeyword=*/true},
76627689
DTST->template_arguments(), /*IsCanonical=*/true);
76637690
assert(NewT.isCanonical());

clang/lib/AST/Type.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1938,6 +1938,14 @@ TagDecl *Type::getAsTagDecl() const {
19381938
return nullptr;
19391939
}
19401940

1941+
const TemplateSpecializationType *
1942+
Type::getAsNonAliasTemplateSpecializationType() const {
1943+
const auto *TST = getAs<TemplateSpecializationType>();
1944+
while (TST && TST->isTypeAlias())
1945+
TST = TST->desugar()->getAs<TemplateSpecializationType>();
1946+
return TST;
1947+
}
1948+
19411949
bool Type::hasAttr(attr::Kind AK) const {
19421950
const Type *Cur = this;
19431951
while (const auto *AT = Cur->getAs<AttributedType>()) {

clang/lib/AST/TypeLoc.cpp

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -546,37 +546,47 @@ void UnaryTransformTypeLoc::initializeLocal(ASTContext &Context,
546546
Context.getTrivialTypeSourceInfo(getTypePtr()->getBaseType(), Loc));
547547
}
548548

549+
template <class TL>
550+
static void initializeElaboratedKeyword(TL T, SourceLocation Loc) {
551+
T.setElaboratedKeywordLoc(T.getTypePtr()->getKeyword() !=
552+
ElaboratedTypeKeyword::None
553+
? Loc
554+
: SourceLocation());
555+
}
556+
557+
static NestedNameSpecifierLoc
558+
initializeQualifier(ASTContext &Context, NestedNameSpecifier *Qualifier,
559+
SourceLocation Loc) {
560+
if (!Qualifier)
561+
return NestedNameSpecifierLoc();
562+
NestedNameSpecifierLocBuilder Builder;
563+
Builder.MakeTrivial(Context, Qualifier, Loc);
564+
return Builder.getWithLocInContext(Context);
565+
}
566+
549567
void ElaboratedTypeLoc::initializeLocal(ASTContext &Context,
550568
SourceLocation Loc) {
551569
if (isEmpty())
552570
return;
553-
setElaboratedKeywordLoc(Loc);
554-
NestedNameSpecifierLocBuilder Builder;
555-
Builder.MakeTrivial(Context, getTypePtr()->getQualifier(), Loc);
556-
setQualifierLoc(Builder.getWithLocInContext(Context));
571+
initializeElaboratedKeyword(*this, Loc);
572+
setQualifierLoc(
573+
initializeQualifier(Context, getTypePtr()->getQualifier(), Loc));
557574
}
558575

559576
void DependentNameTypeLoc::initializeLocal(ASTContext &Context,
560577
SourceLocation Loc) {
561-
setElaboratedKeywordLoc(Loc);
562-
NestedNameSpecifierLocBuilder Builder;
563-
Builder.MakeTrivial(Context, getTypePtr()->getQualifier(), Loc);
564-
setQualifierLoc(Builder.getWithLocInContext(Context));
578+
initializeElaboratedKeyword(*this, Loc);
579+
setQualifierLoc(
580+
initializeQualifier(Context, getTypePtr()->getQualifier(), Loc));
565581
setNameLoc(Loc);
566582
}
567583

568584
void
569585
DependentTemplateSpecializationTypeLoc::initializeLocal(ASTContext &Context,
570586
SourceLocation Loc) {
571-
setElaboratedKeywordLoc(Loc);
572-
if (NestedNameSpecifier *Qualifier =
573-
getTypePtr()->getDependentTemplateName().getQualifier()) {
574-
NestedNameSpecifierLocBuilder Builder;
575-
Builder.MakeTrivial(Context, Qualifier, Loc);
576-
setQualifierLoc(Builder.getWithLocInContext(Context));
577-
} else {
578-
setQualifierLoc(NestedNameSpecifierLoc());
579-
}
587+
initializeElaboratedKeyword(*this, Loc);
588+
setQualifierLoc(initializeQualifier(
589+
Context, getTypePtr()->getDependentTemplateName().getQualifier(), Loc));
580590
setTemplateKeywordLoc(Loc);
581591
setTemplateNameLoc(Loc);
582592
setLAngleLoc(Loc);

clang/lib/Sema/SemaDecl.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -247,15 +247,15 @@ static ParsedType recoverFromTypeInKnownDependentBase(Sema &S,
247247
return nullptr;
248248

249249
// We found some types in dependent base classes. Recover as if the user
250-
// wrote 'typename MyClass::II' instead of 'II'. We'll fully resolve the
251-
// lookup during template instantiation.
250+
// wrote 'MyClass::II' instead of 'II', and this implicit typename was
251+
// allowed. We'll fully resolve the lookup during template instantiation.
252252
S.Diag(NameLoc, diag::ext_found_in_dependent_base) << &II;
253253

254254
ASTContext &Context = S.Context;
255255
auto *NNS = NestedNameSpecifier::Create(
256256
Context, nullptr, cast<Type>(Context.getRecordType(RD)));
257257
QualType T =
258-
Context.getDependentNameType(ElaboratedTypeKeyword::Typename, NNS, &II);
258+
Context.getDependentNameType(ElaboratedTypeKeyword::None, NNS, &II);
259259

260260
CXXScopeSpec SS;
261261
SS.MakeTrivial(Context, NNS, SourceRange(NameLoc));

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12154,33 +12154,31 @@ static bool isStdClassTemplate(Sema &S, QualType SugaredType, QualType *TypeArg,
1215412154
};
1215512155

1215612156
ClassTemplateDecl *Template = nullptr;
12157-
const TemplateArgument *Arguments = nullptr;
12158-
12159-
QualType Ty = S.Context.getCanonicalType(SugaredType);
12160-
if (const RecordType *RT = Ty->getAs<RecordType>()) {
12161-
ClassTemplateSpecializationDecl *Specialization =
12162-
dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl());
12163-
if (!Specialization) {
12164-
ReportMatchingNameAsMalformed(RT->getDecl());
12165-
return false;
12166-
}
12167-
12168-
Template = Specialization->getSpecializedTemplate();
12169-
Arguments = Specialization->getTemplateArgs().data();
12170-
} else {
12171-
const TemplateSpecializationType *TST = nullptr;
12172-
if (auto *ICN = Ty->getAs<InjectedClassNameType>())
12173-
TST = ICN->getInjectedTST();
12174-
else
12175-
TST = Ty->getAs<TemplateSpecializationType>();
12157+
ArrayRef<TemplateArgument> Arguments;
12158+
{
12159+
const TemplateSpecializationType *TST =
12160+
SugaredType->getAsNonAliasTemplateSpecializationType();
12161+
if (!TST)
12162+
if (const auto *ICN = SugaredType->getAs<InjectedClassNameType>())
12163+
TST = ICN->getInjectedTST();
1217612164
if (TST) {
1217712165
Template = dyn_cast_or_null<ClassTemplateDecl>(
1217812166
TST->getTemplateName().getAsTemplateDecl());
12179-
Arguments = TST->template_arguments().begin();
12167+
Arguments = TST->template_arguments();
12168+
} else if (const RecordType *RT = SugaredType->getAs<RecordType>()) {
12169+
ClassTemplateSpecializationDecl *Specialization =
12170+
dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl());
12171+
if (!Specialization) {
12172+
ReportMatchingNameAsMalformed(RT->getDecl());
12173+
return false;
12174+
}
12175+
Template = Specialization->getSpecializedTemplate();
12176+
Arguments = Specialization->getTemplateArgs().asArray();
1218012177
}
1218112178
}
12179+
1218212180
if (!Template) {
12183-
ReportMatchingNameAsMalformed(Ty->getAsTagDecl());
12181+
ReportMatchingNameAsMalformed(SugaredType->getAsTagDecl());
1218412182
return false;
1218512183
}
1218612184

@@ -12200,7 +12198,8 @@ static bool isStdClassTemplate(Sema &S, QualType SugaredType, QualType *TypeArg,
1220012198
// template?
1220112199
TemplateParameterList *Params = Template->getTemplateParameters();
1220212200
if (Params->getMinRequiredArguments() != 1 ||
12203-
!isa<TemplateTypeParmDecl>(Params->getParam(0))) {
12201+
!isa<TemplateTypeParmDecl>(Params->getParam(0)) ||
12202+
Params->getParam(0)->isTemplateParameterPack()) {
1220412203
if (MalformedDecl)
1220512204
*MalformedDecl = TemplateClass;
1220612205
return false;
@@ -12214,8 +12213,21 @@ static bool isStdClassTemplate(Sema &S, QualType SugaredType, QualType *TypeArg,
1221412213
return false;
1221512214

1221612215
// This is an instance of std::{ClassName}. Find the argument type.
12217-
if (TypeArg)
12218-
*TypeArg = Arguments[0].getAsType();
12216+
if (TypeArg) {
12217+
QualType ArgType = Arguments[0].getAsType();
12218+
// FIXME: Since TST only has as-written arguments, we have to perform the
12219+
// only kind of conversion applicable to type arguments; in Objective-C ARC:
12220+
// - If an explicitly-specified template argument type is a lifetime type
12221+
// with no lifetime qualifier, the __strong lifetime qualifier is
12222+
// inferred.
12223+
if (S.getLangOpts().ObjCAutoRefCount && ArgType->isObjCLifetimeType() &&
12224+
!ArgType.getObjCLifetime()) {
12225+
Qualifiers Qs;
12226+
Qs.setObjCLifetime(Qualifiers::OCL_Strong);
12227+
ArgType = S.Context.getQualifiedType(ArgType, Qs);
12228+
}
12229+
*TypeArg = ArgType;
12230+
}
1221912231

1222012232
return true;
1222112233
}

clang/lib/Sema/SemaInit.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9897,7 +9897,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
98979897

98989898
auto TemplateName = DeducedTST->getTemplateName();
98999899
if (TemplateName.isDependent())
9900-
return SubstAutoTypeDependent(TSInfo->getType());
9900+
return SubstAutoTypeSourceInfoDependent(TSInfo)->getType();
99019901

99029902
// We can only perform deduction for class templates or alias templates.
99039903
auto *Template =
@@ -9942,7 +9942,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
99429942
Diag(TSInfo->getTypeLoc().getBeginLoc(),
99439943
diag::warn_cxx14_compat_class_template_argument_deduction)
99449944
<< TSInfo->getTypeLoc().getSourceRange() << 0;
9945-
return SubstAutoTypeDependent(TSInfo->getType());
9945+
return SubstAutoTypeSourceInfoDependent(TSInfo)->getType();
99469946
}
99479947

99489948
// FIXME: Perform "exact type" matching first, per CWG discussion?
@@ -10253,7 +10253,8 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
1025310253
// The placeholder is replaced by the return type of the function selected
1025410254
// by overload resolution for class template deduction.
1025510255
QualType DeducedType =
10256-
SubstAutoType(TSInfo->getType(), Best->Function->getReturnType());
10256+
SubstAutoTypeSourceInfo(TSInfo, Best->Function->getReturnType())
10257+
->getType();
1025710258
Diag(TSInfo->getTypeLoc().getBeginLoc(),
1025810259
diag::warn_cxx14_compat_class_template_argument_deduction)
1025910260
<< TSInfo->getTypeLoc().getSourceRange() << 1 << DeducedType;

0 commit comments

Comments
 (0)