diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp @@ -134,14 +134,9 @@ // Matching on initialization operations where the initial value is a newly // created owner, but the LHS is not an owner. Finder->addMatcher( - traverse( - TK_AsIs, - namedDecl( - varDecl(eachOf(allOf(hasInitializer(CreatesOwner), - unless(IsOwnerType)), - allOf(hasInitializer(ConsideredOwner), - hasType(autoType().bind("deduced_type"))))) - .bind("bad_owner_creation_variable"))), + traverse(TK_AsIs, namedDecl(varDecl(allOf(hasInitializer(CreatesOwner), + unless(IsOwnerType))) + .bind("bad_owner_creation_variable"))), this); // Match on all function calls that expect owners as arguments, but didn't @@ -324,13 +319,6 @@ // FIXME: FixitHint to rewrite the type of the initialized variable // as 'gsl::owner' - - // If the type of the variable was deduced, the wrapping owner typedef is - // eliminated, therefore the check emits a special note for that case. - if (Nodes.getNodeAs("deduced_type")) { - diag(BadOwnerInitialization->getBeginLoc(), - "type deduction did not result in an owner", DiagnosticIDs::Note); - } return true; } diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.cpp --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.cpp @@ -20,9 +20,11 @@ if (!getLangOpts().CPlusPlus) return; - const auto AllPointerTypes = anyOf( - hasType(pointerType()), hasType(autoType(hasDeducedType(pointerType()))), - hasType(decltypeType(hasUnderlyingType(pointerType())))); + const auto AllPointerTypes = + anyOf(hasType(pointerType()), + hasType(autoType( + hasDeducedType(hasUnqualifiedDesugaredType(pointerType())))), + hasType(decltypeType(hasUnderlyingType(pointerType())))); // Flag all operators +, -, +=, -=, ++, -- that result in a pointer Finder->addMatcher( diff --git a/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp b/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp --- a/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp @@ -125,18 +125,22 @@ }; auto IsBoundToType = refersToType(equalsBoundNode("type")); + auto UnlessFunctionType = unless(hasUnqualifiedDesugaredType(functionType())); + auto IsAutoDeducedToPointer = [](const auto &...InnerMatchers) { + return autoType(hasDeducedType( + hasUnqualifiedDesugaredType(pointerType(pointee(InnerMatchers...))))); + }; Finder->addMatcher( - ExplicitSingleVarDecl(hasType(autoType(hasDeducedType( - pointerType(pointee(unless(functionType())))))), + ExplicitSingleVarDecl(hasType(IsAutoDeducedToPointer(UnlessFunctionType)), "auto"), this); Finder->addMatcher( ExplicitSingleVarDeclInTemplate( - allOf(hasType(autoType(hasDeducedType(pointerType( - pointee(hasUnqualifiedType(qualType().bind("type")), - unless(functionType())))))), + allOf(hasType(IsAutoDeducedToPointer( + hasUnqualifiedType(qualType().bind("type")), + UnlessFunctionType)), anyOf(hasAncestor( functionDecl(hasAnyTemplateArgument(IsBoundToType))), hasAncestor(classTemplateSpecializationDecl( diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -491,7 +491,8 @@ } std::vector locateSymbolForType(const ParsedAST &AST, - const QualType &Type) { + const QualType &Type, + bool Underlying = false) { const auto &SM = AST.getSourceManager(); auto MainFilePath = getCanonicalPath(SM.getFileEntryForID(SM.getMainFileID()), SM); @@ -500,9 +501,11 @@ return {}; } - auto Decls = targetDecl(DynTypedNode::create(Type.getNonReferenceType()), - DeclRelation::TemplatePattern | DeclRelation::Alias, - AST.getHeuristicResolver()); + auto Decls = + targetDecl(DynTypedNode::create(Type.getNonReferenceType()), + DeclRelation::TemplatePattern | DeclRelation::Alias | + (Underlying ? DeclRelation::Underlying : DeclRelation()), + AST.getHeuristicResolver()); if (Decls.empty()) return {}; @@ -792,7 +795,7 @@ // go-to-definition on auto should find the definition of the deduced // type, if possible if (auto Deduced = getDeducedType(AST.getASTContext(), Tok.location())) { - auto LocSym = locateSymbolForType(AST, *Deduced); + auto LocSym = locateSymbolForType(AST, *Deduced, /*Underlying=*/true); if (!LocSym.empty()) return LocSym; } diff --git a/clang-tools-extra/clangd/unittests/ASTTests.cpp b/clang-tools-extra/clangd/unittests/ASTTests.cpp --- a/clang-tools-extra/clangd/unittests/ASTTests.cpp +++ b/clang-tools-extra/clangd/unittests/ASTTests.cpp @@ -35,6 +35,7 @@ struct Test { StringRef AnnotatedCode; const char *DeducedType; + const char *DeducedCanonicalType = nullptr; } Tests[] = { {"^auto i = 0;", "int"}, {"^auto f(){ return 1;};", "int"}, @@ -43,6 +44,7 @@ namespace ns1 { struct S {}; } ^auto v = ns1::S{}; )cpp", + "ns1::S", "struct ns1::S", }, { @@ -52,6 +54,7 @@ ^decltype(i) j; )cpp", "ns1::S", + "struct ns1::S", }, { R"cpp(// decltype(auto) on struct& @@ -63,6 +66,7 @@ ns1::S& j = i; ^decltype(auto) k = j; )cpp", + "decltype(j)", "struct ns1::S &", }, { @@ -71,6 +75,7 @@ template class Foo {}; ^auto v = Foo(); )cpp", + "Foo", "class Foo", }, { @@ -149,6 +154,7 @@ return Foo(); } )cpp", + "decltype(struct Foo())", "struct Foo", }, { @@ -159,6 +165,7 @@ return (x); } )cpp", + "decltype((x))", "struct Foo &", }, { @@ -169,6 +176,7 @@ return (x); } )cpp", + "decltype((x))", "const struct Foo &", }, { @@ -177,7 +185,8 @@ using Bar = Foo; ^auto x = Bar(); )cpp", - // FIXME: it'd be nice if this resolved to the alias instead + // It's so nice that this is resolved to the alias instead :-D + "Bar", "struct Foo", }, }; @@ -194,6 +203,10 @@ auto DeducedType = getDeducedType(AST.getASTContext(), *Location); ASSERT_TRUE(DeducedType); EXPECT_EQ(DeducedType->getAsString(), T.DeducedType); + auto DeducedCanonicalType = DeducedType->getCanonicalType(); + EXPECT_EQ(DeducedCanonicalType.getAsString(), T.DeducedCanonicalType + ? T.DeducedCanonicalType + : T.DeducedType); } } } diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -461,7 +461,7 @@ [](HoverInfo &HI) { HI.Name = "auto"; HI.Kind = index::SymbolKind::TypeAlias; - HI.Definition = "class Foo"; + HI.Definition = "Foo"; }}, // auto on specialized template {R"cpp( @@ -474,7 +474,7 @@ [](HoverInfo &HI) { HI.Name = "auto"; HI.Kind = index::SymbolKind::TypeAlias; - HI.Definition = "class Foo"; + HI.Definition = "Foo"; }}, // macro @@ -648,7 +648,7 @@ [](HoverInfo &HI) { HI.Name = "auto"; HI.Kind = index::SymbolKind::TypeAlias; - HI.Definition = "class Foo"; + HI.Definition = "Foo"; }}, {// Falls back to primary template, when the type is not instantiated. R"cpp( @@ -2024,7 +2024,7 @@ [](HoverInfo &HI) { HI.Name = "auto"; HI.Kind = index::SymbolKind::TypeAlias; - HI.Definition = "int"; + HI.Definition = "int_type"; }}, { R"cpp(// auto on alias @@ -2035,7 +2035,7 @@ [](HoverInfo &HI) { HI.Name = "auto"; HI.Kind = index::SymbolKind::TypeAlias; - HI.Definition = "struct cls"; + HI.Definition = "cls_type"; HI.Documentation = "auto on alias"; }}, { @@ -2047,7 +2047,7 @@ [](HoverInfo &HI) { HI.Name = "auto"; HI.Kind = index::SymbolKind::TypeAlias; - HI.Definition = "struct templ"; + HI.Definition = "templ"; HI.Documentation = "auto on alias"; }}, { diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp --- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp +++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp @@ -446,7 +446,7 @@ int& y = x; decltype(auto) $z[[z]] = y; )cpp", - ExpectedHint{": int &", "z"}); + ExpectedHint{": decltype(y)", "z"}); } TEST(TypeHints, NoQualifiers) { @@ -466,7 +466,8 @@ } } )cpp", - ExpectedHint{": S1", "x"}, ExpectedHint{": Inner", "y"}); + ExpectedHint{": S1", "x"}, + ExpectedHint{": S2::Inner", "y"}); } TEST(TypeHints, Lambda) { diff --git a/clang-tools-extra/clangd/unittests/tweaks/ExpandAutoTypeTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/ExpandAutoTypeTests.cpp --- a/clang-tools-extra/clangd/unittests/tweaks/ExpandAutoTypeTests.cpp +++ b/clang-tools-extra/clangd/unittests/tweaks/ExpandAutoTypeTests.cpp @@ -57,7 +57,7 @@ EXPECT_UNAVAILABLE("au^to x = []{};"); // inline namespaces EXPECT_EQ(apply("au^to x = inl_ns::Visible();"), - "Visible x = inl_ns::Visible();"); + "inl_ns::Visible x = inl_ns::Visible();"); // local class EXPECT_EQ(apply("namespace x { void y() { struct S{}; ^auto z = S(); } }"), "namespace x { void y() { struct S{}; S z = S(); } }"); @@ -67,8 +67,9 @@ EXPECT_EQ(apply("ns::Class * foo() { au^to c = foo(); }"), "ns::Class * foo() { ns::Class * c = foo(); }"); - EXPECT_EQ(apply("void ns::Func() { au^to x = new ns::Class::Nested{}; }"), - "void ns::Func() { Class::Nested * x = new ns::Class::Nested{}; }"); + EXPECT_EQ( + apply("void ns::Func() { au^to x = new ns::Class::Nested{}; }"), + "void ns::Func() { ns::Class::Nested * x = new ns::Class::Nested{}; }"); EXPECT_UNAVAILABLE("dec^ltype(au^to) x = 10;"); // expanding types in structured bindings is syntactically invalid. diff --git a/clang-tools-extra/test/clang-tidy/checkers/cert-static-object-exception.cpp b/clang-tools-extra/test/clang-tidy/checkers/cert-static-object-exception.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/cert-static-object-exception.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cert-static-object-exception.cpp @@ -256,7 +256,7 @@ return []{ S s; return s; }; } }; -auto Okay4 = []{ U u; return u.getBadLambda(); }(); +auto Okay4 = []() noexcept { U u; return u.getBadLambda(); }(); auto NotOkay3 = []() noexcept { U u; return u.getBadLambda(); }()(); // Because the lambda returned and called is not noexcept // CHECK-EXCEPTIONS: :[[@LINE-1]]:6: warning: initialization of 'NotOkay3' with static storage duration may throw an exception that cannot be caught [cert-err58-cpp] // CHECK-EXCEPTIONS: :[[@LINE-6]]:12: note: possibly throwing function declared here @@ -272,4 +272,8 @@ // FIXME: the above should be diagnosed because the capture init can trigger // an exception when constructing the Bad object. #endif // NONEXCEPTIONS + +auto NotOkay5 = []() { U u; return u.getBadLambda(); }(); +// CHECK-EXCEPTIONS: :[[@LINE-1]]:6: warning: initialization of 'NotOkay5' with static storage duration may throw an exception that cannot be caught [cert-err58-cpp] +// CHECK-EXCEPTIONS: :[[@LINE-6]]:12: note: possibly throwing function declared here } diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-owning-memory.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-owning-memory.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-owning-memory.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-owning-memory.cpp @@ -91,13 +91,9 @@ // FIXME:, flow analysis for the case of reassignment. Value must be released before owned_int6 = owned_int3; // BAD, because reassignment without resource release - auto owned_int7 = returns_owner1(); // Bad, since type deduction eliminates the owner wrapper - // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' - // CHECK-NOTES: [[@LINE-2]]:3: note: type deduction did not result in an owner + auto owned_int7 = returns_owner1(); // Ok, since type deduction does not eliminate the owner wrapper - const auto owned_int8 = returns_owner2(); // Bad, since type deduction eliminates the owner wrapper - // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *const' with a newly created 'gsl::owner<>' - // CHECK-NOTES: [[@LINE-2]]:3: note: type deduction did not result in an owner + const auto owned_int8 = returns_owner2(); // Ok, since type deduction does not eliminate the owner wrapper gsl::owner owned_int9 = returns_owner1(); // Ok int *unowned_int3 = returns_owner1(); // Bad @@ -285,15 +281,12 @@ ClassWithOwner C2{A}; // Bad, since the owner would be initialized with an non-owner, but catched in the class ClassWithOwner C3{gsl::owner(new ArbitraryClass)}; // Ok - const auto Owner1 = C3.buggy_but_returns_owner(); // BAD, deduces Owner1 to ArbitraryClass *const - // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'ArbitraryClass *const' with a newly created 'gsl::owner<>' - // CHECK-NOTES: [[@LINE-2]]:3: note: type deduction did not result in an owner + const auto Owner1 = C3.buggy_but_returns_owner(); // Ok, deduces Owner1 to owner const - auto Owner2 = C2.buggy_but_returns_owner(); // BAD, deduces Owner2 to ArbitraryClass * - // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'ArbitraryClass *' with a newly created 'gsl::owner<>' - // CHECK-NOTES: [[@LINE-2]]:3: note: type deduction did not result in an owner + auto Owner2 = C2.buggy_but_returns_owner(); //Ok, deduces Owner2 to owner - Owner2 = &A; // Ok, since type deduction did NOT result in owner + Owner2 = &A; // BAD, since type deduction resulted in owner + // CHECK-NOTES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'ArbitraryClass *' gsl::owner Owner3 = C1.buggy_but_returns_owner(); // Ok, still an owner Owner3 = &A; // Bad, since assignment of non-owner to owner diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1525,6 +1525,12 @@ QualType getFunctionTypeInternal(QualType ResultTy, ArrayRef Args, const FunctionProtoType::ExtProtoInfo &EPI, bool OnlyWantCanonical) const; + QualType + getAutoTypeInternal(QualType DeducedType, AutoTypeKeyword Keyword, + bool IsDependent, bool IsPack = false, + ConceptDecl *TypeConstraintConcept = nullptr, + ArrayRef TypeConstraintArgs = {}, + bool IsCanon = false) const; public: /// Return the unique reference to the type for the specified type diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -4944,29 +4944,31 @@ /// type-dependent, there is no deduced type and the type is canonical. In /// the latter case, it is also a dependent type. class DeducedType : public Type { + QualType DeducedAsType; + protected: DeducedType(TypeClass TC, QualType DeducedAsType, - TypeDependence ExtraDependence) - : Type(TC, - // FIXME: Retain the sugared deduced type? - DeducedAsType.isNull() ? QualType(this, 0) - : DeducedAsType.getCanonicalType(), + TypeDependence ExtraDependence, QualType Canon) + : Type(TC, Canon, ExtraDependence | (DeducedAsType.isNull() ? TypeDependence::None : DeducedAsType->getDependence() & - ~TypeDependence::VariablyModified)) {} + ~TypeDependence::VariablyModified)), + DeducedAsType(DeducedAsType) {} public: - bool isSugared() const { return !isCanonicalUnqualified(); } - QualType desugar() const { return getCanonicalTypeInternal(); } + bool isSugared() const { return !DeducedAsType.isNull(); } + QualType desugar() const { + return isSugared() ? DeducedAsType : QualType(this, 0); + } /// Get the type deduced for this placeholder type, or null if it's /// either not been deduced or was deduced to a dependent type. QualType getDeducedType() const { - return !isCanonicalUnqualified() ? getCanonicalTypeInternal() : QualType(); + return DeducedAsType.isNull() ? QualType() : DeducedAsType; } bool isDeduced() const { - return !isCanonicalUnqualified() || isDependentType(); + return !DeducedAsType.isNull() || isDependentType(); } static bool classof(const Type *T) { @@ -4983,7 +4985,7 @@ ConceptDecl *TypeConstraintConcept; AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword, - TypeDependence ExtraDependence, ConceptDecl *CD, + TypeDependence ExtraDependence, QualType Canon, ConceptDecl *CD, ArrayRef TypeConstraintArgs); const TemplateArgument *getArgBuffer() const { @@ -5057,7 +5059,9 @@ toTypeDependence(Template.getDependence()) | (IsDeducedAsDependent ? TypeDependence::DependentInstantiation - : TypeDependence::None)), + : TypeDependence::None), + DeducedAsType.isNull() ? QualType(this, 0) + : DeducedAsType.getCanonicalType()), Template(Template) {} public: diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8552,6 +8552,14 @@ /// Substitute Replacement for auto in TypeWithAuto TypeSourceInfo* SubstAutoTypeSourceInfo(TypeSourceInfo *TypeWithAuto, QualType Replacement); + + // Substitute auto in TypeWithAuto for a Dependent auto type + QualType SubstAutoTypeDependent(QualType TypeWithAuto); + + // Substitute auto in TypeWithAuto for a Dependent auto type + TypeSourceInfo * + SubstAutoTypeSourceInfoDependent(TypeSourceInfo *TypeWithAuto); + /// Completely replace the \c auto in \p TypeWithAuto by /// \p Replacement. This does not retain any \c auto type sugar. QualType ReplaceAutoType(QualType TypeWithAuto, QualType Replacement); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -4817,6 +4817,23 @@ return QualType(Spec, 0); } +static bool +getCanonicalTemplateArguments(const ASTContext &C, + ArrayRef OrigArgs, + SmallVectorImpl &CanonArgs) { + bool AnyNonCanonArgs = false; + unsigned NumArgs = OrigArgs.size(); + CanonArgs.resize(NumArgs); + for (unsigned I = 0; I != NumArgs; ++I) { + const TemplateArgument &OrigArg = OrigArgs[I]; + TemplateArgument &CanonArg = CanonArgs[I]; + CanonArg = C.getCanonicalTemplateArgument(OrigArg); + if (!CanonArg.structurallyEquals(OrigArg)) + AnyNonCanonArgs = true; + } + return AnyNonCanonArgs; +} + QualType ASTContext::getCanonicalTemplateSpecializationType( TemplateName Template, ArrayRef Args) const { assert(!Template.getAsDependentTemplateName() && @@ -4829,10 +4846,7 @@ // Build the canonical template specialization type. TemplateName CanonTemplate = getCanonicalTemplateName(Template); SmallVector CanonArgs; - unsigned NumArgs = Args.size(); - CanonArgs.reserve(NumArgs); - for (const TemplateArgument &Arg : Args) - CanonArgs.push_back(getCanonicalTemplateArgument(Arg)); + ::getCanonicalTemplateArguments(*this, Args, CanonArgs); // Determine whether this canonical template specialization type already // exists. @@ -4847,7 +4861,7 @@ if (!Spec) { // Allocate a new canonical template specialization type. void *Mem = Allocate((sizeof(TemplateSpecializationType) + - sizeof(TemplateArgument) * NumArgs), + sizeof(TemplateArgument) * CanonArgs.size()), TypeAlignment); Spec = new (Mem) TemplateSpecializationType(CanonTemplate, CanonArgs, @@ -4989,14 +5003,9 @@ ElaboratedTypeKeyword CanonKeyword = Keyword; if (Keyword == ETK_None) CanonKeyword = ETK_Typename; - bool AnyNonCanonArgs = false; - unsigned NumArgs = Args.size(); - SmallVector CanonArgs(NumArgs); - for (unsigned I = 0; I != NumArgs; ++I) { - CanonArgs[I] = getCanonicalTemplateArgument(Args[I]); - if (!CanonArgs[I].structurallyEquals(Args[I])) - AnyNonCanonArgs = true; - } + SmallVector CanonArgs; + bool AnyNonCanonArgs = + ::getCanonicalTemplateArguments(*this, Args, CanonArgs); QualType Canon; if (AnyNonCanonArgs || CanonNNS != NNS || CanonKeyword != Keyword) { @@ -5009,7 +5018,7 @@ } void *Mem = Allocate((sizeof(DependentTemplateSpecializationType) + - sizeof(TemplateArgument) * NumArgs), + sizeof(TemplateArgument) * Args.size()), TypeAlignment); T = new (Mem) DependentTemplateSpecializationType(Keyword, NNS, Name, Args, Canon); @@ -5594,15 +5603,10 @@ return QualType(ut, 0); } -/// getAutoType - Return the uniqued reference to the 'auto' type which has been -/// deduced to the given type, or to the canonical undeduced 'auto' type, or the -/// canonical deduced-but-dependent 'auto' type. -QualType -ASTContext::getAutoType(QualType DeducedType, AutoTypeKeyword Keyword, - bool IsDependent, bool IsPack, - ConceptDecl *TypeConstraintConcept, - ArrayRef TypeConstraintArgs) const { - assert((!IsPack || IsDependent) && "only use IsPack for a dependent pack"); +QualType ASTContext::getAutoTypeInternal( + QualType DeducedType, AutoTypeKeyword Keyword, bool IsDependent, + bool IsPack, ConceptDecl *TypeConstraintConcept, + ArrayRef TypeConstraintArgs, bool IsCanon) const { if (DeducedType.isNull() && Keyword == AutoTypeKeyword::Auto && !TypeConstraintConcept && !IsDependent) return getAutoDeductType(); @@ -5615,21 +5619,52 @@ if (AutoType *AT = AutoTypes.FindNodeOrInsertPos(ID, InsertPos)) return QualType(AT, 0); + QualType Canon; + if (!IsCanon) { + if (DeducedType.isNull()) { + SmallVector CanonArgs; + bool AnyNonCanonArgs = + ::getCanonicalTemplateArguments(*this, TypeConstraintArgs, CanonArgs); + if (AnyNonCanonArgs) { + Canon = getAutoTypeInternal(QualType(), Keyword, IsDependent, IsPack, + TypeConstraintConcept, CanonArgs, true); + // Find the insert position again. + AutoTypes.FindNodeOrInsertPos(ID, InsertPos); + } + } else { + Canon = DeducedType.getCanonicalType(); + } + } + void *Mem = Allocate(sizeof(AutoType) + - sizeof(TemplateArgument) * TypeConstraintArgs.size(), + sizeof(TemplateArgument) * TypeConstraintArgs.size(), TypeAlignment); auto *AT = new (Mem) AutoType( DeducedType, Keyword, (IsDependent ? TypeDependence::DependentInstantiation : TypeDependence::None) | (IsPack ? TypeDependence::UnexpandedPack : TypeDependence::None), - TypeConstraintConcept, TypeConstraintArgs); + Canon, TypeConstraintConcept, TypeConstraintArgs); Types.push_back(AT); - if (InsertPos) - AutoTypes.InsertNode(AT, InsertPos); + AutoTypes.InsertNode(AT, InsertPos); return QualType(AT, 0); } +/// getAutoType - Return the uniqued reference to the 'auto' type which has been +/// deduced to the given type, or to the canonical undeduced 'auto' type, or the +/// canonical deduced-but-dependent 'auto' type. +QualType +ASTContext::getAutoType(QualType DeducedType, AutoTypeKeyword Keyword, + bool IsDependent, bool IsPack, + ConceptDecl *TypeConstraintConcept, + ArrayRef TypeConstraintArgs) const { + assert((!IsPack || IsDependent) && "only use IsPack for a dependent pack"); + assert((!IsDependent || DeducedType.isNull()) && + "A dependent auto should be undeduced"); + return getAutoTypeInternal(DeducedType, Keyword, IsDependent, IsPack, + TypeConstraintConcept, TypeConstraintArgs); +} + /// Return the uniqued reference to the deduced template specialization type /// which has been deduced to the given type, or to the canonical undeduced /// such type, or the canonical deduced-but-dependent such type. @@ -5647,8 +5682,7 @@ auto *DTST = new (*this, TypeAlignment) DeducedTemplateSpecializationType(Template, DeducedType, IsDependent); Types.push_back(DTST); - if (InsertPos) - DeducedTemplateSpecializationTypes.InsertNode(DTST, InsertPos); + DeducedTemplateSpecializationTypes.InsertNode(DTST, InsertPos); return QualType(DTST, 0); } @@ -5685,7 +5719,7 @@ if (AutoDeductTy.isNull()) AutoDeductTy = QualType(new (*this, TypeAlignment) AutoType(QualType(), AutoTypeKeyword::Auto, - TypeDependence::None, + TypeDependence::None, QualType(), /*concept*/ nullptr, /*args*/ {}), 0); return AutoDeductTy; diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1644,36 +1644,48 @@ return Importer.getToContext().getObjCObjectPointerType(*ToPointeeTypeOrErr); } +static Error checkCycle(ASTImporter &Importer, const RecordDecl *RD, + QualType QT) { + const Type *LeafT = QT.getCanonicalType().getTypePtr(); + while (LeafT->isPointerType() || LeafT->isArrayType()) + LeafT = LeafT->getPointeeOrArrayElementType(); + + const auto *RT = dyn_cast(LeafT); + if (!RT) + return Error::success(); + + for (const DeclContext *DC = RT->getDecl(); DC; DC = DC->getParent()) + if (DC == RD) { + Importer.FromDiag(RD->getLocation(), diag::err_unsupported_ast_node) + << RD->Decl::getDeclKindName(); + return make_error(ImportError::UnsupportedConstruct); + } + return Error::success(); +} + //---------------------------------------------------------------------------- // Import Declarations //---------------------------------------------------------------------------- Error ASTNodeImporter::ImportDeclParts( NamedDecl *D, DeclContext *&DC, DeclContext *&LexicalDC, DeclarationName &Name, NamedDecl *&ToD, SourceLocation &Loc) { - // Check if RecordDecl is in FunctionDecl parameters to avoid infinite loop. - // example: int struct_in_proto(struct data_t{int a;int b;} *d); + // Check if FunctionDecl return / parameters are in the RecordDecl context, + // in order to avoid an infinite loop. + // Example: int struct_in_proto(struct data_t{int a;int b;} *d); + // Example: auto struct_in_body() { struct X {}; return X(); } // FIXME: We could support these constructs by importing a different type of - // this parameter and by importing the original type of the parameter only + // this return / parameter and then importing that original type only // after the FunctionDecl is created. See // VisitFunctionDecl::UsedDifferentProtoType. - DeclContext *OrigDC = D->getDeclContext(); - FunctionDecl *FunDecl; - if (isa(D) && (FunDecl = dyn_cast(OrigDC)) && - FunDecl->hasBody()) { - auto getLeafPointeeType = [](const Type *T) { - while (T->isPointerType() || T->isArrayType()) { - T = T->getPointeeOrArrayElementType(); - } - return T; - }; - for (const ParmVarDecl *P : FunDecl->parameters()) { - const Type *LeafT = - getLeafPointeeType(P->getType().getCanonicalType().getTypePtr()); - auto *RT = dyn_cast(LeafT); - if (RT && RT->getDecl() == D) { - Importer.FromDiag(D->getLocation(), diag::err_unsupported_ast_node) - << D->getDeclKindName(); - return make_error(ImportError::UnsupportedConstruct); + { + const auto *FD = dyn_cast(D->getDeclContext()); + const auto *RD = dyn_cast(D); + if (RD && FD && FD->hasBody()) { + if (Error Err = checkCycle(Importer, RD, FD->getReturnType())) + return Err; + for (const ParmVarDecl *P : FD->parameters()) { + if (Error Err = checkCycle(Importer, RD, P->getType())) + return Err; } } } diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -4394,10 +4394,10 @@ } AutoType::AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword, - TypeDependence ExtraDependence, + TypeDependence ExtraDependence, QualType Canon, ConceptDecl *TypeConstraintConcept, ArrayRef TypeConstraintArgs) - : DeducedType(Auto, DeducedAsType, ExtraDependence) { + : DeducedType(Auto, DeducedAsType, ExtraDependence, Canon) { AutoTypeBits.Keyword = (unsigned)Keyword; AutoTypeBits.NumArgs = TypeConstraintArgs.size(); this->TypeConstraintConcept = TypeConstraintConcept; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -3608,14 +3608,14 @@ // defined, copy the deduced value from the old declaration. AutoType *OldAT = Old->getReturnType()->getContainedAutoType(); if (OldAT && OldAT->isDeduced()) { - New->setType( - SubstAutoType(New->getType(), - OldAT->isDependentType() ? Context.DependentTy - : OldAT->getDeducedType())); - NewQType = Context.getCanonicalType( - SubstAutoType(NewQType, - OldAT->isDependentType() ? Context.DependentTy - : OldAT->getDeducedType())); + QualType DT = OldAT->getDeducedType(); + if (DT.isNull()) { + New->setType(SubstAutoTypeDependent(New->getType())); + NewQType = Context.getCanonicalType(SubstAutoTypeDependent(NewQType)); + } else { + New->setType(SubstAutoType(New->getType(), DT)); + NewQType = Context.getCanonicalType(SubstAutoType(NewQType, DT)); + } } } @@ -9252,8 +9252,7 @@ // a friend yet, so 'isDependentContext' on the FD doesn't work. const FunctionProtoType *FPT = NewFD->getType()->castAs(); - QualType Result = - SubstAutoType(FPT->getReturnType(), Context.DependentTy); + QualType Result = SubstAutoTypeDependent(FPT->getReturnType()); NewFD->setType(Context.getFunctionType(Result, FPT->getParamTypes(), FPT->getExtProtoInfo())); } @@ -12347,7 +12346,7 @@ /*TreatUnavailableAsInvalid=*/false); ExprResult Result = InitSeq.Perform(*this, Entity, Kind, Args, &DclT); if (Result.isInvalid()) { - // If the provied initializer fails to initialize the var decl, + // If the provided initializer fails to initialize the var decl, // we attach a recovery expr for better recovery. auto RecoveryExpr = CreateRecoveryExpr(Init->getBeginLoc(), Init->getEndLoc(), Args); diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -9969,7 +9969,7 @@ auto TemplateName = DeducedTST->getTemplateName(); if (TemplateName.isDependent()) - return SubstAutoType(TSInfo->getType(), Context.DependentTy); + return SubstAutoTypeDependent(TSInfo->getType()); // We can only perform deduction for class templates. auto *Template = @@ -9988,7 +9988,7 @@ Diag(TSInfo->getTypeLoc().getBeginLoc(), diag::warn_cxx14_compat_class_template_argument_deduction) << TSInfo->getTypeLoc().getSourceRange() << 0; - return SubstAutoType(TSInfo->getType(), Context.DependentTy); + return SubstAutoTypeDependent(TSInfo->getType()); } // FIXME: Perform "exact type" matching first, per CWG discussion? diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -373,7 +373,7 @@ const FunctionProtoType *FPT = MethodType->castAs(); QualType Result = FPT->getReturnType(); if (Result->isUndeducedType()) { - Result = SubstAutoType(Result, Context.DependentTy); + Result = SubstAutoTypeDependent(Result); MethodType = Context.getFunctionType(Result, FPT->getParamTypes(), FPT->getExtProtoInfo()); } diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -2729,7 +2729,7 @@ if (auto *DD = dyn_cast(LoopVar)) for (auto *Binding : DD->bindings()) Binding->setType(Context.DependentTy); - LoopVar->setType(SubstAutoType(LoopVar->getType(), Context.DependentTy)); + LoopVar->setType(SubstAutoTypeDependent(LoopVar->getType())); } } else if (!BeginDeclStmt.get()) { SourceLocation RangeLoc = RangeVar->getLocation(); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1290,7 +1290,7 @@ // - an identifier associated by name lookup with a non-type // template-parameter declared with a type that contains a // placeholder type (7.1.7.4), - TSI = SubstAutoTypeSourceInfo(TSI, Context.DependentTy); + TSI = SubstAutoTypeSourceInfoDependent(TSI); } return CheckNonTypeTemplateParameterType(TSI->getType(), Loc); @@ -10888,7 +10888,7 @@ // - an identifier associated by name lookup with a non-type // template-parameter declared with a type that contains a // placeholder type (7.1.7.4), - NewTSI = SubstAutoTypeSourceInfo(NewTSI, Context.DependentTy); + NewTSI = SubstAutoTypeSourceInfoDependent(NewTSI); } if (NewTSI != NTTP->getTypeSourceInfo()) { diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -139,17 +139,11 @@ TemplateDeductionInfo &Info, SmallVectorImpl &Deduced); -static Sema::TemplateDeductionResult -DeduceTemplateArgumentsByTypeMatch(Sema &S, - TemplateParameterList *TemplateParams, - QualType Param, - QualType Arg, - TemplateDeductionInfo &Info, - SmallVectorImpl & - Deduced, - unsigned TDF, - bool PartialOrdering = false, - bool DeducedFromArrayBound = false); +static Sema::TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( + Sema &S, TemplateParameterList *TemplateParams, QualType Param, + QualType Arg, TemplateDeductionInfo &Info, + SmallVectorImpl &Deduced, unsigned TDF, + bool PartialOrdering = false, bool DeducedFromArrayBound = false); static Sema::TemplateDeductionResult DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, @@ -565,15 +559,20 @@ QualType Arg, TemplateDeductionInfo &Info, SmallVectorImpl &Deduced) { - assert(Arg.isCanonical() && "Argument type must be canonical"); + QualType ArgDesug; + auto updateArg = [&Arg, &ArgDesug, &S](QualType T) { + Arg = T; + ArgDesug = T.getDesugaredType(S.Context); + }; + updateArg(Arg); // Treat an injected-class-name as its underlying template-id. - if (auto *Injected = dyn_cast(Arg)) - Arg = Injected->getInjectedSpecializationType(); + if (auto *Injected = dyn_cast(ArgDesug)) + updateArg(Injected->getInjectedSpecializationType()); // Check whether the template argument is a dependent template-id. - if (const TemplateSpecializationType *SpecArg - = dyn_cast(Arg)) { + if (const TemplateSpecializationType *SpecArg = + dyn_cast(ArgDesug)) { // Perform template argument deduction for the template name. if (Sema::TemplateDeductionResult Result = DeduceTemplateArguments(S, TemplateParams, @@ -595,7 +594,7 @@ // If the argument type is a class template specialization, we // perform template argument deduction using its template // arguments. - const RecordType *RecordArg = dyn_cast(Arg); + const RecordType *RecordArg = dyn_cast(ArgDesug); if (!RecordArg) { Info.FirstArg = TemplateArgument(QualType(Param, 0)); Info.SecondArg = TemplateArgument(Arg); @@ -1047,11 +1046,12 @@ return Sema::TDK_MiscellaneousDeductionFailure; } - if (Sema::TemplateDeductionResult Result - = DeduceTemplateArgumentsByTypeMatch(S, TemplateParams, - Params[ParamIdx], Args[ArgIdx], - Info, Deduced, TDF, - PartialOrdering)) + if (Sema::TemplateDeductionResult Result = + DeduceTemplateArgumentsByTypeMatch( + S, TemplateParams, Params[ParamIdx].getUnqualifiedType(), + Args[ArgIdx].getUnqualifiedType(), Info, Deduced, TDF, + PartialOrdering, + /*DeducedFromArrayBound=*/false)) return Result; ++ArgIdx; @@ -1073,10 +1073,11 @@ if (ParamIdx + 1 == NumParams || PackScope.hasFixedArity()) { for (; ArgIdx < NumArgs && PackScope.hasNextElement(); ++ArgIdx) { // Deduce template arguments from the pattern. - if (Sema::TemplateDeductionResult Result - = DeduceTemplateArgumentsByTypeMatch(S, TemplateParams, Pattern, - Args[ArgIdx], Info, Deduced, - TDF, PartialOrdering)) + if (Sema::TemplateDeductionResult Result = + DeduceTemplateArgumentsByTypeMatch( + S, TemplateParams, Pattern.getUnqualifiedType(), + Args[ArgIdx].getUnqualifiedType(), Info, Deduced, TDF, + PartialOrdering, /*DeducedFromArrayBound=*/false)) return Result; PackScope.nextPackElement(); @@ -1341,24 +1342,28 @@ /// \returns the result of template argument deduction so far. Note that a /// "success" result means that template argument deduction has not yet failed, /// but it may still fail, later, for other reasons. -static Sema::TemplateDeductionResult -DeduceTemplateArgumentsByTypeMatch(Sema &S, - TemplateParameterList *TemplateParams, - QualType ParamIn, QualType ArgIn, - TemplateDeductionInfo &Info, - SmallVectorImpl &Deduced, - unsigned TDF, - bool PartialOrdering, - bool DeducedFromArrayBound) { - // We only want to look at the canonical types, since typedefs and - // sugar are not part of template argument deduction. - QualType Param = S.Context.getCanonicalType(ParamIn); - QualType Arg = S.Context.getCanonicalType(ArgIn); +static Sema::TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( + Sema &S, TemplateParameterList *TemplateParams, QualType Param, + QualType Arg, TemplateDeductionInfo &Info, + SmallVectorImpl &Deduced, unsigned TDF, + bool PartialOrdering, bool DeducedFromArrayBound) { + QualType ParDesug; + auto updatePar = [&Param, &ParDesug, &S](QualType T) { + Param = T; + ParDesug = T.getDesugaredType(S.Context); + }; + updatePar(Param); + + QualType ArgDesug; + auto updateArg = [&Arg, &ArgDesug, &S](QualType T) { + Arg = T; + ArgDesug = T.getDesugaredType(S.Context); + }; + updateArg(Arg); // If the argument type is a pack expansion, look at its pattern. // This isn't explicitly called out - if (const PackExpansionType *ArgExpansion - = dyn_cast(Arg)) + if (const PackExpansionType *ArgExpansion = dyn_cast(Arg)) Arg = ArgExpansion->getPattern(); if (PartialOrdering) { @@ -1368,12 +1373,12 @@ // - If P is a reference type, P is replaced by the type referred to. const ReferenceType *ParamRef = Param->getAs(); if (ParamRef) - Param = ParamRef->getPointeeType(); + updatePar(ParamRef->getPointeeType()); // - If A is a reference type, A is replaced by the type referred to. - const ReferenceType *ArgRef = Arg->getAs(); + const ReferenceType *ArgRef = ArgDesug->getAs(); if (ArgRef) - Arg = ArgRef->getPointeeType(); + updateArg(ArgRef->getPointeeType()); if (ParamRef && ArgRef && S.Context.hasSameUnqualifiedType(Param, Arg)) { // C++11 [temp.deduct.partial]p9: @@ -1404,8 +1409,8 @@ ArgQuals.getObjCLifetime() == Qualifiers::OCL_ExplicitNone && ParamQuals.withoutObjCLifetime() == ArgQuals.withoutObjCLifetime())) { - Info.FirstArg = TemplateArgument(ParamIn); - Info.SecondArg = TemplateArgument(ArgIn); + Info.FirstArg = TemplateArgument(Param); + Info.SecondArg = TemplateArgument(Arg); return Sema::TDK_NonDeducedMismatch; } } @@ -1414,10 +1419,10 @@ // Remove any top-level cv-qualifiers: // - If P is a cv-qualified type, P is replaced by the cv-unqualified // version of P. - Param = Param.getUnqualifiedType(); + updatePar(Param.getUnqualifiedType()); // - If A is a cv-qualified type, A is replaced by the cv-unqualified // version of A. - Arg = Arg.getUnqualifiedType(); + updateArg(Arg.getUnqualifiedType()); } else { // C++0x [temp.deduct.call]p4 bullet 1: // - If the original P is a reference type, the deduced A (i.e., the type @@ -1427,8 +1432,8 @@ Qualifiers Quals; QualType UnqualParam = S.Context.getUnqualifiedArrayType(Param, Quals); Quals.setCVRQualifiers(Quals.getCVRQualifiers() & - Arg.getCVRQualifiers()); - Param = S.Context.getQualifiedType(UnqualParam, Quals); + ArgDesug.getCVRQualifiers()); + updatePar(S.Context.getQualifiedType(UnqualParam, Quals)); } if ((TDF & TDF_TopLevelParameterTypeList) && !Param->isFunctionType()) { @@ -1444,8 +1449,8 @@ // Pi is T&& and Ai is X&, the adjusted Pi will be T, causing T to be // deduced as X&. - end note ] TDF &= ~TDF_TopLevelParameterTypeList; - if (isForwardingReference(Param, 0) && Arg->isLValueReferenceType()) - Param = Param->getPointeeType(); + if (isForwardingReference(Param, 0) && ArgDesug->isLValueReferenceType()) + updatePar(Param->getPointeeType()); } } @@ -1456,32 +1461,29 @@ // // T // cv-list T - if (const TemplateTypeParmType *TemplateTypeParm - = Param->getAs()) { + if (const TemplateTypeParmType *TemplateTypeParm = + ParDesug->getAs()) { // Just skip any attempts to deduce from a placeholder type or a parameter // at a different depth. - if (Arg->isPlaceholderType() || + if (ArgDesug->isPlaceholderType() || Info.getDeducedDepth() != TemplateTypeParm->getDepth()) return Sema::TDK_Success; unsigned Index = TemplateTypeParm->getIndex(); - bool RecanonicalizeArg = false; // If the argument type is an array type, move the qualifiers up to the // top level, so they can be matched with the qualifiers on the parameter. - if (isa(Arg)) { + if (isa(ArgDesug)) { Qualifiers Quals; - Arg = S.Context.getUnqualifiedArrayType(Arg, Quals); - if (Quals) { - Arg = S.Context.getQualifiedType(Arg, Quals); - RecanonicalizeArg = true; - } + updateArg(S.Context.getUnqualifiedArrayType(ArgDesug, Quals)); + if (Quals) + updateArg(S.Context.getQualifiedType(ArgDesug, Quals)); } // The argument type can not be less qualified than the parameter // type. if (!(TDF & TDF_IgnoreQualifiers) && - hasInconsistentOrSupersetQualifiersOf(Param, Arg)) { + hasInconsistentOrSupersetQualifiersOf(Param, ArgDesug)) { Info.Param = cast(TemplateParams->getParam(Index)); Info.FirstArg = TemplateArgument(Param); Info.SecondArg = TemplateArgument(Arg); @@ -1496,7 +1498,8 @@ assert(TemplateTypeParm->getDepth() == Info.getDeducedDepth() && "saw template type parameter with wrong depth"); - assert(Arg != S.Context.OverloadTy && "Unresolved overloaded function"); + assert(ArgDesug != S.Context.OverloadTy && + "Unresolved overloaded function"); QualType DeducedType = Arg; // Remove any qualifiers on the parameter from the deduced type. @@ -1530,11 +1533,8 @@ !DeducedQs.hasObjCLifetime()) DeducedQs.setObjCLifetime(Qualifiers::OCL_Strong); - DeducedType = S.Context.getQualifiedType(DeducedType.getUnqualifiedType(), - DeducedQs); - - if (RecanonicalizeArg) - DeducedType = S.Context.getCanonicalType(DeducedType); + DeducedType = + S.Context.getQualifiedType(DeducedType.getUnqualifiedType(), DeducedQs); DeducedTemplateArgument NewDeduced(DeducedType, DeducedFromArrayBound); DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context, @@ -1552,14 +1552,14 @@ } // Set up the template argument deduction information for a failure. - Info.FirstArg = TemplateArgument(ParamIn); - Info.SecondArg = TemplateArgument(ArgIn); + Info.FirstArg = TemplateArgument(Param); + Info.SecondArg = TemplateArgument(Arg); // If the parameter is an already-substituted template parameter // pack, do nothing: we don't know which of its arguments to look // at, so we have to wait until all of the parameter packs in this // expansion have arguments. - if (isa(Param)) + if (isa(ParDesug)) return Sema::TDK_Success; // Check the cv-qualifiers on the parameter and argument types. @@ -1573,16 +1573,16 @@ // C++ [temp.deduct.conv]p4: // If the original A is a reference type, A can be more cv-qualified // than the deduced A - if (!Arg.getQualifiers().compatiblyIncludes(Param.getQualifiers())) + if (!ArgDesug.getQualifiers().compatiblyIncludes(Param.getQualifiers())) return Sema::TDK_NonDeducedMismatch; // Strip out all extra qualifiers from the argument to figure out the // type we're converting to, prior to the qualification conversion. Qualifiers Quals; - Arg = S.Context.getUnqualifiedArrayType(Arg, Quals); - Arg = S.Context.getQualifiedType(Arg, Param.getQualifiers()); + updateArg(S.Context.getUnqualifiedArrayType(ArgDesug, Quals)); + updateArg(S.Context.getQualifiedType(ArgDesug, Param.getQualifiers())); } else if (!IsPossiblyOpaquelyQualifiedType(Param)) { - if (Param.getCVRQualifiers() != Arg.getCVRQualifiers()) + if (ParDesug.getCVRQualifiers() != ArgDesug.getCVRQualifiers()) return Sema::TDK_NonDeducedMismatch; } @@ -1592,7 +1592,7 @@ bool NonDeduced = (TDF & TDF_AllowCompatibleFunctionType) ? !S.isSameOrCompatibleFunctionType(CanParam, CanArg) - : Param != Arg; + : CanParam != CanArg; if (NonDeduced) { return Sema::TDK_NonDeducedMismatch; } @@ -1614,7 +1614,7 @@ } } - switch (Param->getTypeClass()) { + switch (ParDesug->getTypeClass()) { // Non-canonical types cannot appear here. #define NON_CANONICAL_TYPE(Class, Base) \ case Type::Class: llvm_unreachable("deducing non-canonical type: " #Class); @@ -1641,99 +1641,98 @@ return Sema::TDK_Success; if (TDF & TDF_IgnoreQualifiers) { - Param = Param.getUnqualifiedType(); - Arg = Arg.getUnqualifiedType(); + ParDesug = ParDesug.getUnqualifiedType(); + ArgDesug = ArgDesug.getUnqualifiedType(); } - return Param == Arg? Sema::TDK_Success : Sema::TDK_NonDeducedMismatch; + return ParDesug == ArgDesug ? Sema::TDK_Success + : Sema::TDK_NonDeducedMismatch; // _Complex T [placeholder extension] case Type::Complex: - if (const ComplexType *ComplexArg = Arg->getAs()) - return DeduceTemplateArgumentsByTypeMatch(S, TemplateParams, - cast(Param)->getElementType(), - ComplexArg->getElementType(), - Info, Deduced, TDF); + if (const ComplexType *ComplexArg = ArgDesug->getAs()) + return DeduceTemplateArgumentsByTypeMatch( + S, TemplateParams, cast(ParDesug)->getElementType(), + ComplexArg->getElementType(), Info, Deduced, TDF); return Sema::TDK_NonDeducedMismatch; // _Atomic T [extension] case Type::Atomic: - if (const AtomicType *AtomicArg = Arg->getAs()) - return DeduceTemplateArgumentsByTypeMatch(S, TemplateParams, - cast(Param)->getValueType(), - AtomicArg->getValueType(), - Info, Deduced, TDF); + if (const AtomicType *AtomicArg = ArgDesug->getAs()) + return DeduceTemplateArgumentsByTypeMatch( + S, TemplateParams, cast(ParDesug)->getValueType(), + AtomicArg->getValueType(), Info, Deduced, TDF); return Sema::TDK_NonDeducedMismatch; // T * case Type::Pointer: { QualType PointeeType; - if (const PointerType *PointerArg = Arg->getAs()) { + if (const PointerType *PointerArg = ArgDesug->getAs()) { PointeeType = PointerArg->getPointeeType(); - } else if (const ObjCObjectPointerType *PointerArg - = Arg->getAs()) { + } else if (const ObjCObjectPointerType *PointerArg = + ArgDesug->getAs()) { PointeeType = PointerArg->getPointeeType(); } else { return Sema::TDK_NonDeducedMismatch; } unsigned SubTDF = TDF & (TDF_IgnoreQualifiers | TDF_DerivedClass); - return DeduceTemplateArgumentsByTypeMatch(S, TemplateParams, - cast(Param)->getPointeeType(), - PointeeType, - Info, Deduced, SubTDF); + return DeduceTemplateArgumentsByTypeMatch( + S, TemplateParams, cast(ParDesug)->getPointeeType(), + PointeeType, Info, Deduced, SubTDF); } // T & case Type::LValueReference: { const LValueReferenceType *ReferenceArg = - Arg->getAs(); + ArgDesug->getAs(); if (!ReferenceArg) return Sema::TDK_NonDeducedMismatch; - return DeduceTemplateArgumentsByTypeMatch(S, TemplateParams, - cast(Param)->getPointeeType(), - ReferenceArg->getPointeeType(), Info, Deduced, 0); + return DeduceTemplateArgumentsByTypeMatch( + S, TemplateParams, + cast(ParDesug)->getPointeeType(), + ReferenceArg->getPointeeType(), Info, Deduced, 0); } // T && [C++0x] case Type::RValueReference: { const RValueReferenceType *ReferenceArg = - Arg->getAs(); + ArgDesug->getAs(); if (!ReferenceArg) return Sema::TDK_NonDeducedMismatch; - return DeduceTemplateArgumentsByTypeMatch(S, TemplateParams, - cast(Param)->getPointeeType(), - ReferenceArg->getPointeeType(), - Info, Deduced, 0); + return DeduceTemplateArgumentsByTypeMatch( + S, TemplateParams, + cast(ParDesug)->getPointeeType(), + ReferenceArg->getPointeeType(), Info, Deduced, 0); } // T [] (implied, but not stated explicitly) case Type::IncompleteArray: { const IncompleteArrayType *IncompleteArrayArg = - S.Context.getAsIncompleteArrayType(Arg); + S.Context.getAsIncompleteArrayType(ArgDesug); if (!IncompleteArrayArg) return Sema::TDK_NonDeducedMismatch; unsigned SubTDF = TDF & TDF_IgnoreQualifiers; - return DeduceTemplateArgumentsByTypeMatch(S, TemplateParams, - S.Context.getAsIncompleteArrayType(Param)->getElementType(), - IncompleteArrayArg->getElementType(), - Info, Deduced, SubTDF); + return DeduceTemplateArgumentsByTypeMatch( + S, TemplateParams, + S.Context.getAsIncompleteArrayType(ParDesug)->getElementType(), + IncompleteArrayArg->getElementType(), Info, Deduced, SubTDF); } // T [integer-constant] case Type::ConstantArray: { const ConstantArrayType *ConstantArrayArg = - S.Context.getAsConstantArrayType(Arg); + S.Context.getAsConstantArrayType(ArgDesug); if (!ConstantArrayArg) return Sema::TDK_NonDeducedMismatch; const ConstantArrayType *ConstantArrayParm = - S.Context.getAsConstantArrayType(Param); + S.Context.getAsConstantArrayType(ParDesug); if (ConstantArrayArg->getSize() != ConstantArrayParm->getSize()) return Sema::TDK_NonDeducedMismatch; @@ -1746,15 +1745,15 @@ // type [i] case Type::DependentSizedArray: { - const ArrayType *ArrayArg = S.Context.getAsArrayType(Arg); + const ArrayType *ArrayArg = S.Context.getAsArrayType(ArgDesug); if (!ArrayArg) return Sema::TDK_NonDeducedMismatch; unsigned SubTDF = TDF & TDF_IgnoreQualifiers; // Check the element type of the arrays - const DependentSizedArrayType *DependentArrayParm - = S.Context.getAsDependentSizedArrayType(Param); + const DependentSizedArrayType *DependentArrayParm = + S.Context.getAsDependentSizedArrayType(ParDesug); if (Sema::TemplateDeductionResult Result = DeduceTemplateArgumentsByTypeMatch(S, TemplateParams, DependentArrayParm->getElementType(), @@ -1797,12 +1796,12 @@ case Type::FunctionProto: { unsigned SubTDF = TDF & TDF_TopLevelParameterTypeList; const FunctionProtoType *FunctionProtoArg = - dyn_cast(Arg); + dyn_cast(ArgDesug); if (!FunctionProtoArg) return Sema::TDK_NonDeducedMismatch; const FunctionProtoType *FunctionProtoParam = - cast(Param); + cast(ParDesug); if (FunctionProtoParam->getMethodQuals() != FunctionProtoArg->getMethodQuals() || @@ -1813,8 +1812,12 @@ // Check return types. if (auto Result = DeduceTemplateArgumentsByTypeMatch( - S, TemplateParams, FunctionProtoParam->getReturnType(), - FunctionProtoArg->getReturnType(), Info, Deduced, 0)) + S, TemplateParams, + FunctionProtoParam->getReturnType().getUnqualifiedType(), + FunctionProtoArg->getReturnType().getUnqualifiedType(), Info, + Deduced, 0, + /*PartialOrdering=*/false, + /*DeducedFromArrayBound=*/false)) return Result; // Check parameter types. @@ -1870,9 +1873,9 @@ case Type::InjectedClassName: // Treat a template's injected-class-name as if the template // specialization type had been used. - Param = cast(Param) - ->getInjectedSpecializationType(); - assert(isa(Param) && + updatePar(cast(ParDesug) + ->getInjectedSpecializationType()); + assert(isa(ParDesug) && "injected class name is not a template specialization type"); LLVM_FALLTHROUGH; @@ -1883,11 +1886,11 @@ // TT<> case Type::TemplateSpecialization: { const TemplateSpecializationType *SpecParam = - cast(Param); + cast(ParDesug); // When Arg cannot be a derived class, we can just try to deduce template // arguments from the template-id. - const RecordType *RecordT = Arg->getAs(); + const RecordType *RecordT = ArgDesug->getAs(); if (!(TDF & TDF_DerivedClass) || !RecordT) return DeduceTemplateArguments(S, TemplateParams, SpecParam, Arg, Info, Deduced); @@ -1929,8 +1932,9 @@ // T (T::*)() // T (T::*)(T) case Type::MemberPointer: { - const MemberPointerType *MemPtrParam = cast(Param); - const MemberPointerType *MemPtrArg = dyn_cast(Arg); + const MemberPointerType *MemPtrParam = cast(ParDesug); + const MemberPointerType *MemPtrArg = + dyn_cast(ArgDesug); if (!MemPtrArg) return Sema::TDK_NonDeducedMismatch; @@ -1964,8 +1968,9 @@ // T(^)() // T(^)(T) case Type::BlockPointer: { - const BlockPointerType *BlockPtrParam = cast(Param); - const BlockPointerType *BlockPtrArg = dyn_cast(Arg); + const BlockPointerType *BlockPtrParam = cast(ParDesug); + const BlockPointerType *BlockPtrArg = + dyn_cast(ArgDesug); if (!BlockPtrArg) return Sema::TDK_NonDeducedMismatch; @@ -1980,8 +1985,8 @@ // // T __attribute__(((ext_vector_type()))) case Type::ExtVector: { - const ExtVectorType *VectorParam = cast(Param); - if (const ExtVectorType *VectorArg = dyn_cast(Arg)) { + const ExtVectorType *VectorParam = cast(ParDesug); + if (const ExtVectorType *VectorArg = dyn_cast(ArgDesug)) { // Make sure that the vectors have the same number of elements. if (VectorParam->getNumElements() != VectorArg->getNumElements()) return Sema::TDK_NonDeducedMismatch; @@ -1993,8 +1998,8 @@ Info, Deduced, TDF); } - if (const DependentSizedExtVectorType *VectorArg - = dyn_cast(Arg)) { + if (const DependentSizedExtVectorType *VectorArg = + dyn_cast(ArgDesug)) { // We can't check the number of elements, since the argument has a // dependent number of elements. This can only occur during partial // ordering. @@ -2010,9 +2015,9 @@ } case Type::DependentVector: { - const auto *VectorParam = cast(Param); + const auto *VectorParam = cast(ParDesug); - if (const auto *VectorArg = dyn_cast(Arg)) { + if (const auto *VectorArg = dyn_cast(ArgDesug)) { // Perform deduction on the element types. if (Sema::TemplateDeductionResult Result = DeduceTemplateArgumentsByTypeMatch( @@ -2036,7 +2041,7 @@ Info, Deduced); } - if (const auto *VectorArg = dyn_cast(Arg)) { + if (const auto *VectorArg = dyn_cast(ArgDesug)) { // Perform deduction on the element types. if (Sema::TemplateDeductionResult Result = DeduceTemplateArgumentsByTypeMatch( @@ -2061,10 +2066,10 @@ // // T __attribute__(((ext_vector_type(N)))) case Type::DependentSizedExtVector: { - const DependentSizedExtVectorType *VectorParam - = cast(Param); + const DependentSizedExtVectorType *VectorParam = + cast(ParDesug); - if (const ExtVectorType *VectorArg = dyn_cast(Arg)) { + if (const ExtVectorType *VectorArg = dyn_cast(ArgDesug)) { // Perform deduction on the element types. if (Sema::TemplateDeductionResult Result = DeduceTemplateArgumentsByTypeMatch(S, TemplateParams, @@ -2089,8 +2094,8 @@ Deduced); } - if (const DependentSizedExtVectorType *VectorArg - = dyn_cast(Arg)) { + if (const DependentSizedExtVectorType *VectorArg = + dyn_cast(ArgDesug)) { // Perform deduction on the element types. if (Sema::TemplateDeductionResult Result = DeduceTemplateArgumentsByTypeMatch(S, TemplateParams, @@ -2118,11 +2123,13 @@ // T __attribute__((matrix_type(, // ))) case Type::ConstantMatrix: { - const ConstantMatrixType *MatrixArg = dyn_cast(Arg); + const ConstantMatrixType *MatrixArg = + dyn_cast(ArgDesug); if (!MatrixArg) return Sema::TDK_NonDeducedMismatch; - const ConstantMatrixType *MatrixParam = cast(Param); + const ConstantMatrixType *MatrixParam = + cast(ParDesug); // Check that the dimensions are the same if (MatrixParam->getNumRows() != MatrixArg->getNumRows() || MatrixParam->getNumColumns() != MatrixArg->getNumColumns()) { @@ -2135,13 +2142,13 @@ } case Type::DependentSizedMatrix: { - const MatrixType *MatrixArg = dyn_cast(Arg); + const MatrixType *MatrixArg = dyn_cast(ArgDesug); if (!MatrixArg) return Sema::TDK_NonDeducedMismatch; // Check the element type of the matrixes. const DependentSizedMatrixType *MatrixParam = - cast(Param); + cast(ParDesug); if (Sema::TemplateDeductionResult Result = DeduceTemplateArgumentsByTypeMatch( S, TemplateParams, MatrixParam->getElementType(), @@ -2212,10 +2219,10 @@ // T __attribute__(((address_space(N)))) case Type::DependentAddressSpace: { const DependentAddressSpaceType *AddressSpaceParam = - cast(Param); + cast(ParDesug); if (const DependentAddressSpaceType *AddressSpaceArg = - dyn_cast(Arg)) { + dyn_cast(ArgDesug)) { // Perform deduction on the pointer type. if (Sema::TemplateDeductionResult Result = DeduceTemplateArgumentsByTypeMatch( @@ -2234,16 +2241,17 @@ Deduced); } - if (isTargetAddressSpace(Arg.getAddressSpace())) { + if (isTargetAddressSpace(ArgDesug.getAddressSpace())) { llvm::APSInt ArgAddressSpace(S.Context.getTypeSize(S.Context.IntTy), false); - ArgAddressSpace = toTargetAddressSpace(Arg.getAddressSpace()); + ArgAddressSpace = toTargetAddressSpace(ArgDesug.getAddressSpace()); // Perform deduction on the pointer types. if (Sema::TemplateDeductionResult Result = DeduceTemplateArgumentsByTypeMatch( S, TemplateParams, AddressSpaceParam->getPointeeType(), - S.Context.removeAddrSpaceQualType(Arg), Info, Deduced, TDF)) + S.Context.removeAddrSpaceQualType(ArgDesug), Info, Deduced, + TDF)) return Result; // Perform deduction on the address space, if we can. @@ -2260,9 +2268,9 @@ return Sema::TDK_NonDeducedMismatch; } case Type::DependentExtInt: { - const auto *IntParam = cast(Param); + const auto *IntParam = cast(ParDesug); - if (const auto *IntArg = dyn_cast(Arg)){ + if (const auto *IntArg = dyn_cast(ArgDesug)) { if (IntParam->isUnsigned() != IntArg->isUnsigned()) return Sema::TDK_NonDeducedMismatch; @@ -2279,7 +2287,7 @@ Deduced); } - if (const auto *IntArg = dyn_cast(Arg)) { + if (const auto *IntArg = dyn_cast(ArgDesug)) { if (IntParam->isUnsigned() != IntArg->isUnsigned()) return Sema::TDK_NonDeducedMismatch; return Sema::TDK_Success; @@ -2300,7 +2308,7 @@ case Type::Pipe: // No template argument deduction for these types return Sema::TDK_Success; - } + } llvm_unreachable("Invalid Type Class!"); } @@ -2366,43 +2374,40 @@ if (Arg.getKind() == TemplateArgument::Integral) { if (hasSameExtendedValue(Param.getAsIntegral(), Arg.getAsIntegral())) return Sema::TDK_Success; - - Info.FirstArg = Param; - Info.SecondArg = Arg; - return Sema::TDK_NonDeducedMismatch; - } - - if (Arg.getKind() == TemplateArgument::Expression) { - Info.FirstArg = Param; - Info.SecondArg = Arg; - return Sema::TDK_NonDeducedMismatch; } - Info.FirstArg = Param; Info.SecondArg = Arg; return Sema::TDK_NonDeducedMismatch; - case TemplateArgument::Expression: + case TemplateArgument::Expression: { + auto *E = Param.getAsExpr(); + if (!E->isValueDependent()) { + if (const auto Int = E->getIntegerConstantExpr(S.Context)) { + if (Arg.getKind() == TemplateArgument::Integral) { + if (hasSameExtendedValue(*Int, Arg.getAsIntegral())) + return Sema::TDK_Success; + } + Info.FirstArg = Param; + Info.SecondArg = Arg; + return Sema::TDK_NonDeducedMismatch; + } + } if (const NonTypeTemplateParmDecl *NTTP = - getDeducedParameterFromExpr(Info, Param.getAsExpr())) { + getDeducedParameterFromExpr(Info, E)) { if (Arg.getKind() == TemplateArgument::Integral) - return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, - Arg.getAsIntegral(), - Arg.getIntegralType(), - /*ArrayBound=*/false, - Info, Deduced); + return DeduceNonTypeTemplateArgument( + S, TemplateParams, NTTP, Arg.getAsIntegral(), Arg.getIntegralType(), + /*ArrayBound=*/false, Info, Deduced); if (Arg.getKind() == TemplateArgument::NullPtr) - return DeduceNullPtrTemplateArgument(S, TemplateParams, NTTP, - Arg.getNullPtrType(), - Info, Deduced); + return DeduceNullPtrTemplateArgument( + S, TemplateParams, NTTP, Arg.getNullPtrType(), Info, Deduced); if (Arg.getKind() == TemplateArgument::Expression) return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, Arg.getAsExpr(), Info, Deduced); if (Arg.getKind() == TemplateArgument::Declaration) - return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, - Arg.getAsDecl(), - Arg.getParamTypeForDecl(), - Info, Deduced); + return DeduceNonTypeTemplateArgument( + S, TemplateParams, NTTP, Arg.getAsDecl(), Arg.getParamTypeForDecl(), + Info, Deduced); Info.FirstArg = Param; Info.SecondArg = Arg; @@ -2411,7 +2416,7 @@ // Can't deduce anything, but that's okay. return Sema::TDK_Success; - + } case TemplateArgument::Pack: llvm_unreachable("Argument packs should be expanded by the caller!"); } @@ -4358,7 +4363,7 @@ bool HasDeducedReturnType = false; if (getLangOpts().CPlusPlus14 && IsAddressOfFunction && Function->getReturnType()->getContainedAutoType()) { - FunctionType = SubstAutoType(FunctionType, Context.DependentTy); + FunctionType = SubstAutoTypeDependent(FunctionType); HasDeducedReturnType = true; } @@ -4796,8 +4801,6 @@ QualType Deduced = BuildDecltypeType(Init, Init->getBeginLoc(), false); if (Deduced.isNull()) return DAR_FailedAlreadyDiagnosed; - // FIXME: Support a non-canonical deduced type for 'auto'. - Deduced = Context.getCanonicalType(Deduced); if (AT->isConstrained() && !IgnoreConstraints) { auto ConstraintsResult = CheckDeducedPlaceholderConstraints(*this, *AT, @@ -4832,7 +4835,7 @@ Context, Loc, Loc, TemplParamPtr, Loc, nullptr); QualType FuncParam = - SubstituteDeducedTypeTransform(*this, TemplArg, /*UseTypeSugar*/false) + SubstituteDeducedTypeTransform(*this, TemplArg, /*UseTypeSugar*/ true) .Apply(Type); assert(!FuncParam.isNull() && "substituting template parameter for 'auto' failed"); @@ -4946,27 +4949,29 @@ QualType Sema::SubstAutoType(QualType TypeWithAuto, QualType TypeToReplaceAuto) { - if (TypeToReplaceAuto->isDependentType()) - return SubstituteDeducedTypeTransform( - *this, DependentAuto{ - TypeToReplaceAuto->containsUnexpandedParameterPack()}) - .TransformType(TypeWithAuto); + assert(TypeToReplaceAuto != Context.DependentTy); return SubstituteDeducedTypeTransform(*this, TypeToReplaceAuto) .TransformType(TypeWithAuto); } TypeSourceInfo *Sema::SubstAutoTypeSourceInfo(TypeSourceInfo *TypeWithAuto, QualType TypeToReplaceAuto) { - if (TypeToReplaceAuto->isDependentType()) - return SubstituteDeducedTypeTransform( - *this, - DependentAuto{ - TypeToReplaceAuto->containsUnexpandedParameterPack()}) - .TransformType(TypeWithAuto); + assert(TypeToReplaceAuto != Context.DependentTy); return SubstituteDeducedTypeTransform(*this, TypeToReplaceAuto) .TransformType(TypeWithAuto); } +QualType Sema::SubstAutoTypeDependent(QualType TypeWithAuto) { + return SubstituteDeducedTypeTransform(*this, DependentAuto{false}) + .TransformType(TypeWithAuto); +} + +TypeSourceInfo * +Sema::SubstAutoTypeSourceInfoDependent(TypeSourceInfo *TypeWithAuto) { + return SubstituteDeducedTypeTransform(*this, DependentAuto{false}) + .TransformType(TypeWithAuto); +} + QualType Sema::ReplaceAutoType(QualType TypeWithAuto, QualType TypeToReplaceAuto) { return SubstituteDeducedTypeTransform(*this, TypeToReplaceAuto, @@ -5161,6 +5166,10 @@ Args2.resize(NumComparedArguments); if (Reversed) std::reverse(Args2.begin(), Args2.end()); + + auto TF = [&S](QualType T) { return S.Context.getCanonicalType(T); }; + llvm::transform(Args1, Args1.begin(), TF); + llvm::transform(Args2, Args2.begin(), TF); if (DeduceTemplateArguments(S, TemplateParams, Args2.data(), Args2.size(), Args1.data(), Args1.size(), Info, Deduced, TDF_None, /*PartialOrdering=*/true)) diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -6561,7 +6561,7 @@ T->isDependentType() || T->isConstrained()) { // FIXME: Maybe don't rebuild if all template arguments are the same. llvm::SmallVector NewArgList; - NewArgList.reserve(NewArgList.size()); + NewArgList.reserve(NewTemplateArgs.size()); for (const auto &ArgLoc : NewTemplateArgs.arguments()) NewArgList.push_back(ArgLoc.getArgument()); Result = getDerived().RebuildAutoType(NewDeduced, T->getKeyword(), NewCD, diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-cxx14.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-cxx14.cpp --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-cxx14.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-cxx14.cpp @@ -66,7 +66,7 @@ auto multi1a = 0, &multi1b = multi1a; auto multi1c = multi1a, multi1d = multi1b; -decltype(auto) multi1e = multi1a, multi1f = multi1b; // expected-error {{'decltype(auto)' deduced as 'int' in declaration of 'multi1e' and deduced as 'int &' in declaration of 'multi1f'}} +decltype(auto) multi1e = multi1a, multi1f = multi1b; // expected-error {{'decltype(auto)' deduced as 'decltype(multi1a)' (aka 'int') in declaration of 'multi1e' and deduced as 'decltype(multi1b)' (aka 'int &') in declaration of 'multi1f'}} auto f1a() { return 0; } decltype(auto) f1d() { return 0; } diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4-cxx14.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4-cxx14.cpp --- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4-cxx14.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4-cxx14.cpp @@ -7,7 +7,7 @@ int &c = [] (int &r) -> decltype(auto) { return (r); } (a); int &d = [] (int &r) -> auto & { return r; } (a); int &e = [] (int &r) -> auto { return r; } (a); // expected-error {{cannot bind to a temporary}} -int &f = [] (int r) -> decltype(auto) { return r; } (a); // expected-error {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}} +int &f = [] (int r) -> decltype(auto) { return r; } (a); // expected-error {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'decltype(r)' (aka 'int')}} int &g = [] (int r) -> decltype(auto) { return (r); } (a); // expected-warning {{reference to stack}} // cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}} diff --git a/clang/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p2.cpp b/clang/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p2.cpp --- a/clang/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p2.cpp +++ b/clang/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p2.cpp @@ -36,7 +36,7 @@ namespace p0702r1 { template struct X { // expected-note {{candidate}} - X(std::initializer_list); // expected-note {{candidate template ignored: could not match 'initializer_list' against 'p0702r1::Z'}} + X(std::initializer_list); // expected-note {{candidate template ignored: could not match 'initializer_list' against 'p0702r1::Z'}} }; X xi = {0}; diff --git a/clang/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp b/clang/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp --- a/clang/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp +++ b/clang/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp @@ -321,14 +321,14 @@ namespace NE { struct E { - void begin(); // expected-note {{member is not a candidate because range type 'p0962r1::NE::E' has no 'end' member}} + void begin(); // expected-note {{member is not a candidate because range type 'NE::E' has no 'end' member}} }; int *end(E); } namespace NF { struct F { - void end(); // expected-note {{member is not a candidate because range type 'p0962r1::NF::F' has no 'begin' member}} + void end(); // expected-note {{member is not a candidate because range type 'NF::F' has no 'begin' member}} }; int *begin(F); } @@ -336,9 +336,9 @@ void use(NA::A a, NB::B b, NC::C c, ND::D d, NE::E e, NF::F f) { for (auto x : a) {} for (auto x : b) {} - for (auto x : c) {} // expected-error {{invalid range expression of type 'p0962r1::NC::C'; no viable 'end' function available}} - for (auto x : d) {} // expected-error {{invalid range expression of type 'p0962r1::ND::D'; no viable 'begin' function available}} - for (auto x : e) {} // expected-error {{invalid range expression of type 'p0962r1::NE::E'; no viable 'begin' function available}} - for (auto x : f) {} // expected-error {{invalid range expression of type 'p0962r1::NF::F'; no viable 'end' function available}} + for (auto x : c) {} // expected-error {{invalid range expression of type 'NC::C'; no viable 'end' function available}} + for (auto x : d) {} // expected-error {{invalid range expression of type 'ND::D'; no viable 'begin' function available}} + for (auto x : e) {} // expected-error {{invalid range expression of type 'NE::E'; no viable 'begin' function available}} + for (auto x : f) {} // expected-error {{invalid range expression of type 'NF::F'; no viable 'end' function available}} } } diff --git a/clang/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp b/clang/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp --- a/clang/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp +++ b/clang/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp @@ -284,7 +284,7 @@ void g(U &&...u, T &&...t) {} // expected-note {{candidate}} template void h(tuple &&...) {} - // expected-note@-1 {{candidate template ignored: could not match 'tuple' against 'int'}} + // expected-note@-1 {{candidate template ignored: could not match 'tuple' against 'int'}} // expected-note@-2 {{candidate template ignored: substitution failure: deduced incomplete pack <(no value)> for template parameter 'U'}} template diff --git a/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p1-0x.cpp b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p1-0x.cpp --- a/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p1-0x.cpp +++ b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p1-0x.cpp @@ -53,9 +53,8 @@ } -// FIXME: Use the template parameter names in this diagnostic. template -typename get_nth_type<0, Args1...>::type first_arg_pair(pair...); // expected-note{{candidate template ignored: could not match 'pair' against 'int'}} +typename get_nth_type<0, Args1...>::type first_arg_pair(pair...); // expected-note{{candidate template ignored: could not match 'pair' against 'int'}} template typename get_nth_type<1, Args1...>::type second_arg_pair(pair...); diff --git a/clang/test/Index/print-type.cpp b/clang/test/Index/print-type.cpp --- a/clang/test/Index/print-type.cpp +++ b/clang/test/Index/print-type.cpp @@ -183,7 +183,7 @@ // CHECK: CompoundStmt= [type=] [typekind=Invalid] [isPOD=0] // CHECK: ReturnStmt= [type=] [typekind=Invalid] [isPOD=0] // CHECK: UnexposedExpr= [type=int] [typekind=Int] [isPOD=1] -// CHECK: VarDecl=autoInt:58:16 (Definition) [type=int] [typekind=Auto] [canonicaltype=int] [canonicaltypekind=Int] [isPOD=1] +// CHECK: VarDecl=autoInt:58:16 (Definition) [type=decltype(5)] [typekind=Auto] [canonicaltype=int] [canonicaltypekind=Int] [isPOD=1] // CHECK: IntegerLiteral= [type=int] [typekind=Int] [isPOD=1] // CHECK: TypeAliasTemplateDecl=TypeAlias:61:1 (Definition) [type=] [typekind=Invalid] [isPOD=0] // CHECK: TemplateTypeParameter=T:60:20 (Definition) [type=T] [typekind=Unexposed] [canonicaltype=type-parameter-0-0] [canonicaltypekind=Unexposed] [isPOD=0] @@ -199,7 +199,7 @@ // CHECK: UnexposedExpr=templRefParam:71:40 [type=const Specialization &>] [typekind=Record] const [templateargs/1= [type=Specialization &] [typekind=LValueReference]] [isPOD=1] // CHECK: DeclRefExpr=templRefParam:71:40 [type=Specialization &>] [typekind=Unexposed] [templateargs/1= [type=Specialization &] [typekind=LValueReference]] [canonicaltype=Specialization &>] [canonicaltypekind=Record] [canonicaltemplateargs/1= [type=Specialization &] [typekind=LValueReference]] [isPOD=1] // CHECK: TypeAliasDecl=baz:76:7 (Definition) [type=baz] [typekind=Typedef] [templateargs/1= [type=A] [typekind=Unexposed]] [canonicaltype=A] [canonicaltypekind=Record] [canonicaltemplateargs/1= [type=void] [typekind=Void]] [isPOD=0] -// CHECK: VarDecl=autoTemplPointer:78:6 (Definition) [type=Specialization &> *] [typekind=Auto] [canonicaltype=Specialization &> *] [canonicaltypekind=Pointer] [isPOD=1] [pointeetype=Specialization &>] [pointeekind=Record] +// CHECK: VarDecl=autoTemplPointer:78:6 (Definition) [type=Specialization &> *] [typekind=Auto] [canonicaltype=Specialization &> *] [canonicaltypekind=Pointer] [isPOD=1] [pointeetype=Specialization &>] [pointeekind=Auto] // CHECK: CallExpr=Bar:17:3 [type=outer::inner::Bar] [typekind=Elaborated] [canonicaltype=outer::inner::Bar] [canonicaltypekind=Record] [args= [outer::Foo *] [Pointer]] [isPOD=0] [nbFields=3] // CHECK: StructDecl=:84:3 (Definition) [type=X::(anonymous struct at {{.*}}print-type.cpp:84:3)] [typekind=Record] [isPOD=1] [nbFields=1] [isAnon=1] // CHECK: ClassDecl=:85:3 (Definition) [type=X::(anonymous class at {{.*}}print-type.cpp:85:3)] [typekind=Record] [isPOD=1] [nbFields=1] [isAnon=1] diff --git a/clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp b/clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp --- a/clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp +++ b/clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp @@ -555,6 +555,31 @@ void foo() { insert(Foo(2, 2, 2)); // expected-error{{no viable constructor or deduction guide}} } + +namespace function_prototypes { + template using fptr1 = void (*) (T); + template using fptr2 = fptr1>; + + template void foo1(fptr1) {} + void bar1(const char * __restrict); + void t1() { foo1(&bar1); } + + template void foo2(fptr2) {} + void bar2(fptr1); + void t2() { foo2(&bar2); } + + template void foo3(fptr1) {} + void bar3(char * __restrict); + void t3() { foo3(&bar3); } + // expected-error@-1 {{no matching function for call to 'foo3'}} + // expected-note@-4 {{candidate template ignored: cannot deduce a type for 'T' that would make 'const T' equal 'char'}} + + template void foo4(fptr2) {} + void bar4(fptr1); + void t4() { foo4(&bar4); } + // expected-error@-1 {{no matching function for call to 'foo4'}} + // expected-note@-4 {{candidate template ignored: cannot deduce a type for 'T' that would make 'const T' equal 'char'}} +} #else // expected-no-diagnostics diff --git a/clang/test/SemaCXX/cxx1z-decomposition.cpp b/clang/test/SemaCXX/cxx1z-decomposition.cpp --- a/clang/test/SemaCXX/cxx1z-decomposition.cpp +++ b/clang/test/SemaCXX/cxx1z-decomposition.cpp @@ -9,7 +9,7 @@ int a1[1], a2[2]; auto [] = a0; // expected-warning {{does not allow a decomposition group to be empty}} - auto [v1] = a0; // expected-error {{type 'A0' decomposes into 0 elements, but 1 name was provided}} + auto [v1] = a0; // expected-error {{type 'struct A0' decomposes into 0 elements, but 1 name was provided}} auto [] = a1; // expected-error {{type 'int [1]' decomposes into 1 element, but no names were provided}} expected-warning {{empty}} auto [v2] = a1; auto [v3, v4] = a1; // expected-error {{type 'int [1]' decomposes into 1 element, but 2 names were provided}} @@ -70,7 +70,7 @@ void bitfield() { struct { int a : 3, : 4, b : 5; } a; auto &[x, y] = a; - auto &[p, q, r] = a; // expected-error-re {{type '(unnamed struct at {{.*}})' decomposes into 2 elements, but 3 names were provided}} + auto &[p, q, r] = a; // expected-error-re {{type 'struct (unnamed struct at {{.*}})' decomposes into 2 elements, but 3 names were provided}} } void for_range() { diff --git a/clang/test/SemaCXX/deduced-return-type-cxx14.cpp b/clang/test/SemaCXX/deduced-return-type-cxx14.cpp --- a/clang/test/SemaCXX/deduced-return-type-cxx14.cpp +++ b/clang/test/SemaCXX/deduced-return-type-cxx14.cpp @@ -300,7 +300,7 @@ } struct NonLiteral { ~NonLiteral(); } nl; // cxx14-note {{user-provided destructor}} // cxx20_2b-note@-1 {{'NonLiteral' is not literal because its destructor is not constexpr}} - constexpr auto f2(int n) { return nl; } // expected-error {{return type 'Constexpr::NonLiteral' is not a literal type}} + constexpr auto f2(int n) { return nl; } // expected-error {{return type 'struct NonLiteral' is not a literal type}} } // It's not really clear whether these are valid, but this matches g++. diff --git a/clang/test/SemaCXX/friend.cpp b/clang/test/SemaCXX/friend.cpp --- a/clang/test/SemaCXX/friend.cpp +++ b/clang/test/SemaCXX/friend.cpp @@ -415,7 +415,7 @@ namespace qualified_friend_no_match { void f(int); // expected-note {{type mismatch at 1st parameter}} - template void f(T*); // expected-note {{could not match 'type-parameter-0-0 *' against 'double'}} + template void f(T*); // expected-note {{could not match 'T *' against 'double'}} struct X { friend void qualified_friend_no_match::f(double); // expected-error {{friend declaration of 'f' does not match any declaration in namespace 'qualified_friend_no_match'}} friend void qualified_friend_no_match::g(); // expected-error {{friend declaration of 'g' does not match any declaration in namespace 'qualified_friend_no_match'}} @@ -423,7 +423,7 @@ struct Y { void f(int); // expected-note {{type mismatch at 1st parameter}} - template void f(T*); // expected-note {{could not match 'type-parameter-0-0 *' against 'double'}} + template void f(T*); // expected-note {{could not match 'T *' against 'double'}} }; struct Z { friend void Y::f(double); // expected-error {{friend declaration of 'f' does not match any declaration in 'qualified_friend_no_match::Y'}} diff --git a/clang/test/SemaCXX/recovery-expr-type.cpp b/clang/test/SemaCXX/recovery-expr-type.cpp --- a/clang/test/SemaCXX/recovery-expr-type.cpp +++ b/clang/test/SemaCXX/recovery-expr-type.cpp @@ -133,7 +133,7 @@ template S(T t) -> S; void baz() { - bar(S(123)); // expected-error {{no matching conversion for functional-style cast from 'int' to 'test11::S<>'}} + bar(S(123)); // expected-error {{no matching conversion for functional-style cast from 'int' to 'S'}} } } // namespace test11 diff --git a/clang/test/SemaCXX/sizeless-1.cpp b/clang/test/SemaCXX/sizeless-1.cpp --- a/clang/test/SemaCXX/sizeless-1.cpp +++ b/clang/test/SemaCXX/sizeless-1.cpp @@ -581,7 +581,7 @@ auto auto_int8 = local_int8; auto auto_int16 = local_int16; #if __cplusplus >= 201703L - auto [auto_int8_a] = local_int8; // expected-error {{cannot decompose non-class, non-array type '__SVInt8_t'}} + auto [auto_int8_a] = local_int8; // expected-error {{cannot decompose non-class, non-array type 'svint8_t' (aka '__SVInt8_t')}} #endif #endif @@ -600,7 +600,7 @@ auto fn1 = [&local_int8](svint8_t x) { local_int8 = x; }; auto fn2 = [&local_int8](svint8_t *ptr) { *ptr = local_int8; }; #if __cplusplus >= 201703L - auto fn3 = [a(return_int8())] {}; // expected-error {{field has sizeless type '__SVInt8_t'}} + auto fn3 = [a(return_int8())] {}; // expected-error {{field has sizeless type 'svint8_t' (aka '__SVInt8_t')}} #endif auto fn4 = [local_int8](svint8_t *ptr) { *ptr = local_int8; }; // expected-error {{by-copy capture of variable 'local_int8' with sizeless type 'svint8_t'}} diff --git a/clang/test/SemaCXX/sugared-auto.cpp b/clang/test/SemaCXX/sugared-auto.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/sugared-auto.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++14 + +enum class N {}; + +using T1 = int; +auto x1 = T1(); +N t1 = x1; +// expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type 'T1' (aka 'int')}} + +using T2 = T1 *; +auto x2 = T2(); +N t2 = x2; +// expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type 'T2' (aka 'int *')}} + +auto *x3 = T2(); +N t3 = x3; +// expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type 'T1 *' (aka 'int *')}} + +auto f1() { return T1(); } +auto x4 = f1(); +N t4 = x4; +// expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type 'T1' (aka 'int')}} + +decltype(auto) f2() { return T1(); } +auto x5 = f2(); +N t5 = x5; +// expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type 'decltype(T1())' (aka 'int')}} + +auto x6 = [a = T1()] { return a; }(); +N t6 = x6; +// expected-error@-1 {{cannot initialize a variable of type 'N' with an lvalue of type 'T1' (aka 'int')}} diff --git a/clang/test/SemaTemplate/attributes.cpp b/clang/test/SemaTemplate/attributes.cpp --- a/clang/test/SemaTemplate/attributes.cpp +++ b/clang/test/SemaTemplate/attributes.cpp @@ -125,7 +125,7 @@ clang::preferred_name(const_iterator)]] Iter {}; }; auto it = MemberTemplate::Iter(); - int n = it; // expected-error {{no viable conversion from 'preferred_name::MemberTemplate::const_iterator' to 'int'}} + int n = it; // expected-error {{no viable conversion from 'MemberTemplate::Iter' to 'int'}} template struct Foo; template using Bar = Foo<1, 2, T...>; diff --git a/clang/test/SemaTemplate/friend.cpp b/clang/test/SemaTemplate/friend.cpp --- a/clang/test/SemaTemplate/friend.cpp +++ b/clang/test/SemaTemplate/friend.cpp @@ -50,7 +50,7 @@ namespace qualified_friend { void f(int); // expected-note 2{{type mismatch at 1st parameter}} - template void f(T*); // expected-note 2{{could not match 'type-parameter-0-0 *' against 'double'}} + template void f(T*); // expected-note 2{{could not match 'T *' against 'double'}} template void nondep(); template struct X1 { @@ -66,7 +66,7 @@ struct Y { void f(int); // expected-note 2{{type mismatch at 1st parameter}} - template void f(T*); // expected-note 2{{could not match 'type-parameter-0-0 *' against 'double'}} + template void f(T*); // expected-note 2{{could not match 'T *' against 'double'}} template void nondep(); }; diff --git a/clang/test/SemaTemplate/operator-template.cpp b/clang/test/SemaTemplate/operator-template.cpp --- a/clang/test/SemaTemplate/operator-template.cpp +++ b/clang/test/SemaTemplate/operator-template.cpp @@ -2,7 +2,7 @@ // Make sure we accept this templatestruct A{typedef X Y;}; -templatebool operator==(A,typename A::Y); // expected-note{{candidate template ignored: could not match 'A' against 'B *'}} +templatebool operator==(A,typename A::Y); // expected-note{{candidate template ignored: could not match 'A' against 'B *'}} int a(A x) { return operator==(x,1); } diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -6176,26 +6176,9 @@ FirstDeclMatcher().match(FromTU, functionDecl()); FunctionDecl *To = Import(From, Lang_CXX14); - EXPECT_TRUE(To); - EXPECT_TRUE(isa(To->getReturnType())); -} - -TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredInside2) { - Decl *FromTU = getTuDecl( - R"( - auto foo() { - struct X {}; - return X(); - } - )", - Lang_CXX14, "input0.cc"); - FunctionDecl *From = - FirstDeclMatcher().match(FromTU, functionDecl()); - - // This time import the type directly. - QualType ToT = ImportType(From->getType(), From, Lang_CXX14); - const FunctionProtoType *FPT = cast(ToT); - EXPECT_TRUE(isa(FPT->getReturnType())); + // FIXME: We do not support importing these. + // EXPECT: error: cannot import unsupported AST node CXXRecord + EXPECT_FALSE(To); } TEST_P(ImportAutoFunctions, ReturnWithTypedefToStructDeclaredInside) { @@ -6212,8 +6195,9 @@ FirstDeclMatcher().match(FromTU, functionDecl()); FunctionDecl *To = Import(From, Lang_CXX14); - EXPECT_TRUE(To); - EXPECT_TRUE(isa(To->getReturnType())); + // FIXME: We do not support importing these. + // EXPECT: error: cannot import unsupported AST node CXXRecord + EXPECT_FALSE(To); } TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredNestedInside) { @@ -6229,8 +6213,9 @@ FirstDeclMatcher().match(FromTU, functionDecl()); FunctionDecl *To = Import(From, Lang_CXX14); - EXPECT_TRUE(To); - EXPECT_TRUE(isa(To->getReturnType())); + // FIXME: We do not support importing these. + // EXPECT: error: cannot import unsupported AST node CXXRecord + EXPECT_FALSE(To); } TEST_P(ImportAutoFunctions, ReturnWithInternalLambdaType) { @@ -6249,8 +6234,9 @@ FromTU, functionDecl(hasName("f"))); FunctionDecl *To = Import(From, Lang_CXX17); - EXPECT_TRUE(To); - EXPECT_TRUE(isa(To->getReturnType())); + // FIXME: We do not support importing these. + // EXPECT: error: cannot import unsupported AST node CXXRecord + EXPECT_FALSE(To); } TEST_P(ImportAutoFunctions, ReturnWithTypeInIf) { @@ -6268,8 +6254,9 @@ FromTU, functionDecl(hasName("f"))); FunctionDecl *To = Import(From, Lang_CXX17); - EXPECT_TRUE(To); - EXPECT_TRUE(isa(To->getReturnType())); + // FIXME: We do not support importing these. + // EXPECT: error: cannot import unsupported AST node CXXRecord + EXPECT_FALSE(To); } TEST_P(ImportAutoFunctions, ReturnWithTypeInFor) { @@ -6285,8 +6272,9 @@ FromTU, functionDecl(hasName("f"))); FunctionDecl *To = Import(From, Lang_CXX17); - EXPECT_TRUE(To); - EXPECT_TRUE(isa(To->getReturnType())); + // FIXME: We do not support importing these. + // EXPECT: error: cannot import unsupported AST node CXXRecord + EXPECT_FALSE(To); } TEST_P(ImportAutoFunctions, ReturnWithTypeInSwitch) { @@ -6304,8 +6292,9 @@ FromTU, functionDecl(hasName("f"))); FunctionDecl *To = Import(From, Lang_CXX17); - EXPECT_TRUE(To); - EXPECT_TRUE(isa(To->getReturnType())); + // FIXME: We do not support importing these. + // EXPECT: error: cannot import unsupported AST node CXXRecord + EXPECT_FALSE(To); } struct ImportSourceLocations : ASTImporterOptionSpecificTestBase {};