https://github.com/mizvekov created https://github.com/llvm/llvm-project/pull/135916
This patches makes the rules for canonicalization of the elaborated keyword uniform between DependentNameType and DependentTemplateSpecializationType. `class` and `struct` keywords are functionally equivalent, so relying on their equivalence was IFNDR. This changes it so we treat them as equivalent, as at least that disallows overloading, which is diagnosable. This patch also considers a few drive by fixes to preservation of the presence / absence of the typename keyword, and improves a few assertions in that area. Also improves preservation of sugar on initializer_list deduction in order to avoid a few changes in diagnostics. >From 0bdab9c8e1093eaa80bae640385e4ad7714e4a95 Mon Sep 17 00:00:00 2001 From: Matheus Izvekov <mizve...@gmail.com> Date: Tue, 15 Apr 2025 02:55:50 -0300 Subject: [PATCH 1/2] [clang] preserve sugar on initializer_list deduction --- clang/include/clang/AST/Type.h | 12 +++++++ clang/lib/AST/Type.cpp | 11 +++++++ clang/lib/Sema/SemaDeclCXX.cpp | 60 ++++++++++++++++++++-------------- clang/test/CXX/drs/cwg23xx.cpp | 4 +-- 4 files changed, 61 insertions(+), 26 deletions(-) diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 5bf036e3347eb..d5537cb70aef6 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2838,6 +2838,18 @@ 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. + const TemplateSpecializationType * + getAsNonAliasTemplateSpecializationType() const; + + const TemplateSpecializationType * + castAsNonAliasTemplateSpecializationType() 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/Type.cpp b/clang/lib/AST/Type.cpp index 53620003c9655..f2b396fef262a 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1938,6 +1938,17 @@ TagDecl *Type::getAsTagDecl() const { return nullptr; } +const TemplateSpecializationType * +Type::getAsNonAliasTemplateSpecializationType() const { + for (const auto *T = this; /**/; /**/) { + const TemplateSpecializationType *TST = + T->getAs<TemplateSpecializationType>(); + if (!TST || !TST->isTypeAlias()) + return TST; + T = TST->desugar().getTypePtr(); + } +} + bool Type::hasAttr(attr::Kind AK) const { const Type *Cur = this; while (const auto *AT = Cur->getAs<AttributedType>()) { 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/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@-1 {{conversion function from 'std::initializer_list<InitListCtor>' to 'const cwg2311::InitListCtor' invokes a deleted function}} +// since-cxx17-error@-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 >From d6fb0911d865ba3f64fb9dedfeebe54d45e3879d Mon Sep 17 00:00:00 2001 From: Matheus Izvekov <mizve...@gmail.com> Date: Mon, 14 Apr 2025 22:31:00 -0300 Subject: [PATCH 2/2] [clang] Fix elaborated keyword canonicalization. class and struct keywords are functionally equivalent, so relying on their equivalence was IFNDR. This changes it so we treat them as equivalent, as at least that disallows overloading, which is diagnosable. This patch also considers a few drive by fixes to preservation of the presence / abscence of the typename keyword, and improves a few assertions in that area. Also improves preservervation of sugar on initializer_list deduction in order to avoid a few changes in diagnostics. --- clang/docs/ReleaseNotes.rst | 4 ++ clang/lib/AST/ASTContext.cpp | 49 +++++++++++---- clang/lib/AST/TypeLoc.cpp | 44 ++++++++----- clang/lib/Sema/SemaDecl.cpp | 6 +- clang/lib/Sema/SemaInit.cpp | 7 ++- clang/lib/Sema/SemaTemplate.cpp | 18 +++--- clang/test/Analysis/anonymous-decls.cpp | 4 +- .../dependent-template-recover.cpp | 4 +- .../elaborated-type-specifier.cpp | 63 ++++++++++++++++++- .../SemaTemplate/typename-specifier-3.cpp | 18 ++++++ 10 files changed, 168 insertions(+), 49 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 84ad253c1ec4f..2dfd573dcf4e2 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -476,6 +476,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/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/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 5f811c824e11d..0aeb06d63544c 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/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/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@-1 {{'A<typename T::X>' (aka 'void')}} + // expected-error@-1 {{'A<T::X>' (aka 'void')}} A<T::X<int>> t4; // expected-error {{use 'template' keyword}} expected-error {{did you forget 'typename'}} - // expected-error@-1 {{'A<typename T::X>' (aka 'void')}} + // expected-error@-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@-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@-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@-1 {{previous definition is here}} + template <class T> void t1(class T::X) {} + // expected-error@-1 {{redefinition of 't1'}} + + template <class T> void t2(struct T::template X<int>) {} + // expected-note@-1 {{previous definition is here}} + template <class T> void t2(class T::template X<int>) {} + // expected-error@-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@-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@-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@-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@-1 3{{candidate function}} + template <class T> constexpr int t4(struct T::template X<int>* = 0) { return 1; } + // expected-note@-1 3{{candidate function}} + template <class T> constexpr int t4(union T::template X<int>* = 0) { return 2; } + // expected-note@-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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits