Commit 861d4af8 by Andrew Sutton Committed by Andrew Sutton

re PR c++/92236 ([concepts] Explain non-satisfaction in static_assert)

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.

From-SVN: r278775
parent 50e0c6e4
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
......
......@@ -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))
{
......
......@@ -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)
{
......
......@@ -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;
......
......@@ -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))
......
......@@ -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,
......
......@@ -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)
{
......
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.
......
......@@ -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" }
......@@ -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" }
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment