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 @@ -212,7 +212,9 @@ mutable llvm::ContextualFoldingSet FunctionProtoTypes; mutable llvm::FoldingSet DependentTypeOfExprTypes; - mutable llvm::FoldingSet DependentDecltypeTypes; + mutable llvm::ContextualFoldingSet + DependentDecltypeTypes; + mutable llvm::ContextualFoldingSet DecltypeTypes; mutable llvm::FoldingSet TemplateTypeParmTypes; mutable llvm::FoldingSet ObjCTypeParamTypes; mutable llvm::FoldingSet diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1253,7 +1253,9 @@ // FIXME: location of underlying expr DEF_TRAVERSE_TYPELOC(DecltypeType, { - TRY_TO(TraverseStmt(TL.getTypePtr()->getUnderlyingExpr())); + TRY_TO(TraverseStmt(TL.getUnderlyingExpr() + ? TL.getUnderlyingExpr() + : TL.getTypePtr()->getUnderlyingExpr())); }) DEF_TRAVERSE_TYPELOC(UnaryTransformType, { 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 @@ -4475,7 +4475,7 @@ }; /// Represents the type `decltype(expr)` (C++11). -class DecltypeType : public Type { +class DecltypeType : public Type, public llvm::FoldingSetNode { Expr *E; QualType UnderlyingType; @@ -4495,6 +4495,12 @@ bool isSugared() const; static bool classof(const Type *T) { return T->getTypeClass() == Decltype; } + + void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx) { + Profile(ID, Ctx, E); + } + static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context, + Expr *E); }; /// Internal representation of canonical, dependent @@ -4503,18 +4509,10 @@ /// This class is used internally by the ASTContext to manage /// canonical, dependent types, only. Clients will only see instances /// of this class via DecltypeType nodes. -class DependentDecltypeType : public DecltypeType, public llvm::FoldingSetNode { - const ASTContext &Context; - +/// FIXME: remove this in favor of DecltypeType. +class DependentDecltypeType : public DecltypeType { public: DependentDecltypeType(const ASTContext &Context, Expr *E); - - void Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, Context, getUnderlyingExpr()); - } - - static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context, - Expr *E); }; /// A unary type transform, which is a type constructed from another. diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h --- a/clang/include/clang/AST/TypeLoc.h +++ b/clang/include/clang/AST/TypeLoc.h @@ -1936,12 +1936,28 @@ void initializeLocal(ASTContext &Context, SourceLocation Loc); }; +struct DecltypeTypeLocInfo { + Expr *UnderlyingExpr; + SourceLocation NameLoc; +}; + // FIXME: location of the 'decltype' and parens. -class DecltypeTypeLoc : public InheritingConcreteTypeLoc { +class DecltypeTypeLoc + : public ConcreteTypeLoc { public: - Expr *getUnderlyingExpr() const { return getTypePtr()->getUnderlyingExpr(); } + Expr *getUnderlyingExpr() const { return getLocalData()->UnderlyingExpr; } + void setUnderlyingExpr(Expr *E) { getLocalData()->UnderlyingExpr = E; } + SourceLocation getNameLoc() const { return getLocalData()->NameLoc; } + SourceRange getLocalSourceRange() const { + return SourceRange(getNameLoc(), getNameLoc()); + } + void setNameLoc(SourceLocation Loc) { getLocalData()->NameLoc = Loc; } + + void initializeLocal(ASTContext &Context, SourceLocation Loc) { + getLocalData()->NameLoc = Loc; + getLocalData()->UnderlyingExpr = nullptr; + } }; struct UnaryTransformTypeLocInfo { @@ -1951,10 +1967,9 @@ TypeSourceInfo *UnderlyingTInfo; }; -class UnaryTransformTypeLoc : public ConcreteTypeLoc { +class UnaryTransformTypeLoc + : public ConcreteTypeLoc { public: SourceLocation getKWLoc() const { return getLocalData()->KWLoc; } void setKWLoc(SourceLocation Loc) { getLocalData()->KWLoc = Loc; } 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 @@ -957,6 +957,7 @@ IdentifierTable &idents, SelectorTable &sels, Builtin::Context &builtins) : ConstantArrayTypes(this_()), FunctionProtoTypes(this_()), + DependentDecltypeTypes(this_()), DecltypeTypes(this_()), TemplateSpecializationTypes(this_()), DependentTemplateSpecializationTypes(this_()), AutoTypes(this_()), SubstTemplateTemplateParmPacks(this_()), @@ -5376,17 +5377,18 @@ } /// Unlike many "get" functions, we don't unique DecltypeType -/// nodes. This would never be helpful, since each such type has its own -/// expression, and would not give a significant memory saving, since there -/// is an Expr tree under each such type. +/// nodes unless they are instantiation-dependent-but-not-type-dependent. This +/// would never be helpful, since each such type has its own expression, and +/// would not give a significant memory saving, since there is an Expr tree +/// under each such type. QualType ASTContext::getDecltypeType(Expr *e, QualType UnderlyingType) const { DecltypeType *dt; - // C++11 [temp.type]p2: - // If an expression e involves a template parameter, decltype(e) denotes a + // C++17 [temp.type]p2: + // If an expression e is type-dependent (17.6.2.2), decltype(e) denotes a // unique dependent type. Two such decltype-specifiers refer to the same - // type only if their expressions are equivalent (14.5.6.1). - if (e->isInstantiationDependent()) { + // type only if their expressions are equivalent (17.5.6.1). + if (e->isTypeDependent()) { llvm::FoldingSetNodeID ID; DependentDecltypeType::Profile(ID, *this, e); @@ -5400,6 +5402,15 @@ } dt = new (*this, TypeAlignment) DecltypeType(e, UnderlyingType, QualType((DecltypeType *)Canon, 0)); + } else if (e->isInstantiationDependent()) { + llvm::FoldingSetNodeID ID; + DecltypeType::Profile(ID, *this, e); + void *InsertPos = nullptr; + if (DecltypeType *DT = DecltypeTypes.FindNodeOrInsertPos(ID, InsertPos)) + return QualType(DT, 0); + dt = new (*this, TypeAlignment) + DecltypeType(e, UnderlyingType, getCanonicalType(UnderlyingType)); + DecltypeTypes.InsertNode(dt, InsertPos); } else { dt = new (*this, TypeAlignment) DecltypeType(e, UnderlyingType, getCanonicalType(UnderlyingType)); diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -2500,6 +2500,8 @@ return true; if (Ty->isOpenCLSpecificType()) return true; + if (Ty->isDecltypeType() && Ty->isInstantiationDependentType()) + return true; if (Ty->isBuiltinType()) return false; // Through to Clang 6.0, we accidentally treated undeduced auto types as @@ -2561,6 +2563,12 @@ if (!TST->isTypeAlias()) break; + // Don't desugar through instantiation-dependent-but-not-type-dependent + // DecltypeType, as we want to mangle it as written. + if (const auto *DT = dyn_cast(T)) + if (!DT->isDependentType() && DT->isInstantiationDependentType()) + break; + QualType Desugared = T.getSingleStepDesugaredType(Context.getASTContext()); if (Desugared == T) 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 @@ -3417,18 +3417,19 @@ } DecltypeType::DecltypeType(Expr *E, QualType underlyingType, QualType can) - // C++11 [temp.type]p2: "If an expression e involves a template parameter, - // decltype(e) denotes a unique dependent type." Hence a decltype type is - // type-dependent even if its expression is only instantiation-dependent. + // C++17 [temp.type]p2: "If an expression e is type-dependent, decltype(e) + // denotes a unique dependent type." : Type(Decltype, can, - toTypeDependence(E->getDependence()) | - (E->isInstantiationDependent() ? TypeDependence::Dependent - : TypeDependence::None) | + // C++17 [temp.dep.type]p9: "denoted by decltype(expression), where + // expression is type-dependent" + (toTypeDependence(E->getDependence()) & ~TypeDependence::Dependent) | + (E->isTypeDependent() ? TypeDependence::Dependent + : TypeDependence::None) | (E->getType()->getDependence() & TypeDependence::VariablyModified)), E(E), UnderlyingType(underlyingType) {} -bool DecltypeType::isSugared() const { return !E->isInstantiationDependent(); } +bool DecltypeType::isSugared() const { return !E->isTypeDependent(); } QualType DecltypeType::desugar() const { if (isSugared()) @@ -3437,14 +3438,14 @@ return QualType(this, 0); } -DependentDecltypeType::DependentDecltypeType(const ASTContext &Context, Expr *E) - : DecltypeType(E, Context.DependentTy), Context(Context) {} - -void DependentDecltypeType::Profile(llvm::FoldingSetNodeID &ID, - const ASTContext &Context, Expr *E) { +void DecltypeType::Profile(llvm::FoldingSetNodeID &ID, + const ASTContext &Context, Expr *E) { E->Profile(ID, Context, true); } +DependentDecltypeType::DependentDecltypeType(const ASTContext &Context, Expr *E) + : DecltypeType(E, Context.DependentTy) {} + UnaryTransformType::UnaryTransformType(QualType BaseType, QualType UnderlyingType, UTTKind UKind, QualType CanonicalType) diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -864,6 +864,7 @@ TypeLocBuilder TLB; DecltypeTypeLoc DecltypeTL = TLB.push(T); DecltypeTL.setNameLoc(DS.getTypeSpecTypeLoc()); + DecltypeTL.setUnderlyingExpr(DS.getRepAsExpr()); SS.Extend(Context, SourceLocation(), TLB.getTypeLocInContext(Context, T), ColonColonLoc); return false; diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -7601,6 +7601,7 @@ TypeLocBuilder TLB; DecltypeTypeLoc DecltypeTL = TLB.push(T); DecltypeTL.setNameLoc(DS.getTypeSpecTypeLoc()); + DecltypeTL.setUnderlyingExpr(DS.getRepAsExpr()); TypeSourceInfo *DestructedTypeInfo = TLB.getTypeSourceInfo(Context, T); PseudoDestructorTypeStorage Destructed(DestructedTypeInfo); diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -5990,6 +5990,11 @@ TL.setNameLoc(DS.getTypeSpecTypeLoc()); } + void VisitDecltypeTypeLoc(DecltypeTypeLoc TL) { + TL.setUnderlyingExpr(DS.getRepAsExpr()); + TL.setNameLoc(DS.getTypeSpecTypeLoc()); + } + void VisitTypeLoc(TypeLoc TL) { // FIXME: add other typespec types and change this to an assert. TL.initialize(Context, DS.getTypeSpecTypeLoc()); 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 @@ -6052,7 +6052,11 @@ SemaRef, Sema::ExpressionEvaluationContext::Unevaluated, nullptr, Sema::ExpressionEvaluationContextRecord::EK_Decltype); - ExprResult E = getDerived().TransformExpr(T->getUnderlyingExpr()); + // Prefer the underlying expression from the TypeLoc; the other may have been + // uniqued. + auto *UnderlyingExpr = + TL.getUnderlyingExpr() ? TL.getUnderlyingExpr() : T->getUnderlyingExpr(); + ExprResult E = getDerived().TransformExpr(UnderlyingExpr); if (E.isInvalid()) return QualType(); @@ -6061,15 +6065,15 @@ return QualType(); QualType Result = TL.getType(); - if (getDerived().AlwaysRebuild() || - E.get() != T->getUnderlyingExpr()) { + if (getDerived().AlwaysRebuild() || E.get() != UnderlyingExpr) { Result = getDerived().RebuildDecltypeType(E.get(), TL.getNameLoc()); if (Result.isNull()) return QualType(); - } - else E.get(); + } else + E.get(); DecltypeTypeLoc NewTL = TLB.push(Result); + NewTL.setUnderlyingExpr(E.get()); NewTL.setNameLoc(TL.getNameLoc()); return Result; diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -6607,6 +6607,7 @@ void TypeLocReader::VisitDecltypeTypeLoc(DecltypeTypeLoc TL) { TL.setNameLoc(readSourceLocation()); + TL.setUnderlyingExpr(Reader.readExpr()); } void TypeLocReader::VisitUnaryTransformTypeLoc(UnaryTransformTypeLoc TL) { diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -358,6 +358,7 @@ void TypeLocWriter::VisitDecltypeTypeLoc(DecltypeTypeLoc TL) { Record.AddSourceLocation(TL.getNameLoc()); + Record.AddStmt(TL.getUnderlyingExpr()); } void TypeLocWriter::VisitUnaryTransformTypeLoc(UnaryTransformTypeLoc TL) { diff --git a/clang/test/CodeGenCXX/mangle.cpp b/clang/test/CodeGenCXX/mangle.cpp --- a/clang/test/CodeGenCXX/mangle.cpp +++ b/clang/test/CodeGenCXX/mangle.cpp @@ -805,6 +805,10 @@ // CHECK-LABEL: define weak_odr void @_ZN6test341fIiEEvDTstDTplcvT__EcvS1__EEE template void f(decltype(sizeof(1))); + template void f(decltype(sizeof(T)), decltype(sizeof(T))) {} + // CHECK-LABEL: define weak_odr void @_ZN6test341fIiEEvDTstT_ES2_ + template void f(unsigned long, unsigned long); + // Mangling for non-instantiation-dependent sizeof expressions. template void f2(int (&)[N + sizeof(int*)]) {} diff --git a/clang/test/Sema/invalid-bitwidth-expr.mm b/clang/test/Sema/invalid-bitwidth-expr.mm --- a/clang/test/Sema/invalid-bitwidth-expr.mm +++ b/clang/test/Sema/invalid-bitwidth-expr.mm @@ -25,7 +25,8 @@ template auto func() { // error-bit should be propagated from TemplateArgument to NestNameSpecifier. - class Base::type C; // expected-error {{no matching function for call to 'Foo'}} + class Base::type C; // expected-error {{no matching function for call to 'Foo'}} \ + expected-error {{no class named 'type' in}} return C; } struct Z { diff --git a/clang/test/SemaCXX/invalid-template-base-specifier.cpp b/clang/test/SemaCXX/invalid-template-base-specifier.cpp --- a/clang/test/SemaCXX/invalid-template-base-specifier.cpp +++ b/clang/test/SemaCXX/invalid-template-base-specifier.cpp @@ -12,11 +12,12 @@ template using Alias = decltype(Foo(T())); // expected-error {{no matching function for call to 'Foo'}} template -struct Crash2 : decltype(Alias()) { // expected-note {{in instantiation of template type alias 'Alias' requested here}} +struct Crash2 : decltype(Alias()) { // expected-note {{in instantiation of template type alias 'Alias' requested here}} \ + expected-error {{base specifier must name a class}} Crash2(){}; }; -void test2() { Crash2(); } // expected-note {{in instantiation of template class 'Crash2' requested here}} +void test2() { Crash2(); } // expected-note2 {{in instantiation of template class 'Crash2' requested here}} template class Base {}; diff --git a/clang/test/SemaTemplate/dependent-expr.cpp b/clang/test/SemaTemplate/dependent-expr.cpp --- a/clang/test/SemaTemplate/dependent-expr.cpp +++ b/clang/test/SemaTemplate/dependent-expr.cpp @@ -129,7 +129,7 @@ template void f() { decltype(({})) x; // expected-error {{incomplete type}} } - template void f(); // expected-note {{instantiation of}} + template void f(); template auto g() { auto c = [](auto, int) -> decltype(({})) {}; diff --git a/clang/test/SemaTemplate/temp_arg_template_cxx1z.cpp b/clang/test/SemaTemplate/temp_arg_template_cxx1z.cpp --- a/clang/test/SemaTemplate/temp_arg_template_cxx1z.cpp +++ b/clang/test/SemaTemplate/temp_arg_template_cxx1z.cpp @@ -115,6 +115,6 @@ int n; template struct SubstFailure; - TInt isf; // FIXME: this should be ill-formed + TInt isf; // expected-error {{template template argument has different template parameters than its corresponding template template parameter}} TIntPtr ipsf; } diff --git a/clang/unittests/Tooling/RecursiveASTVisitorTests/DeclRefExpr.cpp b/clang/unittests/Tooling/RecursiveASTVisitorTests/DeclRefExpr.cpp --- a/clang/unittests/Tooling/RecursiveASTVisitorTests/DeclRefExpr.cpp +++ b/clang/unittests/Tooling/RecursiveASTVisitorTests/DeclRefExpr.cpp @@ -121,4 +121,18 @@ DeclRefExprVisitor::Lang_OBJCXX11)); } +TEST(RecursiveASTVisitor, + VisitInstantiationDependentDecltypeTypeUnderlyingExpr) { + DeclRefExprVisitor Visitor; + Visitor.ExpectMatch("x", 3, 57); + Visitor.ExpectMatch("x", 4, 57); + EXPECT_TRUE(Visitor.runOver( + R"cpp( + int x; + template void f(decltype(sizeof(T().f(x)))); + template void g(decltype(sizeof(T().f(x)))); + )cpp", + DeclRefExprVisitor::Lang_CXX17)); +} + } // end anonymous namespace