+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
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
{
/* 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:
|| 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))
{
++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;
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)
{
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)))
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;
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))
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);
}
/* 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,
/* 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))
{
}
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)
{
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);
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
{
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" }
// 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" }
}