Index: error.c =================================================================== --- error.c (revision 201240) +++ error.c (working copy) @@ -57,6 +57,7 @@ static const char *expr_to_string (tree) static const char *fndecl_to_string (tree, int); static const char *op_to_string (enum tree_code); static const char *parm_to_string (int); +static const char *parms_to_string (tree); static const char *type_to_string (tree, int); static void dump_alias_template_specialization (tree, int); @@ -2589,6 +2590,17 @@ dump_expr (tree t, int flags) pp_cxx_nested_requirement (cxx_pp, t); break; + case VALIDEXPR_EXPR: + pp_cxx_validexpr_expr (cxx_pp, t); + break; + + case VALIDTYPE_EXPR: + pp_cxx_validtype_expr (cxx_pp, t); + break; + + case CONSTEXPR_EXPR: + pp_cxx_constexpr_expr (cxx_pp, t); + /* This list is incomplete, but should suffice for now. It is very important that `sorry' does not call `report_error_function'. That could cause an infinite loop. */ @@ -2908,7 +2920,7 @@ args_to_string (tree p, int verbose) int flags = 0; if (verbose) flags |= TFF_CLASS_KEY_OR_ENUM; - + if (p == NULL_TREE) return ""; @@ -2968,6 +2980,14 @@ cv_to_string (tree p, int v) return pp_formatted_text (cxx_pp); } +static const char* +parms_to_string (tree p) +{ + reinit_cxx_pp (); + pp_cxx_parameter_declaration_clause (cxx_pp, p); + return pp_formatted_text (cxx_pp); +} + /* Langhook for print_error_function. */ void @@ -3400,6 +3420,7 @@ cp_printer (pretty_printer *pp, text_inf case 'K': percent_K_format (text); return true; + case 'Z': result = parms_to_string (next_tree); break; default: return false; Index: logic.cc =================================================================== --- logic.cc (revision 201240) +++ logic.cc (working copy) @@ -1,4 +1,4 @@ -/* Process declarations and variables for C++ compiler. +/* Derivation and subsumption rules for constraints. Copyright (C) 2013 Free Software Foundation, Inc. Contributed by Andrew Sutton (andrew.n.sutton@gmail.com) @@ -18,8 +18,7 @@ You should have received a copy of the G along with GCC; see the file COPYING3. If not see . */ -// Algorithms and data structures for the decomposition and validation -// of logical formulas. +// #include "config.h" #include "system.h" Index: cxx-pretty-print.c =================================================================== --- cxx-pretty-print.c (revision 201240) +++ cxx-pretty-print.c (working copy) @@ -42,7 +42,6 @@ static void pp_cxx_ptr_operator (cxx_pre static void pp_cxx_type_id (cxx_pretty_printer *, tree); static void pp_cxx_direct_abstract_declarator (cxx_pretty_printer *, tree); static void pp_cxx_declarator (cxx_pretty_printer *, tree); -static void pp_cxx_parameter_declaration_clause (cxx_pretty_printer *, tree); static void pp_cxx_abstract_declarator (cxx_pretty_printer *, tree); static void pp_cxx_statement (cxx_pretty_printer *, tree); static void pp_cxx_template_parameter (cxx_pretty_printer *, tree); @@ -480,6 +479,18 @@ pp_cxx_primary_expression (cxx_pretty_pr pp_cxx_nested_requirement (pp, t); break; + case VALIDEXPR_EXPR: + pp_cxx_validexpr_expr (pp, t); + break; + + case VALIDTYPE_EXPR: + pp_cxx_validtype_expr (pp, t); + break; + + case CONSTEXPR_EXPR: + pp_cxx_constexpr_expr (pp, t); + break; + default: pp_c_primary_expression (pp_c_base (pp), t); break; @@ -1450,14 +1461,29 @@ pp_cxx_parameter_declaration (cxx_pretty parameter-declaration parameter-declaration-list , parameter-declaration */ -static void +void pp_cxx_parameter_declaration_clause (cxx_pretty_printer *pp, tree t) { - tree args = TYPE_P (t) ? NULL : FUNCTION_FIRST_USER_PARM (t); - tree types = - TYPE_P (t) ? TYPE_ARG_TYPES (t) : FUNCTION_FIRST_USER_PARMTYPE (t); - const bool abstract = args == NULL - || pp_c_base (pp)->flags & pp_c_flag_abstract; + tree args; + tree types; + bool abstract; + + // For a requires clause or the explicit printing of a parameter list + // we expect T to be a TREE_LIST of PARM_DECLs. Otherwise, the list of + // args and types are taken from the function decl T. + if (TREE_CODE (t) == TREE_LIST) + { + args = TREE_VALUE (t); + types = TREE_VALUE (t); + abstract = false; + } + else + { + bool type_p = TYPE_P (t); + args = type_p ? NULL : FUNCTION_FIRST_USER_PARM (t); + types = type_p ? TYPE_ARG_TYPES (t) : FUNCTION_FIRST_USER_PARMTYPE (t); + abstract = args == NULL || pp_c_base (pp)->flags & pp_c_flag_abstract; + } bool first = true; /* Skip artificial parameter for nonstatic member functions. */ @@ -2486,30 +2512,10 @@ static void pp_cxx_requirement_body (cxx_pretty_printer *pp, tree t) { pp_cxx_left_brace (pp); - pp_cxx_requirement_list (pp, TREE_OPERAND (t, 1)); + pp_cxx_requirement_list (pp, t); pp_cxx_right_brace (pp); } -// requirement-parameter-list: -// '(' parameter-declaration-clause ')' -static void -pp_cxx_requirement_parameter_list (cxx_pretty_printer *pp, tree t) -{ - tree p = TREE_OPERAND (t, 0); - pp_left_paren (pp); - while (p) - { - tree parm = TREE_VALUE (p); - pp_cxx_parameter_declaration (pp, parm); - if (!VOID_TYPE_P (TREE_VALUE (TREE_CHAIN (p)))) - pp_separate_with (pp, ','); - else - break; - p = TREE_CHAIN (p); - } - pp_right_paren (pp); -} - // requires-expression: // 'requires' requirement-parameter-list requirement-body void @@ -2517,9 +2523,9 @@ pp_cxx_requires_expr (cxx_pretty_printer { pp_cxx_ws_string (pp, "requires"); pp_space (pp); - pp_cxx_requirement_parameter_list (pp, t); + pp_cxx_parameter_declaration_clause(pp, TREE_OPERAND (t, 0)); pp_space (pp); - pp_cxx_requirement_body (pp, t); + pp_cxx_requirement_body (pp, TREE_OPERAND (t, 1)); } // constraint-specifier: @@ -2627,6 +2633,33 @@ pp_cxx_nested_requirement (cxx_pretty_pr pp_cxx_semicolon (pp); } +void +pp_cxx_validexpr_expr (cxx_pretty_printer *pp, tree t) +{ + pp_cxx_ws_string (pp, "__is_valid_expr"); + pp_cxx_left_paren (pp); + pp_cxx_expression (pp, TREE_OPERAND (t, 0)); + pp_cxx_right_paren (pp); +} + +void +pp_cxx_validtype_expr (cxx_pretty_printer *pp, tree t) +{ + pp_cxx_ws_string (pp, "__is_valid_expr"); + pp_cxx_left_paren (pp); + pp_cxx_type_id (pp, TREE_OPERAND (t, 0)); + pp_cxx_right_paren (pp); +} + +void +pp_cxx_constexpr_expr (cxx_pretty_printer *pp, tree t) +{ + pp_cxx_ws_string (pp, "__is_valid_expr"); + pp_cxx_left_paren (pp); + pp_cxx_expression (pp, TREE_OPERAND (t, 0)); + pp_cxx_right_paren (pp); +} + typedef c_pretty_print_fn pp_fun; Index: pt.c =================================================================== --- pt.c (revision 201240) +++ pt.c (working copy) @@ -168,8 +168,6 @@ static tree expand_template_argument_pac static tree build_template_parm_index (int, int, int, tree, tree); static bool inline_needs_template_parms (tree); static void push_inline_template_parms_recursive (tree, int); -static tree retrieve_local_specialization (tree); -static void register_local_specialization (tree, tree); static hashval_t hash_specialization (const void *p); static tree reduce_template_parm_level (tree, tree, int, tree, tsubst_flags_t); static int mark_template_parm (tree, void *); @@ -209,7 +207,6 @@ static int invalid_nontype_parm_type_p ( static bool dependent_template_arg_p (tree); static bool any_template_arguments_need_structural_equality_p (tree); static bool dependent_type_p_r (tree); -static tree tsubst_expr (tree, tree, tsubst_flags_t, tree, bool); static tree tsubst_copy (tree, tree, tsubst_flags_t, tree); static tree tsubst_pack_expansion (tree, tree, tsubst_flags_t, tree); static tree tsubst_decl (tree, tree, tsubst_flags_t); @@ -1100,7 +1097,7 @@ retrieve_specialization (tree tmpl, tree /* Like retrieve_specialization, but for local declarations. */ -static tree +tree retrieve_local_specialization (tree tmpl) { void **slot; @@ -1711,7 +1708,7 @@ reregister_specialization (tree spec, tr /* Like register_specialization, but for local declarations. We are registering SPEC, an instantiation of TMPL. */ -static void +void register_local_specialization (tree spec, tree tmpl) { void **slot; @@ -3908,7 +3905,7 @@ template_parms_to_args (tree parms) /* Within the declaration of a template, return the currently active template parameters as an argument TREE_VEC. */ -tree +static tree current_template_args (void) { return template_parms_to_args (current_template_parms); @@ -9265,7 +9262,7 @@ tsubst_template_arg (tree t, tree args, instantiated from it at *SPEC_P, return a NONTYPE_ARGUMENT_PACK of them and set *SPEC_P to point at the next point in the list. */ -static tree +tree extract_fnparm_pack (tree tmpl_parm, tree *spec_p) { /* Collect all of the extra "packed" parameters into an @@ -13012,7 +13009,7 @@ tsubst_omp_for_iterator (tree t, int i, /* Like tsubst_copy for expressions, etc. but also does semantic processing. */ -static tree +tree tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl, bool integral_constant_expression_p) { @@ -14788,6 +14785,31 @@ tsubst_copy_and_build (tree t, RECUR (TREE_OPERAND (t, 2)), complain)); + 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. */ { @@ -15394,7 +15416,7 @@ fn_type_unification (tree fn, if (!check_template_constraints (fn, targs)) { if (explain_p) - diagnose_constraint_failure (DECL_SOURCE_LOCATION (fn), fn, targs); + diagnose_constraints (DECL_SOURCE_LOCATION (fn), fn, targs); return error_mark_node; } @@ -21360,26 +21382,4 @@ print_template_statistics (void) htab_collisions (type_specializations)); } -// Substitute the template arguments ARGS into the requirement -// expression REQS. Errors resulting from substitution are not -// diagnosed. -tree -instantiate_requirements (tree reqs, tree args) -{ - return tsubst_expr (reqs, args, tf_none, NULL_TREE, true); -} - -// Create a new constraint info block by substituting ARGS into -// the requirements of CONS. -// -// Note that this does not require evaluation. -tree -tsubst_constraint (tree cons, tree args) -{ - if (!cons) - return NULL_TREE; - tree reqs = instantiate_requirements (CI_REQUIREMENTS (cons), args); - return make_constraints (reqs); -} - #include "gt-cp-pt.h" Index: cxx-pretty-print.h =================================================================== --- cxx-pretty-print.h (revision 201240) +++ cxx-pretty-print.h (working copy) @@ -76,10 +76,14 @@ void pp_cxx_trait_expression (cxx_pretty void pp_cxx_va_arg_expression (cxx_pretty_printer *, tree); void pp_cxx_offsetof_expression (cxx_pretty_printer *, tree); void pp_cxx_userdef_literal (cxx_pretty_printer *, tree); +void pp_cxx_parameter_declaration_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_type_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); #endif /* GCC_CXX_PRETTY_PRINT_H */ Index: semantics.c =================================================================== --- semantics.c (revision 201240) +++ semantics.c (working copy) @@ -8461,6 +8461,13 @@ potential_constant_expression_1 (tree t, case FIELD_DECL: case PARM_DECL: case USING_DECL: + 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: @@ -9118,195 +9125,4 @@ capture_decltype (tree decl) } -// Semantics for constraints. - -// Finish the template requirement, EXPR, by translating it into -// a constraint information record. -tree -finish_template_requirements (tree expr) -{ - if (expr == error_mark_node) - return NULL_TREE; - else - return make_constraints (expr); -} - -// 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) -{ - // 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; -} - -// 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); - return build_check_expr (EXPR_REQ, req); -} - -// 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_epxr (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. -tree -finish_validtype_epxr (tree type) -{ - if (processing_template_decl) - return build_check_expr (VALIDTYPE_EXPR, type); - return truth_node (type && TYPE_P (type)); -} - -// 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. -tree -finish_constexpr_expr (tree expr) -{ - if (processing_template_decl) - return build_check_expr (CONSTEXPR_EXPR, expr); - - // TODO: Actually check that the expression can be constexpr - // evaluatd. - // - // return truth_node (potential_constant_expression (expr)); - sorry ("constexpr requirement"); - return NULL_TREE; -} - - #include "gt-cp-semantics.h" Index: constraint.cc =================================================================== --- constraint.cc (revision 201240) +++ constraint.cc (working copy) @@ -1,4 +1,4 @@ -/* Process declarations and variables for C++ compiler. +/* Processing rules for constraints. Copyright (C) 2013 Free Software Foundation, Inc. Contributed by Andrew Sutton (andrew.n.sutton@gmail.com) @@ -18,8 +18,6 @@ You should have received a copy of the G along with GCC; see the file COPYING3. If not see . */ -// Components for processing constraints and evaluating constraints. - #include "config.h" #include "system.h" #include "coretypes.h" @@ -188,6 +186,10 @@ static tree reduce_misc (tree); static tree reduce_logical (tree); static tree reduce_call (tree); +static tree reduce_requires (tree); +static tree reduce_expr_req (tree); +static tree reduce_type_req (tree); +static tree reduce_nested_req (tree); static tree reduce_template_id (tree); static tree reduce_stmt_list (tree); @@ -238,6 +240,18 @@ reduce_expr (tree t) case CALL_EXPR: return reduce_call (t); + case REQUIRES_EXPR: + return reduce_requires (t); + + case EXPR_REQ: + return reduce_expr_req (t); + + case TYPE_REQ: + return reduce_type_req (t); + + case NESTED_REQ: + return reduce_nested_req (t); + case TEMPLATE_ID_EXPR: return reduce_template_id (t); @@ -389,6 +403,43 @@ reduce_template_id (tree t) return NULL_TREE; } + +// Reduce an expression requirement as a conjunction of its +// individual constraints. +tree +reduce_expr_req (tree t) +{ + tree r = NULL_TREE; + for (tree l = TREE_OPERAND (t, 0); l; l = TREE_CHAIN (l)) + r = conjoin_requirements (r, reduce_expr (TREE_VALUE (l))); + return r; +} + +// Reduce a type requirement by returing its underlying +// constraint. +tree +reduce_type_req (tree t) +{ + return TREE_OPERAND (t, 0); +} + +// Reduce a nested requireemnt by returing its only operand. +tree +reduce_nested_req (tree t) +{ + return TREE_OPERAND (t, 0); +} + +// Reduce a requires expr by reducing each requirement in turn, +// rewriting the list of requirements. +tree +reduce_requires (tree t) +{ + for (tree l = TREE_OPERAND (t, 1); l; l = TREE_CHAIN (l)) + TREE_VALUE (l) = reduce_expr (TREE_VALUE (l)); + return t; +} + // Reduction rules for the statement list STMTS. // // Recursively reduce each statement in the list, concatenating each @@ -405,12 +456,7 @@ reduce_stmt_list (tree stmts) while (!tsi_end_p (i)) { if (tree rhs = reduce_node (tsi_stmt (i))) - { - if (!lhs) - lhs = rhs; - else - lhs = conjoin_requirements (lhs, rhs); - } + lhs = conjoin_requirements (lhs, rhs); tsi_next (&i); } return lhs; @@ -418,7 +464,6 @@ reduce_stmt_list (tree stmts) } // end namespace - // Reduce the requirement REQS into a logical formula written in terms of // atomic propositions. tree @@ -427,6 +472,12 @@ reduce_requirements (tree reqs) return reduce_node (reqs); } +// -------------------------------------------------------------------------- // +// Constraint Semantic Processing +// +// The following functions are called by the parser and substitution rules +// to create and evaluate constraint-related nodes. + // Create a constraint-info node from the specified requirements. tree make_constraints (tree reqs) @@ -451,10 +502,6 @@ make_constraints (tree reqs) return (tree)cinfo; } - -// -------------------------------------------------------------------------- // -// Get Constraints - // Returns the template constraints of declaration T. If T is not a // template, this return NULL_TREE. Note that T must be non-null. tree @@ -471,19 +518,392 @@ get_constraints (tree t) return DECL_CONSTRAINTS (t); } +// Finish the template requirement, EXPR, by translating it into +// a constraint information record. +tree +finish_template_requirements (tree expr) +{ + if (expr == error_mark_node) + return NULL_TREE; + else + return make_constraints (expr); +} + +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 instantiatd requires expr, returning the truth 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. +tree +finish_validtype_expr (tree type) +{ + if (processing_template_decl) + return build_check_expr (VALIDTYPE_EXPR, type); + return truth_node (type && TYPE_P (type)); +} + +// 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. +tree +finish_constexpr_expr (tree expr) +{ + if (processing_template_decl) + return build_check_expr (CONSTEXPR_EXPR, expr); + + // TODO: Actually check that the expression can be constexpr + // evaluatd. + // + // return truth_node (potential_constant_expression (expr)); + sorry ("constexpr requirement"); + return NULL_TREE; +} // -------------------------------------------------------------------------- // -// Check Constraints +// Substitution Rules +// +// The following functions implement substitution rules for constraints. + +namespace { +// In an unevaluated context, the substitution of parm decls are not +// properly chained during substitution. Do that here. +tree +fix_local_parms (tree sparms) +{ + if (!sparms) + return sparms; + tree p = TREE_CHAIN (sparms); + tree q = sparms; + while (p && TREE_VALUE (p) != void_type_node) + { + DECL_CHAIN (TREE_VALUE (q)) = TREE_VALUE (p); + q = p; + p = TREE_CHAIN (p); + } + return sparms; +} + +// 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) +{ + tree s = TREE_VALUE (sparms); + for (tree p = tparms; p && !VOID_TYPE_P (TREE_VALUE (p)); p = TREE_CHAIN (p)) + { + tree t = TREE_VALUE (p); + if (FUNCTION_PARAMETER_PACK_P (t)) + { + tree pack = extract_fnparm_pack (t, &s); + register_local_specialization (pack, t); + } + else + { + register_local_specialization (s, t); + s = TREE_CHAIN (s); + } + } +} + +// Substitute ARGS into the parameter list T, producing a sequence of +// local parameters (variables) in the current scope. +tree +tsubst_local_parms (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) + return error_mark_node; + + // Register the instantiated args as local parameters. + if (t) + declare_local_parms (t, r); + + return r; +} + +// Substitute ARGS into the requirement body (list of requirements), T. +tree +tsubst_requirement_body (tree t, tree args, tree in_decl) +{ + cp_unevaluated guard; + tree r = NULL_TREE; + while (t) + { + // If any substitutions fail, then this is equivalent to + // returning false. + 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; +} +} // namespace + +// Substitute ARGS into the requires expression T. +tree +tsubst_requires_expr (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); +} + +// Substitute ARGS into the valid-expr expression T. +tree +tsubst_validexpr_expr (tree t, tree args, tree in_decl) +{ + tree r = tsubst_expr (TREE_OPERAND (t, 0), args, tf_none, in_decl, false); + return finish_validexpr_expr (r); +} + +// Substitute ARGS into the valid-type expression T. +tree +tsubst_validtype_expr (tree t, tree args, tree in_decl) +{ + tree r = tsubst (TREE_OPERAND (t, 0), args, tf_none, in_decl); + return finish_validtype_expr (r); +} + +// Substitute ARGS into the constexpr expression T. +tree +tsubst_constexpr_expr (tree t, tree args, tree in_decl) +{ + tree r = tsubst_expr (TREE_OPERAND (t, 0), args, tf_none, in_decl, false); + return finish_constexpr_expr (r); +} + +// Substitute ARGS into the expr requirement T. Note that a requirement +// node is instantiated from a non-reduced context (e.g., static_assert). +tree +tsubst_expr_req (tree t, tree args, tree in_decl) +{ + tree r = NULL_TREE; + for (tree l = TREE_OPERAND (t, 0); l; l = TREE_CHAIN (l)) + { + tree e = tsubst_expr (TREE_VALUE (l), args, tf_none, in_decl, false); + r = conjoin_requirements (r, e); + } + return r; +} + +// Substitute ARGS into the type requirement T. Note that a requirement +// node is instantiated from a non-reduced context (e.g., static_assert). +tree +tsubst_type_req (tree t, tree args, tree in_decl) +{ + return tsubst_expr (TREE_OPERAND (t, 0), args, tf_none, in_decl, false); +} + +// Substitute ARGS into the nested requirement T. Note that a requirement +// node is instantiated from a non-reduced context (e.g., static_assert). +tree +tsubst_nested_req (tree t, tree args, tree in_decl) +{ + return tsubst_expr (TREE_OPERAND (t, 0), args, tf_none, in_decl, false); +} + +// Substitute the template arguments ARGS into the requirement +// expression REQS. Errors resulting from substitution are not +// diagnosed. +tree +instantiate_requirements (tree reqs, tree args) +{ + return tsubst_expr (reqs, args, tf_none, NULL_TREE, false); +} + +// -------------------------------------------------------------------------- // +// Constraint Satisfaction +// +// The following functions are responsible for the instantiation and +// evaluation of constraints. + +namespace { // Returns true if the requirements expression REQS is satisfied // and false otherwise. The requirements are checked by simply // evaluating REQS as a constant expression. static inline bool check_requirements (tree reqs) { - // Simplify the expression before evaluating it. This will - // cause TRAIT_EXPR nodes to be reduced before constexpr - // evaluation. + // Reduce any remaining TRAIT_EXPR nodes before evaluating. reqs = fold_non_dependent_expr (reqs); // Requirements are satisfied when REQS evaluates to true. @@ -501,6 +921,7 @@ check_requirements (tree reqs, tree args return false; return check_requirements (reqs); } +} // namespace // Check the instantiated declaration constraints. bool @@ -531,6 +952,11 @@ check_template_constraints (tree t, tree return check_constraints (DECL_CONSTRAINTS (t), args); } +// -------------------------------------------------------------------------- // +// Constraint Relations +// +// Interfaces for determining equivalency and ordering of constraints. + // Returns true when A and B are equivalent constraints. bool equivalent_constraints (tree a, tree b) @@ -702,6 +1128,78 @@ diagnose_call (location_t loc, tree t, t inform (loc, " %qE evaluated to false", t); } +// Diagnose specific constraint failures. +void +diagnose_requires (location_t loc, tree t, tree args) +{ + if (check_requirements (t, args)) + return; + + ++processing_template_decl; + tree subst = instantiate_requirements (t, args); + --processing_template_decl; + + // 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_requirements (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_requirements (t, args)) + return; + + // 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); + } + else + { + inform (loc, " %qT does not name a valid type", name); + } +} + +static void +diagnose_constexpr (location_t loc, tree t, tree args) +{ + if (check_requirements (t, args)) + return; + inform (loc, " %qE is not a constant expression", TREE_OPERAND (t, 0)); +} + +static void +diagnose_noexcept (location_t loc, tree t, tree args) +{ + if (check_requirements (t, args)) + return; + inform (loc, " %qE propagates exceptions", TREE_OPERAND (t, 0)); +} + // Diagnose a constraint failure in the expression T. void diagnose_other (location_t loc, tree t, tree args) @@ -730,9 +1228,31 @@ diagnose_node (location_t loc, tree t, t case TRAIT_EXPR: diagnose_trait (loc, t, args); break; + case CALL_EXPR: diagnose_call (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); + break; + + case NOEXCEPT_EXPR: + diagnose_noexcept (loc, t, args); + break; + default: diagnose_other (loc, t, args); break; @@ -757,12 +1277,12 @@ make_subst (tree tmpl, tree args) return subst; } -} // namesapce +} // namespace // Emit diagnostics detailing the failure ARGS to satisfy the constraints // of the template declaration, TMPL. void -diagnose_constraint_failure (location_t loc, tree tmpl, tree args) +diagnose_constraints (location_t loc, tree tmpl, tree args) { inform (loc, " constraints not satisfied %S", make_subst (tmpl, args)); Index: call.c =================================================================== --- call.c (revision 201240) +++ call.c (working copy) @@ -3294,7 +3294,7 @@ print_z_candidate (location_t loc, const { tree tmpl = r->u.template_instantiation.tmpl; tree args = r->u.template_instantiation.targs; - diagnose_constraint_failure (cloc, tmpl, args); + diagnose_constraints (cloc, tmpl, args); } break; case rr_none: Index: cp-tree.h =================================================================== --- cp-tree.h (revision 201240) +++ cp-tree.h (working copy) @@ -5602,6 +5602,8 @@ extern tree tsubst_default_argument (tr extern tree tsubst (tree, tree, tsubst_flags_t, tree); extern tree tsubst_copy_and_build (tree, tree, tsubst_flags_t, tree, bool, bool); +extern tree tsubst_expr (tree, tree, tsubst_flags_t, + tree, bool); extern tree most_general_template (tree); extern tree get_mostly_instantiated_function_type (tree); extern int problematic_instantiation_changed (void); @@ -5648,9 +5650,10 @@ extern tree get_function_template_decl extern tree resolve_nondeduced_context (tree); extern hashval_t iterative_hash_template_arg (tree arg, hashval_t val); extern tree coerce_template_parms (tree, tree, tree); -extern tree instantiate_requirements (tree, tree); -extern tree tsubst_constraint (tree, tree); -extern tree current_template_args (); +extern void register_local_specialization (tree, tree); +extern tree retrieve_local_specialization (tree); +extern tree extract_fnparm_pack (tree, tree *); + /* in repo.c */ extern void init_repo (void); @@ -5804,16 +5807,6 @@ extern void explain_invalid_constexpr_fn extern vec cx_error_context (void); extern bool is_unary_trait (cp_trait_kind); extern bool is_binary_trait (cp_trait_kind); -extern tree finish_requires_expr (tree, tree); -extern tree finish_expr_requirement (tree, tree, tree); -extern tree finish_expr_requirement (tree); -extern tree finish_type_requirement (tree); -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_epxr (tree); -extern tree finish_constexpr_expr (tree); enum { BCS_NO_SCOPE = 1, @@ -6257,10 +6250,30 @@ extern tree strip_using_decl /* in constraint.cc */ extern tree conjoin_requirements (tree, tree); -extern tree disjoin_requirements (tree, tree); extern tree reduce_requirements (tree); extern tree make_constraints (tree); extern tree get_constraints (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_type_requirement (tree); +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 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 instantiate_requirements (tree, tree); + extern bool check_constraints (tree); extern bool check_constraints (tree, tree); extern bool check_template_constraints (tree, tree); @@ -6269,7 +6282,8 @@ extern bool equivalent_constraints extern bool equivalently_constrained (tree, tree); extern bool more_constraints (tree, tree); extern bool more_constrained (tree, tree); -extern void diagnose_constraint_failure (location_t, tree, tree); + +extern void diagnose_constraints (location_t, tree, tree); /* in logic.cc */ extern tree decompose_assumptions (tree);