Skip to content

Commit

Permalink
[clang] Use decltype((E)) for compound requirement type constraint
Browse files Browse the repository at this point in the history
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
  • Loading branch information
mizvekov committed Mar 30, 2021
1 parent 73adc05 commit 3ad6dd5
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 27 deletions.
1 change: 1 addition & 0 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -2293,6 +2293,7 @@ class Sema final {
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).
Expand Down
11 changes: 6 additions & 5 deletions clang/lib/Sema/SemaConcept.cpp
Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaExprCXX.cpp
Expand Up @@ -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);
Expand Down
40 changes: 24 additions & 16 deletions clang/lib/Sema/SemaType.cpp
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down
Expand Up @@ -79,27 +79,32 @@ 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>;
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; };
Expand All @@ -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 =
Expand Down Expand Up @@ -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]}}
}
}

0 comments on commit 3ad6dd5

Please sign in to comment.