[clang] 3ad6dd5 - [clang] Use decltype((E)) for compound requirement type constraint
Matheus Izvekov via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 30 13:00:41 PDT 2021
Author: Matheus Izvekov
Date: 2021-03-30T22:00:33+02:00
New Revision: 3ad6dd5d8f0a70756f665e8179ad7c5210022c11
URL: https://github.com/llvm/llvm-project/commit/3ad6dd5d8f0a70756f665e8179ad7c5210022c11
DIFF: https://github.com/llvm/llvm-project/commit/3ad6dd5d8f0a70756f665e8179ad7c5210022c11.diff
LOG: [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 at gmail.com>
Reviewed By: rsmith
Differential Revision: https://reviews.llvm.org/D98160
Added:
Modified:
clang/include/clang/Sema/Sema.h
clang/lib/Sema/SemaConcept.cpp
clang/lib/Sema/SemaExprCXX.cpp
clang/lib/Sema/SemaType.cpp
clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index efabc78b45ba9..8e1bc3f2dbdab 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -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).
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 1ff7b1cdd5155..592bf5633ea5f 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -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;
}
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 720e7b81c6378..4a4c8116d6f13 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -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);
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 97971b3009817..ed0480ec2ac3c 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -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,
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
index 19b794ba29ad0..b7366207882f9 100644
--- 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,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 at -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 at -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]}}
-}
\ No newline at end of file
+}
More information about the cfe-commits
mailing list