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 @@ -1560,7 +1560,8 @@ QualType getTypeOfType(QualType t) const; /// C++11 decltype. - QualType getDecltypeType(Expr *e, QualType UnderlyingType) const; + QualType getDecltypeType(Expr *e, QualType UnderlyingType, + bool Parenthesized = false) const; /// Unary type transforms QualType getUnaryTransformType(QualType BaseType, QualType UnderlyingType, 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 @@ -4483,15 +4483,18 @@ class DecltypeType : public Type { Expr *E; QualType UnderlyingType; + bool Parenthesized; protected: friend class ASTContext; // ASTContext creates these. - DecltypeType(Expr *E, QualType underlyingType, QualType can = QualType()); + DecltypeType(Expr *E, QualType underlyingType, QualType can = QualType(), + bool Parenthesized = false); public: Expr *getUnderlyingExpr() const { return E; } QualType getUnderlyingType() const { return UnderlyingType; } + bool getParenthesized() const { return Parenthesized; } /// Remove a single level of sugar. QualType desugar() const; 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 @@ -2288,7 +2288,8 @@ /// If AsUnevaluated is false, E is treated as though it were an evaluated /// context, such as when building a type for decltype(auto). QualType BuildDecltypeType(Expr *E, SourceLocation Loc, - bool AsUnevaluated = true); + bool AsUnevaluated = true, + bool Parenthesized = false); QualType BuildUnaryTransformType(QualType BaseType, UnaryTransformType::UTTKind UKind, SourceLocation 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 @@ -5426,7 +5426,8 @@ /// 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. -QualType ASTContext::getDecltypeType(Expr *e, QualType UnderlyingType) const { +QualType ASTContext::getDecltypeType(Expr *e, QualType UnderlyingType, + bool Parenthesized) const { DecltypeType *dt; // C++11 [temp.type]p2: @@ -5445,11 +5446,11 @@ Canon = new (*this, TypeAlignment) DependentDecltypeType(*this, e); DependentDecltypeTypes.InsertNode(Canon, InsertPos); } - dt = new (*this, TypeAlignment) - DecltypeType(e, UnderlyingType, QualType((DecltypeType *)Canon, 0)); + dt = new (*this, TypeAlignment) DecltypeType( + e, UnderlyingType, QualType((DecltypeType *)Canon, 0), Parenthesized); } else { - dt = new (*this, TypeAlignment) - DecltypeType(e, UnderlyingType, getCanonicalType(UnderlyingType)); + dt = new (*this, TypeAlignment) DecltypeType( + e, UnderlyingType, getCanonicalType(UnderlyingType), Parenthesized); } Types.push_back(dt); return QualType(dt, 0); 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 @@ -3423,7 +3423,8 @@ E->Profile(ID, Context, true); } -DecltypeType::DecltypeType(Expr *E, QualType underlyingType, QualType can) +DecltypeType::DecltypeType(Expr *E, QualType underlyingType, QualType can, + bool Parenthesized) // 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. @@ -3433,7 +3434,7 @@ : TypeDependence::None) | (E->getType()->getDependence() & TypeDependence::VariablyModified)), - E(E), UnderlyingType(underlyingType) {} + E(E), UnderlyingType(underlyingType), Parenthesized(Parenthesized) {} bool DecltypeType::isSugared() const { return !E->isInstantiationDependent(); } diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1083,8 +1083,12 @@ void TypePrinter::printDecltypeBefore(const DecltypeType *T, raw_ostream &OS) { OS << "decltype("; + if (T->getParenthesized()) + OS << "("; if (T->getUnderlyingExpr()) T->getUnderlyingExpr()->printPretty(OS, nullptr, Policy); + if (T->getParenthesized()) + OS << ")"; OS << ')'; spaceBeforePlaceHolder(OS); } diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -444,8 +444,9 @@ // was not provided arguments. S.Diag(ConstraintExpr->getBeginLoc(), diag::note_expr_requirement_constraints_not_satisfied_simple) - << (int)First << S.BuildDecltypeType(Req->getExpr(), - Req->getExpr()->getBeginLoc()) + << (int)First + << S.BuildDecltypeType(Req->getExpr(), + Req->getExpr()->getBeginLoc(), true, true) << ConstraintExpr->getNamedConcept(); else S.Diag(ConstraintExpr->getBeginLoc(), 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 @@ -8640,7 +8640,7 @@ TemplateParameterList *TPL = ReturnTypeRequirement.getTypeConstraintTemplateParameterList(); QualType MatchedType = - BuildDecltypeType(E, E->getBeginLoc()).getCanonicalType(); + BuildDecltypeType(E, E->getBeginLoc(), true, true).getCanonicalType(); llvm::SmallVector Args; Args.push_back(TemplateArgument(MatchedType)); TemplateArgumentList TAL(TemplateArgumentList::OnStack, Args); 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 @@ -8833,13 +8833,36 @@ return Context.getTypeOfExprType(E); } +/// getDecltypeForParenthesizedExpr - Given an expr, will return the type for +/// that expression, as in [dcl.type.simple]p4 but without taking id-expressions +/// and class member access into account. +static QualType getDecltypeForParenthesizedExpr(Sema &S, Expr *E) { + // C++11 [dcl.type.simple]p4: + // [...] + QualType T = E->getType(); + switch (E->getValueKind()) { + // - otherwise, if e is an xvalue, decltype(e) is T&&, where T is the + // type of e; + case VK_XValue: + T = S.Context.getRValueReferenceType(T); + break; + // - otherwise, if e is an lvalue, decltype(e) is T&, where T is the + // type of e; + case VK_LValue: + T = S.Context.getLValueReferenceType(T); + break; + // - otherwise, decltype(e) is the type of e. + case VK_RValue: + break; + } + + return T; +} + /// getDecltypeForExpr - Given an expr, will return the decltype for /// that expression, according to the rules in C++11 /// [dcl.type.simple]p4 and C++11 [expr.lambda.prim]p18. static QualType getDecltypeForExpr(Sema &S, Expr *E) { - if (E->isTypeDependent()) - return S.Context.DependentTy; - // C++11 [dcl.type.simple]p4: // The type denoted by decltype(e) is defined as follows: @@ -8897,26 +8920,11 @@ } } - - // C++11 [dcl.type.simple]p4: - // [...] - QualType T = E->getType(); - switch (E->getValueKind()) { - // - otherwise, if e is an xvalue, decltype(e) is T&&, where T is the - // type of e; - case VK_XValue: T = S.Context.getRValueReferenceType(T); break; - // - otherwise, if e is an lvalue, decltype(e) is T&, where T is the - // type of e; - case VK_LValue: T = S.Context.getLValueReferenceType(T); break; - // - otherwise, decltype(e) is the type of e. - case VK_RValue: break; - } - - return T; + return getDecltypeForParenthesizedExpr(S, E); } QualType Sema::BuildDecltypeType(Expr *E, SourceLocation Loc, - bool AsUnevaluated) { + bool AsUnevaluated, bool Parenthesized) { assert(!E->hasPlaceholderType() && "unexpected placeholder"); if (AsUnevaluated && CodeSynthesisContexts.empty() && @@ -8928,7 +8936,11 @@ Diag(E->getExprLoc(), diag::warn_side_effects_unevaluated_context); } - return Context.getDecltypeType(E, getDecltypeForExpr(*this, E)); + QualType Type = E->isTypeDependent() + ? Context.DependentTy + : (Parenthesized ? getDecltypeForParenthesizedExpr + : getDecltypeForExpr)(*this, E); + return Context.getDecltypeType(E, Type, Parenthesized); } QualType Sema::BuildUnaryTransformType(QualType BaseType, diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp --- a/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp @@ -79,19 +79,34 @@ template constexpr bool is_same_v = true; +template struct reference { + using type = T; + static constexpr bool value = false; +}; +template struct reference { + using type = T; + static constexpr bool value = true; +}; + template concept Same = is_same_v; +template concept Ref = reference::value; + template -concept Large = sizeof(T) >= 4; // expected-note{{because 'sizeof(short) >= 4' (2 >= 4) evaluated to false}} +concept LargeRef = sizeof(typename reference::type) >= 4; +// expected-note@-1{{because 'sizeof(typename reference::type) >= 4' (2 >= 4) evaluated to false}} -template requires requires (T t) { { t } -> Large; } // expected-note{{because 'decltype(t)' (aka 'short') does not satisfy 'Large':}} +template requires requires (T t) { + { t } -> Ref; + { t } -> LargeRef; // expected-note{{because 'decltype((t))' (aka 'short &') does not satisfy 'LargeRef':}} +} struct r7 {}; using r7i1 = r7; using r7i2 = r7; // expected-error{{constraints not satisfied for class template 'r7' [with T = short]}} -template requires requires (T t) { { t } -> Same; } +template requires requires (T t) { { t } -> Same; } struct r8 {}; using r8i1 = r8; @@ -99,7 +114,8 @@ // Substitution failure in type constraint -template requires requires (T t) { { t } -> Same; } // expected-note{{because 'Same' would be invalid: type 'int' cannot be used prior to '::' because it has no members}} +template requires requires (T t) { { t } -> Same; } +// expected-note@-1{{because 'Same' would be invalid: type 'int' cannot be used prior to '::' because it has no members}} struct r9 {}; struct M { using type = M; }; @@ -172,4 +188,4 @@ static_assert(C5); template struct C5_check {}; // expected-note{{because 'short' does not satisfy 'C5'}} using c5 = C5_check; // expected-error{{constraints not satisfied for class template 'C5_check' [with T = short]}} -} \ No newline at end of file +}