[clang] fe94f11 - [clang] Fix elaborated keyword canonicalization (#135916)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 16 12:27:27 PDT 2025
Author: Matheus Izvekov
Date: 2025-04-16T16:27:24-03:00
New Revision: fe94f11407453c2d166597ef6e58d31f5b27d46e
URL: https://github.com/llvm/llvm-project/commit/fe94f11407453c2d166597ef6e58d31f5b27d46e
DIFF: https://github.com/llvm/llvm-project/commit/fe94f11407453c2d166597ef6e58d31f5b27d46e.diff
LOG: [clang] Fix elaborated keyword canonicalization (#135916)
Added:
Modified:
clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp
clang/docs/ReleaseNotes.rst
clang/include/clang/AST/Type.h
clang/lib/AST/ASTContext.cpp
clang/lib/AST/Type.cpp
clang/lib/AST/TypeLoc.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaInit.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/test/Analysis/anonymous-decls.cpp
clang/test/CXX/drs/cwg23xx.cpp
clang/test/SemaTemplate/dependent-template-recover.cpp
clang/test/SemaTemplate/elaborated-type-specifier.cpp
clang/test/SemaTemplate/typename-specifier-3.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp
index fb82efb4dd211..6040cddf0e52a 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp
@@ -54,8 +54,10 @@ static std::optional<TemplateSpecializationTypeLoc>
matchEnableIfSpecializationImplTypename(TypeLoc TheType) {
if (const auto Dep = TheType.getAs<DependentNameTypeLoc>()) {
const IdentifierInfo *Identifier = Dep.getTypePtr()->getIdentifier();
+ ElaboratedTypeKeyword Keyword = Dep.getTypePtr()->getKeyword();
if (!Identifier || Identifier->getName() != "type" ||
- Dep.getTypePtr()->getKeyword() != ElaboratedTypeKeyword::Typename) {
+ (Keyword != ElaboratedTypeKeyword::Typename &&
+ Keyword != ElaboratedTypeKeyword::None)) {
return std::nullopt;
}
TheType = Dep.getQualifierLoc().getTypeLoc();
@@ -108,8 +110,10 @@ matchEnableIfSpecializationImplTrait(TypeLoc TheType) {
if (const auto *AliasedType =
dyn_cast<DependentNameType>(Specialization->getAliasedType())) {
+ ElaboratedTypeKeyword Keyword = AliasedType->getKeyword();
if (AliasedType->getIdentifier()->getName() != "type" ||
- AliasedType->getKeyword() != ElaboratedTypeKeyword::Typename) {
+ (Keyword != ElaboratedTypeKeyword::Typename &&
+ Keyword != ElaboratedTypeKeyword::None)) {
return std::nullopt;
}
} else {
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0891fd058bb57..07ff1251fc1ad 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -486,6 +486,10 @@ Bug Fixes to C++ Support
- Fixes matching of nested template template parameters. (#GH130362)
- Correctly diagnoses template template paramters which have a pack parameter
not in the last position.
+- Disallow overloading on struct vs class on dependent types, which is IFNDR, as
+ this makes the problem diagnosable.
+- Improved preservation of the presence or abscence of typename specifier when
+ printing types in diagnostics.
- Clang now correctly parses ``if constexpr`` expressions in immediate function context. (#GH123524)
- Fixed an assertion failure affecting code that uses C++23 "deducing this". (#GH130272)
- Clang now properly instantiates destructors for initialized members within non-delegating constructors. (#GH93251)
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 5bf036e3347eb..1ecd64539e2de 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2838,6 +2838,20 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
/// immediately following this class.
template <typename T> const T *getAs() const;
+ /// Look through sugar for an instance of TemplateSpecializationType which
+ /// is not a type alias, or null if there is no such type.
+ /// This is used when you want as-written template arguments or the template
+ /// name for a class template specialization.
+ const TemplateSpecializationType *
+ getAsNonAliasTemplateSpecializationType() const;
+
+ const TemplateSpecializationType *
+ castAsNonAliasTemplateSpecializationType() const {
+ const auto *TST = getAsNonAliasTemplateSpecializationType();
+ assert(TST && "not a TemplateSpecializationType");
+ return TST;
+ }
+
/// Member-template getAsAdjusted<specific type>. Look through specific kinds
/// of sugar (parens, attributes, etc) for an instance of \<specific type>.
/// This is used when you need to walk over sugar nodes that represent some
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index c6ffe7bbf5257..bf24704e48eaa 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -5747,6 +5747,30 @@ ASTContext::getMacroQualifiedType(QualType UnderlyingTy,
return QualType(newType, 0);
}
+static ElaboratedTypeKeyword
+getCanonicalElaboratedTypeKeyword(ElaboratedTypeKeyword Keyword) {
+ switch (Keyword) {
+ // These are just themselves.
+ case ElaboratedTypeKeyword::None:
+ case ElaboratedTypeKeyword::Struct:
+ case ElaboratedTypeKeyword::Union:
+ case ElaboratedTypeKeyword::Enum:
+ case ElaboratedTypeKeyword::Interface:
+ return Keyword;
+
+ // These are equivalent.
+ case ElaboratedTypeKeyword::Typename:
+ return ElaboratedTypeKeyword::None;
+
+ // These are functionally equivalent, so relying on their equivalence is
+ // IFNDR. By making them equivalent, we disallow overloading, which at least
+ // can produce a diagnostic.
+ case ElaboratedTypeKeyword::Class:
+ return ElaboratedTypeKeyword::Struct;
+ }
+ llvm_unreachable("unexpected keyword kind");
+}
+
QualType ASTContext::getDependentNameType(ElaboratedTypeKeyword Keyword,
NestedNameSpecifier *NNS,
const IdentifierInfo *Name) const {
@@ -5758,10 +5782,13 @@ QualType ASTContext::getDependentNameType(ElaboratedTypeKeyword Keyword,
DependentNameTypes.FindNodeOrInsertPos(ID, InsertPos))
return QualType(T, 0);
+ ElaboratedTypeKeyword CanonKeyword =
+ getCanonicalElaboratedTypeKeyword(Keyword);
+ NestedNameSpecifier *CanonNNS = getCanonicalNestedNameSpecifier(NNS);
+
QualType Canon;
- if (NestedNameSpecifier *CanonNNS = getCanonicalNestedNameSpecifier(NNS);
- CanonNNS != NNS) {
- Canon = getDependentNameType(Keyword, CanonNNS, Name);
+ if (CanonKeyword != Keyword || CanonNNS != NNS) {
+ Canon = getDependentNameType(CanonKeyword, CanonNNS, Name);
[[maybe_unused]] DependentNameType *T =
DependentNameTypes.FindNodeOrInsertPos(ID, InsertPos);
assert(!T && "broken canonicalization");
@@ -5800,19 +5827,19 @@ QualType ASTContext::getDependentTemplateSpecializationType(
QualType Canon;
if (!IsCanonical) {
- ElaboratedTypeKeyword CanonKeyword = Keyword != ElaboratedTypeKeyword::None
- ? Keyword
- : ElaboratedTypeKeyword::Typename;
+ ElaboratedTypeKeyword CanonKeyword =
+ getCanonicalElaboratedTypeKeyword(Keyword);
NestedNameSpecifier *CanonNNS = getCanonicalNestedNameSpecifier(NNS);
bool AnyNonCanonArgs = false;
auto CanonArgs =
::getCanonicalTemplateArguments(*this, Args, AnyNonCanonArgs);
- if (AnyNonCanonArgs || CanonNNS != NNS || !Name.hasTemplateKeyword() ||
- CanonKeyword != Keyword) {
+ if (CanonKeyword != Keyword || AnyNonCanonArgs || CanonNNS != NNS ||
+ !Name.hasTemplateKeyword()) {
Canon = getDependentTemplateSpecializationType(
CanonKeyword, {CanonNNS, Name.getName(), /*HasTemplateKeyword=*/true},
- CanonArgs, /*IsCanonical=*/true);
+ CanonArgs,
+ /*IsCanonical=*/true);
// Find the insert position again.
[[maybe_unused]] auto *Nothing =
DependentTemplateSpecializationTypes.FindNodeOrInsertPos(ID,
@@ -5820,7 +5847,7 @@ QualType ASTContext::getDependentTemplateSpecializationType(
assert(!Nothing && "canonical type broken");
}
} else {
- assert(Keyword != ElaboratedTypeKeyword::None);
+ assert(Keyword == getCanonicalElaboratedTypeKeyword(Keyword));
assert(Name.hasTemplateKeyword());
assert(NNS == getCanonicalNestedNameSpecifier(NNS));
#ifndef NDEBUG
@@ -7657,7 +7684,7 @@ ASTContext::getCanonicalNestedNameSpecifier(NestedNameSpecifier *NNS) const {
if (const auto *DTST = T->getAs<DependentTemplateSpecializationType>()) {
const DependentTemplateStorage &DTN = DTST->getDependentTemplateName();
QualType NewT = getDependentTemplateSpecializationType(
- ElaboratedTypeKeyword::Typename,
+ ElaboratedTypeKeyword::None,
{/*NNS=*/nullptr, DTN.getName(), /*HasTemplateKeyword=*/true},
DTST->template_arguments(), /*IsCanonical=*/true);
assert(NewT.isCanonical());
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 53620003c9655..42e94d66d1a13 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -1938,6 +1938,14 @@ TagDecl *Type::getAsTagDecl() const {
return nullptr;
}
+const TemplateSpecializationType *
+Type::getAsNonAliasTemplateSpecializationType() const {
+ const auto *TST = getAs<TemplateSpecializationType>();
+ while (TST && TST->isTypeAlias())
+ TST = TST->desugar()->getAs<TemplateSpecializationType>();
+ return TST;
+}
+
bool Type::hasAttr(attr::Kind AK) const {
const Type *Cur = this;
while (const auto *AT = Cur->getAs<AttributedType>()) {
diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp
index 24726901b8f55..3d1b5ca966b66 100644
--- a/clang/lib/AST/TypeLoc.cpp
+++ b/clang/lib/AST/TypeLoc.cpp
@@ -546,37 +546,47 @@ void UnaryTransformTypeLoc::initializeLocal(ASTContext &Context,
Context.getTrivialTypeSourceInfo(getTypePtr()->getBaseType(), Loc));
}
+template <class TL>
+static void initializeElaboratedKeyword(TL T, SourceLocation Loc) {
+ T.setElaboratedKeywordLoc(T.getTypePtr()->getKeyword() !=
+ ElaboratedTypeKeyword::None
+ ? Loc
+ : SourceLocation());
+}
+
+static NestedNameSpecifierLoc
+initializeQualifier(ASTContext &Context, NestedNameSpecifier *Qualifier,
+ SourceLocation Loc) {
+ if (!Qualifier)
+ return NestedNameSpecifierLoc();
+ NestedNameSpecifierLocBuilder Builder;
+ Builder.MakeTrivial(Context, Qualifier, Loc);
+ return Builder.getWithLocInContext(Context);
+}
+
void ElaboratedTypeLoc::initializeLocal(ASTContext &Context,
SourceLocation Loc) {
if (isEmpty())
return;
- setElaboratedKeywordLoc(Loc);
- NestedNameSpecifierLocBuilder Builder;
- Builder.MakeTrivial(Context, getTypePtr()->getQualifier(), Loc);
- setQualifierLoc(Builder.getWithLocInContext(Context));
+ initializeElaboratedKeyword(*this, Loc);
+ setQualifierLoc(
+ initializeQualifier(Context, getTypePtr()->getQualifier(), Loc));
}
void DependentNameTypeLoc::initializeLocal(ASTContext &Context,
SourceLocation Loc) {
- setElaboratedKeywordLoc(Loc);
- NestedNameSpecifierLocBuilder Builder;
- Builder.MakeTrivial(Context, getTypePtr()->getQualifier(), Loc);
- setQualifierLoc(Builder.getWithLocInContext(Context));
+ initializeElaboratedKeyword(*this, Loc);
+ setQualifierLoc(
+ initializeQualifier(Context, getTypePtr()->getQualifier(), Loc));
setNameLoc(Loc);
}
void
DependentTemplateSpecializationTypeLoc::initializeLocal(ASTContext &Context,
SourceLocation Loc) {
- setElaboratedKeywordLoc(Loc);
- if (NestedNameSpecifier *Qualifier =
- getTypePtr()->getDependentTemplateName().getQualifier()) {
- NestedNameSpecifierLocBuilder Builder;
- Builder.MakeTrivial(Context, Qualifier, Loc);
- setQualifierLoc(Builder.getWithLocInContext(Context));
- } else {
- setQualifierLoc(NestedNameSpecifierLoc());
- }
+ initializeElaboratedKeyword(*this, Loc);
+ setQualifierLoc(initializeQualifier(
+ Context, getTypePtr()->getDependentTemplateName().getQualifier(), Loc));
setTemplateKeywordLoc(Loc);
setTemplateNameLoc(Loc);
setLAngleLoc(Loc);
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 127c0a4500a43..46933c5c43168 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -247,15 +247,15 @@ static ParsedType recoverFromTypeInKnownDependentBase(Sema &S,
return nullptr;
// We found some types in dependent base classes. Recover as if the user
- // wrote 'typename MyClass::II' instead of 'II'. We'll fully resolve the
- // lookup during template instantiation.
+ // wrote 'MyClass::II' instead of 'II', and this implicit typename was
+ // allowed. We'll fully resolve the lookup during template instantiation.
S.Diag(NameLoc, diag::ext_found_in_dependent_base) << &II;
ASTContext &Context = S.Context;
auto *NNS = NestedNameSpecifier::Create(
Context, nullptr, cast<Type>(Context.getRecordType(RD)));
QualType T =
- Context.getDependentNameType(ElaboratedTypeKeyword::Typename, NNS, &II);
+ Context.getDependentNameType(ElaboratedTypeKeyword::None, NNS, &II);
CXXScopeSpec SS;
SS.MakeTrivial(Context, NNS, SourceRange(NameLoc));
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 05991228dbfc2..d4e48a14d13c2 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -12154,33 +12154,31 @@ static bool isStdClassTemplate(Sema &S, QualType SugaredType, QualType *TypeArg,
};
ClassTemplateDecl *Template = nullptr;
- const TemplateArgument *Arguments = nullptr;
-
- QualType Ty = S.Context.getCanonicalType(SugaredType);
- if (const RecordType *RT = Ty->getAs<RecordType>()) {
- ClassTemplateSpecializationDecl *Specialization =
- dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl());
- if (!Specialization) {
- ReportMatchingNameAsMalformed(RT->getDecl());
- return false;
- }
-
- Template = Specialization->getSpecializedTemplate();
- Arguments = Specialization->getTemplateArgs().data();
- } else {
- const TemplateSpecializationType *TST = nullptr;
- if (auto *ICN = Ty->getAs<InjectedClassNameType>())
- TST = ICN->getInjectedTST();
- else
- TST = Ty->getAs<TemplateSpecializationType>();
+ ArrayRef<TemplateArgument> Arguments;
+ {
+ const TemplateSpecializationType *TST =
+ SugaredType->getAsNonAliasTemplateSpecializationType();
+ if (!TST)
+ if (const auto *ICN = SugaredType->getAs<InjectedClassNameType>())
+ TST = ICN->getInjectedTST();
if (TST) {
Template = dyn_cast_or_null<ClassTemplateDecl>(
TST->getTemplateName().getAsTemplateDecl());
- Arguments = TST->template_arguments().begin();
+ Arguments = TST->template_arguments();
+ } else if (const RecordType *RT = SugaredType->getAs<RecordType>()) {
+ ClassTemplateSpecializationDecl *Specialization =
+ dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl());
+ if (!Specialization) {
+ ReportMatchingNameAsMalformed(RT->getDecl());
+ return false;
+ }
+ Template = Specialization->getSpecializedTemplate();
+ Arguments = Specialization->getTemplateArgs().asArray();
}
}
+
if (!Template) {
- ReportMatchingNameAsMalformed(Ty->getAsTagDecl());
+ ReportMatchingNameAsMalformed(SugaredType->getAsTagDecl());
return false;
}
@@ -12200,7 +12198,8 @@ static bool isStdClassTemplate(Sema &S, QualType SugaredType, QualType *TypeArg,
// template?
TemplateParameterList *Params = Template->getTemplateParameters();
if (Params->getMinRequiredArguments() != 1 ||
- !isa<TemplateTypeParmDecl>(Params->getParam(0))) {
+ !isa<TemplateTypeParmDecl>(Params->getParam(0)) ||
+ Params->getParam(0)->isTemplateParameterPack()) {
if (MalformedDecl)
*MalformedDecl = TemplateClass;
return false;
@@ -12214,8 +12213,21 @@ static bool isStdClassTemplate(Sema &S, QualType SugaredType, QualType *TypeArg,
return false;
// This is an instance of std::{ClassName}. Find the argument type.
- if (TypeArg)
- *TypeArg = Arguments[0].getAsType();
+ if (TypeArg) {
+ QualType ArgType = Arguments[0].getAsType();
+ // FIXME: Since TST only has as-written arguments, we have to perform the
+ // only kind of conversion applicable to type arguments; in Objective-C ARC:
+ // - If an explicitly-specified template argument type is a lifetime type
+ // with no lifetime qualifier, the __strong lifetime qualifier is
+ // inferred.
+ if (S.getLangOpts().ObjCAutoRefCount && ArgType->isObjCLifetimeType() &&
+ !ArgType.getObjCLifetime()) {
+ Qualifiers Qs;
+ Qs.setObjCLifetime(Qualifiers::OCL_Strong);
+ ArgType = S.Context.getQualifiedType(ArgType, Qs);
+ }
+ *TypeArg = ArgType;
+ }
return true;
}
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index a1e4bb4321d53..77d7f821f2011 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -9897,7 +9897,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
auto TemplateName = DeducedTST->getTemplateName();
if (TemplateName.isDependent())
- return SubstAutoTypeDependent(TSInfo->getType());
+ return SubstAutoTypeSourceInfoDependent(TSInfo)->getType();
// We can only perform deduction for class templates or alias templates.
auto *Template =
@@ -9942,7 +9942,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
Diag(TSInfo->getTypeLoc().getBeginLoc(),
diag::warn_cxx14_compat_class_template_argument_deduction)
<< TSInfo->getTypeLoc().getSourceRange() << 0;
- return SubstAutoTypeDependent(TSInfo->getType());
+ return SubstAutoTypeSourceInfoDependent(TSInfo)->getType();
}
// FIXME: Perform "exact type" matching first, per CWG discussion?
@@ -10253,7 +10253,8 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
// The placeholder is replaced by the return type of the function selected
// by overload resolution for class template deduction.
QualType DeducedType =
- SubstAutoType(TSInfo->getType(), Best->Function->getReturnType());
+ SubstAutoTypeSourceInfo(TSInfo, Best->Function->getReturnType())
+ ->getType();
Diag(TSInfo->getTypeLoc().getBeginLoc(),
diag::warn_cxx14_compat_class_template_argument_deduction)
<< TSInfo->getTypeLoc().getSourceRange() << 1 << DeducedType;
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 6b7892fa30989..894f072d84989 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4892,7 +4892,7 @@ bool Sema::CheckTemplateTypeArgument(
// Recover by synthesizing a type using the location information that we
// already have.
- ArgType = Context.getDependentNameType(ElaboratedTypeKeyword::Typename,
+ ArgType = Context.getDependentNameType(ElaboratedTypeKeyword::None,
SS.getScopeRep(), II);
TypeLocBuilder TLB;
DependentNameTypeLoc TL = TLB.push<DependentNameTypeLoc>(ArgType);
@@ -10672,10 +10672,8 @@ TypeResult Sema::ActOnTypenameType(Scope *S, SourceLocation TypenameLoc,
NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
TypeSourceInfo *TSI = nullptr;
QualType T =
- CheckTypenameType((TypenameLoc.isValid() ||
- IsImplicitTypename == ImplicitTypenameContext::Yes)
- ? ElaboratedTypeKeyword::Typename
- : ElaboratedTypeKeyword::None,
+ CheckTypenameType(TypenameLoc.isValid() ? ElaboratedTypeKeyword::Typename
+ : ElaboratedTypeKeyword::None,
TypenameLoc, QualifierLoc, II, IdLoc, &TSI,
/*DeducedTSTContext=*/true);
if (T.isNull())
@@ -10713,6 +10711,9 @@ Sema::ActOnTypenameType(Scope *S, SourceLocation TypenameLoc,
TemplateArgumentListInfo TemplateArgs(LAngleLoc, RAngleLoc);
translateTemplateArguments(TemplateArgsIn, TemplateArgs);
+ auto Keyword = TypenameLoc.isValid() ? ElaboratedTypeKeyword::Typename
+ : ElaboratedTypeKeyword::None;
+
TemplateName Template = TemplateIn.get();
if (DependentTemplateName *DTN = Template.getAsDependentTemplateName()) {
// Construct a dependent template specialization type.
@@ -10726,7 +10727,7 @@ Sema::ActOnTypenameType(Scope *S, SourceLocation TypenameLoc,
}
QualType T = Context.getDependentTemplateSpecializationType(
- ElaboratedTypeKeyword::Typename, *DTN, TemplateArgs.arguments());
+ Keyword, *DTN, TemplateArgs.arguments());
// Create source-location information for this type.
TypeLocBuilder Builder;
@@ -10758,8 +10759,7 @@ Sema::ActOnTypenameType(Scope *S, SourceLocation TypenameLoc,
for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I)
SpecTL.setArgLocInfo(I, TemplateArgs[I].getLocInfo());
- T = Context.getElaboratedType(ElaboratedTypeKeyword::Typename,
- SS.getScopeRep(), T);
+ T = Context.getElaboratedType(Keyword, SS.getScopeRep(), T);
ElaboratedTypeLoc TL = Builder.push<ElaboratedTypeLoc>(T);
TL.setElaboratedKeywordLoc(TypenameLoc);
TL.setQualifierLoc(SS.getWithLocInContext(Context));
@@ -10853,6 +10853,8 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
NestedNameSpecifierLoc QualifierLoc,
const IdentifierInfo &II,
SourceLocation IILoc, bool DeducedTSTContext) {
+ assert((Keyword != ElaboratedTypeKeyword::None) == KeywordLoc.isValid());
+
CXXScopeSpec SS;
SS.Adopt(QualifierLoc);
diff --git a/clang/test/Analysis/anonymous-decls.cpp b/clang/test/Analysis/anonymous-decls.cpp
index 211184523aa51..85449caa46972 100644
--- a/clang/test/Analysis/anonymous-decls.cpp
+++ b/clang/test/Analysis/anonymous-decls.cpp
@@ -74,13 +74,13 @@ int main() {
// CHECK-NEXT: 4: * [B3.3] (OperatorCall)
// CHECK-NEXT: 5: auto &;
// CHECK-NEXT: 6: get<0UL>
-// CHECK-NEXT: 7: [B3.6] (ImplicitCastExpr, FunctionToPointerDecay, typename tuple_element<0L, pair<int, int> >::type (*)(pair<int, int> &))
+// CHECK-NEXT: 7: [B3.6] (ImplicitCastExpr, FunctionToPointerDecay, tuple_element<0L, pair<int, int> >::type (*)(pair<int, int> &))
// CHECK-NEXT: 8: decomposition-a-b
// CHECK-NEXT: 9: [B3.7]([B3.8])
// CHECK-NEXT: 10: [B3.9]
// CHECK-NEXT: 11: std::tuple_element<0, std::pair<int, int>>::type a = get<0UL>(decomposition-a-b);
// CHECK-NEXT: 12: get<1UL>
-// CHECK-NEXT: 13: [B3.12] (ImplicitCastExpr, FunctionToPointerDecay, typename tuple_element<1L, pair<int, int> >::type (*)(pair<int, int> &))
+// CHECK-NEXT: 13: [B3.12] (ImplicitCastExpr, FunctionToPointerDecay, tuple_element<1L, pair<int, int> >::type (*)(pair<int, int> &))
// CHECK-NEXT: 14: decomposition-a-b
// CHECK-NEXT: 15: [B3.13]([B3.14])
// CHECK-NEXT: 16: [B3.15]
diff --git a/clang/test/CXX/drs/cwg23xx.cpp b/clang/test/CXX/drs/cwg23xx.cpp
index 78cecb8b71bca..74e72f2371e2a 100644
--- a/clang/test/CXX/drs/cwg23xx.cpp
+++ b/clang/test/CXX/drs/cwg23xx.cpp
@@ -91,7 +91,7 @@ struct Y {};
struct Z : W,
X, check_derived_from<Z, X>, // #cwg2310-X
check_derived_from<Z, Y>, Y // #cwg2310-Y
-{
+{
// FIXME: It was properly rejected before, but we're crashing since Clang 11 in C++11 and C++14 modes.
// See https://github.com/llvm/llvm-project/issues/59920
#if __cplusplus >= 201703L
@@ -188,7 +188,7 @@ struct InitListCtor {
std::initializer_list<InitListCtor> i;
auto j = std::initializer_list<InitListCtor>{ i };
-// since-cxx17-error at -1 {{conversion function from 'std::initializer_list<InitListCtor>' to 'const cwg2311::InitListCtor' invokes a deleted function}}
+// since-cxx17-error at -1 {{conversion function from 'std::initializer_list<InitListCtor>' to 'const InitListCtor' invokes a deleted function}}
// since-cxx17-note@#cwg2311-InitListCtor {{'InitListCtor' has been explicitly marked deleted here}}
#endif
} // namespace cwg2311
diff --git a/clang/test/SemaTemplate/dependent-template-recover.cpp b/clang/test/SemaTemplate/dependent-template-recover.cpp
index 251a8f9816417..21e6a963719bd 100644
--- a/clang/test/SemaTemplate/dependent-template-recover.cpp
+++ b/clang/test/SemaTemplate/dependent-template-recover.cpp
@@ -146,9 +146,9 @@ namespace templ_spec {
// FIXME: Why error recovery for the non-typename case is so bad?
A<T::template X<int>> t3; // expected-error {{did you forget 'typename'}}
- // expected-error at -1 {{'A<typename T::X>' (aka 'void')}}
+ // expected-error at -1 {{'A<T::X>' (aka 'void')}}
A<T::X<int>> t4; // expected-error {{use 'template' keyword}} expected-error {{did you forget 'typename'}}
- // expected-error at -1 {{'A<typename T::X>' (aka 'void')}}
+ // expected-error at -1 {{'A<T::X>' (aka 'void')}}
};
} // namespace templ_spec
diff --git a/clang/test/SemaTemplate/elaborated-type-specifier.cpp b/clang/test/SemaTemplate/elaborated-type-specifier.cpp
index 27b3f36ee14dd..95c2aa9f60a39 100644
--- a/clang/test/SemaTemplate/elaborated-type-specifier.cpp
+++ b/clang/test/SemaTemplate/elaborated-type-specifier.cpp
@@ -10,7 +10,7 @@ namespace PR6915 {
struct D1 {
enum X { value };
};
- struct D2 {
+ struct D2 {
class X { }; // expected-note{{previous use is here}}
};
struct D3 { };
@@ -25,12 +25,12 @@ struct DeclOrDef {
enum T::foo; // expected-error{{nested name specifier for a declaration cannot depend on a template parameter}}
// expected-error at -1{{forward declaration of enum cannot have a nested name specifier}}
enum T::bar { // expected-error{{nested name specifier for a declaration cannot depend on a template parameter}}
- value
+ value
};
};
namespace PR6649 {
- template <typename T> struct foo {
+ template <typename T> struct foo {
class T::bar; // expected-error{{nested name specifier for a declaration cannot depend on a template parameter}}
// expected-error at -1{{forward declaration of class cannot have a nested name specifier}}
class T::bar { int x; }; // expected-error{{nested name specifier for a declaration cannot depend on a template parameter}}
@@ -40,3 +40,60 @@ namespace PR6649 {
namespace rdar8568507 {
template <class T> struct A *makeA(T t);
}
+
+namespace canon {
+ template <class T> void t1(struct T::X) {}
+ // expected-note at -1 {{previous definition is here}}
+ template <class T> void t1(class T::X) {}
+ // expected-error at -1 {{redefinition of 't1'}}
+
+ template <class T> void t2(struct T::template X<int>) {}
+ // expected-note at -1 {{previous definition is here}}
+ template <class T> void t2(class T::template X<int>) {}
+ // expected-error at -1 {{redefinition of 't2'}}
+
+ template <class T> constexpr int t3(typename T::X* = 0) { return 0; } // #canon-t3-0
+ template <class T> constexpr int t3(struct T::X* = 0) { return 1; } // #canon-t3-1
+ template <class T> constexpr int t3(union T::X* = 0) { return 2; } // #canon-t3-2
+ template <class T> constexpr int t3(enum T::X* = 0) { return 3; } // #canon-t3-3
+
+ struct A { using X = int; };
+ static_assert(t3<A>() == 0);
+
+ struct B { struct X {}; };
+ static_assert(t3<B>() == 1);
+ // expected-error at -1 {{call to 't3' is ambiguous}}
+ // expected-note@#canon-t3-0 {{candidate function}}
+ // expected-note@#canon-t3-1 {{candidate function}}
+
+ struct C { union X {}; };
+ static_assert(t3<C>() == 2);
+ // expected-error at -1 {{call to 't3' is ambiguous}}
+ // expected-note@#canon-t3-0 {{candidate function}}
+ // expected-note@#canon-t3-2 {{candidate function}}
+
+ struct D { enum X {}; };
+ static_assert(t3<D>() == 3);
+ // expected-error at -1 {{call to 't3' is ambiguous}}
+ // expected-note@#canon-t3-0 {{candidate function}}
+ // expected-note@#canon-t3-3 {{candidate function}}
+
+ template <class T> constexpr int t4(typename T::template X<int>* = 0) { return 0; }
+ // expected-note at -1 3{{candidate function}}
+ template <class T> constexpr int t4(struct T::template X<int>* = 0) { return 1; }
+ // expected-note at -1 3{{candidate function}}
+ template <class T> constexpr int t4(union T::template X<int>* = 0) { return 2; }
+ // expected-note at -1 3{{candidate function}}
+
+ // FIXME: This should work.
+ struct E { template <class T> using X = T; };
+ static_assert(t4<E>() == 0); // expected-error {{call to 't4' is ambiguous}}
+
+ // FIXME: Should not match the union overload.
+ struct F { template <class> struct X {}; };
+ static_assert(t4<F>() == 1); // expected-error {{call to 't4' is ambiguous}}
+
+ // FIXME: Should not match the struct overload.
+ struct G { template <class> union X {}; };
+ static_assert(t4<G>() == 2); // expected-error {{call to 't4' is ambiguous}}
+} // namespace canon
diff --git a/clang/test/SemaTemplate/typename-specifier-3.cpp b/clang/test/SemaTemplate/typename-specifier-3.cpp
index 0140b1a479c2d..cdd065c98bb0a 100644
--- a/clang/test/SemaTemplate/typename-specifier-3.cpp
+++ b/clang/test/SemaTemplate/typename-specifier-3.cpp
@@ -75,3 +75,21 @@ namespace PR12884_fixed {
A<int>::C::x a; // ok
}
+
+namespace preserve_keyword {
+ template <class T> struct A {
+ using type = T;
+ };
+
+ template <class T> using B = A<T>::type*; // precxx17-warning {{missing 'typename'}}
+ void *t1 = *B<int>(); // expected-error {{lvalue of type 'A<int>::type' (aka 'int')}}
+
+ template <class T> using C = typename A<T>::type*;
+ void *t2 = *C<int>(); // expected-error {{lvalue of type 'typename A<int>::type' (aka 'int')}}
+
+ using D = A<int>::type*;
+ void *t3 = *D(); // expected-error {{lvalue of type 'A<int>::type' (aka 'int')}}
+
+ using D = typename A<int>::type*;
+ void *t4 = *D(); // expected-error {{lvalue of type 'typename A<int>::type' (aka 'int')}}
+} // namespace preserve_keyword
More information about the cfe-commits
mailing list