aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Sutton <asutton@lock3software.com>2019-11-27 15:23:02 +0000
committerAndrew Sutton <asutton@lock3software.com>2019-11-27 15:23:02 +0000
commita7520ddce117864baa4c6d9c1261a838f994125b (patch)
tree231f0fe9b111f132f4b47a343e458ff6be85c9f3
parenta59ae882bf6fe03991a4bddb167be219ce8442b2 (diff)
2019-11-27 Andrew Sutton <asutton@lock3software.com>
PR c++/92236 Defer evaluation of concept checks so that static assertions can emit more detailed diagnostics. gcc/cp/ * constexpr.c (cxx_eval_call_expression): Handle concept checks. (cxx_eval_constant_expression): Diagnose misuse of function concepts as template-id expressions. Follow the usual return path for results. (cxx_eval_outermost_constant_expr): Avoid calling cp_get_callee_fndecl_nofold for function concepts. * constraint.cc (build_function_check): Fully type the concept check so that we don't ICE in conversions. * cp-gimplify.c (cp_genericize_r) [CALL_EXPR]: Handle concept checks. [TEMPLATE_ID_EXPR] Likewise. * cvt.c (convert_to_void): Always evaluate concept checks so we don't accidentally ignore them. Substitution during satisfaction can make a program ill-formed (example in g++.dg/cpp2a/concepts6.C). * pt.c (tsubst_copy_and_build): [CALL_EXPR]: Don't evaluate concepts. [TEMPLATE_ID_EXPR]: Likewise. * semantics.c (finish_call_expr): Don't evaluate concepts. (finish_id_expression_1): Likewise. (finish_static_assert): Preserve the original condition so we can diagnose concept errors when a check returns false. gcc/testsuite/ * g++.dg/cpp2a/concepts-iconv1.C: Update diagnostics. * g++.dg/cpp2a/concepts-requires5.C: Likewise. * g++.dg/cpp2a/concepts6.C: New test. git-svn-id: https://gcc.gnu.org/svn/gcc/trunk@278775 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/cp/ChangeLog24
-rw-r--r--gcc/cp/constexpr.c38
-rw-r--r--gcc/cp/constraint.cc2
-rw-r--r--gcc/cp/cp-gimplify.c16
-rw-r--r--gcc/cp/cvt.c6
-rw-r--r--gcc/cp/pt.c11
-rw-r--r--gcc/cp/semantics.c20
-rw-r--r--gcc/testsuite/ChangeLog7
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/concepts-iconv1.C8
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/concepts-requires5.C4
10 files changed, 99 insertions, 37 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 36302e43aca..c3e66d4e40f 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,29 @@
2019-11-27 Andrew Sutton <asutton@lock3software.com>
+ PR c++/92236
+ Defer evaluation of concept checks so that static assertions can
+ emit more detailed diagnostics.
+ * constexpr.c (cxx_eval_call_expression): Handle concept checks.
+ (cxx_eval_constant_expression): Diagnose misuse of function concepts
+ as template-id expressions. Follow the usual return path for results.
+ (cxx_eval_outermost_constant_expr): Avoid calling
+ cp_get_callee_fndecl_nofold for function concepts.
+ * constraint.cc (build_function_check): Fully type the concept check
+ so that we don't ICE in conversions.
+ * cp-gimplify.c (cp_genericize_r) [CALL_EXPR]: Handle concept checks.
+ [TEMPLATE_ID_EXPR] Likewise.
+ * cvt.c (convert_to_void): Always evaluate concept checks so we don't
+ accidentally ignore them. Substitution during satisfaction can make
+ a program ill-formed (example in g++.dg/cpp2a/concepts6.C).
+ * pt.c (tsubst_copy_and_build): [CALL_EXPR]: Don't evaluate concepts.
+ [TEMPLATE_ID_EXPR]: Likewise.
+ * semantics.c (finish_call_expr): Don't evaluate concepts.
+ (finish_id_expression_1): Likewise.
+ (finish_static_assert): Preserve the original condition so we can
+ diagnose concept errors when a check returns false.
+
+2019-11-27 Andrew Sutton <asutton@lock3software.com>
+
PR c++/92439
Improve quality of diagnostics for subexpressions that need parens.
* parser.c (cp_parser_requires_clause_opt): Add a flag to indicate
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 32d929b82f3..ee3ccb9691c 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -1672,6 +1672,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
bool lval,
bool *non_constant_p, bool *overflow_p)
{
+ /* Handle concept checks separately. */
+ if (concept_check_p (t))
+ return evaluate_concept_check (t, tf_warning_or_error);
+
location_t loc = cp_expr_loc_or_input_loc (t);
tree fun = get_function_named_in_call (t);
constexpr_call new_call
@@ -5645,14 +5649,26 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
{
/* We can evaluate template-id that refers to a concept only if
the template arguments are non-dependent. */
- if (!concept_definition_p (TREE_OPERAND (t, 0)))
+ tree id = unpack_concept_check (t);
+ tree tmpl = TREE_OPERAND (id, 0);
+ if (!concept_definition_p (tmpl))
internal_error ("unexpected template-id %qE", t);
+ if (function_concept_p (tmpl))
+ {
+ if (!ctx->quiet)
+ error_at (cp_expr_loc_or_input_loc (t),
+ "function concept must be called");
+ r = error_mark_node;
+ break;
+ }
+
if (!processing_template_decl)
- return evaluate_concept_check (t, tf_warning_or_error);
+ r = evaluate_concept_check (t, tf_warning_or_error);
else
*non_constant_p = true;
- return t;
+
+ break;
}
case ASM_EXPR:
@@ -5809,12 +5825,16 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
|| TREE_CODE (t) == AGGR_INIT_EXPR
|| TREE_CODE (t) == TARGET_EXPR))
{
- tree x = t;
- if (TREE_CODE (x) == TARGET_EXPR)
- x = TARGET_EXPR_INITIAL (x);
- tree fndecl = cp_get_callee_fndecl_nofold (x);
- if (fndecl && DECL_IMMEDIATE_FUNCTION_P (fndecl))
- is_consteval = true;
+ /* For non-concept checks, determine if it is consteval. */
+ if (!concept_check_p (t))
+ {
+ tree x = t;
+ if (TREE_CODE (x) == TARGET_EXPR)
+ x = TARGET_EXPR_INITIAL (x);
+ tree fndecl = cp_get_callee_fndecl_nofold (x);
+ if (fndecl && DECL_IMMEDIATE_FUNCTION_P (fndecl))
+ is_consteval = true;
+ }
}
if (AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type))
{
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 9967b1ef996..fadbe7c8ac0 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -1263,6 +1263,7 @@ build_function_check (tree tmpl, tree args, tsubst_flags_t /*complain*/)
++processing_template_decl;
vec<tree, va_gc> *fargs = make_tree_vector ();
tree call = build_min_nt_call_vec (id, fargs);
+ TREE_TYPE (call) = boolean_type_node;
release_tree_vector (fargs);
--processing_template_decl;
@@ -1397,6 +1398,7 @@ build_constrained_parameter (tree cnc, tree proto, tree args)
Note that the constraints are neither reduced nor decomposed. That is
done only after the requires clause has been parsed (or not). */
+
tree
finish_shorthand_constraint (tree decl, tree constr)
{
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index 60766978c71..7942afa7ece 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -1622,6 +1622,15 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
break;
case CALL_EXPR:
+ /* Evaluate function concept checks instead of treating them as
+ normal functions. */
+ if (concept_check_p (stmt))
+ {
+ *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error);
+ * walk_subtrees = 0;
+ break;
+ }
+
if (!wtd->no_sanitize_p
&& sanitize_flags_p ((SANITIZE_NULL
| SANITIZE_ALIGNMENT | SANITIZE_VPTR)))
@@ -1679,6 +1688,13 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
TARGET_EXPR_NO_ELIDE (stmt) = 1;
break;
+ case TEMPLATE_ID_EXPR:
+ gcc_assert (concept_check_p (stmt));
+ /* Emit the value of the concept check. */
+ *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error);
+ walk_subtrees = 0;
+ break;
+
default:
if (IS_TYPE_OR_DECL_P (stmt))
*walk_subtrees = 0;
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index e67b32ea3b0..d916e39ee90 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -1135,6 +1135,12 @@ convert_to_void (tree expr, impl_conv_void implicit, tsubst_flags_t complain)
error_at (loc, "pseudo-destructor is not called");
return error_mark_node;
}
+
+ /* Explicitly evaluate void-converted concept checks since their
+ satisfaction may produce ill-formed programs. */
+ if (concept_check_p (expr))
+ expr = evaluate_concept_check (expr, tf_warning_or_error);
+
if (VOID_TYPE_P (TREE_TYPE (expr)))
return expr;
switch (TREE_CODE (expr))
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index eb907c56385..3eed27bb426 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -18860,12 +18860,6 @@ tsubst_copy_and_build (tree t,
if (function_concept_p (TREE_OPERAND (id, 0)))
RETURN (id);
- /* Evaluate the concept, if needed. */
- tree args = TREE_OPERAND (id, 1);
- if (!uses_template_parms (args)
- && !processing_constraint_expression_p ())
- RETURN (evaluate_concept_check (check, complain));
-
RETURN (check);
}
@@ -19650,11 +19644,6 @@ tsubst_copy_and_build (tree t,
/* Ensure the result is wrapped as a call expression. */
ret = build_concept_check (tmpl, args, tf_warning_or_error);
-
- /* Possibly evaluate the check if it is non-dependent. */
- if (!uses_template_parms (args)
- && !processing_constraint_expression_p ())
- ret = evaluate_concept_check (ret, complain);
}
else
ret = finish_call_expr (function, &call_args,
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 6c4785c6858..4a5479cb4b4 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2605,10 +2605,6 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
/* Ensure the result is wrapped as a call expression. */
result = build_concept_check (tmpl, args, tf_warning_or_error);
-
- /* Evaluate the check if it is non-dependent. */
- if (!uses_template_parms (args))
- result = evaluate_concept_check (result, complain);
}
else if (is_overloaded_fn (fn))
{
@@ -3890,13 +3886,8 @@ finish_id_expression_1 (tree id_expression,
}
else if (concept_check_p (decl))
{
- /* If this is a standard or variable concept check, potentially
- evaluate it. Function concepts need to be called as functions,
- so don't try evaluating them here. */
- tree tmpl = TREE_OPERAND (decl, 0);
- tree args = TREE_OPERAND (decl, 1);
- if (!function_concept_p (tmpl) && !uses_template_parms (args))
- decl = evaluate_concept_check (decl, tf_warning_or_error);
+ /* Nothing more to do. All of the analysis for concept checks
+ is done by build_conept_id, called from the parser. */
}
else if (scope)
{
@@ -9564,6 +9555,9 @@ finish_static_assert (tree condition, tree message, location_t location,
return;
}
+ /* Save the condition in case it was a concept check. */
+ tree orig_condition = condition;
+
/* Fold the expression and convert it to a boolean value. */
condition = perform_implicit_conversion_flags (boolean_type_node, condition,
complain, LOOKUP_NORMAL);
@@ -9590,6 +9584,10 @@ finish_static_assert (tree condition, tree message, location_t location,
else
error ("static assertion failed: %s",
TREE_STRING_POINTER (message));
+
+ /* Actually explain the failure if this is a concept check. */
+ if (concept_check_p (orig_condition))
+ diagnose_constraints (location, orig_condition, NULL_TREE);
}
else if (condition && condition != error_mark_node)
{
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 9b22c91ce69..aca3864b184 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,12 @@
2019-11-27 Andrew Sutton <asutton@lock3software.com>
+ PR c++/92236
+ * g++.dg/cpp2a/concepts-iconv1.C: Update diagnostics.
+ * g++.dg/cpp2a/concepts-requires5.C: Likewise.
+ * g++.dg/cpp2a/concepts6.C: New test.
+
+2019-11-27 Andrew Sutton <asutton@lock3software.com>
+
PR c++/92439
* g++.dg/cpp2a/concepts-requires20.C: New.
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-iconv1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-iconv1.C
index cc2ce7e321d..4d521c30748 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-iconv1.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-iconv1.C
@@ -7,16 +7,16 @@ int foo(int x)
{
return x;
}
-
+
template <typename T>
concept C1 = requires (T x) {
- {foo(x)} -> Same<int&>;
+ {foo(x)} -> Same<int&>; // { dg-error "placeholder constraints" }
};
template <typename T>
concept C2 = requires (T x) {
- {foo(x)} -> Same<void>;
+ {foo(x)} -> Same<void>; // { dg-error "placeholder constraints" }
};
-
+
static_assert( C1<int> ); // { dg-error "assert" }
static_assert( C2<int> ); // { dg-error "assert" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires5.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires5.C
index fe37ed48e79..133d29e45a4 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-requires5.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires5.C
@@ -33,13 +33,13 @@ int driver_1()
// Test implicit conversion requirements
template<typename T, typename U>
-concept ConvertibleTo = requires(T& t) { {t} -> U&; };
+concept ConvertibleTo = requires(T& t) { {t} -> U&; }; // { dg-error "inaccessible" }
struct B { };
class D : /*private*/ B { };
void driver_2()
{
- static_assert(ConvertibleTo<D, B>()); // { dg-error "cannot be used as a function" }
+ static_assert(ConvertibleTo<D, B>()); // { dg-error "cannot call" }
static_assert(ConvertibleTo<D, B>); // { dg-error "static assertion failed" }
}