forked from OSchip/llvm-project
[clang] Use decltype((E)) for compound requirement type constraint
See PR45088. Compound requirement type constraints were using decltype(E) instead of decltype((E)), as per `[expr.prim.req]p1.3.3`. Since neither instantiation nor type dependence should matter for the constraints, this uses an approach where a `decltype` type is not built, and just the canonical type of the expression after template instantiation is used on the requirement. Signed-off-by: Matheus Izvekov <mizvekov@gmail.com> Reviewed By: rsmith Differential Revision: https://reviews.llvm.org/D98160
This commit is contained in:
parent
73adc05ced
commit
3ad6dd5d8f
|
@ -2293,6 +2293,7 @@ public:
|
|||
const CXXScopeSpec &SS, QualType T,
|
||||
TagDecl *OwnedTagDecl = nullptr);
|
||||
|
||||
QualType getDecltypeForParenthesizedExpr(Expr *E);
|
||||
QualType BuildTypeofExprType(Expr *E, SourceLocation Loc);
|
||||
/// If AsUnevaluated is false, E is treated as though it were an evaluated
|
||||
/// context, such as when building a type for decltype(auto).
|
||||
|
|
|
@ -439,18 +439,19 @@ static void diagnoseUnsatisfiedRequirement(Sema &S,
|
|||
case concepts::ExprRequirement::SS_ConstraintsNotSatisfied: {
|
||||
ConceptSpecializationExpr *ConstraintExpr =
|
||||
Req->getReturnTypeRequirementSubstitutedConstraintExpr();
|
||||
if (ConstraintExpr->getTemplateArgsAsWritten()->NumTemplateArgs == 1)
|
||||
if (ConstraintExpr->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
|
||||
// A simple case - expr type is the type being constrained and the concept
|
||||
// was not provided arguments.
|
||||
S.Diag(ConstraintExpr->getBeginLoc(),
|
||||
Expr *e = Req->getExpr();
|
||||
S.Diag(e->getBeginLoc(),
|
||||
diag::note_expr_requirement_constraints_not_satisfied_simple)
|
||||
<< (int)First << S.BuildDecltypeType(Req->getExpr(),
|
||||
Req->getExpr()->getBeginLoc())
|
||||
<< (int)First << S.getDecltypeForParenthesizedExpr(e)
|
||||
<< ConstraintExpr->getNamedConcept();
|
||||
else
|
||||
} else {
|
||||
S.Diag(ConstraintExpr->getBeginLoc(),
|
||||
diag::note_expr_requirement_constraints_not_satisfied)
|
||||
<< (int)First << ConstraintExpr;
|
||||
}
|
||||
S.DiagnoseUnsatisfiedConstraint(ConstraintExpr->getSatisfaction());
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -8663,7 +8663,7 @@ Sema::BuildExprRequirement(
|
|||
TemplateParameterList *TPL =
|
||||
ReturnTypeRequirement.getTypeConstraintTemplateParameterList();
|
||||
QualType MatchedType =
|
||||
BuildDecltypeType(E, E->getBeginLoc()).getCanonicalType();
|
||||
getDecltypeForParenthesizedExpr(E).getCanonicalType();
|
||||
llvm::SmallVector<TemplateArgument, 1> Args;
|
||||
Args.push_back(TemplateArgument(MatchedType));
|
||||
TemplateArgumentList TAL(TemplateArgumentList::OnStack, Args);
|
||||
|
|
|
@ -8830,6 +8830,29 @@ QualType Sema::BuildTypeofExprType(Expr *E, SourceLocation Loc) {
|
|||
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.
|
||||
QualType Sema::getDecltypeForParenthesizedExpr(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:
|
||||
return Context.getRValueReferenceType(T);
|
||||
// - otherwise, if e is an lvalue, decltype(e) is T&, where T is the
|
||||
// type of e;
|
||||
case VK_LValue:
|
||||
return Context.getLValueReferenceType(T);
|
||||
// - otherwise, decltype(e) is the type of e.
|
||||
case VK_RValue:
|
||||
return T;
|
||||
}
|
||||
llvm_unreachable("Unknown value kind");
|
||||
}
|
||||
|
||||
/// 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.
|
||||
|
@ -8894,22 +8917,7 @@ static QualType getDecltypeForExpr(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;
|
||||
return S.getDecltypeForParenthesizedExpr(E);
|
||||
}
|
||||
|
||||
QualType Sema::BuildDecltypeType(Expr *E, SourceLocation Loc,
|
||||
|
|
|
@ -79,19 +79,23 @@ constexpr bool is_same_v = false;
|
|||
template<typename T>
|
||||
constexpr bool is_same_v<T, T> = true;
|
||||
|
||||
template<typename T> struct remove_reference { using type = T; };
|
||||
template<typename T> struct remove_reference<T&> { using type = T; };
|
||||
|
||||
template<typename T, typename U>
|
||||
concept Same = is_same_v<T, U>;
|
||||
|
||||
template<typename T>
|
||||
concept Large = sizeof(T) >= 4; // expected-note{{because 'sizeof(short) >= 4' (2 >= 4) evaluated to false}}
|
||||
concept Large = sizeof(typename remove_reference<T>::type) >= 4;
|
||||
// expected-note@-1{{because 'sizeof(typename remove_reference<short &>::type) >= 4' (2 >= 4) evaluated to false}}
|
||||
|
||||
template<typename T> requires requires (T t) { { t } -> Large; } // expected-note{{because 'decltype(t)' (aka 'short') does not satisfy 'Large':}}
|
||||
template<typename T> requires requires (T t) { { t } -> Large; } // expected-note{{because 'short &' does not satisfy 'Large':}}
|
||||
struct r7 {};
|
||||
|
||||
using r7i1 = r7<int>;
|
||||
using r7i2 = r7<short>; // expected-error{{constraints not satisfied for class template 'r7' [with T = short]}}
|
||||
|
||||
template<typename T> requires requires (T t) { { t } -> Same<T>; }
|
||||
template<typename T> requires requires (T t) { { t } -> Same<T&>; }
|
||||
struct r8 {};
|
||||
|
||||
using r8i1 = r8<int>;
|
||||
|
@ -99,7 +103,8 @@ using r8i2 = r8<short*>;
|
|||
|
||||
// Substitution failure in type constraint
|
||||
|
||||
template<typename T> requires requires (T t) { { t } -> Same<typename T::type>; } // expected-note{{because 'Same<expr-type, typename T::type>' would be invalid: type 'int' cannot be used prior to '::' because it has no members}}
|
||||
template<typename T> requires requires (T t) { { t } -> Same<typename T::type&>; }
|
||||
// expected-note@-1{{because 'Same<expr-type, typename T::type &>' would be invalid: type 'int' cannot be used prior to '::' because it has no members}}
|
||||
struct r9 {};
|
||||
|
||||
struct M { using type = M; };
|
||||
|
@ -122,6 +127,17 @@ concept IsEven = (T % 2) == 0;
|
|||
template<typename T> requires requires (T t) { { t } -> IsEven; } // expected-error{{concept named in type constraint is not a type concept}}
|
||||
struct r11 {};
|
||||
|
||||
// Value categories
|
||||
|
||||
template<auto a = 0>
|
||||
requires requires (int b) {
|
||||
{ a } -> Same<int>;
|
||||
{ b } -> Same<int&>;
|
||||
{ 0 } -> Same<int>;
|
||||
{ static_cast<int&&>(a) } -> Same<int&&>;
|
||||
} void f1() {}
|
||||
template void f1<>();
|
||||
|
||||
// C++ [expr.prim.req.compound] Example
|
||||
namespace std_example {
|
||||
template<typename T> concept C1 =
|
||||
|
@ -172,4 +188,4 @@ namespace std_example {
|
|||
static_assert(C5<char>);
|
||||
template<C5 T> struct C5_check {}; // expected-note{{because 'short' does not satisfy 'C5'}}
|
||||
using c5 = C5_check<short>; // expected-error{{constraints not satisfied for class template 'C5_check' [with T = short]}}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue