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@-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 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