@@ -1,3 +1,141 @@
+2015-03-03 Andrew Sutton <andrew.n.sutton@gmail.com>
+
+ Rewrite to use new constraint model.
+ * gcc/cp/call.c (add_function_candidate): Use new constraint
+ checking interface.
+ (build_new_function_call): Evaluate concepts when selected
+ by overload resolution.
+ * gcc/cp/class.c (resolve_address_of_overloaded_function):
+ Use new constraint interface.
+ * gcc/cp/constexpr.c (cxx_eval_constant_expression): Don't
+ try to constexpr evaluate a requires-expression.
+ (potential_constant_expression_1): Stop using old
+ requires/constraint features.
+ gcc/cp/constraint.cc: Much rewriting, reorganization, refactoring
+ to support new constraint/requirement terms.
+ (lift_function_definition): New. Factor lifting code out of
+ lift_call for reuse in evaluate_function_concept.
+ (lift_variable_intializer): New. Factor lifting code out of
+ lift_var for reuse in evaluate_variable_concept.
+ (lift_template_id): Restore checking code for mis-written
+ variable concepts.
+ (lift_requires_expr): Handle these separately.
+ (xform_*_requirement): New. Transform requires-expressions
+ into constraints.
+ (get/set/remove_constraints, decl_constraints): Move to
+ pt.c to allow for garbage collection.
+ (processing_constraint): Kill this global.
+ (diagnose_*): Update diagnostics to use the new constraint
+ interface.
+ * gcc/cp/cp-objcp-common.c (cp_common_init_ts): Remove typing
+ of old nodes.
+ * gcc/cp/cp-tree.def: Add new nodes for requirements and
+ constraints. Remove previous constraint/req nodes.
+ gcc/cp/cp-tree.h (COMPOUND_REQ_NOEXCEPT_P): New.
+ (ICONV_CONSTR_EXPR, ICONV_CONSTR_TYPE): Fix operands.
+ (constraint_p, make_predicate_constraint_p, valid_constraints_p): New.
+ (misc): Remove unused declarations.
+ gcc/cp/cxx-pretty-print.c: Reorganize/rewrite for new constraint
+ model.
+ (primary_expression, expression): Remove unused nodes, add options
+ for constraints
+ gcc/cp/cxx-pretty-print.h: Declare new functions for pretty
+ printing.
+ gcc/cp/decl.c (duplicate_decls): Only reclaim when flag_concepts
+ is on.
+ (grokfndecl): Associate predicate constraints.
+ gcc/cp/decl2.c (mark_used): Don't instantiate concepts.
+ gcc/cp/error.c (dump_expr): Handle new constraints.
+ gcc/cp/method.c (implicitly_declare_fn): Use new constraint
+ interface.
+ gcc/cp/parser.c (make_call_declarator): Pass a requires-clause for
+ call declarators.
+ (cp_parser_type_name): Take a flag to indicate the presence of
+ a pre-parsed 'typename'.
+ (cp_parser_requires_expression): Move sentinel into function.
+ (cp_parser_type_requirement): Parse type requirements in accordance
+ with Concepts TS.
+ (cp_parser_compound_requirement): Parse compound requirements in
+ accordance with Concepts TS. Remove constexpr requirements.
+ (cp_parser_template_declaration_after_export): Associate
+ predicate constraints.
+ gcc/cp/pt.c (get_template_for_ordering): New. Extract a template
+ decl from a list of candidates.
+ (lookup_template_class_1): Use new constraint interface.
+ (tsubst_pack_conjunction): Build an expression, not constraints.
+ (tsubst_decl): Only associate constraints when substituting
+ through members.
+ (tsubst): Kill subst rules for old nodes.
+ (most_specialized_partial_spec): Save candidates correctly
+ in the presence of constraints.
+ (always_instantiate_p): Never always instantiate a concept.
+ (type_dependent_expression_p): Requires expressions have type bool.
+ (decl_constraints): Moved from constraint.cc, use hash_table
+ instead of hash_map.
+ gcc/cp/semantics.c (finish_call_expr): Remove constraints from
+ functions, not overload sets.
+ (finish_template_variable): Evaluate variable concepts by
+ determining satisfaction.
+ gcc/cp/typeck.c (cp_build_function_call_vec): Use new concept
+ interface.
+ gcc/cp/testsuite/g++.dg/concepts/*: Update tests to match syntax,
+ diagnostics.
+ gcc/cp/testsuite/g++.dg/concepts/req1.C: Test requires-expression
+ with no parens.
+
+2015-02-25 Braden Obrzut <admin@maniacsvault.net>
+
+ * gcc/cp/constraint.cc (lift_operands): New.
+ (lift_call): Lift operands on function calls and don't use
+ tsubst_constraint_expr.
+ (lift_var): Don't use tsubst_constraint_expr.
+ (lift_constraints): Use the returned number of operands for most nodes
+ and also lift from TREE_LISTs.
+
+2015-02-17 Braden Obrzut <admin@maniacsvault.net>
+
+ * gcc/cp/constraint.cc (lift_call): Converted from normalize_call.
+ (lift_var): Converted from normalize_var.
+ (lift_template_id): Converted from normalize_template_id.
+ (lift_constraints): Implemented.
+
+2015-02-16 Andrew Sutton <andrew.n.sutton@gmail.com>
+
+ * gcc/cp/cp-tree.h: (EXPR_CONSTR_EXPR, TYPE_CONSTR_TYPE): Fix
+ typos in macro names.
+ * gcc/cp/constraint.cc: (check_constraint*): New. Rewrite the
+ constraint checking implementation so that it matches the
+ wording and rules in n4377.
+
+2015-02-10 Andrew Sutton <andrew.n.sutton@gmail.com>
+
+ * gcc/cp/logic.cc: Rewrite to use new constraint model and
+ update formatting.
+ * gcc/cp/constraint.cc: Documentation and organization.
+
+2015-02-10 Andrew Sutton <andrew.n.sutton@gmail.com>
+
+ * gcc/cp/cp-tree.h (is_constraint): Make static.
+
+2015-02-09 Andrew Sutton <andrew.n.sutton@gmail.com>
+
+ Rewrite normalization in terms of constraints.
+ * gcc/cp/cp-tree.h (is_constraint): New.
+ * gcc/cp/constraint.cc (normalize_*): Rewrite the previous normalization
+ model so that it conforms with the specification. Normalization applies
+ to constraints. Transformation of expressions into constraints now
+ happens in two phases: lifting concept definitions, and the actual
+ transformation.
+ (tranform_expression, xform_*): New. Define transformation of
+ expressions into constraints.
+ (lift_constraints): New. Stubbed out inlining function.
+
+2015-02-09 Andrew Sutton <andrew.n.sutton@gmail.com>
+
+ Start refactoring constraints to match the specification.
+ * gcc/cp/cp-tree.def: Add new TREECODEs for constraints.
+ * gcc/cp/cp-tree.h: Add accessor macros for constraint operands.
+
2015-02-05 Braden Obrzut <admin@maniacsvault.net>
* gcc/cp/constexpr.c (potential_constant_expression_1): Readded missing
@@ -1994,17 +1994,16 @@ add_function_candidate (struct z_candidate **candidates,
reason = arity_rejection (first_arg, i + remaining, len);
}
- // Second, a constrained function is not viable if its constraints are not
- // satisfied.
- if (viable && flag_concepts) {
- if (tree ci = get_constraints (fn)) {
- if (!check_constraints (ci))
+ /* Second, for a function to be viable, it's constraints must be
+ satisfied. */
+ if (flag_concepts && viable)
+ {
+ if (!constraints_satisfied_p (fn))
{
reason = constraint_failure (fn);
viable = false;
}
}
- }
/* When looking for a function from a subobject from an implicit
copy/move constructor/operator=, don't consider anything that takes (a
@@ -4124,7 +4123,29 @@ build_new_function_call (tree fn, vec<tree, va_gc> **args, bool koenig_p,
through flags so that later we can use it to decide whether to warn
about peculiar null pointer conversion. */
if (TREE_CODE (fn) == TEMPLATE_ID_EXPR)
- flags |= LOOKUP_EXPLICIT_TMPL_ARGS;
+ {
+ /* If overload resolution selects a specialization of a
+ function concept, the expression is true if the
+ constraints are satisfied and false otherwise.
+
+ NOTE: This is an extension of Concepts Lite TS that
+ allows constraints to be used in expressions. */
+ if (flag_concepts)
+ {
+ tree tmpl = DECL_TI_TEMPLATE (cand->fn);
+ tree decl = DECL_TEMPLATE_RESULT (tmpl);
+ if (DECL_DECLARED_CONCEPT_P (decl))
+ {
+ tree targs = DECL_TI_ARGS (cand->fn);
+ tree eval = evaluate_function_concept (decl, targs);
+ return eval;
+ }
+ }
+
+
+ flags |= LOOKUP_EXPLICIT_TMPL_ARGS;
+ }
+
result = build_over_call (cand, flags, complain);
}
@@ -7483,11 +7483,11 @@ resolve_address_of_overloaded_function (tree target_type,
/* Instantiation failed. */
continue;
- // Constraints must be satisfied. Note that this is done
- // before return type deduction.
- if (tree ci = get_constraints (instantiation))
- if (!check_constraints (ci))
- continue;
+ /* Constraints must be satisfied. This is done before
+ return type deduction since that instantiates the
+ declaration. */
+ if (flag_concepts && !constraints_satisfied_p (instantiation))
+ continue;
/* And now force instantiation to do return type deduction. */
if (undeduced_auto_decl (instantiation))
@@ -2730,6 +2730,15 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
}
break;
+ case REQUIRES_EXPR:
+ /* A requires-expression appearing as the initializer of
+ variable concept is evaluated as a constant expression.
+ We can't actually evaluate it because we've defined
+ those to be instantiation dependent. */
+ gcc_assert (processing_template_decl);
+ *non_constant_p = true;
+ return t;
+
default:
internal_error ("unexpected expression %qE of kind %s", t,
get_tree_code_name (TREE_CODE (t)));
@@ -3022,12 +3031,6 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags)
case USING_DECL:
case PLACEHOLDER_EXPR:
case REQUIRES_EXPR:
- case EXPR_REQ:
- case TYPE_REQ:
- case NESTED_REQ:
- case VALIDEXPR_EXPR:
- case VALIDTYPE_EXPR:
- case CONSTEXPR_EXPR:
return true;
case AGGR_INIT_EXPR:
@@ -36,48 +36,69 @@ along with GCC; see the file COPYING3. If not see
#include "tree-iterator.h"
#include "vec.h"
#include "target.h"
-#include "bitmap.h"
-#include "hash-map.h"
+#include "hash-table.h"
-// -------------------------------------------------------------------------- //
-// Requirement Construction
-//
-// Facilities for building and manipulating template requirements.
-//
-// TODO: Simply assigning boolean_type_node to the result type of the
-// expression seems right for constraints, but in the long-term we might want
-// to be more flexible (i.e., allow some form of overload resolution?).
+/*---------------------------------------------------------------------------
+ Operations on constraints
+---------------------------------------------------------------------------*/
-// Create a new logical node joining the subexpressions a and b.
-static inline tree
-join_requirements (tree_code c, tree a, tree b)
+/* Returns true if C is a constraint tree code. */
+static inline bool
+constraint_p (tree_code c)
{
- gcc_assert (a != NULL_TREE && b != NULL_TREE);
- gcc_assert (c == TRUTH_ANDIF_EXPR || c == TRUTH_ORIF_EXPR);
- return build_min (c, boolean_type_node, a, b);
+ return PRED_CONSTR <= c && c <= DISJ_CONSTR;
}
-// Returns the conjunction of two requirements A and B, where A and B are
-// reduced terms in the constraints language. Note that conjoining a non-null
-// expression with NULL_TREE is an identity operation. That is, for
-// non-null A,
-//
-// conjoin_constraints(a, NULL_TREE) == a
-//
-// If both A and B are NULL_TREE, the result is also NULL_TREE.
+/* Returns true if T is a constraint. */
+bool
+constraint_p (tree t)
+{
+ return constraint_p (TREE_CODE (t));
+}
+
+/* Returns true if T can be used as a predicate constraint. */
+static inline bool
+valid_predicate_p (tree t)
+{
+ return CONSTANT_CLASS_P (t) || EXPR_P (t) || t == error_mark_node;
+}
+
+/* Make a predicate constraint from the given expression. */
+tree
+make_predicate_constraint (tree expr)
+{
+ if (!valid_predicate_p (expr))
+ debug_tree (expr);
+ gcc_assert(valid_predicate_p (expr));
+ return build_nt (PRED_CONSTR, expr);
+}
+
+/* Returns the conjunction of two constraints A and B. Note that
+ conjoining a non-null constraint with NULL_TREE is an identity
+ operation. That is, for non-null A,
+
+ conjoin_constraints(a, NULL_TREE) == a
+
+ and
+
+ conjoin_constraints (NULL_TREE, a) == a
+
+ If both A and B are NULL_TREE, the result is also NULL_TREE. */
tree
conjoin_constraints (tree a, tree b)
{
+ gcc_assert (a ? constraint_p (a) : true);
+ gcc_assert (b ? constraint_p (b) : true);
if (a)
- return b ? join_requirements (TRUTH_ANDIF_EXPR, a, b) : a;
+ return b ? build_nt (CONJ_CONSTR, a, b) : a;
else if (b)
return b;
else
return NULL_TREE;
}
-// Transform the list of expressions in the T into a conjunction
-// of requirements. T must be a TREE_VEC.
+/* Transform the vector of expressions in the T into a conjunction
+ of requirements. T must be a TREE_VEC. */
tree
conjoin_constraints (tree t)
{
@@ -88,18 +109,18 @@ conjoin_constraints (tree t)
return r;
}
+/*---------------------------------------------------------------------------
+ Resolution of qualified concept names
+---------------------------------------------------------------------------*/
-// -------------------------------------------------------------------------- //
-// Constraint Resolution
-//
-// This facility is used to resolve constraint checks from requirement
-// expressions. A constraint check is a call to a function template declared
-// with the keyword 'concept'.
-//
-// The result of resolution is a pair (a TREE_LIST) whose value is the
-// matched declaration, and whose purpose contains the coerced template
-// arguments that can be substituted into the call.
+/* This facility is used to resolve constraint checks from
+ requirement expressions. A constraint check is a call to
+ a function template declared with the keyword 'concept'.
+ The result of resolution is a pair (a TREE_LIST) whose value
+ is the matched declaration, and whose purpose contains the
+ coerced template arguments that can be substituted into the
+ call. */
// Given an overload set OVL, try to find a unique definition that can be
// instantiated by the template arguments ARGS.
@@ -169,7 +190,7 @@ resolve_constraint_check (tree call)
// A constraint check must be only a template-id expression. If
// it's a call to a base-link, its function(s) should be a
- // template-id expressson. If this is not a template-id, then it
+ // template-id expression. If this is not a template-id, then it
// cannot be a concept-check.
tree target = CALL_EXPR_FN (call);
if (BASELINK_P (target))
@@ -180,13 +201,21 @@ resolve_constraint_check (tree call)
// Get the overload set and template arguments and try to
// resolve the target.
tree ovl = TREE_OPERAND (target, 0);
+
+ /* This is a function call of a variable concept... ill-formed. */
+ if (TREE_CODE (ovl) == TEMPLATE_DECL)
+ {
+ error ("function call of variable concept %qE", call);
+ return error_mark_node;
+ }
+
tree args = TREE_OPERAND (target, 1);
return resolve_constraint_check (ovl, args);
}
// Given a call expression or template-id expression to a concept, EXPR,
// possibly including a placeholder argument, deduce the concept being checked
-// and the prototype paraemter. Returns true if the constraint and prototype
+// and the prototype parameter. Returns true if the constraint and prototype
// can be deduced and false otherwise. Note that the CHECK and PROTO arguments
// are set to NULL_TREE if this returns false.
bool
@@ -205,7 +234,8 @@ deduce_constrained_parameter (tree expr, tree& check, tree& proto)
else if (TREE_CODE (expr) == CALL_EXPR)
{
// Resolve the constraint check to deduce the prototype parameter.
- if (tree info = resolve_constraint_check (expr))
+ tree info = resolve_constraint_check (expr);
+ if (info && info != error_mark_node)
{
// Get function and argument from the resolved check expression and
// the prototype parameter. Note that if the first argument was a
@@ -247,7 +277,8 @@ deduce_concept_introduction (tree expr)
else if (TREE_CODE (expr) == CALL_EXPR)
{
// Resolve the constraint check and return arguments.
- if (tree info = resolve_constraint_check (expr))
+ tree info = resolve_constraint_check (expr);
+ if (info && info != error_mark_node)
return TREE_PURPOSE (info);
return NULL_TREE;
}
@@ -255,496 +286,565 @@ deduce_concept_introduction (tree expr)
gcc_unreachable ();
}
+namespace {
+
+/*---------------------------------------------------------------------------
+ Lifting of concept definitions
+---------------------------------------------------------------------------*/
-// -------------------------------------------------------------------------- //
-// Declarations
+tree lift_constraints (tree);
-// Check that FN satisfies the structural requirements of a
-// function concept definition.
+/* If the tree T has operands, then lift any concepts out of them. */
tree
-check_function_concept (tree fn)
+lift_operands (tree t)
{
- location_t loc = DECL_SOURCE_LOCATION (fn);
+ if (int n = tree_operand_length (t))
+ {
+ t = copy_node (t);
+ for (int i = 0; i < n; ++i)
+ TREE_OPERAND (t, i) = lift_constraints (TREE_OPERAND (t, i));
+ }
+ return t;
+}
- // Check that the function is comprised of only a single
- // return statement.
+/* Inline a function (concept) definition by substituting
+ ARGS into its body. */
+tree
+lift_function_definition (tree fn, tree args)
+{
+ /* Extract the body of the function minus the return expression. */
tree body = DECL_SAVED_TREE (fn);
+ if (!body)
+ return error_mark_node;
if (TREE_CODE (body) == BIND_EXPR)
body = BIND_EXPR_BODY (body);
-
- // Sometimes a function call results in the creation of clean up
- // points. Allow these to be preserved in the body of the
- // constraint, as we might actually need them for some constexpr
- // evaluations.
- if (TREE_CODE (body) == CLEANUP_POINT_EXPR)
- body = TREE_OPERAND (body, 0);
-
+
+ /* FIXME: Why isn't this being checked at the point of definition? */
if (TREE_CODE (body) != RETURN_EXPR)
- error_at (loc, "function concept definition %qD has multiple statements",
- fn);
-
- return NULL_TREE;
-}
+ {
+ error_at (DECL_SOURCE_LOCATION (fn),
+ "concept definition %qD has multiple statements", fn);
+ return error_mark_node;
+ }
+ gcc_assert (TREE_CODE (body) == RETURN_EXPR);
+ body = TREE_OPERAND (body, 0);
-// -------------------------------------------------------------------------- //
-// Normalization
-//
-// Normalize a template requirement to a logical formula written in terms of
-// atomic propositions, returing the new expression. If the expression cannot
-// be normalized, a NULL_TREE is returned.
+ /* Substitute template arguments to produce our inline expression. */
+ tree result = tsubst_expr (body, args, tf_none, NULL_TREE, false);
+ if (result == error_mark_node)
+ return error_mark_node;
-namespace {
+ return lift_constraints (result);
+}
-// Helper functions
-tree normalize_constraints (tree);
-tree normalize_node (tree);
-tree normalize_expr (tree);
-tree normalize_stmt (tree);
-tree normalize_decl (tree);
-tree normalize_misc (tree);
-tree normalize_logical (tree);
-tree normalize_call (tree);
-tree normalize_requires (tree);
-tree normalize_expr_req (tree);
-tree normalize_type_req (tree);
-tree normalize_nested_req (tree);
-tree normalize_var (tree);
-tree normalize_cleanup_point (tree);
-tree normalize_template_id (tree);
-tree normalize_atom (tree);
-
-// Reduce the requirement T into a logical formula written in terms of
-// atomic propositions.
+/* Inline a reference to a function concept. */
tree
-normalize_node (tree t)
+lift_call (tree t)
{
- switch (TREE_CODE_CLASS (TREE_CODE (t)))
- {
- case tcc_unary:
- case tcc_binary:
- case tcc_expression:
- case tcc_vl_exp:
- return normalize_expr (t);
-
- case tcc_statement:
- return normalize_stmt (t);
-
- case tcc_declaration:
- return normalize_decl (t);
-
- case tcc_exceptional:
- return normalize_misc (t);
-
- // These kinds of expressions are atomic.
- case tcc_constant:
- case tcc_reference:
- case tcc_comparison:
- return t;
+ /* Try to resolve this function call as a concept. If not, then
+ it can be returned as-is. */
+ tree check = resolve_constraint_check (t);
+ if (!check)
+ return lift_operands (t);
+ if (check == error_mark_node)
+ return error_mark_node;
- default:
- gcc_unreachable ();
- }
- return NULL_TREE;
+ tree fn = TREE_VALUE (check);
+ tree args = TREE_PURPOSE (check);
+ return lift_function_definition (fn, args);
}
-// Reduction rules for the expression node T.
tree
-normalize_expr (tree t)
+lift_variable_initializer (tree var, tree args)
{
- switch (TREE_CODE (t))
- {
- case TRUTH_ANDIF_EXPR:
- case TRUTH_ORIF_EXPR:
- return normalize_logical (t);
-
- case CALL_EXPR:
- return normalize_call (t);
-
- case REQUIRES_EXPR:
- return normalize_requires (t);
-
- case EXPR_REQ:
- return normalize_expr_req (t);
-
- case TYPE_REQ:
- return normalize_type_req (t);
-
- case NESTED_REQ:
- return normalize_nested_req (t);
-
- case TEMPLATE_ID_EXPR:
- return normalize_template_id (t);
-
- case BIND_EXPR:
- return normalize_node (BIND_EXPR_BODY (t));
+ /* Extract the body from the variable initializer. */
+ tree init = DECL_INITIAL (var);
+ if (!init)
+ return error_mark_node;
- case CLEANUP_POINT_EXPR:
- return normalize_cleanup_point (t);
+ /* Substitute the arguments to form our new inline expression. */
+ tree result = tsubst_expr (init, args, tf_none, NULL_TREE, false);
+ if (result == error_mark_node)
+ return error_mark_node;
- // Do not recurse.
- case TAG_DEFN:
- return NULL_TREE;
+ return lift_constraints (result);
+}
- // Everything else is atomic.
- default:
- return normalize_atom (t);
- }
+/* Inline a reference to a variable concept. */
+tree
+lift_var (tree t)
+{
+ tree tmpl = TREE_OPERAND (t, 0);
+ tree args = TREE_OPERAND (t, 1);
+ tree decl = DECL_TEMPLATE_RESULT (tmpl);
+ if (!DECL_DECLARED_CONCEPT_P (decl))
+ return t;
+ return lift_variable_initializer (decl, args);
}
-// Reduction rules for the statement T.
+/* Determine if a template-id is a variable concept and inline. */
tree
-normalize_stmt (tree t)
+lift_template_id (tree t)
{
- switch (TREE_CODE (t))
- {
- // Reduce the returned expression.
- case RETURN_EXPR:
- return normalize_node (TREE_OPERAND (t, 0));
+ if (variable_concept_p (TREE_OPERAND (t, 0)))
+ return lift_var (t);
- // These statements do not introduce propositions
- // in the constraints language. Do not recurse.
- case DECL_EXPR:
- case USING_STMT:
- return NULL_TREE;
+ /* Check that we didn't refer to a function concept like
+ a variable.
- default:
- gcc_unreachable ();
+ TODO: Add a note on how to fix this. */
+ tree tmpl = TREE_OPERAND (t, 0);
+ if (TREE_CODE (tmpl) == OVERLOAD)
+ {
+ tree fn = OVL_FUNCTION (tmpl);
+ if (TREE_CODE (fn) == TEMPLATE_DECL
+ && DECL_DECLARED_CONCEPT_P (DECL_TEMPLATE_RESULT (fn)))
+ {
+ error ("invalid reference to function concept %qD", fn);
+ return error_mark_node;
+ }
}
- return NULL_TREE;
+
+ return t;
}
-// Reduction rules for the declaration T.
+/* Lift any constraints appearing in a nested requirement of
+ a requires-expression. */
tree
-normalize_decl (tree t)
+lift_requires_expr (tree t)
{
- switch (TREE_CODE (t))
+ tree parms = TREE_OPERAND (t, 0);
+ tree reqs = TREE_OPERAND (t, 1);
+ tree result = NULL_TREE;
+ for (; reqs != NULL_TREE; reqs = TREE_CHAIN (reqs))
{
- // References to var decls are atomic.
- case VAR_DECL:
- return t;
-
- default:
- gcc_unreachable ();
+ tree req = TREE_VALUE (reqs);
+ if (TREE_CODE (req) == NESTED_REQ)
+ {
+ tree expr = lift_constraints (TREE_OPERAND (req, 0));
+ req = finish_nested_requirement (expr);
+ }
+ result = tree_cons (NULL_TREE, req, result);
}
- return NULL_TREE;
+ return finish_requires_expr (parms, result);
}
-// Reduction rules for the node T.
-tree
-normalize_misc (tree t)
+/* Inline references to specializations of concepts. */
+tree
+lift_constraints (tree t)
{
+ if (t == NULL_TREE)
+ return NULL_TREE;
+
+ if (t == error_mark_node)
+ return error_mark_node;
+
+ /* Concepts can be referred to by call or variable. All other
+ nodes are preserved. */
switch (TREE_CODE (t))
{
- // All of these are atomic.
- case ERROR_MARK:
- case TRAIT_EXPR:
- case CONSTRUCTOR:
- return t;
+ case CALL_EXPR:
+ return lift_call (t);
+
+ case TEMPLATE_ID_EXPR:
+ return lift_template_id (t);
+
+ case REQUIRES_EXPR:
+ return lift_requires_expr (t);
- // This should have been caught as an error.
- case STATEMENT_LIST:
- return NULL_TREE;
+ case TREE_LIST:
+ {
+ t = copy_node (t);
+ TREE_VALUE (t) = lift_constraints (TREE_VALUE (t));
+ TREE_CHAIN (t) = lift_constraints (TREE_CHAIN (t));
+ return t;
+ }
default:
- gcc_unreachable ();
+ return lift_operands (t);
}
- return NULL_TREE;
}
-// Check that the logical expression is not a user-defined operator.
+/*---------------------------------------------------------------------------
+ Constraint normalization
+---------------------------------------------------------------------------*/
+
+tree transform_expression (tree);
+
+/* Check that the logical-or or logical-and expression does
+ not result in a call to a user-defined user-defined operator
+ (temp.constr.op). Returns true if the logical operator is
+ admissible and false otherwise. */
bool
-check_logical (tree t)
+check_logical_expr (tree t)
{
- // We can't do much for type dependent expressions.
- if (type_dependent_expression_p (t) || value_dependent_expression_p (t))
+ /* We can't do much for type dependent expressions. */
+ if (type_dependent_expression_p (t))
return true;
- // Resolve the logical operator. Note that template processing is
- // disabled so we get the actual call or target expression back.
- // not_processing_template_sentinel sentinel;
+ /* Resolve the logical operator. Note that template processing is
+ disabled so we get the actual call or target expression back.
+ not_processing_template_sentinel sentinel. */
tree arg1 = TREE_OPERAND (t, 0);
tree arg2 = TREE_OPERAND (t, 1);
-
tree ovl = NULL_TREE;
- tree expr = build_new_op (input_location, TREE_CODE (t), LOOKUP_NORMAL,
- arg1, arg2, /*arg3*/NULL_TREE,
+ tree expr = build_new_op (input_location, TREE_CODE (t), LOOKUP_NORMAL,
+ arg1, arg2, /*arg3*/NULL_TREE,
&ovl, tf_none);
if (TREE_CODE (expr) != TREE_CODE (t))
{
error ("user-defined operator %qs in constraint %qE",
operator_name_info[TREE_CODE (t)].name, t);
- ;
return false;
}
return true;
}
-// Reduction rules for the binary logical expression T (&& and ||).
-//
-// Generate a new expression from the reduced operands. If either operand
-// cannot be reduced, then the resulting expression is null.
+/* Transform a logical-or or logical-and expression into either
+ a conjunction or disjunction. */
tree
-normalize_logical (tree t)
+xform_logical (tree t, tree_code c)
{
- if (!check_logical (t))
- return NULL_TREE;
+ if (!check_logical_expr (t))
+ return error_mark_node;
+ tree t0 = transform_expression (TREE_OPERAND (t, 0));
+ tree t1 = transform_expression (TREE_OPERAND (t, 1));
+ return build_nt (c, t0, t1);
+}
- tree l = normalize_expr (TREE_OPERAND (t, 0));
- tree r = normalize_expr (TREE_OPERAND (t, 1));
- if (l && r)
- {
- tree result = copy_node (t);
- SET_EXPR_LOCATION (result, EXPR_LOCATION (t));
- TREE_OPERAND (result, 0) = l;
- TREE_OPERAND (result, 1) = r;
- return result;
- }
- else
- return NULL_TREE;
+/* A simple requirement T introduces an expression constraint
+ for its expression. */
+inline tree
+xform_simple_requirement (tree t)
+{
+ return build_nt (EXPR_CONSTR, TREE_OPERAND (t, 0));
}
-// Do a cursory investigation of the target in the call expression
-// with the aim of early diagnosis of ill-formed constraints.
-inline bool
-check_call (tree t)
+/* A type requirement T introduce a type constraint for its
+ type. */
+inline tree
+xform_type_requirement (tree t)
{
- tree target = CALL_EXPR_FN (t);
- if (TREE_CODE (target) != TEMPLATE_ID_EXPR)
- return true;
- tree tmpl = TREE_OPERAND (target, 0);
- if (TREE_CODE (tmpl) != TEMPLATE_DECL)
- return true;
- tree decl = DECL_TEMPLATE_RESULT (tmpl);
- if (TREE_CODE (decl) == VAR_DECL && DECL_DECLARED_CONCEPT_P (decl))
- {
- error ("invalid constraint %qE", t);
- inform (input_location, "did you mean %qE", target);
- return false;
- }
- return true;
+ return build_nt (TYPE_CONSTR, TREE_OPERAND (t, 0));
}
-// Reduction rules for the call expression T.
-//
-// If T is a call to a constraint instantiate its definition and
-// recursively reduce its returned expression.
+/* A compound requirement T introduces a conjunction of constraints
+ depending on its form. The conjunction always includes
+ an expression constraint for the expression of the requirement.
+ If a trailing return type was specified, the conjunction includes
+ either an implicit conversion constraint or an argument
+ deduction constraint. If the noexcept specifier is present, the
+ conjunction includes an exception constraint. */
tree
-normalize_call (tree t)
+xform_compound_requirement (tree t)
{
- if (!check_call (t))
- return NULL_TREE;
+ tree expr = TREE_OPERAND (t, 0);
+ tree constr = build_nt (EXPR_CONSTR, TREE_OPERAND (t, 0));
- // Is the function call actually a constraint check? If not, then it's
- // an atom, and needs to be treated as such.
- tree check = resolve_constraint_check (t);
- if (!check)
- return normalize_atom (t);
-
- tree fn = TREE_VALUE (check);
- tree args = TREE_PURPOSE (check);
+ /* If a type is given, append an implicit conversion or
+ argument deduction constraint.
- // Normalize the body of the function into the constraints language.
- tree body = normalize_constraints (DECL_SAVED_TREE (fn));
- if (!body)
- return error_mark_node;
+ FIXME: Handle argument deduction constraints. */
+ if (tree type = TREE_OPERAND (t, 1))
+ {
+ tree iconv = build_nt (ICONV_CONSTR, expr, type);
+ constr = conjoin_constraints (constr, iconv);
+ }
- // Instantiate the reduced results using the deduced args.
- tree result = tsubst_constraint_expr (body, args, true);
- if (result == error_mark_node)
- return error_mark_node;
+ /* If noexcept is present, append an exception constraint. */
+ if (COMPOUND_REQ_NOEXCEPT_P (t))
+ {
+ tree except = build_nt (EXCEPT_CONSTR, expr);
+ constr = conjoin_constraints (constr, except);
+ }
- return result;
+ return constr;
}
-// Reduction rules for a variable template-id T.
-//
-// If T is a constraint, instantiate its initializer and recursively reduce its
-// expression.
-tree
-normalize_var (tree t)
+/* A nested requirement T introduces a conjunction of constraints
+ corresponding to its constraint-expression. */
+inline tree
+xform_nested_requirement (tree t)
{
- tree decl = DECL_TEMPLATE_RESULT (TREE_OPERAND (t, 0));
- if (!DECL_DECLARED_CONCEPT_P (decl))
- return t;
-
- // Reduce the initializer of the variable into the constraints language.
- tree body = normalize_constraints (DECL_INITIAL (decl));
- if (!body)
- return error_mark_node;
+ return transform_expression (TREE_OPERAND (t, 0));
+}
- // Instantiate the reduced results.
- tree result = tsubst_constraint_expr (body, TREE_OPERAND (t, 1), false);
- if (result == error_mark_node)
- return error_mark_node;
+/* Transform a requirement T into one or more constraints. */
+tree
+xform_requirement (tree t)
+{
+ switch (TREE_CODE (t))
+ {
+ case SIMPLE_REQ:
+ return xform_simple_requirement (t);
+
+ case TYPE_REQ:
+ return xform_type_requirement (t);
+
+ case COMPOUND_REQ:
+ return xform_compound_requirement (t);
+
+ case NESTED_REQ:
+ return xform_nested_requirement (t);
+
+ default:
+ gcc_unreachable ();
+ }
+ return error_mark_node;
+}
+/* Transform a sequence of requirements into a conjunction of
+ constraints. */
+tree
+xform_requirements (tree t)
+{
+ tree result = NULL_TREE;
+ for (; t; t = TREE_CHAIN (t)) {
+ tree constr = xform_requirement (TREE_VALUE (t));
+ result = conjoin_constraints (result, constr);
+ }
return result;
}
-// Reduction rules for the template-id T.
-//
-// It turns out that we often get requirements being written like this:
-//
-// template<typename T>
-// requires Foo<T>
-// void f()
-//
-// Where Foo<T> should actually be written as Foo<T>(). Generate an
-// error and suggest the improved writing.
+/* Transform a requires-expression into a parameterized constraint. */
tree
-normalize_template_id (tree t)
+xform_requires_expr (tree t)
{
- tree tmpl = TREE_OPERAND (t, 0);
- if (variable_concept_p (tmpl))
- return normalize_var (t);
+ tree operand = xform_requirements (TREE_OPERAND (t, 1));
+ if (tree parms = TREE_OPERAND (t, 0))
+ return build_nt (PARM_CONSTR, parms, operand);
else
- {
- location_t locus = EXPR_LOC_OR_LOC (t, input_location);
- error_at (locus, "invalid constraint %qE", t);
-
- vec<tree, va_gc>* args = NULL;
- tree c = finish_call_expr (t, &args, true, false, 0);
- inform (locus, "did you mean %qE", c);
-
- return error_mark_node;
- }
+ return operand;
}
-// Reduce an expression requirement as a conjunction of its
-// individual constraints.
+/* Transform an expression into an atomic predicate constraint.
+ After substitution, the expression of a predicate constraint shall
+ have type bool (temp.constr.pred). For non-type- dependent
+ expressions, we can check that now. */
tree
-normalize_expr_req (tree t)
+xform_atomic (tree t)
{
- tree r = NULL_TREE;
- for (tree l = TREE_OPERAND (t, 0); l; l = TREE_CHAIN (l))
- r = conjoin_constraints (r, normalize_expr (TREE_VALUE (l)));
- return r;
+ if (!type_dependent_expression_p (t))
+ {
+ tree type = cv_unqualified (TREE_TYPE (t));
+ if (!same_type_p (type, boolean_type_node))
+ {
+ error ("predicate constraint %qE does not have type %<bool%>", t);
+ return error_mark_node;
+ }
+ }
+ return build_nt (PRED_CONSTR, t);
}
-// Reduce a type requirement by returning its underlying
-// constraint.
+/* Transform an expression into a constraint. */
tree
-normalize_type_req (tree t)
+xform_expr (tree t)
{
- return TREE_OPERAND (t, 0);
+ switch (TREE_CODE (t))
+ {
+ case TRUTH_ANDIF_EXPR:
+ return xform_logical (t, CONJ_CONSTR);
+
+ case TRUTH_ORIF_EXPR:
+ return xform_logical (t, DISJ_CONSTR);
+
+ case REQUIRES_EXPR:
+ return xform_requires_expr (t);
+
+ case BIND_EXPR:
+ return transform_expression (BIND_EXPR_BODY (t));
+
+ default:
+ /* All other constraints are atomic. */
+ return xform_atomic (t);
+ }
}
-// Reduce a nested requirement by returning its only operand.
+/* Transform a statement into an expression. */
tree
-normalize_nested_req (tree t)
+xform_stmt (tree t)
{
- return TREE_OPERAND (t, 0);
+ switch (TREE_CODE (t))
+ {
+ case RETURN_EXPR:
+ return transform_expression (TREE_OPERAND (t, 0));
+ default:
+ gcc_unreachable ();
+ }
+ return error_mark_node;
}
-// Reduce a requires expr by reducing each requirement in turn,
-// rewriting the list of requirements so that we end up with a
-// list of expressions, some of which may be conjunctions.
+// Reduction rules for the declaration T.
tree
-normalize_requires (tree t)
+xform_decl (tree t)
{
- for (tree l = TREE_OPERAND (t, 1); l; l = TREE_CHAIN (l))
- TREE_VALUE (l) = normalize_expr (TREE_VALUE (l));
- return t;
+ switch (TREE_CODE (t))
+ {
+ case VAR_DECL:
+ return xform_atomic (t);
+ default:
+ gcc_unreachable ();
+ }
+ return error_mark_node;
}
-// Normalize a cleanup point by normalizing the underlying
-// expression.
+/* Transform an exceptional node into a constraint. */
tree
-normalize_cleanup_point (tree t)
+xform_misc (tree t)
{
- return normalize_node (TREE_OPERAND (t, 0));
+ switch (TREE_CODE (t))
+ {
+ case TRAIT_EXPR:
+ return xform_atomic (t);
+ case CONSTRUCTOR:
+ return xform_atomic (t);
+ default:
+ gcc_unreachable ();
+ }
+ return error_mark_node;
}
-// Normalize an atomic expression by performing some basic checks.
-// In particular, if the type is known, it must be convertible to
-// bool.
+
+/* Transform a lifted expression into a constraint. This either
+ returns a constraint, or it returns error_mark_node when
+ a constraint cannot be formed. */
tree
-normalize_atom (tree t)
+transform_expression (tree t)
{
- if (!type_dependent_expression_p (t))
- if (!can_convert (boolean_type_node, TREE_TYPE (t), tf_none))
- {
- error ("predicate constraint %qE is not convertible to %<bool%>", t);
- return NULL_TREE;
- }
- return t;
-}
-
-// Reduce the requirement REQS into a logical formula written in terms of
-// atomic propositions.
-tree
-normalize_constraints (tree reqs)
-{
- if (!reqs)
+ if (!t)
return NULL_TREE;
-
- ++processing_template_decl;
- tree expr = normalize_node (reqs);
- --processing_template_decl;
-
- // If we couldn't normalize, then these constraints are ill-formed.
- if (!expr)
+
+ if (t == error_mark_node)
return error_mark_node;
- return expr;
+ switch (TREE_CODE_CLASS (TREE_CODE (t)))
+ {
+ case tcc_unary:
+ case tcc_binary:
+ case tcc_expression:
+ case tcc_vl_exp:
+ return xform_expr (t);
+
+ case tcc_statement:
+ return xform_stmt (t);
+
+ case tcc_declaration:
+ return xform_decl (t);
+
+ case tcc_exceptional:
+ return xform_misc (t);
+
+ case tcc_constant:
+ case tcc_reference:
+ case tcc_comparison:
+ /* These are atomic predicate constraints. */
+ return xform_atomic (t);
+
+ default:
+ /* Unhandled node kind. */
+ gcc_unreachable ();
+ }
+ return error_mark_node;
}
-} // end namespace
+/*---------------------------------------------------------------------------
+ Constraint normalization
+---------------------------------------------------------------------------*/
+tree normalize_constraint (tree);
-// -------------------------------------------------------------------------- //
-// Constraint Semantic Processing
-//
-// The following functions are called by the parser and substitution rules
-// to create and evaluate constraint-related nodes.
+/* The normal form of the disjunction T0 /\ T1 is the conjunction
+ of the normal form of T0 and the normal form of T1 */
+inline tree
+normalize_conjunction (tree t)
+{
+ tree t0 = normalize_constraint (TREE_OPERAND (t, 0));
+ tree t1 = normalize_constraint (TREE_OPERAND (t, 1));
+ return build_nt (CONJ_CONSTR, t0, t1);
+}
+
+/* The normal form of the disjunction T0 \/ T1 is the disjunction
+ of the normal form of T0 and the normal form of T1 */
+inline tree
+normalize_disjunction (tree t)
+{
+ tree t0 = normalize_constraint (TREE_OPERAND (t, 0));
+ tree t1 = normalize_constraint (TREE_OPERAND (t, 1));
+ return build_nt (DISJ_CONSTR, t0, t1);
+}
-// A mapping from declarations to constraint information. Note that
-// both templates and their underlying declarations are mapped to the
-// same constraint information.
-static hash_map<tree, tree> decl_constraints;
+/* A predicate constraint is normalized in two stages. First all
+ references specializations of concepts are replaced by their
+ substituted definitions. Then, the resulting expression is
+ transformed into a constraint by transforming && expressions
+ into conjunctions and || into disjunctions. */
+tree
+normalize_predicate_constraint (tree t)
+{
+ tree expr = PRED_CONSTR_EXPR (t);
+ tree lifted = lift_constraints (expr);
+ tree constr = transform_expression (lifted);
+ return constr;
+}
-// Returns the template constraints of declaration T. If T is not
-// constrained, return NULL_TREE. Note that T must be non-null.
+/* The normal form of a parameterized constraint is the normal
+ form of its operand. */
tree
-get_constraints (tree t)
+normalize_parameterized_constraint (tree t)
{
- gcc_assert (DECL_P (t));
- if (TREE_CODE (t) == TEMPLATE_DECL)
- t = DECL_TEMPLATE_RESULT (t);
- if (tree *r = decl_constraints.get (t))
- return *r;
- else
- return NULL_TREE;
+ tree parms = PARM_CONSTR_PARMS (t);
+ tree operand = normalize_constraint (PARM_CONSTR_OPERAND (t));
+ return build_nt (PARM_CONSTR, parms, operand);
}
-// Associate the given constraint information with the declaration. Don't
-// build associations if ci is NULL_TREE.
-void
-set_constraints (tree t, tree ci)
+/* Normalize the constraint T by reducing it so that it is
+ comprised of only conjunctions and disjunctions of atomic
+ constraints. */
+tree
+normalize_constraint (tree t)
{
- gcc_assert (t && DECL_P (t) && TREE_CODE (t) != TEMPLATE_DECL);
- if (!ci)
- return;
-
- gcc_assert (!decl_constraints.get (t));
- gcc_assert (check_constraint_info (ci));
- decl_constraints.put (t, ci);
+ if (!t)
+ return NULL_TREE;
+
+ switch (TREE_CODE (t))
+ {
+ case CONJ_CONSTR:
+ return normalize_conjunction (t);
+
+ case DISJ_CONSTR:
+ return normalize_disjunction (t);
+
+ case PRED_CONSTR:
+ return normalize_predicate_constraint (t);
+
+ case PARM_CONSTR:
+ return normalize_parameterized_constraint (t);
+
+ case EXPR_CONSTR:
+ case TYPE_CONSTR:
+ case ICONV_CONSTR:
+ case DEDUCT_CONSTR:
+ case EXCEPT_CONSTR:
+ /* These constraints are defined to be atomic. */
+ return t;
+
+ default:
+ /* CONSTR was not a constraint. */
+ gcc_unreachable();
+ }
+ return error_mark_node;
}
-// Remove the associated constraints of the declaration T.
+} /* namespace */
+
+
+// -------------------------------------------------------------------------- //
+// Constraint Semantic Processing
//
-// FIXME: What if T is a template? What if it's a non-template? we
-// should remove both associations.
-void
-remove_constraints (tree t)
-{
- gcc_assert (DECL_P (t));
- if (TREE_CODE (t) == TEMPLATE_DECL)
- t = DECL_TEMPLATE_RESULT (t);
+// The following functions are called by the parser and substitution rules
+// to create and evaluate constraint-related nodes.
- if (decl_constraints.get (t))
- decl_constraints.remove (t);
-}
// If the recently parsed TYPE declares or defines a template or template
// specialization, get its corresponding constraints from the current
@@ -785,22 +885,6 @@ associate_classtype_constraints (tree type)
return type;
}
-// Returns a conjunction of shorthand requirements for the template
-// parameter list PARMS. Note that the requirements are stored in
-// the TYPE of each tree node.
-tree
-get_shorthand_constraints (tree parms)
-{
- tree reqs = NULL_TREE;
- parms = INNERMOST_TEMPLATE_PARMS (parms);
- for (int i = 0; i < TREE_VEC_LENGTH (parms); ++i)
- {
- tree parm = TREE_VEC_ELT (parms, i);
- reqs = conjoin_constraints (reqs, TEMPLATE_PARM_CONSTRAINTS (parm));
- }
- return reqs;
-}
-
namespace {
// Create an empty constraint info block.
@@ -812,274 +896,35 @@ build_constraint_info ()
} // namespace
-// Build a constraint-info object that contains the associated requirements
-// of a declaration. This also includes the declaration's template
-// requirements TR (if any) and declaration requirements DR (if any).
-//
-// If the declaration has neither template nor declaration requirements
-// this returns NULL_TREE, indicating an unconstrained declaration.
-tree
-build_constraints (tree tr, tree dr)
-{
- if (!tr && !dr)
- return NULL_TREE;
- tree_constraint_info* ci = build_constraint_info ();
- ci->template_reqs = tr;
- ci->declarator_reqs = dr;
- ci->associated_constr = conjoin_constraints (tr, dr);
- ci->normalized_constr = normalize_constraints (ci->associated_constr);
- ci->assumptions = decompose_assumptions (ci->normalized_constr);
- return (tree)ci;
-}
-
-// Returns true iff cinfo contains a valid constraint expression.
-// This is the case when the associated requirements can be successfully
-// decomposed into lists of atomic constraints.
-bool
-valid_requirements_p (tree cinfo)
-{
- gcc_assert (cinfo);
- return CI_ASSUMPTIONS (cinfo) != error_mark_node;
-}
-
-// Constructs a REQUIRES_EXPR with parameters, PARMS, and requirements, REQS,
-// that can be evaluated as a constant expression.
-tree
-build_requires_expr (tree parms, tree reqs)
-{
- // Modify the declared parameters by removing their context (so they
- // don't refer to the enclosing scope), and marking them constant (so
- // we can actually check constexpr properties).
- for (tree p = parms; p && !VOID_TYPE_P (TREE_VALUE (p)); p = TREE_CHAIN (p))
- {
- tree parm = TREE_VALUE (p);
- DECL_CONTEXT (parm) = NULL_TREE;
- TREE_CONSTANT (parm) = true;
- }
-
- // Build the node.
- tree r = build_min (REQUIRES_EXPR, boolean_type_node, parms, reqs);
- TREE_SIDE_EFFECTS (r) = false;
- TREE_CONSTANT (r) = true;
- return r;
-}
-
-// Evaluate an instantiated requires expr, returning the true node
-// only when all sub-requirements have evaluated to true.
-tree
-eval_requires_expr (tree reqs)
-{
- for (tree t = reqs ; t; t = TREE_CHAIN (t))
- {
- tree r = TREE_VALUE (t);
- r = fold_non_dependent_expr (r);
- r = maybe_constant_value (r);
- if (r != boolean_true_node)
- return boolean_false_node;
- }
- return boolean_true_node;
-}
-
-// Finish a requires expression, returning a node wrapping the parameters,
-// PARMS, and the list of requirements REQS.
-tree
-finish_requires_expr (tree parms, tree reqs)
-{
- if (processing_template_decl)
- return build_requires_expr (parms, reqs);
- else
- return eval_requires_expr (reqs);
-}
-
-// Construct a unary expression that evaluates properties of the
-// expression or type T, and has a boolean result type.
-static inline tree
-build_check_expr (tree_code c, tree t)
-{
- tree r = build_min (c, boolean_type_node, t);
- TREE_SIDE_EFFECTS (r) = false;
- TREE_READONLY (r) = true;
- TREE_CONSTANT (r) = true;
- return r;
-}
-
-// Finish a syntax requirement, constructing a list embodying a sequence
-// of checks for the validity of EXPR and TYPE, the convertibility of
-// EXPR to TYPE, and the expression properties specified in SPECS.
-tree
-finish_expr_requirement (tree expr, tree type, tree specs)
-{
- gcc_assert (processing_template_decl);
-
- // Build a list of checks, starting with the valid expression.
- tree result = tree_cons (NULL_TREE, finish_validexpr_expr (expr), NULL_TREE);
-
- // If a type requirement was provided, build the result type checks.
- if (type)
- {
- // If the type is dependent, ensure that it can be validly
- // instantiated.
- //
- // NOTE: We can also disregard checks that result in the template
- // parameter.
- if (dependent_type_p (type))
- {
- tree treq = finish_type_requirement (type);
- result = tree_cons (NULL_TREE, treq, result);
- }
-
- // Ensure that the result of the expression can be converted to
- // the result type.
- tree decl_type = finish_decltype_type (expr, false, tf_none);
- tree creq = finish_trait_expr (CPTK_IS_CONVERTIBLE_TO, decl_type, type);
- result = tree_cons (NULL_TREE, creq, result);
- }
-
- // If constraint specifiers are present, make them part of the
- // list of constraints.
- if (specs)
- {
- TREE_CHAIN (tree_last (specs)) = result;
- result = specs;
- }
-
- // Finally, construct the syntactic requirement.
- return build_check_expr (EXPR_REQ, nreverse (result));
-}
-
-// Finish a simple syntax requirement, returning a node representing
-// a check that EXPR is a valid expression.
-tree
-finish_expr_requirement (tree expr)
-{
- gcc_assert (processing_template_decl);
- tree req = finish_validexpr_expr (expr);
- tree reqs = tree_cons (NULL_TREE, req, NULL_TREE);
- return build_check_expr (EXPR_REQ, reqs);
-}
-
-// Finish a type requirement, returning a node representing a check
-// that TYPE will result in a valid type when instantiated.
-tree
-finish_type_requirement (tree type)
-{
- gcc_assert (processing_template_decl);
- tree req = finish_validtype_expr (type);
- return build_check_expr (TYPE_REQ, req);
-}
-
-tree
-finish_nested_requirement (tree expr)
-{
- gcc_assert (processing_template_decl);
- return build_check_expr (NESTED_REQ, expr);
-}
-
-// Finish a constexpr requirement, returning a node representing a
-// check that EXPR, when instantiated, may be evaluated at compile time.
-tree
-finish_constexpr_requirement (tree expr)
-{
- gcc_assert (processing_template_decl);
- return finish_constexpr_expr (expr);
-}
-
-// Finish the noexcept requirement by constructing a noexcept
-// expression evaluating EXPR.
-tree
-finish_noexcept_requirement (tree expr)
-{
- gcc_assert (processing_template_decl);
- return finish_noexcept_expr (expr, tf_none);
-}
-
-// Returns the true or false node depending on the truth value of B.
-static inline tree
-truth_node (bool b)
-{
- return b ? boolean_true_node : boolean_false_node;
-}
-
-// Returns a finished validexpr-expr. Returns the true or false node
-// depending on whether EXPR denotes a valid expression. This is the case
-// when the expression has been successfully type checked.
-//
-// When processing a template declaration, the result is an expression
-// representing the check.
-tree
-finish_validexpr_expr (tree expr)
-{
- if (processing_template_decl)
- return build_check_expr (VALIDEXPR_EXPR, expr);
- return truth_node (expr && expr != error_mark_node);
-}
-
-// Returns a finished validtype-expr. Returns the true or false node
-// depending on whether T denotes a valid type name.
-//
-// When processing a template declaration, the result is an expression
-// representing the check.
-//
-// FIXME: Semantics need to be aligned with the new version of the
-// specification (i.e., we must be able to invent a function and
-// perform argument deduction against it).
-tree
-finish_validtype_expr (tree type)
-{
- if (is_auto (type))
- {
- sorry ("%<auto%> not supported in result type constraints");
- return error_mark_node;
- }
-
- if (processing_template_decl)
- return build_check_expr (VALIDTYPE_EXPR, type);
- return truth_node (type && TYPE_P (type));
-}
+/* Build a constraint-info object that contains the
+ associated condstraints of a declaration. This also
+ includes the declaration's template requirements (TREQS)
+ and any trailing requirements for a function declarator
+ (DREQS). Note that both TREQS and DREQS must be constraints.
-// Returns a finished constexpr-expr. Returns the true or false node
-// depending on whether the expression T may be evaluated at compile
-// time.
-//
-// When processing a template declaration, the result is an expression
-// representing the check.
+ If the declaration has neither template nor declaration
+ requirements this returns NULL_TREE, indicating an
+ unconstrained declaration. */
tree
-finish_constexpr_expr (tree expr)
+build_constraints (tree tmpl_reqs, tree decl_reqs)
{
- if (processing_template_decl)
- return build_check_expr (CONSTEXPR_EXPR, expr);
-
- // TODO: Actually check that the expression can be constexpr
- // evaluated.
- //
- // return truth_node (potential_constant_expression (expr));
- sorry ("constexpr requirement");
- return NULL_TREE;
-}
+ gcc_assert (tmpl_reqs ? constraint_p (tmpl_reqs) : true);
+ gcc_assert (decl_reqs ? constraint_p (decl_reqs) : true);
-// Check that a constrained friend declaration function declaration,
-// FN, is admissable. This is the case only when the declaration depends
-// on template parameters and does not declare a specialization.
-void
-check_constrained_friend (tree fn, tree reqs)
-{
- if (fn == error_mark_node)
- return;
- gcc_assert (TREE_CODE (fn) == FUNCTION_DECL);
+ if (!tmpl_reqs && !decl_reqs)
+ return NULL_TREE;
- // If there are not constraints, this cannot be an error.
- if (!reqs)
- return;
+ tree_constraint_info* ci = build_constraint_info ();
+ ci->template_reqs = tmpl_reqs;
+ ci->declarator_reqs = decl_reqs;
+ ci->associated_constr = conjoin_constraints (tmpl_reqs, decl_reqs);
- // Constrained friend functions that don't depend on template
- // arguments are effectively meaningless.
- tree parms = DECL_ARGUMENTS (fn);
- tree result = TREE_TYPE (TREE_TYPE (fn));
- if (!(parms && uses_template_parms (parms)) && !uses_template_parms (result))
- {
- error ("constrained friend does not depend on template parameters");
- return;
- }
+ ++processing_template_decl;
+ ci->normalized_constr = normalize_constraint (ci->associated_constr);
+ --processing_template_decl;
+
+ ci->assumptions = decompose_assumptions (ci->normalized_constr);
+ return (tree)ci;
}
namespace {
@@ -1099,7 +944,7 @@ build_call_check (tree id)
// Construct a concept check for the given TARGET. The target may be
// an overload set or a baselink referring to an overload set. Template
// arguments to the target are given by ARG and REST. If the target is
-// a function (overload set or baselink reffering to an overload set),
+// a function (overload set or baselink referring to an overload set),
// then this builds the call expression TARGET<ARG, REST>(). If REST is
// NULL_TREE, then the resulting check is just TARGET<ARG>(). If ARG is
// NULL_TREE, then the resulting check is TARGET<REST>().
@@ -1153,13 +998,15 @@ build_constrained_parameter (tree fn, tree proto, tree args)
return decl;
}
-// Create a constraint expression for the given DECL that evaluates the
-// requirements specified by CONSTR, a TYPE_DECL that contains all the
-// information necessary to build the requirements (see finish_concept_name
-// for the layout of that TYPE_DECL).
-//
-// Note that the constraints are neither reduced nor decomposed. That is
-// done only after the requires clause has been parsed (or not).
+/* Create a constraint expression for the given DECL that
+ evaluates the requirements specified by CONSTR, a TYPE_DECL
+ that contains all the information necessary to build the
+ requirements (see finish_concept_name for the layout of
+ that TYPE_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)
{
@@ -1211,7 +1058,24 @@ finish_shorthand_constraint (tree decl, tree constr)
TREE_TYPE (check) = boolean_type_node;
}
- return check;
+ return make_predicate_constraint (check);
+}
+
+/* Returns a conjunction of shorthand requirements for the template
+ parameter list PARMS. Note that the requirements are stored in
+ the TYPE of each tree node. */
+tree
+get_shorthand_constraints (tree parms)
+{
+ tree result = NULL_TREE;
+ parms = INNERMOST_TEMPLATE_PARMS (parms);
+ for (int i = 0; i < TREE_VEC_LENGTH (parms); ++i)
+ {
+ tree parm = TREE_VEC_ELT (parms, i);
+ tree constr = TEMPLATE_PARM_CONSTRAINTS (parm);
+ result = conjoin_constraints (result, constr);
+ }
+ return result;
}
// Returns and chains a new parameter for PARAMETER_LIST which will conform
@@ -1224,7 +1088,7 @@ process_introduction_parm (tree parameter_list, tree src_parm)
// placeholder we want to look at.
bool is_parameter_pack = ARGUMENT_PACK_P (src_parm);
if (is_parameter_pack)
- src_parm = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (src_parm), 0);
+ src_parm = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (src_parm), 0);
// At this point we should have a INTRODUCED_PARM_DECL, but we want to grab
// the associated decl from it. Also grab the stored identifier and location
@@ -1238,7 +1102,7 @@ process_introduction_parm (tree parameter_list, tree src_parm)
// template is using a pack and we didn't declare a pack, throw an error.
if (is_parameter_pack != INTRODUCED_PACK_P (src_parm))
{
- error_at (parm_loc, "can not match pack for introduced parameter");
+ error_at (parm_loc, "cannot match pack for introduced parameter");
tree err_parm = build_tree_list (error_mark_node, error_mark_node);
return chainon (parameter_list, err_parm);
}
@@ -1276,20 +1140,22 @@ process_introduction_parm (tree parameter_list, tree src_parm)
is_non_type, is_parameter_pack);
}
-// Associates a constraint check to the current template based on the
-// introduction parameters. INTRO_LIST should be a TREE_VEC of
-// INTRODUCED_PARM_DECLs containing a chained PARM_DECL which contains the
-// identifier as well as the source location. TMPL_DECL is the decl for the
-// concept being used. If we take some concept, C, this will form a check in
-// the form of C<INTRO_LIST> filling in any extra arguments needed by the
-// defaults deduced.
-//
-// Returns the template parameters as given from end_template_parm_list or
-// NULL_TREE if the process fails.
+/* Associates a constraint check to the current template based
+ on the introduction parameters. INTRO_LIST should be a TREE_VEC
+ of INTRODUCED_PARM_DECLs containing a chained PARM_DECL which
+ contains the identifier as well as the source location.
+ TMPL_DECL is the decl for the concept being used. If we take
+ some concept, C, this will form a check in the form of
+ C<INTRO_LIST> filling in any extra arguments needed by the
+ defaults deduced.
+
+ Returns NULL_TREE if no concept could be matched and
+ error_mark_node if an error occurred when matching.
+*/
tree
-finish_concept_introduction (tree tmpl_decl, tree intro_list)
+finish_template_introduction (tree tmpl_decl, tree intro_list)
{
- // Deduce the concept check.
+ /* Deduce the concept check. */
tree expr = build_concept_check (tmpl_decl, NULL_TREE, intro_list);
if (expr == error_mark_node)
return NULL_TREE;
@@ -1298,16 +1164,12 @@ finish_concept_introduction (tree tmpl_decl, tree intro_list)
if (!parms)
return NULL_TREE;
- // Build template parameter scope for introduction.
+ /* Build template parameter scope for introduction. */
tree parm_list = NULL_TREE;
begin_template_parm_list ();
-
- // Produce a parameter for each introduction argument according to the
- // deduced form.
int nargs = MIN (TREE_VEC_LENGTH (parms), TREE_VEC_LENGTH (intro_list));
for (int n = 0; n < nargs; ++n)
parm_list = process_introduction_parm (parm_list, TREE_VEC_ELT (parms, n));
-
parm_list = end_template_parm_list (parm_list);
for (int i = 0; i < TREE_VEC_LENGTH (parm_list); ++i)
if (TREE_VALUE (TREE_VEC_ELT (parm_list, i)) == error_mark_node)
@@ -1316,10 +1178,8 @@ finish_concept_introduction (tree tmpl_decl, tree intro_list)
return error_mark_node;
}
- // Build a concept check for our constraint.
+ /* Build a concept check for our constraint. */
tree check_args = make_tree_vec (TREE_VEC_LENGTH (parms));
-
- // Start with introduction parameters.
int n = 0;
for (; n < TREE_VEC_LENGTH (parm_list); ++n)
{
@@ -1327,50 +1187,116 @@ finish_concept_introduction (tree tmpl_decl, tree intro_list)
TREE_VEC_ELT (check_args, n) = template_parm_to_arg (parm);
}
- // If the template expects more parameters we should be able to use the
- // defaults from our deduced form.
+ /* If the template expects more parameters we should be able
+ to use the defaults from our deduced concept. */
for (; n < TREE_VEC_LENGTH (parms); ++n)
TREE_VEC_ELT (check_args, n) = TREE_VEC_ELT (parms, n);
- // Associate the constraint.
- tree reqs = build_concept_check (tmpl_decl, NULL_TREE, check_args);
- TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs;
+ /* Associate the constraint. */
+ tree check = build_concept_check (tmpl_decl, NULL_TREE, check_args);
+ tree constr = make_predicate_constraint (check);
+ TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = constr;
return parm_list;
}
-// -------------------------------------------------------------------------- //
-// Substitution Rules
-//
-// The following functions implement substitution rules for constraints.
+/*---------------------------------------------------------------------------
+ Constraint substitution
+---------------------------------------------------------------------------*/
+
+tree tsubst_constraint (tree, tree, tsubst_flags_t, tree);
+
+/* The following functions implement substitution rules for constraints.
+ Substitution without checking constraints happens only in the
+ instantiation of class templates. For example:
+
+ template<C1 T> struct S {
+ void f(T) requires C2<T>;
+ void g(T) requires T::value;
+ };
+
+ S<int> s; // error instantiating S<int>::g(T)
+
+ When we instantiate S, we substitute into its member declarations,
+ including their constraints. However, those constraints are not
+ checked. Substituting int into C2<T> yields C2<int>, and substituting
+ into T::value yields a substitution failure, making the program
+ ill-formed.
+
+ Note that we only ever substitute into the associated constraints
+ of a declaration. That is, substitute is defined only for predicate
+ constraints and conjunctions. */
+
+/* Substitute into the predicate constraints. Returns error_mark_node
+ if the substitution into the expression fails. */
+tree
+tsubst_predicate_constraint (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ tree expr = PRED_CONSTR_EXPR (t);
+ ++processing_template_decl;
+ tree result = tsubst_expr (expr, args, complain, in_decl, false);
+ --processing_template_decl;
+ return build_nt (PRED_CONSTR, result);
+}
+
+/* Substitute into the conjunction of constraints. Returns
+ error_mark_node if substitution into either operand fails. */
+tree
+tsubst_conjunction (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ tree t0 = TREE_OPERAND (t, 0);
+ tree r0 = tsubst_constraint (t0, args, complain, in_decl);
+ tree t1 = TREE_OPERAND (t, 1);
+ tree r1 = tsubst_constraint (t1, args, complain, in_decl);
+ return build_nt (CONJ_CONSTR, r0, r1);
+}
+
+/* Substitute ARGS into the constraint T. */
+tree
+tsubst_constraint (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+{
+ if (TREE_CODE (t) == CONJ_CONSTR)
+ return tsubst_conjunction (t, args, complain, in_decl);
+ else if (TREE_CODE (t) == PRED_CONSTR)
+ return tsubst_predicate_constraint (t, args, complain, in_decl);
+ else
+ gcc_unreachable ();
+ return error_mark_node;
+}
namespace {
-// In an unevaluated context, the substitution of parm decls are not
-// properly chained during substitution. Do that here.
+
+/* A subroutine of tsubst_constraint_variables. In an unevaluated
+ context, the substitution of PARM_DECLs are not properly chained
+ during substitution. Do that here. */
tree
-fix_local_parms (tree sparms)
+fixup_constraint_vars (tree parms)
{
- if (!sparms)
- return sparms;
+ if (!parms)
+ return parms;
- tree p = TREE_CHAIN (sparms);
- tree q = sparms;
+ tree p = TREE_CHAIN (parms);
+ tree q = parms;
while (p && TREE_VALUE (p) != void_type_node)
{
DECL_CHAIN (TREE_VALUE (q)) = TREE_VALUE (p);
q = p;
p = TREE_CHAIN (p);
}
- return sparms;
+ return parms;
}
-// Register local specializations for each of tparm and the corresponding
-// sparm. This is a helper function for tsubst_requires_expr.
-void
-declare_local_parms (tree tparms, tree sparms)
+/* A subroutine of tsubst_constraint_variables. Register local
+ specializations for each of parameter in PARMS and its
+ corresponding substituted constraint variable in VARS.
+ Returns VARS. */
+tree
+declare_constraint_vars (tree parms, tree vars)
{
- tree s = TREE_VALUE (sparms);
- for (tree p = tparms; p && !VOID_TYPE_P (TREE_VALUE (p)); p = TREE_CHAIN (p))
+ tree s = TREE_VALUE (vars);
+ for (tree p = parms; p && !VOID_TYPE_P (TREE_VALUE (p)); p = TREE_CHAIN (p))
{
tree t = TREE_VALUE (p);
if (DECL_PACK_P (t))
@@ -1384,240 +1310,613 @@ declare_local_parms (tree tparms, tree sparms)
s = TREE_CHAIN (s);
}
}
+ return vars;
}
-// Substitute ARGS into the parameter list T, producing a sequence of
-// local parameters (variables) in the current scope.
+/* A subroutine of tsubst_parameterized_constraint. Substitute ARGS
+ into the parameter list T, producing a sequence of constraint
+ variables, declared in the current scope.
+
+ Note that the caller must establish a local specialization stack
+ prior to calling this function since this substitution will
+ declare the substituted parameters. */
tree
-tsubst_local_parms (tree t,
- tree args,
- tsubst_flags_t complain,
- tree in_decl)
+tsubst_constraint_variables (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
{
- tree r = fix_local_parms (tsubst (t, args, complain, in_decl));
- if (r == error_mark_node)
+ tree vars = tsubst (t, args, complain, in_decl);
+ if (vars == error_mark_node)
return error_mark_node;
+ return declare_constraint_vars (t, fixup_constraint_vars (vars));
+}
- // Register the instantiated args as local parameters.
- if (t)
- declare_local_parms (t, r);
+/* Substitute ARGS into the simple requirement T. Note that
+ substitution may result in an ill-formed expression without
+ causing the program to be ill-formed. In such cases, the
+ requirement wraps an error_mark_node. */
+inline tree
+tsubst_simple_requirement (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ tree expr = tsubst_expr (TREE_OPERAND (t, 0), args, complain, in_decl, false);
+ return finish_simple_requirement (expr);
+}
- return r;
+/* Substitute ARGS into the type requirement T. Note that
+ substitution may result in an ill-formed type without
+ causing the program to be ill-formed. In such cases, the
+ requirement wraps an error_mark_node. */
+inline tree
+tsubst_type_requirement (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ tree type = tsubst (TREE_OPERAND (t, 0), args, complain, in_decl);
+ return finish_type_requirement (type);
}
-// Substitute ARGS into the requirement body (list of requirements), T.
-// Note that if any substitutions fail, then this is equivalent to
-// returning false.
+/* Substitute args into the compound requirement T. If substituting
+ into either the expression or the type fails, the corresponding
+ operands in the resulting node will be error_mark_node. This
+ preserves a requirement for the purpose of partial ordering, but
+ it will never be satisfied. */
tree
-tsubst_requirement_body (tree t, tree args, tree in_decl)
+tsubst_compound_requirement (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
{
- tree r = NULL_TREE;
- while (t)
- {
- tree e = tsubst_expr (TREE_VALUE (t), args, tf_none, in_decl, false);
- if (e == error_mark_node)
- e = boolean_false_node;
- r = tree_cons (NULL_TREE, e, r);
- t = TREE_CHAIN (t);
- }
- return r;
+ tree expr = tsubst_expr (TREE_OPERAND (t, 0), args, complain, in_decl, false);
+ tree type = tsubst (TREE_OPERAND (t, 1), args, complain, in_decl);
+ bool noexcept_p = COMPOUND_REQ_NOEXCEPT_P (t);
+ return finish_compound_requirement (expr, type, noexcept_p);
}
-} // namespace
-// Substitute ARGS into the requires expression T.
+/* Substitute ARGS into the nested requirement T. */
tree
-tsubst_requires_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+tsubst_nested_requirement (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
{
- local_specialization_stack stack;
- tree p = tsubst_local_parms (TREE_OPERAND (t, 0), args, complain, in_decl);
- tree r = tsubst_requirement_body (TREE_OPERAND (t, 1), args, in_decl);
- return finish_requires_expr (p, r);
+ tree expr = tsubst_expr (TREE_OPERAND (t, 0), args, complain, in_decl, false);
+ return finish_nested_requirement (expr);
}
-// Substitute ARGS into the valid-expr expression T.
+inline tree
+tsubst_requirement (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+{
+ switch (TREE_CODE (t))
+ {
+ case SIMPLE_REQ:
+ return tsubst_simple_requirement (t, args, complain, in_decl);
+ case TYPE_REQ:
+ return tsubst_type_requirement (t, args, complain, in_decl);
+ case COMPOUND_REQ:
+ return tsubst_compound_requirement (t, args, complain, in_decl);
+ case NESTED_REQ:
+ return tsubst_nested_requirement (t, args, complain, in_decl);
+ default:
+ gcc_unreachable ();
+ }
+ return error_mark_node;
+}
+
+/* Substitute ARGS into the list of requirements T. Note that
+ substitution failures here result in ill-formed programs. */
tree
-tsubst_validexpr_expr (tree t, tree args, tree in_decl)
+tsubst_requirement_body (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
{
- tree r = tsubst_expr (TREE_OPERAND (t, 0), args, tf_none, in_decl, false);
- return finish_validexpr_expr (r);
+ tree r = NULL_TREE;
+ while (t)
+ {
+ tree e = tsubst_requirement (TREE_VALUE (t), args, complain, in_decl);
+ if (e == error_mark_node)
+ return error_mark_node;
+ r = tree_cons (NULL_TREE, e, r);
+ t = TREE_CHAIN (t);
+ }
+ return r;
}
-// Substitute ARGS into the valid-type expression T.
+} /* namespace */
+
+/* Substitute ARGS into the requires expression T. Note that this
+ results in the re-declaration of local parameters when
+ substituting through the parameter list. If either substitution
+ fails, the program is ill-formed. */
tree
-tsubst_validtype_expr (tree t, tree args, tree in_decl)
+tsubst_requires_expr (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
{
- tree r = tsubst (TREE_OPERAND (t, 0), args, tf_none, in_decl);
- return finish_validtype_expr (r);
+ local_specialization_stack stack;
+
+ tree parms = TREE_OPERAND (t, 0);
+ if (parms)
+ {
+ parms = tsubst_constraint_variables (parms, args, complain, in_decl);
+ if (parms == error_mark_node)
+ return error_mark_node;
+ }
+
+ tree reqs = TREE_OPERAND (t, 1);
+ reqs = tsubst_requirement_body (reqs, args, complain, in_decl);
+ if (reqs == error_mark_node)
+ return error_mark_node;
+
+ return finish_requires_expr (parms, reqs);
}
-// Substitute ARGS into the constexpr expression T.
+/* Substitute ARGS into the constraint information CI, producing a new
+ constraint record. */
tree
-tsubst_constexpr_expr (tree t, tree args, tree in_decl)
+tsubst_constraint_info (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
{
- tree r = tsubst_expr (TREE_OPERAND (t, 0), args, tf_none, in_decl, false);
- return finish_constexpr_expr (r);
+ if (!t || t == error_mark_node || !check_constraint_info (t))
+ return NULL_TREE;
+
+ tree tmpl_constr = NULL_TREE;
+ if (tree r = CI_TEMPLATE_REQS (t))
+ tmpl_constr = tsubst_constraint (r, args, complain, in_decl);
+
+ tree decl_constr = NULL_TREE;
+ if (tree r = CI_DECLARATOR_REQS (t))
+ decl_constr = tsubst_constraint (r, args, complain, in_decl);
+
+ return build_constraints (tmpl_constr, decl_constr);
}
-// Substitute ARGS into the expr requirement T. Note that a requirement
-// node is instantiated from a non-reduced context (e.g., static_assert).
+
+/*---------------------------------------------------------------------------
+ Constraint satisfaction
+---------------------------------------------------------------------------*/
+
+/* The following functions determine if a constraint, when
+ substituting template arguments, is satisfied. For convenience,
+ satisfaction reduces a constraint to either true or false (and
+ nothing else). */
+
+namespace {
+
+tree check_constraint (tree, tree, tsubst_flags_t, tree);
+
+/* A predicate constraint is satisfied if its expression evaluates
+ to true. If substitution into that node fails, the constraint
+ is not satisfied ([temp.constr.pred]).
+
+ Note that a predicate constraint is a constraint expression
+ of type bool. If neither of those are true, the program is
+ ill-formed; they are not SFINAE'able errors. */
tree
-tsubst_expr_req (tree t, tree args, tree in_decl)
+check_predicate_constraint (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
{
- tree r = NULL_TREE;
- for (tree l = TREE_OPERAND (t, 0); l; l = TREE_CHAIN (l))
+ tree expr = tsubst_expr (TREE_OPERAND (t, 0), args, complain, in_decl, false);
+ if (expr == error_mark_node)
+ return boolean_false_node;
+
+ tree result = fold_non_dependent_expr (expr);
+ if (result == error_mark_node)
+ return boolean_false_node;
+
+ /* A predicate constraint shall have type bool. In some
+ cases, substitution gives us const-qualified bool, which
+ is also acceptable. */
+ tree type = cv_unqualified (TREE_TYPE (result));
+ if (!same_type_p (type, boolean_type_node))
{
- tree e = tsubst_expr (TREE_VALUE (l), args, tf_none, in_decl, false);
- r = conjoin_constraints (r, e);
+ error_at (EXPR_LOC_OR_LOC (t, input_location),
+ "constraint %qE does not have type %qT",
+ result, boolean_type_node);
+ return boolean_false_node;
}
- return r;
+
+ return cxx_constant_value (result);
}
-// Substitute ARGS into the type requirement T. Note that a requirement
-// node is instantiated from a non-reduced context (e.g., static_assert).
+/* Check an expression constraint. The constraint is satisfied if
+ substitution succeeds ([temp.constr.expr]).
+
+ Note that the expression is unevaluated. */
tree
-tsubst_type_req (tree t, tree args, tree in_decl)
+check_expression_constraint (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
{
- return tsubst_expr (TREE_OPERAND (t, 0), args, tf_none, in_decl, false);
+ cp_unevaluated guard;
+ tree expr = EXPR_CONSTR_EXPR (t);
+ tree check = tsubst_expr (expr, args, complain, in_decl, false);
+ if (check == error_mark_node)
+ return boolean_false_node;
+ return boolean_true_node;
+}
+
+/* Check a type constraint. The constraint is satisfied if
+ substitution succeeds. */
+inline tree
+check_type_constraint (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ tree type = TYPE_CONSTR_TYPE (t);
+ tree check = tsubst (type, args, complain, in_decl);
+ if (check == error_mark_node)
+ return boolean_false_node;
+ return boolean_true_node;
+}
+
+/* Check an implicit conversion constraint. */
+tree
+check_implicit_conversion_constraint (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ tree expr = ICONV_CONSTR_EXPR (t);
+
+ /* Don't tsubst as if we're processing a template. If we try
+ to we can end up generating template-like expressions
+ (e.g., modop-exprs) that aren't properly typed. */
+ int saved_template_decl = processing_template_decl;
+ processing_template_decl = 0;
+ tree arg = tsubst_expr (expr, args, complain, in_decl, false);
+ processing_template_decl = saved_template_decl;
+
+ if (arg == error_mark_node)
+ return boolean_false_node;
+ tree from = TREE_TYPE (arg);
+
+ tree type = ICONV_CONSTR_TYPE (t);
+ tree to = tsubst (type, args, complain, in_decl);
+ if (to == error_mark_node)
+ return boolean_false_node;
+
+ if (can_convert_arg (to, from, arg, LOOKUP_IMPLICIT, complain))
+ return boolean_true_node;
+ else
+ return boolean_false_node;
}
-// Substitute ARGS into the nested requirement T. Note that a requirement
-// node is instantiated from a non-reduced context (e.g., static_assert).
+/* Check an argument deduction constraint.
+
+ TODO: Implement me. We need generalized auto for this to work. */
tree
-tsubst_nested_req (tree t, tree args, tree in_decl)
+check_argument_deduction_constraint (tree /*t*/, tree /*args*/,
+ tsubst_flags_t /*complain*/,
+ tree /*in_decl*/)
{
- return tsubst_expr (TREE_OPERAND (t, 0), args, tf_none, in_decl, false);
+ gcc_unreachable ();
+ return boolean_false_node;
}
-// Used in various contexts to control substitution. In particular, when
-// non-zero, the substitution of NULL arguments into a type will still
-// process the type as if passing non-NULL arguments, allowing type
-// expressions to be fully elaborated during substitution.
-int processing_constraint;
+/* Check an exception constraint. An exception constraint for an
+ expression e is satisfied when noexcept(e) is true. */
+tree
+check_exception_constraint (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ tree expr = EXCEPT_CONSTR_EXPR (t);
+ tree check = tsubst_expr (expr, args, complain, in_decl, false);
+ if (check == error_mark_node)
+ return boolean_false_node;
-// Substitute the template arguments ARGS into the requirement
-// expression REQS. Errors resulting from substitution are not
-// diagnosed.
-//
-// If DO_NOT_FOLD is true, then the requirements are substituted as
-// if parsing a template declaration, which causes the resulting expression
-// to not be folded.
+ if (expr_noexcept_p (check, complain))
+ return boolean_true_node;
+ else
+ return boolean_false_node;
+}
+
+/* Check a parameterized constraint. */
tree
-tsubst_constraint_expr (tree reqs, tree args, bool do_not_fold)
+check_parameterized_constraint (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
{
- cp_unevaluated guard;
- ++processing_constraint;
- if (do_not_fold)
- ++processing_template_decl;
- tree r = tsubst_expr (reqs, args, tf_none, NULL_TREE, false);
- if (do_not_fold)
- --processing_template_decl;
- --processing_constraint;
- return r;
+ local_specialization_stack stack;
+ tree parms = PARM_CONSTR_PARMS (t);
+ tree vars = tsubst_constraint_variables (parms, args, complain, in_decl);
+ if (vars == error_mark_node)
+ return boolean_false_node;
+ tree constr = PARM_CONSTR_OPERAND (t);
+ return check_constraint (constr, args, complain, in_decl);
+}
+
+/* Check that the conjunction of constraints is satisfied. Note
+ that if left operand is not satisfied, the right operand
+ is not checked.
+
+ FIXME: Check that this wouldn't result in a user-defined
+ operator. Note that this error is partially diagnosed in
+ check_predicate_constriant. It would be nice to diagnose
+ the overload, but I don't think it's strictly necessary. */
+tree
+check_conjunction (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+{
+ tree t0 = check_constraint (TREE_OPERAND (t, 0), args, complain, in_decl);
+ if (t0 == boolean_false_node)
+ return t0;
+ tree t1 = check_constraint (TREE_OPERAND (t, 1), args, complain, in_decl);
+ if (t1 == boolean_false_node)
+ return t1;
+ return boolean_true_node;
}
-// Substitute into the constraint information, producing a new constraint
-// record.
+/* Check that the disjunction of constraints is satisfied. Note
+ that if the left operand is satisfied, the right operand is not
+ checked. */
tree
-tsubst_constraint_info (tree ci, tree args)
+check_disjunction (tree t, tree args, tsubst_flags_t complain, tree in_decl)
{
- if (!ci || ci == error_mark_node || !check_constraint_info (ci))
- return NULL_TREE;
+ tree t0 = check_constraint (TREE_OPERAND (t, 0), args, complain, in_decl);
+ if (t0 == boolean_true_node)
+ return t0;
+ tree t1 = check_constraint (TREE_OPERAND (t, 1), args, complain, in_decl);
+ if (t1 == boolean_true_node)
+ return t0;
+ return boolean_false_node;
+}
- tree tr = NULL_TREE;
- if (tree r = CI_TEMPLATE_REQS (ci))
- tr = tsubst_constraint_expr (r, args, true);
+/* Check that the constraint is satisfied, according to the rules
+ for that constraint. Note that each check_* function returns
+ true or false, depending on whether it is satisfied or not. */
+tree
+check_constraint (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+{
+ if (!t)
+ return boolean_false_node;
- tree dr = NULL_TREE;
- if (tree r = CI_DECLARATOR_REQS (ci))
- dr = tsubst_constraint_expr (r, args, true);
+ if (t == error_mark_node)
+ return boolean_false_node;
- // TODO: This is re-normalizing and re-decomposing. We probably
- // don't need to do this.
- return build_constraints (tr, dr);
+ switch (TREE_CODE (t))
+ {
+ case PRED_CONSTR:
+ return check_predicate_constraint (t, args, complain, in_decl);
+
+ case EXPR_CONSTR:
+ return check_expression_constraint (t, args, complain, in_decl);
+
+ case TYPE_CONSTR:
+ return check_type_constraint (t, args, complain, in_decl);
+
+ case ICONV_CONSTR:
+ return check_implicit_conversion_constraint (t, args, complain, in_decl);
+
+ case DEDUCT_CONSTR:
+ return check_argument_deduction_constraint (t, args, complain, in_decl);
+
+ case EXCEPT_CONSTR:
+ return check_exception_constraint (t, args, complain, in_decl);
+
+ case PARM_CONSTR:
+ return check_parameterized_constraint (t, args, complain, in_decl);
+
+ case CONJ_CONSTR:
+ return check_conjunction (t, args, complain, in_decl);
+
+ case DISJ_CONSTR:
+ return check_disjunction (t, args, complain, in_decl);
+
+ default:
+ gcc_unreachable ();
+ }
+ return boolean_false_node;
}
-// -------------------------------------------------------------------------- //
-// Constraint Satisfaction
-//
-// The following functions are responsible for the instantiation and
-// evaluation of constraints.
+} /* namespace */
-namespace {
-// Returns true iff the atomic constraint, REQ, is satisfied. This
-// is the case when substitution succeeds and the resulting expression
-// evaluates to true.
-static bool
-check_satisfied (tree req, tree args)
-{
- // If any arguments are dependent, then we can't check the
- // requirements. Just return true.
- if (args && uses_template_parms (args))
+/* Check the constraints in CI against the given ARGS, returning
+ true when the constraints are satisfied and false otherwise.
+
+ Note that this is the only place that we instantiate the
+ constraints. */
+bool
+check_constraints (tree ci, tree args)
+{
+ /* If there are no constraints then this is trivially satisfied. */
+ if (!ci)
return true;
- // Instantiate and evaluate the requirements.
- req = tsubst_constraint_expr (req, args, false);
- if (req == error_mark_node)
+ /* If any arguments depend on template parameters, we can't
+ check constraints. */
+ if (args && uses_template_parms (args))
+ return true;
+
+ /* Invalid requirements cannot be satisfied. */
+ if (!valid_constraints_p (ci))
return false;
- // Reduce any remaining TRAIT_EXPR nodes before evaluating.
- req = fold_non_dependent_expr (req);
-
- // Requirements are satisfied when REQS evaluates to true.
- tree result = cxx_constant_value (req);
+ tree constr = CI_NORMALIZED_CONSTRAINTS (ci);
+ tree result = check_constraint (constr, args, tf_none, NULL_TREE);
return result == boolean_true_node;
}
-} // namespace
-
-// Check the instantiated declaration constraints.
+/* Returns true if the declaration's constraints are satisfied.
+ This is used in cases where a declaration is formed but
+ before it is used (e.g., overload resolution). */
bool
-check_constraints (tree cinfo)
+constraints_satisfied_p (tree decl)
{
- // No constraints? Satisfied.
- if (!cinfo)
- return true;
- // Invalid constraints, not satisfied.
- else if (!valid_requirements_p (cinfo))
- return false;
- // Funnel back into the dependent checking branch. This forces
- // one more substitution through the constraints, which removes
- // all remaining expressions that are not constant expressions
- // (e.g., template-id expressions).
+ /* Get the constraints to check for satisfaction. This depends
+ on whether we're looking at a template specialization or not. */
+ tree ci = NULL_TREE;
+ tree args = NULL_TREE;
+ if (tree ti = DECL_TEMPLATE_INFO (decl))
+ {
+ ci = get_constraints (TI_TEMPLATE (ti));
+ args = INNERMOST_TEMPLATE_ARGS (TI_ARGS (ti));
+ }
else
- return check_satisfied (CI_NORMALIZED_CONSTRAINTS (cinfo), NULL_TREE);
+ {
+ ci = get_constraints (decl);
+ }
+
+ return check_constraints (ci, args);
}
-// Check the constraints in CINFO against the given ARGS, returning
-// true when the constraints are satisfied and false otherwise.
+/* Returns true if the template's constrains are satisfied
+ by the given arguments. This is used in cases where a
+ declaration cannot be formed unless the constraints are
+ checked (e.g., the use of a template-id that names a
+ class). */
bool
-check_constraints (tree cinfo, tree args)
+constraints_satisfied_p (tree tmpl, tree args)
{
- // If there are no constraints then this is trivally satisfied.
- if (!cinfo)
- return true;
- // Invlaid requirements cannot be satisfied.
- else if (!valid_requirements_p (cinfo))
- return false;
- else {
- return check_satisfied (CI_NORMALIZED_CONSTRAINTS (cinfo), args);
- }
+ return check_constraints (get_constraints (tmpl), args);
+}
+
+/* Evaluate the function concept FN by substituting ARGS into its
+ definition and evaluating that as the result. Returns
+ boolean_true_node if the constraints are satisfied and
+ boolean_false_node otherwise. */
+tree
+evaluate_function_concept (tree fn, tree args)
+{
+ ++processing_template_decl;
+ tree constr = transform_expression (lift_function_definition (fn, args));
+ --processing_template_decl;
+ return check_constraint (constr, args, tf_none, NULL_TREE);
+}
+
+/* Evaluate the variable concept VAR by substituting ARGS into
+ its initializer and checking the resulting constraint. Returns
+ boolean_true_node if the constraints are satisfied and
+ boolean_false_node otherwise. */
+tree
+evaluate_variable_concept (tree decl, tree args)
+{
+ ++processing_template_decl;
+ tree constr = transform_expression (lift_variable_initializer (decl, args));
+ --processing_template_decl;
+ return check_constraint (constr, args, tf_none, NULL_TREE);
}
-// Check the constraints of the declaration or type T, against
-// the specified arguments. Returns true if the constraints are
-// satisfied and false otherwise.
+/* Evaluate the given expression as if it were a predicate
+ constraint. Returns boolean_true_node if the constraint
+ is satisfied and boolean_false_node otherwise. */
+tree
+evaluate_constraint_expression (tree expr, tree args)
+{
+ ++processing_template_decl;
+ tree constr = transform_expression (lift_constraints (expr));
+ --processing_template_decl;
+ return check_constraint (constr, args, tf_none, NULL_TREE);
+}
+
+/* Normalize EXPR and determine if the resulting constraint is
+ satisfied by ARGS. Returns true if and only if the constraint
+ is satisfied. */
bool
-check_template_constraints (tree t, tree args)
+check_constraint_expression (tree expr, tree args)
{
- return check_constraints (get_constraints (t), args);
+ return evaluate_constraint_expression (expr, args) == boolean_true_node;
}
-// -------------------------------------------------------------------------- //
-// Constraint Relations
-//
-// Interfaces for determining equivalency and ordering of constraints.
+/*---------------------------------------------------------------------------
+ Semantic analysis of requires-expressions
+---------------------------------------------------------------------------*/
-// Returns true when A and B are equivalent constraints.
+/* Finish a requires expression for the given PARMS (possibly
+ null) and the non-empty sequence of requirements. */
+tree
+finish_requires_expr (tree parms, tree reqs)
+{
+ /* Modify the declared parameters by removing their context
+ so they don't refer to the enclosing scope. */
+ for (tree p = parms; p && !VOID_TYPE_P (TREE_VALUE (p)); p = TREE_CHAIN (p))
+ DECL_CONTEXT (TREE_VALUE (p)) = NULL_TREE;
+
+ /* Build the node. */
+ tree r = build_min (REQUIRES_EXPR, boolean_type_node, parms, reqs);
+ TREE_SIDE_EFFECTS (r) = false;
+ TREE_CONSTANT (r) = true;
+ return r;
+}
+
+/* Construct a requirement for the validity of EXPR. */
+tree
+finish_simple_requirement (tree expr)
+{
+ return build_nt (SIMPLE_REQ, expr);
+}
+
+/* Construct a requirement for the validity of TYPE. */
+tree
+finish_type_requirement (tree type)
+{
+ return build_nt (TYPE_REQ, type);
+}
+
+/* Construct a requirement for the validity of EXPR, along with
+ its properties. if TYPE is non-null, then it specifies either
+ an implicit conversion or argument deduction constraint,
+ depending on whether any placeholders occur in the type name.
+ NOEXCEPT_P is true iff the noexcept keyword was specified. */
+tree
+finish_compound_requirement (tree expr, tree type, bool noexcept_p)
+{
+ tree req = build_nt (COMPOUND_REQ, expr, type);
+ COMPOUND_REQ_NOEXCEPT_P (req) = noexcept_p;
+ return req;
+}
+
+/* Finish a nested requirement. */
+tree
+finish_nested_requirement (tree expr)
+{
+ return build_nt (NESTED_REQ, expr);
+}
+
+// Check that FN satisfies the structural requirements of a
+// function concept definition.
+tree
+check_function_concept (tree fn)
+{
+ location_t loc = DECL_SOURCE_LOCATION (fn);
+
+ // Check that the function is comprised of only a single
+ // return statement.
+ tree body = DECL_SAVED_TREE (fn);
+ if (TREE_CODE (body) == BIND_EXPR)
+ body = BIND_EXPR_BODY (body);
+
+ // Sometimes a function call results in the creation of clean up
+ // points. Allow these to be preserved in the body of the
+ // constraint, as we might actually need them for some constexpr
+ // evaluations.
+ if (TREE_CODE (body) == CLEANUP_POINT_EXPR)
+ body = TREE_OPERAND (body, 0);
+
+ if (TREE_CODE (body) != RETURN_EXPR)
+ error_at (loc, "function concept definition %qD has multiple statements",
+ fn);
+
+ return NULL_TREE;
+}
+
+
+// Check that a constrained friend declaration function declaration,
+// FN, is admissible. This is the case only when the declaration depends
+// on template parameters and does not declare a specialization.
+void
+check_constrained_friend (tree fn, tree reqs)
+{
+ if (fn == error_mark_node)
+ return;
+ gcc_assert (TREE_CODE (fn) == FUNCTION_DECL);
+
+ // If there are not constraints, this cannot be an error.
+ if (!reqs)
+ return;
+
+ // Constrained friend functions that don't depend on template
+ // arguments are effectively meaningless.
+ tree parms = DECL_ARGUMENTS (fn);
+ tree result = TREE_TYPE (TREE_TYPE (fn));
+ if (!(parms && uses_template_parms (parms)) && !uses_template_parms (result))
+ {
+ error ("constrained friend does not depend on template parameters");
+ return;
+ }
+}
+
+/*---------------------------------------------------------------------------
+ Equivalence of constraints
+---------------------------------------------------------------------------*/
+
+/* Returns true when A and B are equivalent constraints. */
bool
equivalent_constraints (tree a, tree b)
{
@@ -1626,9 +1925,9 @@ equivalent_constraints (tree a, tree b)
return cp_tree_equal (a, b);
}
-// Returns true if the template declarations A and B have equivalent
-// constraints. This is the case when A's constraints subsume B's and
-// when B's also constrain A's.
+/* Returns true if the template declarations A and B have equivalent
+ constraints. This is the case when A's constraints subsume B's and
+ when B's also constrain A's. */
bool
equivalently_constrained (tree d1, tree d2)
{
@@ -1636,7 +1935,11 @@ equivalently_constrained (tree d1, tree d2)
return equivalent_constraints (get_constraints (d1), get_constraints (d2));
}
-// Returns true when the the constraints in A subsume those in B.
+/*---------------------------------------------------------------------------
+ Partial ordering of constraints
+---------------------------------------------------------------------------*/
+
+/* Returns true when the the constraints in A subsume those in B. */
bool
subsumes_constraints (tree a, tree b)
{
@@ -1645,12 +1948,12 @@ subsumes_constraints (tree a, tree b)
return subsumes (a, b);
}
-// Determines which of the declarations, A or B, is more constrained.
-// That is, which declaration's constraints subsume but are not subsumed
-// by the other's?
-//
-// Returns 1 if A is more constrained than B, -1 if B is more constrained
-// than A, and 0 otherwise.
+/* Determines which of the declarations, A or B, is more constrained.
+ That is, which declaration's constraints subsume but are not subsumed
+ by the other's?
+
+ Returns 1 if A is more constrained than B, -1 if B is more constrained
+ than A, and 0 otherwise. */
int
more_constrained (tree d1, tree d2)
{
@@ -1664,9 +1967,9 @@ more_constrained (tree d1, tree d2)
return winner;
}
-// Returns true if d1 is at least as constrained as d2. That is, the
-// associated constraints of d1 subsume those of d2, or both declarations
-// are unconstrained.
+/* Returns true if D1 is at least as constrained as D2. That is, the
+ associated constraints of D1 subsume those of D2, or both declarations
+ are unconstrained. */
bool
at_least_as_constrained (tree d1, tree d2)
{
@@ -1676,367 +1979,295 @@ at_least_as_constrained (tree d1, tree d2)
}
-// -------------------------------------------------------------------------- //
-// Constraint Diagnostics
+/*---------------------------------------------------------------------------
+ Constraint diagnostics
+---------------------------------------------------------------------------*/
+
+/* The diagnosis of constraints performs a combination of
+ normalization and satisfaction testing. We recursively
+ walk through the conjunction (or disjunctions) of associated
+ constraints, testing each sub-expression in turn.
+
+ We currently restrict diagnostics to just the top-level
+ conjunctions within the associated constraints. A fully
+ recursive walk is possible, but it can generate a lot
+ of errors. */
+
namespace {
-// Given an arbitrary constraint expression, normalize it and
-// then check it. We have to normalize so we don't accidentally
-// instantiate concept declarations.
-inline bool
-check_diagnostic_constraints (tree reqs, tree args)
+void diagnose_expression (location_t, tree, tree);
+void diagnose_constraint (location_t, tree, tree);
+
+/* Diagnose a conjunction of constraints. */
+void
+diagnose_logical_operation (location_t loc, tree t, tree args)
{
- return check_satisfied (normalize_constraints (reqs), args);
+ diagnose_expression (loc, TREE_OPERAND (t, 0), args);
+ diagnose_expression (loc, TREE_OPERAND (t, 0), args);
}
-void diagnose_node (location_t, tree, tree);
-
-// Diagnose a constraint failure for type trait expressions.
+/* Determine if the trait expression T is satisfied by ARGS.
+ Emit a precise diagnostic if it is not. */
void
-diagnose_trait (location_t loc, tree t, tree args)
+diagnose_trait_expression (location_t loc, tree t, tree args)
{
- if (check_diagnostic_constraints (t, args))
+ if (check_constraint_expression (t, args))
return;
- tree subst = tsubst_constraint_expr (t, args, true);
-
- if (subst == error_mark_node)
- {
- inform (input_location, " substitution failure in %qE", t);
- return;
- }
+ /* Rebuild the trait expression so we can diagnose the
+ specific failure. */
+ ++processing_template_decl;
+ tree expr = tsubst_expr (t, args, tf_none, NULL_TREE, false);
+ --processing_template_decl;
- tree t1 = TRAIT_EXPR_TYPE1 (subst);
- tree t2 = TRAIT_EXPR_TYPE2 (subst);
+ tree t1 = TRAIT_EXPR_TYPE1 (expr);
+ tree t2 = TRAIT_EXPR_TYPE2 (expr);
switch (TRAIT_EXPR_KIND (t))
{
- case CPTK_HAS_NOTHROW_ASSIGN:
- inform (loc, " %qT is not nothrow copy assignable", t1);
- break;
- case CPTK_HAS_NOTHROW_CONSTRUCTOR:
- inform (loc, " %qT is not nothrow default constructible", t1);
- break;
- case CPTK_HAS_NOTHROW_COPY:
- inform (loc, " %qT is not nothrow copy constructible", t1);
- break;
- case CPTK_HAS_TRIVIAL_ASSIGN:
- inform (loc, " %qT is not trivially copy assignable", t1);
- break;
- case CPTK_HAS_TRIVIAL_CONSTRUCTOR:
- inform (loc, " %qT is not trivially default constructible", t1);
- break;
- case CPTK_HAS_TRIVIAL_COPY:
- inform (loc, " %qT is not trivially copy constructible", t1);
- break;
- case CPTK_HAS_TRIVIAL_DESTRUCTOR:
- inform (loc, " %qT is not trivially destructible", t1);
- break;
- case CPTK_HAS_VIRTUAL_DESTRUCTOR:
- inform (loc, " %qT does not have a virtual destructor", t1);
- break;
- case CPTK_IS_ABSTRACT:
- inform (loc, " %qT is not an abstract class", t1);
- break;
- case CPTK_IS_BASE_OF:
- inform (loc, " %qT is not a base of %qT", t1, t2);
- break;
- case CPTK_IS_CLASS:
- inform (loc, " %qT is not a class", t1);
- break;
- case CPTK_IS_CONVERTIBLE_TO:
- inform (loc, " %qT is not convertible to %qT", t1, t2);
- break;
- case CPTK_IS_EMPTY:
- inform (loc, " %qT is not an empty class", t1);
- break;
- case CPTK_IS_ENUM:
- inform (loc, " %qT is not an enum", t1);
- break;
- case CPTK_IS_FINAL:
- inform (loc, " %qT is not a final class", t1);
- break;
- case CPTK_IS_LITERAL_TYPE:
- inform (loc, " %qT is not a literal type", t1);
- break;
- case CPTK_IS_POD:
- inform (loc, " %qT is not a POD type", t1);
- break;
- case CPTK_IS_POLYMORPHIC:
- inform (loc, " %qT is not a polymorphic type", t1);
- break;
- case CPTK_IS_SAME_AS:
- inform (loc, " %qT is not the same as %qT", t1, t2);
- break;
- case CPTK_IS_STD_LAYOUT:
- inform (loc, " %qT is not an standard layout type", t1);
- break;
- case CPTK_IS_TRIVIAL:
- inform (loc, " %qT is not a trivial type", t1);
- break;
- case CPTK_IS_UNION:
- inform (loc, " %qT is not a union", t1);
- break;
- default:
- gcc_unreachable ();
+ case CPTK_HAS_NOTHROW_ASSIGN:
+ inform (loc, " %qT is not nothrow copy assignable", t1);
+ break;
+ case CPTK_HAS_NOTHROW_CONSTRUCTOR:
+ inform (loc, " %qT is not nothrow default constructible", t1);
+ break;
+ case CPTK_HAS_NOTHROW_COPY:
+ inform (loc, " %qT is not nothrow copy constructible", t1);
+ break;
+ case CPTK_HAS_TRIVIAL_ASSIGN:
+ inform (loc, " %qT is not trivially copy assignable", t1);
+ break;
+ case CPTK_HAS_TRIVIAL_CONSTRUCTOR:
+ inform (loc, " %qT is not trivially default constructible", t1);
+ break;
+ case CPTK_HAS_TRIVIAL_COPY:
+ inform (loc, " %qT is not trivially copy constructible", t1);
+ break;
+ case CPTK_HAS_TRIVIAL_DESTRUCTOR:
+ inform (loc, " %qT is not trivially destructible", t1);
+ break;
+ case CPTK_HAS_VIRTUAL_DESTRUCTOR:
+ inform (loc, " %qT does not have a virtual destructor", t1);
+ break;
+ case CPTK_IS_ABSTRACT:
+ inform (loc, " %qT is not an abstract class", t1);
+ break;
+ case CPTK_IS_BASE_OF:
+ inform (loc, " %qT is not a base of %qT", t1, t2);
+ break;
+ case CPTK_IS_CLASS:
+ inform (loc, " %qT is not a class", t1);
+ break;
+ case CPTK_IS_CONVERTIBLE_TO:
+ inform (loc, " %qT is not convertible to %qT", t1, t2);
+ break;
+ case CPTK_IS_EMPTY:
+ inform (loc, " %qT is not an empty class", t1);
+ break;
+ case CPTK_IS_ENUM:
+ inform (loc, " %qT is not an enum", t1);
+ break;
+ case CPTK_IS_FINAL:
+ inform (loc, " %qT is not a final class", t1);
+ break;
+ case CPTK_IS_LITERAL_TYPE:
+ inform (loc, " %qT is not a literal type", t1);
+ break;
+ case CPTK_IS_POD:
+ inform (loc, " %qT is not a POD type", t1);
+ break;
+ case CPTK_IS_POLYMORPHIC:
+ inform (loc, " %qT is not a polymorphic type", t1);
+ break;
+ case CPTK_IS_SAME_AS:
+ inform (loc, " %qT is not the same as %qT", t1, t2);
+ break;
+ case CPTK_IS_STD_LAYOUT:
+ inform (loc, " %qT is not an standard layout type", t1);
+ break;
+ case CPTK_IS_TRIVIAL:
+ inform (loc, " %qT is not a trivial type", t1);
+ break;
+ case CPTK_IS_UNION:
+ inform (loc, " %qT is not a union", t1);
+ break;
+ default:
+ gcc_unreachable ();
}
}
-// Diagnose a failed concept check in concept indicated by T, where
-// T is the result of resolve_constraint_check. Recursively analyze
-// the nested requiremets for details.
-void
-diagnose_check (location_t loc, tree t, tree args)
-{
- tree fn = TREE_VALUE (t);
- tree targs = TREE_PURPOSE (t);
- tree body = DECL_SAVED_TREE (fn);
- if (!body)
- return;
-
- inform (loc, " failure in constraint %q#D", DECL_TI_TEMPLATE (fn));
+/* Determine if the call expression T, when normalized as a constraint,
+ is satisfied by ARGS.
- // Perform a mini-reduction on the constraint.
- if (TREE_CODE (body) == BIND_EXPR)
- body = BIND_EXPR_BODY (body);
- if (TREE_CODE (body) == RETURN_EXPR)
- body = TREE_OPERAND (body, 0);
-
- // Locally instantiate the body with the call's template args,
- // and recursively diagnose.
- body = tsubst_constraint_expr (body, targs, true);
-
- diagnose_node (loc, body, args);
-}
-
-// Diagnose constraint failures from the call expression T.
+ TODO: If T is refers to a concept, We could recursively analyze
+ its definition to identify the exact failure, but that could
+ emit a *lot* of error messages (defeating the purpose of
+ improved diagnostics). Consider adding a flag to control the
+ depth of diagnostics. */
void
-diagnose_call (location_t loc, tree t, tree args)
+diagnose_call_expression (location_t loc, tree t, tree args)
{
- if (check_diagnostic_constraints (t, args))
+ if (check_constraint_expression (t, args))
return;
- // If this is a concept, we're going to recurse.
- // If it's just a call, then we can emit a simple message.
- if (tree check = resolve_constraint_check (t))
- diagnose_check (loc, check, args);
+ /* Rebuild the expression for the purpose of diagnostics. */
+ ++processing_template_decl;
+ tree expr = tsubst_expr (t, args, tf_none, NULL_TREE, false);
+ --processing_template_decl;
+
+ /* If the function call is known to be a concept check, then
+ diagnose it differently (i.e., we may recurse). */
+ if (resolve_constraint_check (t))
+ inform (loc, " concept %qE was not satisfied", expr);
else
- inform (loc, " %qE evaluated to false", t);
+ inform (loc, " %qE evaluated to false", expr);
}
-// Diagnose constraint failures in a variable template-id T.
+/* Determine if the template-id T, when normalized as a constraint
+ is satisfied by ARGS. */
void
-diagnose_var (location_t loc, tree t, tree args)
+diagnose_template_id (location_t loc, tree t, tree args)
{
- // If the template-id isn't a variable template, it can't be a
- // valid constraint.
+ /* Check for invalid template-ids. */
if (!variable_template_p (TREE_OPERAND (t, 0)))
{
inform (loc, " invalid constraint %qE", t);
return;
}
- if (check_diagnostic_constraints (t, args))
- return;
-
- tree var = DECL_TEMPLATE_RESULT (TREE_OPERAND (t, 0));
- tree body = DECL_INITIAL (var);
- tree targs = TREE_OPERAND (t, 1);
- tree subst = tsubst_constraint_expr (body, targs, true);
-
- inform (loc, " failure in constraint %q#D", DECL_TI_TEMPLATE (var));
-
- diagnose_node (loc, subst, args);
-}
-
-// Diagnose specific constraint failures.
-void
-diagnose_requires (location_t loc, tree t, tree args)
-{
- if (check_diagnostic_constraints (t, args))
- return;
-
- tree subst = tsubst_constraint_expr (t, args, true);
-
- // Print the header for the requires expression.
- tree parms = TREE_OPERAND (subst, 0);
- if (!VOID_TYPE_P (TREE_VALUE (parms)))
- inform (loc, " requiring syntax with values %Z", TREE_OPERAND (subst, 0));
-
- // Create a new local specialization binding for the arguments.
- // This lets us instantiate sub-expressions separately from the
- // requires clause.
- local_specialization_stack locals;
- declare_local_parms (TREE_OPERAND (t, 0), TREE_OPERAND (subst, 0));
-
- // Iterate over the sub-requirements and try instantiating each.
- for (tree l = TREE_OPERAND (t, 1); l; l = TREE_CHAIN (l))
- diagnose_node (loc, TREE_VALUE (l), args);
-}
-
-static void
-diagnose_validexpr (location_t loc, tree t, tree args)
-{
- if (check_diagnostic_constraints (t, args))
+ if (check_constraint_expression (t, args))
return;
- inform (loc, " %qE is not a valid expression", TREE_OPERAND (t, 0));
-}
-static void
-diagnose_validtype (location_t loc, tree t, tree args)
-{
- if (check_diagnostic_constraints (t, args))
- return;
+ /* Rebuild the expression for the purpose of diagnostics. */
+ ++processing_template_decl;
+ tree expr = tsubst_expr (t, args, tf_none, NULL_TREE, false);
+ --processing_template_decl;
- // Substitute into the qualified name.
- tree name = TREE_OPERAND (t, 0);
- if (tree cxt = TYPE_CONTEXT (name))
- {
- tree id = TYPE_IDENTIFIER (name);
- cxt = tsubst (cxt, args, tf_none, NULL_TREE);
- name = build_qualified_name (NULL_TREE, cxt, id, false);
- inform (loc, " %qE does not name a valid type", name);
- }
+ tree var = DECL_TEMPLATE_RESULT (TREE_OPERAND (t, 0));
+ if (DECL_DECLARED_CONCEPT_P (var))
+ inform (loc, " concept %qE was not satisfied", expr);
else
- {
- inform (loc, " %qT does not name a valid type", name);
- }
+ inform (loc, " %qE evaluated to false", expr);
}
-static void
-diagnose_constexpr (location_t loc, tree t, tree args)
-{
- if (check_diagnostic_constraints (t, args))
- return;
- inform (loc, " %qE is not a constant expression", TREE_OPERAND (t, 0));
-}
+/* Determine if the requires-expression, when normalized as a
+ constraint is satisfied by ARGS.
-static void
-diagnose_noexcept (location_t loc, tree t, tree args)
+ TODO: Build sets of expressions, types, and constraints
+ based on the requirements in T and emit specific diagnostics
+ for those. */
+void
+diagnose_requires_expression (location_t loc, tree t, tree args)
{
- if (check_diagnostic_constraints (t, args))
+ if (check_constraint_expression (t, args))
return;
- inform (loc, " %qE propagates exceptions", TREE_OPERAND (t, 0));
+ inform (loc, "requirements not satisfied");
}
-// Diagnose a constraint failure in the expression T.
+/* Diagnose an expression that would be characterized as
+ a predicate constraint. */
void
-diagnose_other (location_t loc, tree t, tree args)
+diagnose_other_expression (location_t loc, tree t, tree args)
{
- if (check_diagnostic_constraints (t, args))
+ if (check_constraint_expression (t, args))
return;
inform (loc, " %qE evaluated to false", t);
}
-// Diagnose a constraint failure in the subtree T.
void
-diagnose_node (location_t loc, tree t, tree args)
+diagnose_expression (location_t loc, tree t, tree args)
{
switch (TREE_CODE (t))
{
case TRUTH_ANDIF_EXPR:
- diagnose_node (loc, TREE_OPERAND (t, 0), args);
- diagnose_node (loc, TREE_OPERAND (t, 1), args);
+ diagnose_logical_operation (loc, t, args);
break;
case TRUTH_ORIF_EXPR:
- // TODO: Design better diagnostics for dijunctions.
- diagnose_other (loc, t, args);
- break;
-
- case TRAIT_EXPR:
- diagnose_trait (loc, t, args);
+ diagnose_logical_operation (loc, t, args);
break;
case CALL_EXPR:
- diagnose_call (loc, t, args);
+ diagnose_call_expression (loc, t, args);
break;
- case REQUIRES_EXPR:
- diagnose_requires (loc, t, args);
- break;
-
- case VALIDEXPR_EXPR:
- diagnose_validexpr (loc, t, args);
- break;
-
- case VALIDTYPE_EXPR:
- diagnose_validtype (loc, t, args);
- break;
-
- case CONSTEXPR_EXPR:
- diagnose_constexpr (loc, t, args);
+ case TEMPLATE_ID_EXPR:
+ diagnose_template_id (loc, t, args);
break;
- case NOEXCEPT_EXPR:
- diagnose_noexcept (loc, t, args);
+ case REQUIRES_EXPR:
+ diagnose_requires_expression (loc, t, args);
break;
- case TEMPLATE_ID_EXPR:
- diagnose_var (loc, t, args);
+ case TRAIT_EXPR:
+ diagnose_trait_expression (loc, t, args);
break;
default:
- diagnose_other (loc, t, args);
+ diagnose_other_expression (loc, t, args);
break;
}
}
-// Diagnose a constraint failure in the requirements expression REQS.
inline void
-diagnose_requirements (location_t loc, tree reqs, tree args)
+diagnose_predicate_constraint (location_t loc, tree t, tree args)
{
- diagnose_node (loc, reqs, args);
+ diagnose_expression (loc, PRED_CONSTR_EXPR (t), args);
}
-// Create a tree node representing the substitution of ARGS into
-// the parameters of TMPL. The resulting structure is passed as an
-// for diagnosing substitutions.
-inline tree
-make_subst (tree tmpl, tree args)
+inline void
+diagnose_conjunction (location_t loc, tree t, tree args)
{
- tree subst = tree_cons (NULL_TREE, args, NULL_TREE);
- TREE_TYPE (subst) = DECL_TEMPLATE_PARMS (tmpl);
- return subst;
+ diagnose_constraint (loc, TREE_OPERAND (t, 0), args);
+ diagnose_constraint (loc, TREE_OPERAND (t, 1), args);
+}
+
+/* Diagnose the constraint T for the given ARGS. This is only
+ ever invoked on the associated constraints, so we can
+ only have conjunctions of predicate constraints. */
+void
+diagnose_constraint (location_t loc, tree t, tree args)
+{
+ switch (TREE_CODE (t))
+ {
+ case CONJ_CONSTR:
+ diagnose_conjunction (loc, t, args);
+ break;
+
+ case PRED_CONSTR:
+ diagnose_predicate_constraint (loc, t, args);
+ break;
+
+ default:
+ gcc_unreachable ();
+ break;
+ }
}
} // namespace
-// Emit diagnostics detailing the failure ARGS to satisfy the constraints
-// of the template declaration, TMPL.
+/* Emit diagnostics detailing the failure ARGS to satisfy the constraints
+ of the template declaration, DECL. */
void
diagnose_constraints (location_t loc, tree decl, tree args)
{
- tree ci = get_constraints (decl);
+ inform (loc, " constraints not satisfied");
- // If the constraints could not be reduced, then we can't diagnose them.
- if (!valid_requirements_p (ci))
+ /* Constraints are attached to the template. */
+ if (tree ti = DECL_TEMPLATE_INFO (decl)) {
+ decl = TI_TEMPLATE (ti);
+ args = TI_ARGS (ti);
+ }
+
+ /* Check that the constraints are actually valid. */
+ tree ci = get_constraints (decl);
+ if (!valid_constraints_p (ci))
{
- inform (loc, " invalid constraints");
+ inform (loc, " invalid constraints");
return;
}
- // FIXME: Re-think how we recurse through the expression to emit
- // diagnostics.
-
- // If this is a specialization of a template, we want to diagnose
- // the dependent constraints. Also update the template arguments.
- // if (DECL_USE_TEMPLATE (decl))
- // {
- // args = DECL_TI_ARGS (decl);
- // decl = DECL_TI_TEMPLATE (decl);
- // }
-
- // // Otherwise, diagnose the actual failed constraints.
- // if (TREE_CODE (decl) == TEMPLATE_DECL)
- // inform (loc, " constraints not satisfied %S", make_subst (decl, args));
- // else
- // inform (loc, " constraints not satisfied");
-
- inform (loc, " constraints not satisfied");
-
- // Diagnose the constraints by recursively decomposing and
- // evaluating the template requirements.
- tree reqs = CI_ASSOCIATED_CONSTRAINTS (get_constraints (decl));
- diagnose_requirements (loc, reqs, args);
+ /* Recursively diagnose the associated constraints. */
+ tree assoc = CI_ASSOCIATED_CONSTRAINTS (ci);
+ diagnose_constraint (loc, assoc, args);
}
@@ -318,13 +318,6 @@ cp_common_init_ts (void)
MARK_TS_TYPED (CTOR_INITIALIZER);
MARK_TS_TYPED (ARRAY_NOTATION_REF);
MARK_TS_TYPED (REQUIRES_EXPR);
- MARK_TS_TYPED (EXPR_REQ);
- MARK_TS_TYPED (TYPE_REQ);
- MARK_TS_TYPED (NESTED_REQ);
- MARK_TS_TYPED (VALIDEXPR_EXPR);
- MARK_TS_TYPED (VALIDTYPE_EXPR);
- MARK_TS_TYPED (CONSTEXPR_EXPR);
-
}
#include "gt-cp-cp-objcp-common.h"
@@ -475,20 +475,35 @@ DEFTREECODE (BASES, "bases", tcc_type, 0)
instantiation time. */
DEFTREECODE (TEMPLATE_INFO, "template_info", tcc_exceptional, 0)
-/* Concepts. */
+/* Extensions for Concepts. */
/* Used to represent information associated with constrained declarations. */
DEFTREECODE (CONSTRAINT_INFO, "constraint_info", tcc_exceptional, 0)
-/* A requires-expr is a binary expression. The first operand is its
- parameter list. The second is a list of requirements. */
+/* Alternative PLACEHOLDER_EXPR which is used for concept introductions. This
+ is a temporary type which is used to produce normal template parameters. */
+DEFTREECODE (INTRODUCED_PARM_DECL, "introduced_parm_decl", tcc_declaration, 0)
+
+/* A requires-expr is a binary expression. The first operand is
+ its parameter list (possibly NULL). The second is a list of
+ requirements, which are denoted by the _REQ* tree codes
+ below. */
DEFTREECODE (REQUIRES_EXPR, "requires_expr", tcc_expression, 2)
-/* The REQ expressions are unary expressions that specify individual
- statements in a requires clause. */
-DEFTREECODE (EXPR_REQ, "expr-req", tcc_expression, 1)
-DEFTREECODE (TYPE_REQ, "type-req", tcc_expression, 1)
-DEFTREECODE (NESTED_REQ, "nested-req", tcc_expression, 1)
+/* A requirement for an expression. */
+DEFTREECODE (SIMPLE_REQ, "simple_req", tcc_expression, 1)
+
+/* A requirement for a type. */
+DEFTREECODE (TYPE_REQ, "type_req", tcc_expression, 1)
+
+/* A requirement for an expression and its properties. The
+ first operand is the expression, and the 2nd is its type.
+ The accessor COMPOUND_REQ_NOEXCEPT determines whether
+ the noexcept keyword was present. */
+DEFTREECODE (COMPOUND_REQ, "compound_req", tcc_expression, 2)
+
+/* A requires clause within a requires expression. */
+DEFTREECODE (NESTED_REQ, "nested_req", tcc_expression, 1)
/* A unary expression representing a requirement for a valid expression. */
DEFTREECODE (VALIDEXPR_EXPR, "validexpr_expr", tcc_expression, 1)
@@ -500,9 +515,61 @@ DEFTREECODE (VALIDTYPE_EXPR, "validtype_expr", tcc_expression, 1)
can be evaluated at compile time. */
DEFTREECODE (CONSTEXPR_EXPR, "contexpr_expr", tcc_expression, 1)
-/* Alternative PLACEHOLDER_EXPR which is used for concept introductions. This
- is a temporary type which is used to produce normal template parameters. */
-DEFTREECODE (INTRODUCED_PARM_DECL, "introduced_parm_decl", tcc_declaration, 0)
+
+/* Constraints are modeled as kinds of expressions.
+ The operands of a constraint can be either types or expressions.
+ Unlike expressions, constraints do not have a type. */
+
+/* A predicate constraint evaluates an expression E.
+
+ PRED_CONSTR_EXPR has the expression to be evaluated. */
+DEFTREECODE (PRED_CONSTR, "pred_constr", tcc_expression, 1)
+
+/* An expression constraint determines the validity of a expression E.
+
+ EXPR_CONST_EXPR has the expression being validated. */
+DEFTREECODE (EXPR_CONSTR, "expr_constr", tcc_expression, 1)
+
+/* A type constraint determines the validity of a type T. Note that
+
+ TYPE_CONST_TYPE has the type being validated */
+DEFTREECODE (TYPE_CONSTR, "type_constr", tcc_expression, 1)
+
+/* An implicit conversion constraint determines if an expression
+ E is implicitly convertible to a type T. Note that T may
+ be dependent but does not contain any placeholders.
+
+ ICONV_CONSTR_EXPR has the expression E.
+ ICONV_CONSTR_TYPE has the type T.
+ */
+DEFTREECODE (ICONV_CONSTR, "iconv_constr", tcc_expression, 2)
+
+/* An argument deduction constraint determines if the type of an
+ expression E can be deduced from a type pattern T. Note that
+ T must contain at least one place holder.
+
+ DEDUCT_CONSTR_EXPR has the expression E
+ DEDUCT_CONSTR_PATTERN has the type patter T. */
+DEFTREECODE (DEDUCT_CONSTR, "deduct_constr", tcc_expression, 2)
+
+/* An exception constraint determines if, for an expression E,
+ noexcept(E) is true.
+
+ EXCEPT_CONSTR_EXPR has the expression E. */
+DEFTREECODE (EXCEPT_CONSTR, "except_constr", tcc_expression, 1)
+
+/* A parameterized constraint declares constraint variables, which
+ are used in expression, type, and exception constraints.
+
+ PARM_CONSTR_PARMS has a TREE_LIST of parameter declarations.
+ PARM_CONSTR_OPERAND has the nested constraint. */
+DEFTREECODE (PARM_CONSTR, "parm_constr", tcc_expression, 2)
+
+/* The conjunction and disjunction of two constraints, respectively.
+ Operands are accessed using TREE_OPERAND. */
+DEFTREECODE (CONJ_CONSTR, "conj_constr", tcc_expression, 2)
+DEFTREECODE (DISJ_CONSTR, "disj_constr", tcc_expression, 2)
+
/*
Local variables:
@@ -93,6 +93,7 @@ c-common.h, not after.
PACK_EXPANSION_LOCAL_P (in *_PACK_EXPANSION)
TINFO_RECHECK_ACCESS_P (in TEMPLATE_INFO)
SIZEOF_EXPR_TYPE_P (in SIZEOF_EXPR)
+ COMPOUND_REQ_NOEXCEPT_P (in COMPOUND_REQ)
INTRODUCED_PACK_P (in INTRODUCED_PARM_DECL)
1: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE)
TI_PENDING_TEMPLATE_FLAG.
@@ -890,6 +891,50 @@ check_constraint_info (tree t)
#define TEMPLATE_PARM_CONSTRAINTS(NODE) \
TREE_TYPE (TREE_LIST_CHECK (NODE))
+/* Non-zero if the noexcept is present in a compound requirement. */
+#define COMPOUND_REQ_NOEXCEPT_P(NODE) \
+ TREE_LANG_FLAG_0 (TREE_CHECK (NODE, COMPOUND_REQ))
+
+/* The expression evaluated by the predicate constraint. */
+#define PRED_CONSTR_EXPR(NODE) \
+ TREE_OPERAND (TREE_CHECK (NODE, PRED_CONSTR), 0)
+
+/* The expression validated by the predicate constraint. */
+#define EXPR_CONSTR_EXPR(NODE) \
+ TREE_OPERAND (TREE_CHECK (NODE, EXPR_CONSTR), 0)
+
+/* The type validated by the predicate constraint. */
+#define TYPE_CONSTR_TYPE(NODE) \
+ TREE_OPERAND (TREE_CHECK (NODE, TYPE_CONSTR), 0)
+
+/* In an implicit conversion constraint, the source expression. */
+#define ICONV_CONSTR_EXPR(NODE) \
+ TREE_OPERAND (TREE_CHECK (NODE, ICONV_CONSTR), 0)
+
+/* In an implicit conversion constraint, the target type. */
+#define ICONV_CONSTR_TYPE(NODE) \
+ TREE_OPERAND (TREE_CHECK (NODE, ICONV_CONSTR), 1)
+
+/* In an argument deduction constraint, the source expression. */
+#define DEDUCT_CONSTR_EXPR(NODE) \
+ TREE_OPERAND (TREE_CHECK (NODE, DEDUCT_CONSTR), 0)
+
+/* In an argument deduction constraint, the target type pattern. */
+#define DEDUCT_CONSTR_PATTERN(NODE) \
+ TREE_OPERAND (TREE_CHECK (NODE, DEDUCT_CONSTR), 1)
+
+/* The expression of an exception constraint. */
+#define EXCEPT_CONSTR_EXPR(NODE) \
+ TREE_OPERAND (TREE_CHECK (NODE, EXCEPT_CONSTR), 0)
+
+/* In a parameterized constraint, the local parameters. */
+#define PARM_CONSTR_PARMS(NODE) \
+ TREE_OPERAND (TREE_CHECK (NODE, PARM_CONSTR), 0)
+
+/* In a parameterized constraint, the operand. */
+#define PARM_CONSTR_OPERAND(NODE) \
+ TREE_OPERAND (TREE_CHECK (NODE, PARM_CONSTR), 1)
+
enum cp_tree_node_structure_enum {
TS_CP_GENERIC,
TS_CP_IDENTIFIER,
@@ -6460,56 +6505,43 @@ extern void suggest_alternatives_for (location_t, tree);
extern tree strip_using_decl (tree);
/* in constraint.cc */
+
+extern bool constraint_p (tree);
+extern tree make_predicate_constraint (tree);
extern tree conjoin_constraints (tree, tree);
extern tree conjoin_constraints (tree);
+extern bool valid_constraints_p (tree);
extern tree get_constraints (tree);
extern void set_constraints (tree, tree);
extern void remove_constraints (tree);
extern tree associate_classtype_constraints (tree);
extern tree build_constraints (tree, tree);
extern tree get_shorthand_constraints (tree);
-
extern tree build_concept_check (tree, tree, tree = NULL_TREE);
extern tree build_constrained_parameter (tree, tree, tree = NULL_TREE);
extern bool deduce_constrained_parameter (tree, tree&, tree&);
extern tree resolve_constraint_check (tree);
extern tree check_function_concept (tree);
-
-extern tree finish_concept_introduction (tree, tree);
-extern tree finish_template_constraints (tree);
-extern tree save_leading_constraints (tree);
-extern tree save_trailing_constraints (tree);
+extern tree finish_template_introduction (tree, tree);
extern bool valid_requirements_p (tree);
extern tree finish_concept_name (tree);
extern tree finish_shorthand_constraint (tree, tree);
extern tree finish_requires_expr (tree, tree);
-extern tree finish_expr_requirement (tree, tree, tree);
-extern tree finish_expr_requirement (tree);
+extern tree finish_simple_requirement (tree);
extern tree finish_type_requirement (tree);
+extern tree finish_compound_requirement (tree, tree, bool);
extern tree finish_nested_requirement (tree);
-extern tree finish_constexpr_requirement (tree);
-extern tree finish_noexcept_requirement (tree);
-extern tree finish_validexpr_expr (tree);
-extern tree finish_validtype_expr (tree);
-extern tree finish_constexpr_expr (tree);
-
extern void check_constrained_friend (tree, tree);
-
extern tree tsubst_requires_expr (tree, tree, tsubst_flags_t, tree);
-extern tree tsubst_validexpr_expr (tree, tree, tree);
-extern tree tsubst_validtype_expr (tree, tree, tree);
-extern tree tsubst_constexpr_expr (tree, tree, tree);
-extern tree tsubst_expr_req (tree, tree, tree);
-extern tree tsubst_type_req (tree, tree, tree);
-extern tree tsubst_nested_req (tree, tree, tree);
-
-extern tree tsubst_constraint_info (tree, tree);
-extern tree tsubst_constraint_expr (tree, tree, bool);
-
-extern bool check_constraints (tree);
-extern bool check_constraints (tree, tree);
-extern bool check_template_constraints (tree, tree);
-extern tree subst_template_constraints (tree, tree);
+extern tree tsubst_constraint_info (tree, tree, tsubst_flags_t, tree);
+
+extern bool constraints_satisfied_p (tree);
+extern bool constraints_satisfied_p (tree, tree);
+extern tree evaluate_function_concept (tree, tree);
+extern tree evaluate_variable_concept (tree, tree);
+extern tree evaluate_constraint_expression (tree, tree);
+extern bool check_constraint_expression (tree, tree);
+
extern bool equivalent_constraints (tree, tree);
extern bool equivalently_constrained (tree, tree);
extern bool subsumes_constraints (tree, tree);
@@ -453,30 +453,6 @@ cxx_pretty_printer::primary_expression (tree t)
pp_cxx_requires_expr (this, t);
break;
- case EXPR_REQ:
- pp_cxx_expr_requirement (this, t);
- break;
-
- case TYPE_REQ:
- pp_cxx_type_requirement (this, t);
- break;
-
- case NESTED_REQ:
- pp_cxx_nested_requirement (this, t);
- break;
-
- case VALIDEXPR_EXPR:
- pp_cxx_validexpr_expr (this, t);
- break;
-
- case VALIDTYPE_EXPR:
- pp_cxx_validtype_expr (this, t);
- break;
-
- case CONSTEXPR_EXPR:
- pp_cxx_constexpr_expr (this, t);
- break;
-
default:
c_pretty_printer::primary_expression (t);
break;
@@ -1091,9 +1067,6 @@ cxx_pretty_printer::expression (tree t)
case TEMPLATE_TEMPLATE_PARM:
case STMT_EXPR:
case REQUIRES_EXPR:
- case EXPR_REQ:
- case TYPE_REQ:
- case NESTED_REQ:
primary_expression (t);
break;
@@ -1188,6 +1161,18 @@ cxx_pretty_printer::expression (tree t)
pp_cxx_ws_string (this, "<lambda>");
break;
+ case PRED_CONSTR:
+ case EXPR_CONSTR:
+ case TYPE_CONSTR:
+ case ICONV_CONSTR:
+ case DEDUCT_CONSTR:
+ case EXCEPT_CONSTR:
+ case PARM_CONSTR:
+ case CONJ_CONSTR:
+ case DISJ_CONSTR:
+ pp_cxx_constraint (this, t);
+ break;
+
case PAREN_EXPR:
pp_cxx_left_paren (this);
expression (TREE_OPERAND (t, 0));
@@ -2549,24 +2534,60 @@ pp_cxx_trait_expression (cxx_pretty_printer *pp, tree t)
pp_cxx_right_paren (pp);
}
+// requires-clause:
+// 'requires' logical-or-expression
+void
+pp_cxx_requires_clause (cxx_pretty_printer *pp, tree t)
+{
+ if (!t)
+ return;
+ pp->padding = pp_before;
+ pp_cxx_ws_string (pp, "requires");
+ pp_space (pp);
+ pp->expression (t);
+}
+
+/* requirement:
+ simple-requirement
+ compound-requirement
+ type-requirement
+ nested-requirement */
+static void
+pp_cxx_requirement (cxx_pretty_printer *pp, tree t)
+{
+ switch (TREE_CODE (t))
+ {
+ case SIMPLE_REQ:
+ pp_cxx_simple_requirement (pp, t);
+ break;
+
+ case TYPE_REQ:
+ pp_cxx_type_requirement (pp, t);
+ break;
+
+ case COMPOUND_REQ:
+ pp_cxx_compound_requirement (pp, t);
+
+ case NESTED_REQ:
+ pp_cxx_nested_requirement (pp, t);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
// requirement-list:
// requirement
// requirement-list ';' requirement[opt]
//
-// requirement:
-// simple-requirement
-// compound-requirement
-// type-requirement
-// nested-requirement
static void
pp_cxx_requirement_list (cxx_pretty_printer *pp, tree t)
{
- int n = 3;
for (; t; t = TREE_CHAIN (t))
{
- pp_newline_and_indent (pp, n);
- pp->expression (TREE_VALUE (t));
- n = 0;
+ pp_newline_and_indent (pp, 3);
+ pp_cxx_requirement (pp, TREE_VALUE (t));
}
pp_newline_and_indent (pp, -3);
}
@@ -2581,19 +2602,6 @@ pp_cxx_requirement_body (cxx_pretty_printer *pp, tree t)
pp_cxx_right_brace (pp);
}
-// requires-clause:
-// 'requires' logical-or-expression
-void
-pp_cxx_requires_clause (cxx_pretty_printer *pp, tree t)
-{
- if (!t)
- return;
- pp->padding = pp_before;
- pp_cxx_ws_string (pp, "requires");
- pp_space (pp);
- pp->expression (t);
-}
-
// requires-expression:
// 'requires' requirement-parameter-list requirement-body
void
@@ -2601,143 +2609,187 @@ pp_cxx_requires_expr (cxx_pretty_printer *pp, tree t)
{
pp_cxx_ws_string (pp, "requires");
pp_space (pp);
- pp_cxx_parameter_declaration_clause (pp, TREE_OPERAND (t, 0));
+ if (TREE_OPERAND (t, 0))
+ pp_cxx_parameter_declaration_clause (pp, TREE_OPERAND (t, 0));
pp_space (pp);
pp_cxx_requirement_body (pp, TREE_OPERAND (t, 1));
}
-// constraint-specifier:
-// noexcept
-// constexpr
-static void
-pp_cxx_constraint_specifier (cxx_pretty_printer *pp, tree t)
+/* simple-requirement:
+ expression ';' */
+void
+pp_cxx_simple_requirement (cxx_pretty_printer *pp, tree t)
{
- if (TREE_CODE (t) == NOEXCEPT_EXPR)
- pp_cxx_ws_string (pp, "noexcept");
- else if (TREE_CODE (t) == CONSTEXPR_EXPR)
- pp_cxx_ws_string (pp, "constexpr");
- else
- gcc_unreachable ();
+ pp->expression (TREE_OPERAND (t, 0));
+ pp_cxx_semicolon (pp);
}
-// compound-requirement:
-// '{' expression '}' trailing-constraint-specifiers
-//
-// trailing-constraint-specifiers:
-// constraint-specifiers-seq[opt] result-type-requirement[opt]
-//
-// result-type-requirement:
-// '->' type-id
-static void
-pp_cxx_compound_requirement (cxx_pretty_printer *pp, tree t)
+/* type-requirement:
+ typename type-name ';' */
+void
+pp_cxx_type_requirement (cxx_pretty_printer *pp, tree t)
{
- // Get the expression requirement.
- tree ereq = TREE_OPERAND (t, 0);
-
- // Find the tree node containing the result type requirement.
- // Note that validtype requirements are implicit.
- tree treq = TREE_CHAIN (ereq);
- if (TREE_CODE (TREE_VALUE (treq)) == VALIDTYPE_EXPR)
- treq = TREE_CHAIN (treq);
-
- // Find tree nodes for any additional constraint specifiers.
- tree spec1 = TREE_CHAIN (treq);
- tree spec2 = spec1 ? TREE_CHAIN (spec1) : NULL_TREE;
+ pp->type_id (TREE_OPERAND (t, 0));
+ pp_cxx_semicolon (pp);
+}
- // Pretty print the {expr} requirement
- tree expr = TREE_OPERAND (TREE_VALUE (ereq), 0);
+/* compound-requirement:
+ '{' expression '}' 'noexcept' [opt] trailing-return-type [opt] */
+void
+pp_cxx_compound_requirement (cxx_pretty_printer *pp, tree t)
+{
pp_cxx_left_brace (pp);
- pp->expression (expr);
+ pp->expression (TREE_OPERAND (t, 0));
pp_cxx_right_brace (pp);
- // Pretty constraint specifiers, if any.
- if (spec1)
- {
- pp_space (pp);
- pp_cxx_constraint_specifier (pp, TREE_VALUE (spec1));
- if (spec2)
- pp_cxx_constraint_specifier (pp, TREE_VALUE (spec2));
- }
+ if (COMPOUND_REQ_NOEXCEPT_P (t))
+ pp_cxx_ws_string (pp, "noexcept");
- // Pretty print the '-> type-id' part of the expression.
- // Note that treq will contain a TRAIT_EXPR.
- if (treq)
+ if (tree type = TREE_OPERAND (t, 1))
{
- tree type = TRAIT_EXPR_TYPE2 (TREE_VALUE (treq));
- pp_space (pp);
- pp_cxx_arrow (pp);
- pp_space (pp);
+ pp_cxx_ws_string (pp, "->");
pp->type_id (type);
}
}
-// simple-requirement:
-// expression
-static void
-pp_cxx_simple_requirement (cxx_pretty_printer *pp, tree t)
+/* nested requirement:
+ 'requires' constraint-expression */
+void
+pp_cxx_nested_requirement (cxx_pretty_printer *pp, tree t)
{
- tree req = TREE_OPERAND (t, 0);
- pp->expression (TREE_OPERAND (req, 0));
+ pp_cxx_ws_string (pp, "requires");
+ pp->expression (TREE_OPERAND (t, 0));
+ pp_cxx_semicolon (pp);
}
void
-pp_cxx_expr_requirement (cxx_pretty_printer *pp, tree t)
+pp_cxx_predicate_constraint (cxx_pretty_printer *pp, tree t)
{
- tree reqs = TREE_OPERAND (t, 0);
- if (TREE_CODE (reqs) == TREE_LIST)
- pp_cxx_compound_requirement (pp, t);
- else
- pp_cxx_simple_requirement (pp, t);
- pp_cxx_semicolon (pp);
+ pp_cxx_ws_string (pp, "__pred");
+ pp->expression (TREE_OPERAND (t, 0));
}
-// type-requirement:
-// type-id
void
-pp_cxx_type_requirement (cxx_pretty_printer *pp, tree t)
+pp_cxx_expression_constraint (cxx_pretty_printer *pp, tree t)
{
- tree req = TREE_OPERAND (t, 0);
- pp->type_id (TREE_OPERAND (req, 0));
- pp_cxx_semicolon (pp);
+ pp_cxx_ws_string (pp, "__is_valid ");
+ pp_left_paren (pp);
+ pp->expression (TREE_OPERAND (t, 0));
+ pp_right_paren (pp);
}
-// nested requirement:
-// 'requires' logical-or-expression
void
-pp_cxx_nested_requirement (cxx_pretty_printer *pp, tree t)
+pp_cxx_type_constraint (cxx_pretty_printer *pp, tree t)
{
- pp_cxx_ws_string (pp, "requires");
- pp->expression (TREE_OPERAND (t, 0));
- pp_cxx_semicolon (pp);
+ pp_cxx_ws_string (pp, "__is_valid ");
+ pp_left_paren (pp);
+ pp->expression (TREE_OPERAND (t, 0));
+ pp_right_paren (pp);
}
void
-pp_cxx_validexpr_expr (cxx_pretty_printer *pp, tree t)
+pp_cxx_implicit_conversion_constraint (cxx_pretty_printer *pp, tree t)
{
- pp_cxx_ws_string (pp, "__is_valid_expr");
- pp_cxx_left_paren (pp);
- pp->expression (TREE_OPERAND (t, 0));
- pp_cxx_right_paren (pp);
+ pp_cxx_ws_string (pp, "__implicitly_convertible");
+ pp_left_paren (pp);
+ pp->expression (ICONV_CONSTR_EXPR (t));
+ pp->expression (ICONV_CONSTR_TYPE (t));
+ pp_right_paren (pp);
}
void
-pp_cxx_validtype_expr (cxx_pretty_printer *pp, tree t)
+pp_cxx_argument_deduction_constraint (cxx_pretty_printer *pp, tree t)
{
- pp_cxx_ws_string (pp, "__is_valid_expr");
- pp_cxx_left_paren (pp);
- pp->type_id(TREE_OPERAND (t, 0));
- pp_cxx_right_paren (pp);
+ pp_cxx_ws_string (pp, "__deducible");
+ pp_left_paren (pp);
+ pp->expression (DEDUCT_CONSTR_EXPR (t));
+ pp->expression (DEDUCT_CONSTR_PATTERN (t));
+ pp_right_paren (pp);
}
void
-pp_cxx_constexpr_expr (cxx_pretty_printer *pp, tree t)
+pp_cxx_exception_constraint (cxx_pretty_printer *pp, tree t)
{
- pp_cxx_ws_string (pp, "__is_valid_expr");
- pp_cxx_left_paren (pp);
+ pp_cxx_ws_string (pp, "noexcept");
+ pp_left_paren (pp);
pp->expression (TREE_OPERAND (t, 0));
- pp_cxx_right_paren (pp);
+ pp_right_paren (pp);
}
+void
+pp_cxx_parameterized_constraint (cxx_pretty_printer *pp, tree t)
+{
+ pp_cxx_ws_string (pp, "\\...");
+ pp_left_paren (pp);
+ pp_cxx_parameter_declaration_clause (pp, PARM_CONSTR_PARMS (t));
+ pp_cxx_whitespace (pp);
+ pp_cxx_constraint (pp, PARM_CONSTR_OPERAND (t));
+ pp_right_paren (pp);
+}
+
+void
+pp_cxx_conjunction (cxx_pretty_printer *pp, tree t)
+{
+ pp_cxx_constraint (pp, TREE_OPERAND (t, 0));
+ pp_cxx_ws_string (pp, "and");
+ pp_cxx_constraint (pp, TREE_OPERAND (t, 1));
+}
+
+void
+pp_cxx_disjunction (cxx_pretty_printer *pp, tree t)
+{
+ pp_cxx_constraint (pp, TREE_OPERAND (t, 0));
+ pp_cxx_ws_string (pp, "or");
+ pp_cxx_constraint (pp, TREE_OPERAND (t, 1));
+}
+
+void
+pp_cxx_constraint (cxx_pretty_printer *pp, tree t)
+{
+ switch (TREE_CODE (t))
+ {
+ case PRED_CONSTR:
+ pp_cxx_predicate_constraint (pp, t);
+ break;
+
+ case EXPR_CONSTR:
+ pp_cxx_expression_constraint (pp, t);
+ break;
+
+ case TYPE_CONSTR:
+ pp_cxx_type_constraint (pp, t);
+ break;
+
+ case ICONV_CONSTR:
+ pp_cxx_implicit_conversion_constraint (pp, t);
+ break;
+
+ case DEDUCT_CONSTR:
+ pp_cxx_argument_deduction_constraint (pp, t);
+ break;
+
+ case EXCEPT_CONSTR:
+ pp_cxx_exception_constraint (pp, t);
+ break;
+
+ case PARM_CONSTR:
+ pp_cxx_parameterized_constraint (pp, t);
+ break;
+
+ case CONJ_CONSTR:
+ pp_cxx_conjunction (pp, t);
+ break;
+
+ case DISJ_CONSTR:
+ pp_cxx_disjunction (pp, t);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+
typedef c_pretty_print_fn pp_fun;
@@ -94,12 +94,19 @@ void pp_cxx_userdef_literal (cxx_pretty_printer *, tree);
void pp_cxx_parameter_declaration_clause (cxx_pretty_printer *, tree);
void pp_cxx_requires_clause (cxx_pretty_printer *, tree);
void pp_cxx_requires_expr (cxx_pretty_printer *, tree);
-void pp_cxx_expr_requirement (cxx_pretty_printer *, tree);
+void pp_cxx_simple_requirement (cxx_pretty_printer *, tree);
void pp_cxx_type_requirement (cxx_pretty_printer *, tree);
+void pp_cxx_compound_requirement (cxx_pretty_printer *, tree);
void pp_cxx_nested_requirement (cxx_pretty_printer *, tree);
-void pp_cxx_validexpr_expr (cxx_pretty_printer *, tree);
-void pp_cxx_validtype_expr (cxx_pretty_printer *, tree);
-void pp_cxx_constexpr_expr (cxx_pretty_printer *, tree);
-
+void pp_cxx_predicate_constraint (cxx_pretty_printer *, tree);
+void pp_cxx_expression_constraint (cxx_pretty_printer *, tree);
+void pp_cxx_type_constraint (cxx_pretty_printer *, tree);
+void pp_cxx_implicit_conversion_constraint (cxx_pretty_printer *, tree);
+void pp_cxx_argument_deduction_constraint (cxx_pretty_printer *, tree);
+void pp_cxx_exception_constraint (cxx_pretty_printer *, tree);
+void pp_cxx_parameterized_constraint (cxx_pretty_printer *, tree);
+void pp_cxx_conjunction (cxx_pretty_printer *, tree);
+void pp_cxx_disjunction (cxx_pretty_printer *, tree);
+void pp_cxx_constraint (cxx_pretty_printer *, tree);
#endif /* GCC_CXX_PRETTY_PRINT_H */
@@ -2604,9 +2604,10 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
snode->remove ();
}
- // Remove the associated constraints for newdecl, if any, before
- // explicitly reclaiming memory.
- remove_constraints (newdecl);
+ /* Remove the associated constraints for newdecl, if any, before
+ reclaiming memory. */
+ if (flag_concepts)
+ remove_constraints (newdecl);
ggc_free (newdecl);
@@ -7678,13 +7679,18 @@ grokfndecl (tree ctype,
decl = build_lang_decl (FUNCTION_DECL, declarator, type);
- // Set the constraints on the declaration.
+ /* Set the constraints on the declaration. */
if (flag_concepts)
{
- tree temp_reqs = NULL_TREE;
+ tree tmpl_reqs = NULL_TREE;
if (processing_template_decl > template_class_depth (ctype))
- temp_reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms);
- tree ci = build_constraints (temp_reqs, decl_reqs);
+ tmpl_reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms);
+
+ /* Adjust the required expression into a constraint. */
+ if (decl_reqs)
+ decl_reqs = make_predicate_constraint (decl_reqs);
+
+ tree ci = build_constraints (tmpl_reqs, decl_reqs);
set_constraints (decl, ci);
}
@@ -4941,6 +4941,7 @@ mark_used (tree decl, tsubst_flags_t complain)
&& (DECL_DECLARED_CONSTEXPR_P (decl)
|| DECL_OMP_DECLARE_REDUCTION_P (decl)))
|| undeduced_auto_decl (decl))
+ && !DECL_DECLARED_CONCEPT_P (decl)
&& !uses_template_parms (DECL_TI_ARGS (decl)))
{
/* Instantiating a function will result in garbage collection. We
@@ -5034,6 +5035,7 @@ mark_used (tree decl, tsubst_flags_t complain)
}
else if (VAR_OR_FUNCTION_DECL_P (decl)
&& DECL_TEMPLATE_INFO (decl)
+ && !DECL_DECLARED_CONCEPT_P (decl)
&& (!DECL_EXPLICIT_INSTANTIATION (decl)
|| always_instantiate_p (decl)))
/* If this is a function or variable that is an instance of some
@@ -2696,29 +2696,38 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
pp_cxx_requires_expr (cxx_pp, t);
break;
- case EXPR_REQ:
- pp_cxx_expr_requirement (cxx_pp, t);
+ case SIMPLE_REQ:
+ pp_cxx_simple_requirement (cxx_pp, t);
break;
case TYPE_REQ:
pp_cxx_type_requirement (cxx_pp, t);
break;
+ case COMPOUND_REQ:
+ pp_cxx_compound_requirement (cxx_pp, t);
+ break;
+
case NESTED_REQ:
pp_cxx_nested_requirement (cxx_pp, t);
break;
- case VALIDEXPR_EXPR:
- pp_cxx_validexpr_expr (cxx_pp, t);
+ case PRED_CONSTR:
+ pp_cxx_ws_string (cxx_pp, "__pred");
+ dump_expr (cxx_pp, TREE_OPERAND (t, 0), flags | TFF_EXPR_IN_PARENS);
break;
- case VALIDTYPE_EXPR:
- pp_cxx_validtype_expr (cxx_pp, t);
+ case EXPR_CONSTR:
+ case TYPE_CONSTR:
+ case ICONV_CONSTR:
+ case DEDUCT_CONSTR:
+ case EXCEPT_CONSTR:
+ case PARM_CONSTR:
+ case CONJ_CONSTR:
+ case DISJ_CONSTR:
+ pp_cxx_constraint (cxx_pp, t);
break;
- case CONSTEXPR_EXPR:
- pp_cxx_constexpr_expr (cxx_pp, t);
-
case PLACEHOLDER_EXPR:
pp_string (pp, M_("*this"));
break;
@@ -52,16 +52,17 @@ template<typename I1, typename I2, typename I3>
return iter;
}
+/*---------------------------------------------------------------------------
+ Proof state
+---------------------------------------------------------------------------*/
-// -------------------------------------------------------------------------- //
-// Term List
-//
-// A term list is a list of propositions in the constraint language. It is
-// used to maintain the lists of assumptions and conclusions in a proof goal.
-//
-// Each term list maintains an iterator that refers to the current term. This
-// can be used by various tactics to support iteration and stateful
-// manipulation of the list.
+/* A term list is a list of atomic constraints. It is used
+ to maintain the lists of assumptions and conclusions in a
+ proof goal.
+
+ Each term list maintains an iterator that refers to the current
+ term. This can be used by various tactics to support iteration
+ and stateful manipulation of the list. */
struct term_list : std::list<tree>
{
term_list ();
@@ -101,19 +102,19 @@ term_list::operator= (const term_list &x)
return *this;
}
-// Insert the term T into the list before the current position, making
-// this term current.
+/* Insert the term T into the list before the current
+ position, making this term current. */
inline void
term_list::insert (tree t)
{
current = std::list<tree>::insert (current, t);
}
-// Remove the current term from the list, repositioning to the term
-// following the removed term. Note that the new position could be past
-// the end of the list.
-//
-// The removed term is returned.
+/* Remove the current term from the list, repositioning to
+ the term following the removed term. Note that the new
+ position could be past the end of the list.
+
+ The removed term is returned. */
inline tree
term_list::erase ()
{
@@ -122,21 +123,21 @@ term_list::erase ()
return t;
}
-// Initialize the current term to the first in the list.
+/* Initialize the current term to the first in the list. */
inline void
term_list::start ()
{
current = begin ();
}
-// Advance to the next term in the list.
+/* Advance to the next term in the list. */
inline void
term_list::next ()
{
++current;
}
-// Returns true when the current position is past the end.
+/* Returns true when the current position is past the end. */
inline bool
term_list::done () const
{
@@ -144,25 +145,20 @@ term_list::done () const
}
-// -------------------------------------------------------------------------- //
-// Proof Goal
-//
-// A goal (or subgoal) models a sequent of the form 'A |- C' where A and C are
-// lists of assumptions and conclusions written as propositions in the
-// constraint language (i.e., lists of trees).
+/* A goal (or subgoal) models a sequent of the form
+ 'A |- C' where A and C are lists of assumptions and
+ conclusions written as propositions in the constraint
+ language (i.e., lists of trees).
+*/
struct proof_goal
{
term_list assumptions;
term_list conclusions;
};
-
-// -------------------------------------------------------------------------- //
-// Proof State
-//
-// A proof state owns a list of goals and tracks the current sub-goal.
-// The class also provides facilities for managing subgoals and constructing
-// term lists.
+/* A proof state owns a list of goals and tracks the
+ current sub-goal. The class also provides facilities
+ for managing subgoals and constructing term lists. */
struct proof_state : std::list<proof_goal>
{
proof_state ();
@@ -170,144 +166,95 @@ struct proof_state : std::list<proof_goal>
iterator branch (iterator i);
};
-// An alias for proof state iterators.
+/* An alias for proof state iterators. */
typedef proof_state::iterator goal_iterator;
-// Initialize the state with a single empty goal, and set that goal as the
-// current subgoal.
+/* Initialize the state with a single empty goal,
+ and set that goal as the current subgoal. */
inline
proof_state::proof_state ()
: std::list<proof_goal> (1)
{ }
-// Branch the current goal by creating a new subgoal, returning a reference to
-// the new object. This does not update the current goal.
+/* Branch the current goal by creating a new subgoal,
+ returning a reference to // the new object. This does
+ not update the current goal. */
inline proof_state::iterator
proof_state::branch (iterator i)
{
return insert (++i, *i);
}
+/*---------------------------------------------------------------------------
+ Logical rules
+---------------------------------------------------------------------------*/
-// -------------------------------------------------------------------------- //
-// Logical Rules
-//
-// These functions modify the current state and goal by decomposing
-// logical expressions using the logical rules of sequent calculus for
-// first order logic.
-//
-// Note that in each decomposition rule, the term T has been erased
-// from term list before the specific rule is applied.
-
-// Left-and logical rule.
-//
-// Gamma, P, Q |- Delta
-// -------------------------
-// Gamma, P and Q |- Delta
-inline void
-left_and (proof_state &, goal_iterator i, tree t)
+/*These functions modify the current state and goal by decomposing
+ logical expressions using the logical rules of sequent calculus for
+ first order logic.
+
+ Note that in each decomposition rule, the term T has been erased
+ from term list before the specific rule is applied. */
+
+/* The left logical rule for conjunction adds both operands
+ to the current set of constraints. */
+void
+left_conjunction (proof_state &, goal_iterator i, tree t)
{
- gcc_assert (TREE_CODE (t) == TRUTH_ANDIF_EXPR);
+ gcc_assert (TREE_CODE (t) == CONJ_CONSTR);
- // Insert the operands into the current branch. Note that the
- // final order of insertion is left-to-right.
+ /* Insert the operands into the current branch. Note that the
+ final order of insertion is left-to-right. */
term_list &l = i->assumptions;
l.insert (TREE_OPERAND (t, 1));
l.insert (TREE_OPERAND (t, 0));
}
-// Left-or logical rule.
-//
-// Gamma, P |- Delta Gamma, Q |- Delta
-// -----------------------------------------
-// Gamma, P or Q |- Delta
-inline void
-left_or (proof_state &s, goal_iterator i, tree t)
+/* The left logical rule for disjunction creates a new goal,
+ adding the first operand to the original set of
+ constraints and the second operand to the new set
+ of constraints. */
+void
+left_disjunction (proof_state &s, goal_iterator i, tree t)
{
- gcc_assert (TREE_CODE (t) == TRUTH_ORIF_EXPR);
+ gcc_assert (TREE_CODE (t) == DISJ_CONSTR);
- // Branch the current subgoal.
+ /* Branch the current subgoal. */
goal_iterator j = s.branch (i);
term_list &l1 = i->assumptions;
term_list &l2 = j->assumptions;
- // Insert operands into the different branches.
+ /* Insert operands into the different branches. */
l1.insert (TREE_OPERAND (t, 0));
l2.insert (TREE_OPERAND (t, 1));
}
-// Left-requires logical rule
-// A requirement is effectively a conjunction of syntactic requirements.
-// The arguments are discarded.
-//
-// Gamma, P1, P2, ..., Pn |- Delta
-// --------------------------------------------------
-// Gamma, requires(args) { P1, P2, ..., Pn } |- Delta
-inline void
-left_requires (proof_state &, goal_iterator i, tree t)
+/* The left logical rules for parameterized constraints
+ adds its operand to the current goal. The list of
+ parameters are effectively discarded. */
+void
+left_parameterized_constraint (proof_state &, goal_iterator i, tree t)
{
- gcc_assert (TREE_CODE (t) == REQUIRES_EXPR);
-
- // Insert the sub-expressions into the curent term list.
+ gcc_assert (TREE_CODE (t) == PARM_CONSTR);
term_list &l = i->assumptions;
- for (tree p = TREE_OPERAND (t, 1); p; p = TREE_CHAIN (p))
- l.insert (TREE_VALUE (p));
+ l.insert (PARM_CONSTR_OPERAND (t));
}
-// Right-and logical rule.
-//
-// Gamma |- P, Delta Gamma |- Q, Delta
-// -----------------------------------------
-// Gamma |- P and Q, Delta
-inline void
-right_and (proof_state &s, goal_iterator i, tree t)
-{
- gcc_assert (TREE_CODE (t) == TRUTH_ORIF_EXPR);
+/*---------------------------------------------------------------------------
+ Decomposition
+---------------------------------------------------------------------------*/
- // Branch the current subgoal.
- goal_iterator j = s.branch (i);
- term_list &l1 = i->conclusions;
- term_list &l2 = j->conclusions;
+/* The following algorithms decompose expressions into sets of
+ atomic propositions. In terms of the sequent calculus, these
+ functions exercise the logical rules only.
- // Insert operands into the different branches.
- l1.insert (TREE_OPERAND (t, 0));
- l2.insert (TREE_OPERAND (t, 1));
-}
-
-// Right-or logical rule.
-//
-// Gamma |- P, Q, Delta
-// ------------------------
-// Gamma |- P or Q, Delta
-inline void
-right_or (proof_state &, goal_iterator i, tree t)
-{
- gcc_assert (TREE_CODE (t) == TRUTH_ANDIF_EXPR);
-
- // Insert the operands into the current branch. Note that the
- // final order of insertion is left-to-right.
- term_list &l = i->conclusions;
- l.insert (TREE_OPERAND (t, 1));
- l.insert (TREE_OPERAND (t, 0));
-}
-
-
-// -------------------------------------------------------------------------- //
-// Decomposition
-//
-// The following algorithms decompose expressions into sets of atomic
-// propositions.
-//
-// These decomposition strategies do not branch conclusions so that
-// only a single term exists on the right side. These algorithms decompose
-// expressions until we have a set of atomic sequent that contain no
-// more logical expressions.
-
-// Left decomposition.
-// Continually decompose assumptions until there are no terms in any
-// subgoal that can be further decomposed.
+ This is equivalent, for the purpose of determining subsumption,
+ to rewriting a constraint in disjunctive normal form. It also
+ allows the resulting assumptions to be used as declarations
+ for the purpose of separate checking. */
+/* Apply the left logical rules to the proof state. */
void
decompose_left_term (proof_state &s, goal_iterator i)
{
@@ -315,14 +262,14 @@ decompose_left_term (proof_state &s, goal_iterator i)
tree t = l.current_term ();
switch (TREE_CODE (t))
{
- case TRUTH_ANDIF_EXPR:
- left_and (s, i, l.erase ());
+ case CONJ_CONSTR:
+ left_conjunction (s, i, l.erase ());
break;
- case TRUTH_ORIF_EXPR:
- left_or (s, i, l.erase ());
+ case DISJ_CONSTR:
+ left_disjunction (s, i, l.erase ());
break;
- case REQUIRES_EXPR:
- left_requires (s, i, l.erase ());
+ case PARM_CONSTR:
+ left_parameterized_constraint (s, i, l.erase ());
break;
default:
l.next ();
@@ -330,6 +277,9 @@ decompose_left_term (proof_state &s, goal_iterator i)
}
}
+/* Apply the left logical rules of the sequent calculus
+ until the current goal is fully decomposed into atomic
+ constraints. */
void
decompose_left_goal (proof_state &s, goal_iterator i)
{
@@ -339,7 +289,10 @@ decompose_left_goal (proof_state &s, goal_iterator i)
decompose_left_term (s, i);
}
-inline void
+/* Apply the left logical rules of the sequent calculus
+ until the antecedents are fully decomposed into atomic
+ constraints. */
+void
decompose_left (proof_state& s)
{
goal_iterator iter = s.begin ();
@@ -348,14 +301,7 @@ decompose_left (proof_state& s)
decompose_left_goal (s, iter);
}
-
-// -------------------------------------------------------------------------- //
-// Term Extraction
-//
-// Extract a list of term lists from a proof state, and return it as a
-// a tree (a vector of vectors).
-
-// Returns a vector of terms from the given term list.
+/* Returns a vector of terms from the term list L. */
tree
extract_terms (term_list& l)
{
@@ -367,7 +313,8 @@ extract_terms (term_list& l)
return result;
}
-// Extract the assumption vector from the proof state s.
+/* Extract the assumptions from the proof state S
+ as a vector of vectors of atomic constraints. */
inline tree
extract_assumptions (proof_state& s)
{
@@ -381,59 +328,56 @@ extract_assumptions (proof_state& s)
} // namespace
-
-// Decompose the required expression T into a constraint set: a
-// vector of vectors containing only atomic propositions. If T is
-// invalid, return an error.
+/* Decompose the required expression T into a constraint set: a
+ vector of vectors containing only atomic propositions. If T is
+ invalid, return an error. */
tree
decompose_assumptions (tree t)
{
if (!t || t == error_mark_node)
return t;
- // Create a proof state, and insert T as the sole assumption.
+ /* Create a proof state, and insert T as the sole assumption. */
proof_state s;
term_list &l = s.begin ()->assumptions;
l.insert (t);
- // Decompose the expression into a constraint set, and then
- // extract the terms for the AST.
+ /* Decompose the expression into a constraint set, and then
+ extract the terms for the AST. */
decompose_left (s);
return extract_assumptions (s);
}
-// -------------------------------------------------------------------------- //
-// Subsumption and Entailment
-//
-// The following framework implements the subsumption and entailment
-// logic used for overload resolution and lookup.
+/*---------------------------------------------------------------------------
+ Subsumption Rules
+---------------------------------------------------------------------------*/
namespace {
-bool subsumes_prop(tree, tree);
-bool subsumes_atom(tree, tree);
-bool subsumes_and(tree, tree);
-bool subsumes_or(tree, tree);
-
-// Returns true if the assumption A matches the conclusion C. This
-// is generally the case when A and C have the same syntax.
-//
-// TODO: Implement special cases for:
-// * __is_same_as |- __is_convertible_to
-// * __is_same_as |- __is_derived_from
-// * Any other built-in predicates?
+bool subsumes_constraint (tree, tree);
+bool subsumes_conjunction (tree, tree);
+bool subsumes_disjunction (tree, tree);
+bool subsumes_parameterized_constraint (tree, tree);
+bool subsumes_atomic_constraint (tree, tree);
+
+/* Returns true if the assumption A matches the conclusion C. This
+ is generally the case when A and C have the same syntax.
+
+ NOTE: There will be specialized matching rules to accommodate
+ type equivalence, conversion, inheritance, etc. But this is not
+ in the current concepts draft. */
inline bool
match_terms (tree a, tree c)
{
return cp_tree_equal (a, c);
}
-// Returns true if the list of assumptions AS subsumes the atomic
-// proposition C. This is the case when we can find a proposition in
-// AS that entails the conclusion C.
+/* Returns true if the list of assumptions AS subsumes the atomic
+ proposition C. This is the case when we can find a proposition
+ in AS that entails the conclusion C. */
bool
-subsumes_atom (tree as, tree c)
+subsumes_atomic_constraint (tree as, tree c)
{
for (int i = 0; i < TREE_VEC_LENGTH (as); ++i)
if (match_terms (TREE_VEC_ELT (as, i), c))
@@ -441,80 +385,79 @@ subsumes_atom (tree as, tree c)
return false;
}
-// Returns true when both operands of C are subsumed by the assumptions AS.
+/* Returns true when both operands of C are subsumed by the
+ assumptions AS. */
inline bool
-subsumes_and (tree as, tree c)
+subsumes_conjunction (tree as, tree c)
{
tree l = TREE_OPERAND (c, 0);
tree r = TREE_OPERAND (c, 1);
- return subsumes_prop (as, l) && subsumes_prop (as, r);
+ return subsumes_constraint (as, l) && subsumes_constraint (as, r);
}
-// Returns true when either operand of C is subsumed by the assumptions AS.
+/* Returns true when either operand of C is subsumed by the
+ assumptions AS. */
inline bool
-subsumes_or (tree as, tree c)
+subsumes_disjunction (tree as, tree c)
{
tree l = TREE_OPERAND (c, 0);
tree r = TREE_OPERAND (c, 1);
- return subsumes_prop (as, l) || subsumes_prop (as, r);
+ return subsumes_constraint (as, l) || subsumes_constraint (as, r);
}
-// Returns true when the sub-requirements of C are subsumed by the
-// assumptions in AS.
+/* Returns true when the operand of C is subsumed by the
+ assumptions in AS. The parameters are not considered in
+ the subsumption rules. */
bool
-subsumes_requires (tree as, tree c)
+subsumes_parameterized_constraint (tree as, tree c)
{
- tree t = TREE_OPERAND (c, 1);
- while (t && subsumes_prop (as, TREE_VALUE (t)))
- t = TREE_CHAIN (t);
- return !t;
+ tree t = PARM_CONSTR_OPERAND (c);
+ return subsumes_constraint (as, t);
}
-// Returns true when the list of assumptions AS subsumes the
-// concluded proposition C.
-//
-// This is a simple recursive descent on C, matching against
-// propositions in the assumption list AS.
+
+/* Returns true when the list of assumptions AS subsumes the
+ concluded proposition C. This is a simple recursive descent
+ on C, matching against propositions in the assumption list AS. */
bool
-subsumes_prop (tree as, tree c)
+subsumes_constraint (tree as, tree c)
{
switch (TREE_CODE (c))
{
- case TRUTH_ANDIF_EXPR:
- return subsumes_and (as, c);
- case TRUTH_ORIF_EXPR:
- return subsumes_or (as, c);
- case REQUIRES_EXPR:
- return subsumes_requires (as, c);
+ case CONJ_CONSTR:
+ return subsumes_conjunction (as, c);
+ case DISJ_CONSTR:
+ return subsumes_disjunction (as, c);
+ case PARM_CONSTR:
+ return subsumes_parameterized_constraint (as, c);
default:
- return subsumes_atom (as, c);
+ return subsumes_atomic_constraint (as, c);
}
}
-// Returns true if the LEFT constraints subsume the RIGHT constraints.
-// This is done by checking that the RIGHT requirements follow from
-// each of the LEFT subgoals.
+/* Returns true if the LEFT constraints subsume the RIGHT constraints.
+ This is done by checking that the RIGHT requirements follow from
+ each of the LEFT subgoals. */
bool
subsumes_constraints_nonnull (tree left, tree right)
{
gcc_assert (check_constraint_info (left));
gcc_assert (check_constraint_info (right));
- // Check that the required expression in RIGHT is subsumed by each
- // subgoal in the assumptions of LEFT.
+ /* Check that the required expression in RIGHT is subsumed by each
+ subgoal in the assumptions of LEFT. */
tree as = CI_ASSUMPTIONS (left);
tree c = CI_NORMALIZED_CONSTRAINTS (right);
for (int i = 0; i < TREE_VEC_LENGTH (as); ++i)
- if (!subsumes_prop (TREE_VEC_ELT (as, i), c))
+ if (!subsumes_constraint (TREE_VEC_ELT (as, i), c))
return false;
return true;
}
-} // end namespace
-
+} /* namespace */
-// Returns true if the LEFT constraints subsume the RIGHT constraints. Note
-// that subsumption is a reflexive relation (e.g., <=)
+/* Returns true if the LEFT constraints subsume the RIGHT
+ constraints. */
bool
subsumes (tree left, tree right)
{
@@ -1939,13 +1939,14 @@ implicitly_declare_fn (special_function_kind kind, tree type,
rest_of_decl_compilation (fn, toplevel_bindings_p (), at_eof);
gcc_assert (!TREE_USED (fn));
- // If the inherited constructor was constrained, copy the
- // constraints.
+ /* Propagate constraints from the inherited constructor. */
if (flag_concepts && inherited_ctor)
{
- tree orig_ci = get_constraints (inherited_ctor);
- tree new_ci = copy_node (orig_ci);
- set_constraints (fn, new_ci);
+ if (tree orig_ci = get_constraints (inherited_ctor))
+ {
+ tree new_ci = copy_node (orig_ci);
+ set_constraints (fn, new_ci);
+ }
}
/* Restore PROCESSING_TEMPLATE_DECL. */
@@ -1363,7 +1363,7 @@ clear_decl_specs (cp_decl_specifier_seq *decl_specs)
VAR_DECLs or FUNCTION_DECLs) should do that directly. */
static cp_declarator *make_call_declarator
- (cp_declarator *, tree, cp_cv_quals, cp_virt_specifiers, cp_ref_qualifier, tree, tree);
+ (cp_declarator *, tree, cp_cv_quals, cp_virt_specifiers, cp_ref_qualifier, tree, tree, tree);
static cp_declarator *make_array_declarator
(cp_declarator *, tree);
static cp_declarator *make_pointer_declarator
@@ -1544,7 +1544,8 @@ make_call_declarator (cp_declarator *target,
cp_virt_specifiers virt_specifiers,
cp_ref_qualifier ref_qualifier,
tree exception_specification,
- tree late_return_type)
+ tree late_return_type,
+ tree requires_clause)
{
cp_declarator *declarator;
@@ -1556,6 +1557,7 @@ make_call_declarator (cp_declarator *target,
declarator->u.function.ref_qualifier = ref_qualifier;
declarator->u.function.exception_specification = exception_specification;
declarator->u.function.late_return_type = late_return_type;
+ declarator->u.function.requires_clause = requires_clause;
if (target)
{
declarator->id_loc = target->id_loc;
@@ -2099,6 +2101,8 @@ static tree cp_parser_type_specifier
static tree cp_parser_simple_type_specifier
(cp_parser *, cp_decl_specifier_seq *, cp_parser_flags);
static tree cp_parser_type_name
+ (cp_parser *, bool);
+static tree cp_parser_type_name
(cp_parser *);
static tree cp_parser_nonclass_name
(cp_parser* parser);
@@ -2354,14 +2358,6 @@ static tree cp_parser_type_requirement
(cp_parser *);
static tree cp_parser_nested_requirement
(cp_parser *);
-static tree cp_parser_constexpr_constraint_spec
- (cp_parser *, tree);
-static tree cp_parser_noexcept_constraint_spec
- (cp_parser *, tree);
-static tree cp_parser_constraint_spec
- (cp_parser *, tree);
-static tree cp_parser_constraint_specifier_seq
- (cp_parser *, tree);
/* Transactional Memory Extensions */
@@ -9457,9 +9453,11 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
VIRT_SPEC_UNSPECIFIED,
REF_QUAL_NONE,
exception_spec,
- /*late_return_type=*/NULL_TREE);
+ /*late_return_type=*/NULL_TREE,
+ /*requires_clause*/NULL_TREE);
declarator->id_loc = LAMBDA_EXPR_LOCATION (lambda_expr);
+
fco = grokmethod (&return_type_specs,
declarator,
attributes);
@@ -13523,6 +13521,7 @@ finish_constrained_parameter (cp_parser *parser,
// default argument and constraint.
parm = build_tree_list (def, parm);
TEMPLATE_PARM_CONSTRAINTS (parm) = decl;
+
return parm;
}
@@ -13912,7 +13911,7 @@ cp_parser_type_parameter (cp_parser* parser, bool *is_parameter_pack)
{
tree reqs = get_shorthand_constraints (current_template_parms);
if (tree r = cp_parser_requires_clause_opt (parser))
- reqs = conjoin_constraints (reqs, r);
+ reqs = conjoin_constraints (reqs, make_predicate_constraint (r));
TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs;
}
@@ -15488,13 +15487,20 @@ cp_parser_simple_type_specifier (cp_parser* parser,
static tree
cp_parser_type_name (cp_parser* parser)
{
+ return cp_parser_type_name (parser, /*typename_keyword_p=*/false);
+}
+
+/* See above. */
+static tree
+cp_parser_type_name (cp_parser* parser, bool typename_keyword_p)
+{
tree type_decl;
/* We can't know yet whether it is a class-name or not. */
cp_parser_parse_tentatively (parser);
/* Try a class-name. */
type_decl = cp_parser_class_name (parser,
- /*typename_keyword_p=*/false,
+ typename_keyword_p,
/*template_keyword_p=*/false,
none_type,
/*check_dependency_p=*/true,
@@ -17048,7 +17054,7 @@ cp_parser_alias_declaration (cp_parser* parser)
return decl;
// Attach constraints to the alias declaration.
- if (flag_concepts)
+ if (flag_concepts && current_template_parms)
{
tree reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms);
tree constr = build_constraints (reqs, NULL_TREE);
@@ -18123,7 +18129,8 @@ cp_parser_direct_declarator (cp_parser* parser,
virt_specifiers,
ref_qual,
exception_specification,
- late_return);
+ late_return,
+ /*requires_clause=*/NULL_TREE);
declarator->std_attributes = attrs;
/* Any subsequent parameter lists are to do with
return type, so are not those of the declared
@@ -23256,39 +23263,24 @@ cp_parser_requires_clause_opt (cp_parser *parser)
}
-// -------------------------------------------------------------------------- //
-// Requires Expression
-
-
-// An RAII helper that provides scoped control for entering and exiting
-// the local scope defined by a requires expression. Name bindings introduced
-// within the scope are popped prior to exiting the scope.
-struct cp_parser_requires_expr_scope
-{
- cp_parser_requires_expr_scope ()
- {
- begin_scope (sk_block, NULL_TREE);
- }
+/*---------------------------------------------------------------------------
+ Requires expressions
+---------------------------------------------------------------------------*/
- ~cp_parser_requires_expr_scope ()
- {
- pop_bindings_and_leave_scope ();
- }
-};
+/* Parse a requires expression
-// Parse a requires expression
-//
-// requirement-expression:
-// 'requires' requirement-parameter-list requirement-body
+ requirement-expression:
+ 'requires' requirement-parameter-list [opt] requirement-body */
static tree
cp_parser_requires_expression (cp_parser *parser)
{
gcc_assert (cp_lexer_next_token_is_keyword (parser->lexer, RID_REQUIRES));
location_t loc = cp_lexer_consume_token (parser->lexer)->location;
- // TODO: Earlier versions of the concepts lite spec did not allow
- // requires expressions outside of template declarations. That
- // restriction was relaxed in Chicago, but it has not been implemented.
+ /* A requires-expression shall appear only within a concept
+ definition or a requires-clause.
+
+ TODO: Implement this diagnostic correctly. */
if (!processing_template_decl)
{
error_at (loc, "a requires expression cannot appear outside a template");
@@ -23296,15 +23288,32 @@ cp_parser_requires_expression (cp_parser *parser)
return error_mark_node;
}
- // Parse the optional parameter list. Any local parameter declarations
- // are added to a new scope and are visible within the nested
- // requirement list.
- cp_parser_requires_expr_scope guard;
- tree parms = cp_parser_requirement_parameter_list (parser);
- if (parms == error_mark_node)
- return error_mark_node;
+ /* Local parameters are delared as variables within the scope
+ of the expression. They are not visible past the end of
+ the expression. */
+ struct scope_sentinel
+ {
+ scope_sentinel ()
+ {
+ begin_scope (sk_block, NULL_TREE);
+ }
- // Parse the requirement body.
+ ~scope_sentinel ()
+ {
+ pop_bindings_and_leave_scope ();
+ }
+ } s;
+
+ /* Parse the optional parameter list. */
+ tree parms = NULL_TREE;
+ if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
+ {
+ parms = cp_parser_requirement_parameter_list (parser);
+ if (parms == error_mark_node)
+ return error_mark_node;
+ }
+
+ /* Parse the requirement body. */
tree reqs = cp_parser_requirement_body (parser);
if (reqs == error_mark_node)
return error_mark_node;
@@ -23312,17 +23321,16 @@ cp_parser_requires_expression (cp_parser *parser)
return finish_requires_expr (parms, reqs);
}
-// Parse a parameterized requirement.
-//
-// requirement-parameter-list:
-// '(' parameter-declaration-clause ')'
+/* Parse a parameterized requirement.
+
+ requirement-parameter-list:
+ '(' parameter-declaration-clause ')' */
static tree
cp_parser_requirement_parameter_list (cp_parser *parser)
{
if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
return error_mark_node;
- // Parse the nested parameter declaration clause.
tree parms = cp_parser_parameter_declaration_clause (parser);
if (parms == error_mark_node)
return error_mark_node;
@@ -23333,10 +23341,10 @@ cp_parser_requirement_parameter_list (cp_parser *parser)
return parms;
}
-// Parse the body of a requirement.
-//
-// requirement-body:
-// '{' requirement-list '}'
+/* Parse the body of a requirement.
+
+ requirement-body:
+ '{' requirement-list '}' */
static tree
cp_parser_requirement_body (cp_parser *parser)
{
@@ -23351,11 +23359,11 @@ cp_parser_requirement_body (cp_parser *parser)
return reqs;
}
-// Parse a list of requirements.
-//
-// requirement-list:
-// requirement
-// requirement-list ';' requirement[opt]
+/* Parse a list of requirements.
+
+ requirement-list:
+ requirement
+ requirement-list ';' requirement[opt] */
static tree
cp_parser_requirement_list (cp_parser *parser)
{
@@ -23364,91 +23372,95 @@ cp_parser_requirement_list (cp_parser *parser)
{
tree req = cp_parser_requirement (parser);
if (req == error_mark_node)
- return req;
+ return error_mark_node;
result = tree_cons (NULL_TREE, req, result);
- // If we see a semi-colon, consume it.
+ /* If we see a semi-colon, consume it. */
if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
cp_lexer_consume_token (parser->lexer);
- // If we've found the end of the list, stop processing
- // the list.
+ /* Stop processing at the end of the list. */
if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE))
break;
}
- // Reverse the order of requirements so they are analyzed in
- // declaration order.
+ /* Reverse the order of requirements so they are analyzed in
+ declaration order. */
return nreverse (result);
}
-// Parse a syntactic requirement or type requirement.
-//
-// requirement:
-// simple-requirement
-// compound-requirement
-// type-requirement
-// nested-requirement
+/* Parse a syntactic requirement or type requirement.
+
+ requirement:
+ simple-requirement
+ compound-requirement
+ type-requirement
+ nested-requirement */
static tree
cp_parser_requirement (cp_parser *parser)
{
- if (cp_lexer_next_token_is_keyword (parser->lexer, RID_REQUIRES))
- return cp_parser_nested_requirement (parser);
-
if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
return cp_parser_compound_requirement (parser);
-
- // Try parsing a type requirement first.
- cp_parser_parse_tentatively (parser);
- tree req = cp_parser_type_requirement (parser);
- if (!cp_parser_parse_definitely (parser))
- req = cp_parser_simple_requirement (parser);
-
- return req;
+ else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TYPENAME))
+ return cp_parser_type_requirement (parser);
+ else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_REQUIRES))
+ return cp_parser_nested_requirement (parser);
+ else
+ return cp_parser_simple_requirement (parser);
}
-// Parse a nested requirement. This is the same as a requires clause.
-//
-// nested-requirement:
-// requires-clause
+/* Parse a simple requirement.
+
+ simple-requirement:
+ expression ';' */
static tree
-cp_parser_nested_requirement (cp_parser *parser)
+cp_parser_simple_requirement (cp_parser *parser)
{
- cp_lexer_consume_token (parser->lexer);
- tree req = cp_parser_requires_clause (parser);
- if (req == error_mark_node)
+ tree expr = cp_parser_expression (parser, NULL, false, false);
+ if (!expr || expr == error_mark_node)
return error_mark_node;
- return finish_nested_requirement (req);
+
+ if (!cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON))
+ return error_mark_node;
+
+ return finish_simple_requirement (expr);
}
-// Parse a simple requirement.
-//
-// simple-requirement:
-// expression
+/* Parse a type requirement
+
+ type-requirement
+ nested-name-specifier [opt] type-name ';' */
static tree
-cp_parser_simple_requirement (cp_parser *parser)
+cp_parser_type_requirement (cp_parser *parser)
{
- tree expr = cp_parser_expression (parser, NULL, false, false);
- if (!expr || expr == error_mark_node)
+ cp_lexer_consume_token (parser->lexer);
+
+ cp_parser_global_scope_opt (parser, /*current_scope_valid_p=*/true);
+ cp_parser_nested_name_specifier_opt (parser,
+ /*typename_keyword_p=*/true,
+ /*check_dependency_p=*/false,
+ /*type_p=*/true,
+ /*is_declaration=*/false);
+
+ tree type = cp_parser_type_name (parser, /*typename_keyword_p=*/true);
+ if (type == error_mark_node)
+ return error_mark_node;
+
+ if (!cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON))
return error_mark_node;
- return finish_expr_requirement (expr);
+
+ return finish_type_requirement (type);
}
-// Parse a compound requirement
-//
-// compound-requirement:
-// '{' expression '}' trailing-constraint-specifiers
-//
-// trailing-constraint-specifiers:
-// constraint-specifiers-seq[opt] result-type-requirement[opt]
-//
-// result-type-requirement:
-// '->' type-id
+/* Parse a compound requirement
+
+ compound-requirement:
+ '{' expression '}' 'noexcept' [opt] trailing-return-type [opt] ';' */
static tree
cp_parser_compound_requirement (cp_parser *parser)
{
- // Parse an expression enclosed in '{ }'s.
+ /* Parse an expression enclosed in '{ }'s. */
if (!cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE))
return error_mark_node;
@@ -23459,10 +23471,15 @@ cp_parser_compound_requirement (cp_parser *parser)
if (!cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE))
return error_mark_node;
- // Parse trailing expression specifiers.
- tree cs = cp_parser_constraint_specifier_seq (parser, expr);
+ /* Parse the optional noexcept. */
+ bool noexcept_p = false;
+ if (cp_lexer_next_token_is_keyword (parser->lexer, RID_NOEXCEPT))
+ {
+ cp_lexer_consume_token (parser->lexer);
+ noexcept_p = true;
+ }
- // Parse the optional trailing type requirement.
+ /* Parse the optional trailing return type. */
tree type = NULL_TREE;
if (cp_lexer_next_token_is (parser->lexer, CPP_DEREF))
{
@@ -23475,79 +23492,21 @@ cp_parser_compound_requirement (cp_parser *parser)
return error_mark_node;
}
- return finish_expr_requirement (expr, type, cs);
+ return finish_compound_requirement (expr, type, noexcept_p);
}
-// Parse a type requirement
-//
-// type-requirement
-// type-id
-static tree
-cp_parser_type_requirement (cp_parser *parser)
-{
- // Try parsing the type-id
- tree type = cp_parser_type_id (parser);
- if (type == error_mark_node)
- return error_mark_node;
-
- // It can only be a type requirement if nothing comes after it.
- // For example, this:
- //
- // typename T::value_type x = a;
- //
- // Is not a type requirement even though it stars with a type-id.
- if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON))
- return error_mark_node;
+/* Parse a nested requirement. This is the same as a requires clause.
- return finish_type_requirement (type);
-}
-
-// Parse an optional constexpr specifier in a constraint expression.
+ nested-requirement:
+ requires-clause */
static tree
-cp_parser_constexpr_constraint_spec (cp_parser *parser, tree expr)
-{
- cp_lexer_consume_token (parser->lexer);
- return finish_constexpr_requirement (expr);
-}
-
-// Parse an optional noexcept specifier in a constraint expression.
-static tree
-cp_parser_noexcept_constraint_spec (cp_parser *parser, tree expr)
+cp_parser_nested_requirement (cp_parser *parser)
{
cp_lexer_consume_token (parser->lexer);
- return finish_noexcept_requirement (expr);
-}
-
-static tree
-cp_parser_constraint_spec (cp_parser *parser, tree expr)
-{
- if (cp_lexer_next_token_is_keyword (parser->lexer, RID_CONSTEXPR))
- return cp_parser_constexpr_constraint_spec (parser, expr);
- if (cp_lexer_next_token_is_keyword (parser->lexer, RID_NOEXCEPT))
- return cp_parser_noexcept_constraint_spec (parser, expr);
- return NULL_TREE;
-}
-
-// Parse an optional expression specifier sequence.
-//
-// constraint-specifier-sequence:
-// constexpr [opt] noexcept [opt]
-static tree
-cp_parser_constraint_specifier_seq (cp_parser *parser, tree expr)
-{
- tree result = NULL_TREE;
- while (1)
- {
- // If we can parse a constraint specifier, insert it into
- // the list of requirements.
- if (tree spec = cp_parser_constraint_spec (parser, expr))
- {
- result = tree_cons (NULL_TREE, spec, result);
- continue;
- }
- break;
- }
- return result;
+ tree req = cp_parser_requires_clause (parser);
+ if (req == error_mark_node)
+ return error_mark_node;
+ return finish_nested_requirement (req);
}
/* Support Functions */
@@ -24428,7 +24387,7 @@ cp_parser_template_introduction (cp_parser* parser)
}
// Build and associate the constraint.
- tree parms = finish_concept_introduction (tmpl_decl, introduction_list);
+ tree parms = finish_template_introduction (tmpl_decl, introduction_list);
if (parms)
return parms;
@@ -24515,14 +24474,15 @@ cp_parser_template_declaration_after_export (cp_parser* parser, bool member_p)
/* Look for the `>'. */
cp_parser_skip_to_end_of_template_parameter_list (parser);
- // Manage template requirements
- if (flag_concepts)
- {
- tree reqs = get_shorthand_constraints (current_template_parms);
- if (tree r = cp_parser_requires_clause_opt (parser))
- reqs = conjoin_constraints (reqs, r);
+ /* Manage template requirements */
+ if (flag_concepts)
+ {
+ tree reqs = get_shorthand_constraints (current_template_parms);
+ if (tree r = cp_parser_requires_clause_opt (parser)) {
+ reqs = conjoin_constraints (reqs, make_predicate_constraint (r));
+ }
TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs;
- }
+ }
}
else if (flag_concepts)
{
@@ -33624,14 +33584,16 @@ tree_type_is_auto_or_concept (const_tree t)
return TREE_TYPE (t) && is_auto_or_concept (TREE_TYPE (t));
}
-// Return the template decl being called or evaluated as part of the
-// constraint check.
+/* Returns the template declaration being called or evaluated as
+ part of the constraint check. Note that T must be a predicate
+ constraint (it can't be any other kind of constraint). */
static tree
get_concept_from_constraint (tree t)
{
+ gcc_assert (TREE_CODE (t) == PRED_CONSTR);
+ t = PRED_CONSTR_EXPR (t);
gcc_assert (TREE_CODE (t) == CALL_EXPR || TREE_CODE (t) == TEMPLATE_ID_EXPR);
- // Variable concepts will be a TEMPLATE_ID_EXPR.
if (TREE_CODE (t) == TEMPLATE_ID_EXPR)
return DECL_TEMPLATE_RESULT (TREE_OPERAND (t, 0));
else
@@ -46,6 +46,7 @@ along with GCC; see the file COPYING3. If not see
#include "timevar.h"
#include "tree-iterator.h"
#include "type-utils.h"
+#include "hash-map.h"
#include "gimplify.h"
/* The type of functions taking a tree, and some additional data, and
@@ -916,9 +917,9 @@ maybe_new_partial_specialization (tree type)
// If the constraints are not the same as those of the primary
// then, we can probably create a new specialization.
- tree type_reqs = TEMPLATE_PARM_CONSTRAINTS (current_template_parms);
- tree type_constr = build_constraints (type_reqs, NULL_TREE);
- tree main_constr = get_constraints (DECL_TEMPLATE_RESULT (tmpl));
+ tree tmpl_constr = TEMPLATE_PARM_CONSTRAINTS (current_template_parms);
+ tree type_constr = build_constraints (tmpl_constr, NULL_TREE);
+ tree main_constr = get_constraints (tmpl);
if (equivalent_constraints (type_constr, main_constr))
return NULL_TREE;
@@ -928,7 +929,7 @@ maybe_new_partial_specialization (tree type)
while (specs)
{
tree spec_tmpl = TREE_VALUE (specs);
- tree spec_constr = get_constraints (DECL_TEMPLATE_RESULT (spec_tmpl));
+ tree spec_constr = get_constraints (spec_tmpl);
if (equivalent_constraints (type_constr, spec_constr))
return NULL_TREE;
specs = TREE_CHAIN (specs);
@@ -1954,10 +1955,26 @@ print_candidates (tree fns)
gcc_assert (str == NULL);
}
-// Among candidates having the same signature, return the most constrained
-// or NULL_TREE if there is no best candidate. If the signatures of candidates
-// vary (e.g., template specialization vs. member function), then there can
-// be no most constrained.
+/* Get a (possibly) constrained template declaration for the
+ purpose of ordering candidates. */
+static tree
+get_template_for_ordering (tree list)
+{
+ gcc_assert (TREE_CODE (list) == TREE_LIST);
+ tree f = TREE_VALUE (list);
+ if (tree ti = DECL_TEMPLATE_INFO (f))
+ return TI_TEMPLATE (ti);
+ return f;
+}
+
+/* Among candidates having the same signature, return the
+ most constrained or NULL_TREE if there is no best candidate.
+ If the signatures of candidates vary (e.g., template
+ specialization vs. member function), then there can be no
+ most constrained.
+
+ Note that we don't compare constraints on the functions
+ themselves, but rather those of their templates. */
static tree
most_constrained_function (tree candidates)
{
@@ -1965,7 +1982,8 @@ most_constrained_function (tree candidates)
tree champ = candidates;
for (tree c = TREE_CHAIN (champ); c; c = TREE_CHAIN (c))
{
- int winner = more_constrained (TREE_VALUE (champ), TREE_VALUE (c));
+ int winner = more_constrained (get_template_for_ordering (champ),
+ get_template_for_ordering (c));
if (winner == -1)
champ = c; // The candidate is more constrained
else if (winner == 0)
@@ -1974,7 +1992,8 @@ most_constrained_function (tree candidates)
// Verify that the champ is better than previous candidates.
for (tree c = candidates; c != champ; c = TREE_CHAIN (c)) {
- if (!more_constrained (TREE_VALUE (champ), TREE_VALUE (c)))
+ if (!more_constrained (get_template_for_ordering (champ),
+ get_template_for_ordering (c)))
return NULL_TREE;
}
@@ -2174,9 +2193,9 @@ determine_specialization (tree template_id,
specialize TMPL will produce DECL. */
continue;
- // The deduced arguments must satisfy the template constraints
- // in order to be viable.
- if (!check_template_constraints (fn, targs))
+ /* Remove, from the set of candidates, all those functions
+ whose constraints are not satisfied. */
+ if (flag_concepts && !constraints_satisfied_p (fn, targs))
continue;
// Then, try to form the new function type.
@@ -2245,8 +2264,7 @@ determine_specialization (tree template_id,
// If the deduced arguments do not satisfy the constraints,
// this is not a candidate. If it fails, record the
// rejected candidate.
- tree targs = DECL_TI_ARGS (fn);
- if (!check_template_constraints (fn, targs))
+ if (flag_concepts && !constraints_satisfied_p (fn))
{
rejections = tree_cons (NULL_TREE, fn, rejections);
continue;
@@ -2366,9 +2384,14 @@ determine_specialization (tree template_id,
*targs_out = TREE_PURPOSE (templates);
// Associate the deduced constraints with the new declaration.
+ /*
tree tmpl = TREE_VALUE (templates);
- if (tree ci = get_constraints (DECL_TEMPLATE_RESULT (tmpl)))
- set_constraints (decl, tsubst_constraint_info (ci, *targs_out));
+ if (tree ci = get_constraints (tmpl))
+ {
+ ci = tsubst_constraint_info (ci, *targs_out, tf_none, NULL_TREE);
+ set_constraints (decl, ci);
+ }
+ */
return TREE_VALUE (templates);
}
@@ -4473,7 +4496,7 @@ process_partial_specialization (tree decl)
the implicit argument list of the primary template.
FIXME: This is redundant with the requirement that a specialization
- shall be more specialized than the primary. H
+ shall be more specialized than the primary.
Note that concepts allow partial specializations with the same list of
arguments but different constraints. */
@@ -4649,6 +4672,7 @@ process_partial_specialization (tree decl)
SET_DECL_TEMPLATE_SPECIALIZATION (tmpl);
DECL_TEMPLATE_INFO (tmpl) = build_template_info (maintmpl, specargs);
DECL_PRIMARY_TEMPLATE (tmpl) = maintmpl;
+ set_constraints (tmpl, tmpl_constr);
DECL_TEMPLATE_SPECIALIZATIONS (maintmpl)
= tree_cons (specargs, tmpl,
@@ -6812,7 +6836,7 @@ is_compatible_template_arg (tree parm, tree arg)
if (parm_cons)
{
tree args = template_parms_to_args (DECL_TEMPLATE_PARMS (arg));
- parm_cons = tsubst_constraint_info (parm_cons, args);
+ parm_cons = tsubst_constraint_info (parm_cons, args, tf_none, NULL_TREE);
if (parm_cons == error_mark_node)
return false;
}
@@ -8154,21 +8178,19 @@ lookup_template_class_1 (tree d1, tree arglist, tree in_decl, tree context,
if (entry)
return entry->spec;
- // If the the template's constraints are not satisfied, then we
- // cannot form a valid type.
- //
- // Note that the check is deferred until after the hash-lookup.
- // This prevents redundant checks on specializations.
- if (!check_template_constraints (gen_tmpl, arglist))
+ /* If the the template's constraints are not satisfied,
+ then we cannot form a valid type.
+
+ Note that the check is deferred until after the hash
+ lookup. This prevents redundant checks on previously
+ instantiated specializations. */
+ if (flag_concepts && !constraints_satisfied_p (gen_tmpl, arglist))
{
- // Diagnose constraints here since they are not diagnosed
- // anywhere else.
if (complain & tf_error)
{
error ("template constraint failure");
diagnose_constraints (input_location, gen_tmpl, arglist);
}
- return error_mark_node;
}
is_dependent_type = uses_template_parms (arglist);
@@ -10279,20 +10301,22 @@ gen_elem_of_pack_expansion_instantiation (tree pattern,
return t;
}
-// Substitute args into the pack expansion T, and rewrite the resulting
-// list as a conjunction of the specified terms. If the result is an empty
-// expression, return boolean_true_node.
+/* Substitute args into the pack expansion T, and rewrite the resulting
+ list as a conjunction of the specified terms. If the result is an empty
+ expression, return boolean_true_node.
+
+ FIXME: This goes away with fold expressions. */
tree
tsubst_pack_conjunction (tree t, tree args, tsubst_flags_t complain,
tree in_decl)
{
tree terms = tsubst_pack_expansion (t, args, complain, in_decl);
- // If the resulting expression is type- or value-dependent, then
- // return it after setting the result type to bool (so it can be
- // expanded as a conjunction). This happens when instantiating
- // constrained variadic member function templates. Just rebuild
- // the dependent pack expansion.
+/* If the resulting expression is type- or value-dependent, then
+ return it after setting the result type to bool (so it can be
+ expanded as a conjunction). This happens when instantiating
+ constrained variadic member function templates. Just rebuild
+ the dependent pack expansion. */
if (instantiation_dependent_expression_p (terms))
{
terms = TREE_VEC_ELT (terms, 0);
@@ -10300,11 +10324,18 @@ tsubst_pack_conjunction (tree t, tree args, tsubst_flags_t complain,
return terms;
}
- // Conjoin requirements. An empty conjunction is equivalent to ture.
- if (tree reqs = conjoin_constraints (terms))
- return reqs;
- else
+ /* Empty expansions are equivalent to 'true'. */
+ if (TREE_VEC_LENGTH (terms) == 0)
return boolean_true_node;
+
+ /* Otherwise, and the terms together. These are transformed
+ into constraints later. */
+ tree result = TREE_VEC_ELT (terms, 0);
+ for (int i = 1; i < TREE_VEC_LENGTH (terms); ++i) {
+ tree t = TREE_VEC_ELT (terms, i);
+ result = build_min (TRUTH_ANDIF_EXPR, boolean_type_node, result, t);
+ }
+ return result;
}
@@ -11299,16 +11330,15 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
&& !grok_op_properties (r, /*complain=*/false))
RETURN (error_mark_node);
- // Propagate constraints to the new declaration.
+ /* When instantiating a constrained member, substitute
+ into the constraints to create a new constraint into
+ the */
if (tree ci = get_constraints (t)) {
- if (!uses_template_parms (argvec))
+ if (member)
{
- tree tr = CI_TEMPLATE_REQS (ci);
- tree dr = CI_DECLARATOR_REQS (ci);
- ci = build_constraints (NULL_TREE, conjoin_constraints (tr, dr));
+ ci = tsubst_constraint_info (ci, argvec, complain, NULL_TREE);
+ set_constraints (r, ci);
}
- tree new_ci = tsubst_constraint_info (ci, argvec);
- set_constraints (r, new_ci);
}
/* Set up the DECL_TEMPLATE_INFO for R. There's no need to do
@@ -12197,8 +12227,6 @@ tsubst_exception_specification (tree fntype,
return new_specs;
}
-extern int processing_constraint;
-
/* Take the tree structure T and replace template parameters used
therein with the argument vector ARGS. IN_DECL is an associated
decl for diagnostics. If an error occurs, returns ERROR_MARK_NODE.
@@ -12233,7 +12261,7 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
if (DECL_P (t))
return tsubst_decl (t, args, complain);
- if (args == NULL_TREE && !processing_constraint)
+ if (args == NULL_TREE)
return t;
code = TREE_CODE (t);
@@ -12379,7 +12407,6 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
if (level <= levels)
{
arg = TMPL_ARG (args, level, idx);
-
if (arg && TREE_CODE (arg) == ARGUMENT_PACK_SELECT)
{
/* See through ARGUMENT_PACK_SELECT arguments. */
@@ -16178,28 +16205,6 @@ tsubst_copy_and_build (tree t,
case REQUIRES_EXPR:
RETURN (tsubst_requires_expr (t, args, complain, in_decl));
- case VALIDEXPR_EXPR:
- RETURN (tsubst_validexpr_expr (t, args, in_decl));
-
- case VALIDTYPE_EXPR:
- RETURN (tsubst_validtype_expr (t, args, in_decl));
-
- case CONSTEXPR_EXPR:
- RETURN (tsubst_constexpr_expr (t, args, in_decl));
-
- // Normally, *_REQ are reduced out of requiremetns when used
- // as constraints. If a concept is checked directly via e.g.,
- // a static_assert, however, these appear in the input tree.
-
- case EXPR_REQ:
- RETURN (tsubst_expr_req (t, args, in_decl));
-
- case TYPE_REQ:
- RETURN (tsubst_type_req (t, args, in_decl));
-
- case NESTED_REQ:
- RETURN (tsubst_nested_req (t, args, in_decl));
-
default:
/* Handle Objective-C++ constructs, if appropriate. */
{
@@ -16528,8 +16533,9 @@ instantiate_alias_template (tree tmpl, tree args, tsubst_flags_t complain)
/* We can't free this if a pending_template entry or last_error_tinst_level
is pointing at it. */
if (last_pending_template == old_last_pend
- && last_error_tinst_level == old_error_tinst)
+ && last_error_tinst_level == old_error_tinst) {
ggc_free (tinst);
+ }
return r;
}
@@ -16897,8 +16903,9 @@ fn_type_unification (tree fn,
/* We can't free this if a pending_template entry or last_error_tinst_level
is pointing at it. */
if (last_pending_template == old_last_pend
- && last_error_tinst_level == old_error_tinst)
+ && last_error_tinst_level == old_error_tinst) {
ggc_free (tinst);
+ }
return r;
}
@@ -19881,13 +19888,12 @@ most_specialized_partial_spec (tree target, tsubst_flags_t complain)
if (outer_args)
spec_args = add_to_template_args (outer_args, spec_args);
- // Keep the candidate only if the constraints are satisfied.
- tree spec_decl = DECL_TEMPLATE_RESULT (spec_tmpl);
- tree spec_constr = get_constraints (spec_decl);
- if (check_constraints (spec_constr, spec_args))
+ /* Keep the candidate only if the constraints are satisfied,
+ or if we're not compiling with concepts. */
+ if (!flag_concepts || constraints_satisfied_p (spec_tmpl, spec_args))
{
- list = tree_cons (spec_args, TREE_VALUE (t), list);
- TREE_TYPE (list) = TREE_TYPE (t);
+ list = tree_cons (spec_args, TREE_VALUE (t), list);
+ TREE_TYPE (list) = TREE_TYPE (t);
}
}
}
@@ -20456,7 +20462,7 @@ template_for_substitution (tree decl)
}
/* Returns true if we need to instantiate this template instance even if we
- know we aren't going to emit it.. */
+ know we aren't going to emit it. */
bool
always_instantiate_p (tree decl)
@@ -20566,6 +20572,9 @@ instantiate_decl (tree d, int defer_ok,
functions and static member variables. */
gcc_assert (VAR_OR_FUNCTION_DECL_P (d));
+ /* A concept is never instantiated. */
+ gcc_assert (!DECL_DECLARED_CONCEPT_P (d));
+
/* Variables are never deferred; if instantiation is required, they
are instantiated right away. That allows for better code in the
case that an expression refers to the value of the variable --
@@ -21704,20 +21713,6 @@ value_dependent_expression_p (tree expression)
|| has_value_dependent_address (op));
}
- case REQUIRES_EXPR:
- {
- // Check if any parts of a requires expression are dependent.
- tree req = TREE_OPERAND (expression, 1);
- while (req != NULL_TREE)
- {
- tree r = TREE_VALUE (req);
- if (value_dependent_expression_p (r))
- return true;
- req = TREE_CHAIN (req);
- }
- return false;
- }
-
case TYPE_REQ:
case VALIDEXPR_EXPR:
case VALIDTYPE_EXPR:
@@ -21837,7 +21832,8 @@ type_dependent_expression_p (tree expression)
|| TREE_CODE (expression) == TYPEID_EXPR
|| TREE_CODE (expression) == DELETE_EXPR
|| TREE_CODE (expression) == VEC_DELETE_EXPR
- || TREE_CODE (expression) == THROW_EXPR)
+ || TREE_CODE (expression) == THROW_EXPR
+ || TREE_CODE (expression) == REQUIRES_EXPR)
return false;
/* The types of these expressions depends only on the type to which
@@ -21869,16 +21865,6 @@ type_dependent_expression_p (tree expression)
return dependent_type_p (type);
}
- // A requires expression has type bool, but is always treated as if
- // it were a dependent expression.
- //
- // FIXME: This could be improved. Perhaps the type of the requires
- // expression depends on the satisfaction of its constraints. That
- // is, its type is bool only if its substitution into its normalized
- // constraints succeeds.
- if (TREE_CODE (expression) == REQUIRES_EXPR)
- return true;
-
if (TREE_CODE (expression) == SCOPE_REF)
{
tree scope = TREE_OPERAND (expression, 0);
@@ -22091,6 +22077,10 @@ instantiation_dependent_r (tree *tp, int *walk_subtrees,
case BIND_EXPR:
return *tp;
+ case REQUIRES_EXPR:
+ /* Treat requires-expressions as dependent. */
+ return *tp;
+
default:
break;
}
@@ -23015,6 +23005,100 @@ convert_generic_types_to_packs (tree parm, int start_idx, int end_idx)
return tsubst (parm, replacement, tf_none, NULL_TREE);
}
+/* Entries in the decl_constraint hash table. */
+struct GTY((for_user)) constr_entry
+{
+ tree decl;
+ tree ci;
+};
+
+/* Hashing function and equality fo constraint entries.. */
+struct constr_hasher : ggc_hasher<constr_entry *>
+{
+ static hashval_t hash (constr_entry *e)
+ {
+ return (hashval_t)DECL_UID (e->decl);
+ }
+
+ static bool equal (constr_entry *e1, constr_entry *e2)
+ {
+ return e1->decl == e2->decl;
+ }
+};
+
+
+/* A mapping from declarations to constraint information. Note that
+ both templates and their underlying declarations are mapped to the
+ same constraint information.
+
+ FIXME: This is defined in pt.c because it's garbage collection
+ code is not being generated for constraint.cc. */
+static GTY (()) hash_table<constr_hasher> *decl_constraints;
+
+/* Returns true iff cinfo contains a valid set of constraints.
+ This is the case when the associated requirements have been
+ successfully decomposed into lists of atomic constraints.
+ That is, when the saved assumptions are not error_mark_node. */
+bool
+valid_constraints_p (tree cinfo)
+{
+ gcc_assert (cinfo);
+ return CI_ASSUMPTIONS (cinfo) != error_mark_node;
+}
+
+/* Returns the template constraints of declaration T. If T is not
+ constrained, return NULL_TREE. Note that T must be non-null. */
+tree
+get_constraints (tree t)
+{
+ gcc_assert (DECL_P (t));
+ if (TREE_CODE (t) == TEMPLATE_DECL)
+ t = DECL_TEMPLATE_RESULT (t);
+ constr_entry elt = { t, NULL_TREE };
+ constr_entry* found = decl_constraints->find (&elt);
+ if (found)
+ return found->ci;
+ else
+ return NULL_TREE;
+}
+
+/* Associate the given constraint information CI with the declaration
+ T. If T is a template, then the constraints are associated with
+ its underlying declaration. Don't build associations if CI is
+ NULL_TREE. */
+void
+set_constraints (tree t, tree ci)
+{
+ if (!ci)
+ return;
+ gcc_assert (t);
+ if (TREE_CODE (t) == TEMPLATE_DECL)
+ t = DECL_TEMPLATE_RESULT (t);
+ gcc_assert (!get_constraints (t));
+ constr_entry elt = {t, ci};
+ constr_entry** slot = decl_constraints->find_slot (&elt, INSERT);
+ constr_entry* entry = ggc_alloc<constr_entry> ();
+ *entry = elt;
+ *slot = entry;
+}
+
+/* Remove the associated constraints of the declaration T.
+ If there are no constraints associated with T, then
+ return NULL_TREE. */
+void
+remove_constraints (tree t)
+{
+ if (!DECL_P (t))
+ debug_tree (t);
+ gcc_assert (DECL_P (t));
+ if (TREE_CODE (t) == TEMPLATE_DECL)
+ t = DECL_TEMPLATE_RESULT (t);
+
+ constr_entry elt = {t, NULL_TREE};
+ constr_entry** slot = decl_constraints->find_slot (&elt, NO_INSERT);
+ if (slot)
+ decl_constraints->clear_slot(slot);
+}
/* Set up the hash tables for template instantiations. */
@@ -23023,6 +23107,7 @@ init_template_processing (void)
{
decl_specializations = hash_table<spec_hasher>::create_ggc (37);
type_specializations = hash_table<spec_hasher>::create_ggc (37);
+ decl_constraints = hash_table<constr_hasher>::create_ggc(37);
}
/* Print stats about the template hash tables for -fstats. */
@@ -2440,7 +2440,7 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
{
next = OVL_CHAIN (fn);
if (flag_concepts)
- remove_constraints (fn);
+ remove_constraints (OVL_FUNCTION (fn));
ggc_free (fn);
}
}
@@ -2454,11 +2454,24 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
tree
finish_template_variable (tree var)
{
+ gcc_assert (TREE_CODE (var) == TEMPLATE_ID_EXPR);
if (processing_template_decl)
return var;
- return instantiate_template (TREE_OPERAND (var, 0), TREE_OPERAND (var, 1),
- tf_error);
+ /* If a template-id refers to a specialization of a variable
+ concept, then the expression is true if and only if the
+ concept's constraints are satisfied by the given template
+ arguments.
+
+ NOTE: This is an extension of Concepts Lite TS that
+ allows constraints to be used in expressions. */
+ tree tmpl = TREE_OPERAND (var, 0);
+ tree args = TREE_OPERAND (var, 1);
+ tree decl = DECL_TEMPLATE_RESULT (tmpl);
+ if (flag_concepts && DECL_DECLARED_CONCEPT_P (decl))
+ return evaluate_variable_concept (decl, args);
+
+ return instantiate_template (tmpl, args, tf_error);
}
/* Finish a call to a postfix increment or decrement or EXPR. (Which
@@ -3463,22 +3463,22 @@ cp_build_function_call_vec (tree function, vec<tree, va_gc> **params,
if (TREE_CODE (function) == FUNCTION_DECL)
{
- // If the function is a non-template member function or a
- // non-template friend, then we need to check the constraints.
- //
- // Note that if overload resolution failed with a single candidate
- // this function will be used to explicitly diagnose the failure
- // for the single call expression. The check is technically
- // redundant since we would have failed in add_function_candidate.
- if (flag_concepts)
- if (tree ci = get_constraints (function))
- if (!check_constraints (ci))
- {
- error ("cannot call function %qD", function);
- location_t loc = DECL_SOURCE_LOCATION (function);
- diagnose_constraints (loc, function, NULL_TREE);
- return error_mark_node;
- }
+ /* If the function is a non-template member function
+ or a non-template friend, then we need to check the
+ constraints.
+
+ Note that if overload resolution failed with a single
+ candidate this function will be used to explicitly diagnose
+ the failure for the single call expression. The check is
+ technically redundant since we also would have failed in
+ add_function_candidate. */
+ if (flag_concepts && !constraints_satisfied_p (function))
+ {
+ error ("cannot call function %qD", function);
+ location_t loc = DECL_SOURCE_LOCATION (function);
+ diagnose_constraints (loc, function, NULL_TREE);
+ return error_mark_node;
+ }
mark_used (function);
fndecl = function;
new file mode 100644
@@ -0,0 +1,35 @@
+# Copyright (C) 2002-2013 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# Load support procs.
+load_lib g++-dg.exp
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_CXXFLAGS
+if ![info exists DEFAULT_CXXFLAGS] then {
+ set DEFAULT_CXXFLAGS " -pedantic-errors -Wno-long-long"
+}
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+g++-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C]] "" $DEFAULT_CXXFLAGS
+
+# All done.
+dg-finish
new file mode 100644
@@ -0,0 +1,20 @@
+// { dg-do run }
+// { dg-options "-std=c++1z" }
+
+#include <cassert>
+#include <iostream>
+
+template<typename T>
+ concept bool C1 = __is_class(T);
+
+template<typename T>
+ concept bool C2() { return __is_class(T); }
+
+template<typename T>
+ concept bool C3() { return requires (T a) { ++a; }; }
+
+int main() {
+ if (C1<int>) assert(false);
+ if (C2<int>()) assert(false);
+ if (!C3<int>()) assert(false);
+}
@@ -1,8 +1,8 @@
// { dg-do link }
// { dg-options "-std=c++1z" }
-#include <cassert>
+// FIXME: What is this actually testing?
-void f() requires true { }; // { dg-xfail-if "" { *-*-* } }
+void f() requires true { }
int main() { }
@@ -1,7 +1,5 @@
// { dg-options "-std=c++1z" }
-#include <cassert>
-
template<typename T>
concept bool C() { return __is_class(T); }
@@ -18,9 +18,9 @@ template<typename T, typename U = int>
concept bool C5() { return __is_class(U); }
C1{...A, B} void f1() {}; // { dg-error "no matching|wrong number" }
-C1{A} void f2() {} // { dg-error "match pack" }
-C2{A, B} void f3() {}; // { dg-error "match pack" }
-C3{...A} void f4() {}; // { dg-error "match pack" }
+C1{A} void f2() {} // { dg-error "cannot match pack|no matching concept" }
+C2{A, B} void f3() {}; // { dg-error "cannot match pack|no matching concept" }
+C3{...A} void f4() {}; // { dg-error "cannot match pack|no matching concept" }
C4{A} void f5() {}; // { dg-error "no matching concept" }
C5{A, B} void f6() {};
@@ -4,6 +4,10 @@
template<typename T>
concept bool Class () { return __is_class(T); }
+// Allow a requires-expression with no parms.
+template<typename T>
+ concept bool C = requires { typename T::type; };
+
void f1(auto a) requires Class<decltype(a)>() { }
void f2(auto a) requires requires (decltype(a) x) { -x; } { }
@@ -1,7 +1,5 @@
// { dg-options "-std=c++1z" }
-#include <cassert>
-
template<typename T>
concept bool Class () { return __is_class(T); }
@@ -19,3 +17,5 @@ int main() {
f1(0); // { dg-error "cannot call" }
f2((void*)0); // { dg-error "cannot call" }
}
+
+// { dg-excess-errors "x|with" }
@@ -9,10 +9,13 @@ template<typename T> constexpr fool p1() { return {}; }
template<typename T> constexpr fool p2() { return {}; }
template<typename T>
- concept bool C() { return p1<T>() && p2<T>(); }
+ concept bool C() { return p1<T>() && p2<T>(); }
-template<C T> void f(T x) { }
+template<C T> void f(T x) { } // { dg-error "user-defined operator" }
int main() {
- f(0); // { dg-error "cannot call|predicate constraint" }
+ // FIXME: We should be diagnosing the failed lookup
+ // of operator&&, not the fact that neither have the
+ // right type (which is true, but not the best error).
+ f(0); // { dg-error "not|bool" }
}
@@ -9,10 +9,10 @@ template<typename T> constexpr fool p1() { return {}; }
template<typename T> constexpr fool p2() { return {}; }
template<typename T>
- concept bool C() { return p1<T>() && p2<T>(); }
+ concept bool C() { return p1<T>() && p2<T>(); }
-template<C T> void f(T x) { }
+template<C T> void f(T x) { } // { dg-error "user-defined operator" }
int main() {
- f(0); // { dg-error "cannot call|predicate constraint" }
+ f(0); // { dg-error "not|bool" }
}
@@ -7,7 +7,7 @@ template<typename T>
concept bool C1() { return X(); }
template<C1 T>
- void h(T) { } // { dg-error "not convertible" }
+ void h(T) { } // { dg-error "not|bool" }
template<typename T>
concept bool C2() { return X() == X(); } // OK
@@ -20,6 +20,6 @@ template<typename Seq, typename Fn>
}
int main() {
- all(vector<int>{0, 2}, true); // { dg-error "cannot call" }
+ all(vector<int>{0, 2}, true); // { dg-error "not|bool" }
}
@@ -12,11 +12,11 @@ template<typename T>
template<typename U>
requires C1<U>()
- void f1(U) { } // { dg-error "invalid constraint" }
+ void f1(U) { } // { dg-error "function call" }
template<typename U>
requires C2<U>
- void f2(U) { } // { dg-error "invalid constraint" }
+ void f2(U) { } // { dg-error "invalid reference" }
template<C3 T> // { dg-error "not a type" }
void f(T) { } // { dg-error "declared void" }