From cb57504a550158913258e5be8ddb991376475efb Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Wed, 9 Oct 2019 13:20:32 -0400 Subject: [PATCH] Update the concepts implementation to conform to C++20. gcc/c-family/ * c-cppbuiltin.c (c_cpp_builtins): Use new feature test values for concepts when -std=c++2a. Bump __cpp_concepts to 201907. * c.opt: Add -Wconcepts-ts. * c-opts.c (c_common_post_options): Warn when -fconcepts is used with -std=c++2a. Disable warning for -fconcepts in C++20 mode. (set_std_cxx2a): Enable concepts by default. gcc/cp/ * call.c (build_new_function_call): Don't evaluate concepts here. (constraint_failure): Don't record the template. (print_z_candidate): Don't extract the template. * class.c (add_method): When overloading, hide ineligible special member fns. (check_methods): Set TYPE_HAS_COMPLEX_* here. * constexpr.c (cxx_eval_constant_expression): Evaluate concepts. (maybe_initialize_fundef_copies_table): Remove. (get_fundef_copy): Use hash_map_safe_get_or_insert. (clear_cv_and_fold_caches): Clear the satisfaction cache. * constraint.cc (known_non_bool_p): New. (parsing_constraint_expression_sentinel): Renamed from expanding_constraint_sentinel. (check_constraint_operands): New. (check_constraint_atom): New. (finish_constraint_binary_op): New. (finish_constraint_or_expr): Likewise. (finish_constraint_and_expr): Likewise. (finish_constraint_primary_expr): Likewise. (combine_constraint_expressions): New. (finish_requires_expr): Add location parm. (get_concept_definition): Return the initializer of concept definitions. (get_template_head_requirements): New. (get_trailing_function_requirements): New. (deduce_constrained_parameter): Check if the identifier or template-id is a concept definition. (resolve_concept_definition_check): Removed. (resolve_variable_concept_check): Removed. (resolve_concept_check): New. (resolve_constraint_check): Handle concept definitions. converting arguments. (function_concept_check_p): Removed. (variable_concept_check_p): Removed. (unpack_concept_check): New. (get_concept_check_template): New. (build_call_check): Moved and renamed to build_function_check. (build_concept_check_arguments): make static. (build_function_check): Always do overload resolution in order to force conversion of template arguments (i.e., actually check that the use of a concept is valid). (build_standard_check): Renamed from build_real_concept_check. (build_real_concept_check): Build checks for C++2a concepts by (build_wildcard_concept_check): New. (build_concept_check): Use build_real_concept_check. New overload. (build_constraints): Save expressions, not normalized constraints. (build_concept_id): New. Pass tf_warning_or_error. (build_type_constraint): New. (finish_type_constraints): New. (associate_classtype_constraints): Also add constraints to union types. Note the original declaration in errors. Don't return error_mark_node in order to avoid an assertion later. (push_down_pack_expansion): Remove. (finish_shorthand_constraint): Make fold expressions, not naked parameter packs. Always apply the constraint to each template argument. (check_introduction_list): New. Fail if not enough names are introduced. (finish_template_introduction): Don't normalize constraints. Pass tsubst flags. Check for insufficient introductions. (placeholder_extract_concept_and_args): Handle the template-id case. Unpack function concept checks correctly. (tsubst_simple_requirement): Return errors if they occur. Don't process as a template. (tsubst_type_requirement): Likewise. (type_deducible_p): New. Wrap the input expression in parens for the purpose of deduction. (expression_convertible_t): New. (tsubst_compound_requirement): Use new deduction, conversion predicates. (tsubst_nested_requirement): Return errors if they occur. Don't process as a template. Instantiate and evaluate the nested requirement. (tsubst_valid_expression_requirement): New. (tsubst_simple_requirement): Use tsubst_valid_expression_requirement. (tsubst_compound_requirement): Use tsubst_valid_expression_requirement. (check_constaint_variables): New. (tsubst_constraint_variables): Check that type substitutions are valid. (tsubst_requires_expr): Likewise. Produce new requires-exprs during template substitution. Copy the previous local specialization stack, so references to non-local parameters can be found. Use cp_unevaluated. (tsubst_constraint): New. Don't evaluate concept checks. (subst_info): New. (norm_info): New. Used to build a normalization tree for concept check diagnostics. (debug_parameter_mapping): New. (debug_argument_list): New. (expand_concept): Removed. (normalize_logical_operation): Pass subst_info through call. (normalize_pack_expansion): Remove. (normalize_simple_requirement): Removed (normalize_type_requirement): Removed (normalize_compound_requirement): Removed (normalize_nested_requirement): Removed (normalize_requirement): Removed (normalize_requirements): Removed (normalize_requires_expression): Removed (normalize_variable_concept_check): Removed. (normalize_function_concept_check): Removed. (normalize_concept_check): Merged all normalize_*_check here. Substitute through written template arguments before normalizing the definition. Only substitute the innermost template arguments. (check_for_logical_overloads): Delete. (map_arguments): New. Associate template parameters with arguments. (build_parameter_mapping): New. Extract used parameters. (normalize_expression): Rewrite. (normalize_conjunction): Removed (normalize_disjunction): Removed (normalize_predicate_constraint): Removed (normalize_parameterized_constraint): Removed (normalized_map): New variable. (get_normalized_constraints): New entry point for normalization. Establishes a timer. (get_normalized_constraints_from_info): New. (get_normalized_constraints_from_decl): New. Turn on template processing prior to normalization. Handle inheriting ctors. Build the normalization arguments from the full set of template parameters of the most general template. This guarantees that we have no concrete arguments in the parameter mapping (e.g., from template members of class templates). Cache normalizations. (normalize_concept_definition): New. Cache normalizations. (normalize_template_requirements): New. (normalize_nontemplate_requirements): New. (normalize_constraint_expression): New. (tsubst_parameter_mapping): New. (get_mapped_args): New. (parameter_mapping_equivalent_p): New. Use template_args_equal. (atomic_constraints_identical_p): New. (hash_atomic_constraint): New. (satisfying_constraint_p): New. Guard against recursive evaluation of constraints during satisfaction. (satisfy_conjunction): New. (satisfy_disjunction): New. (sat_entry): New class for hashing satisfaction results. (sat_hasher): New hash traits. (sat_cache): New. (get_satisfaction): New. Returns cached satisfaction result. (save_satisfaction): New. Caches a satisfaction result. (clear_satisfaction_cache): New. (satisfaction_cache): New. Helps manage satisfaction cache requests. (decl_satisfied_cache): New. (satisfy_atom): New. (satisfy_constraint_r): New. (satisfy_constraint): Use new satisfaction algorithm. (evaluate_concept_check): New. (evaluate_concept): Removed. (evaluate_function_concept): Removed. (evaluate_variable_concept): Removed. (satisfy_constraint_expression): New. (constraint_expression_satisfied_p): New. (constraints_satisfied_p): Use strip_inheriting_ctors. Use push_/pop_access_scope. (more_constrained): Normalize before calling out to subsumption. Allow classes as arguments. (strictly_subsumes): Allow non-templates as arguments. Accept a new template argument. (weakly_subsumes): New. (at_least_as_constrained): Removed. (diagnose_other_expression): Removed. (diagnose_predicate_constraint): Removed. (diagnose_pack_expansion): Removed. (diagnose_check_constraint): Removed. (diagnose_logical_constraint): Removed. (diagnose_expression_constraint): Removed. (diagnose_type_constraint): Removed. (diagnose_implicit_conversion_constraint): Removed. (diagnose_argument_deduction_constraint): Removed. (diagnose_exception_constraint): Removed. (diagnose_parameterized_constraint): Removed. (diagnose_argument_deduction_constraint): Removed. (diagnose_argument_deduction_constraint): Removed. (diagnose_argument_deduction_constraint): Removed. (diagnose_trait_expr): New. (diagnose_requires_expr): New. (diagnose_atomic_constraint): New. (diagnose_valid_expression) Stop wrongly diagnosing valid expressions. Don't substitute as if in template decls. This causes substitution to generate expressions that aren't suitable for use with the noexcept routines. (diagnose_valid_type) Likewise. (diagnose_compound_requirement) Actually emit diagnostics for the causes of errors.Call force_paren_expr_uneval. (diagnose_declaration_constraints): Turn on template processing to suppress certain analyses. * cp-objcp-common.c (cp_common_init_ts): Make concepts typed. (cp_get_debug_type): Use hash_map_safe_*. * cp-tree.h: New function declarations for semantic actions, other facilities. Remove declaration no longer used or needed. Remove unused _CONSTR macros. (LANG_DECL_HAS_MIN): Add CONCEPT_DECL. (template_info_decl_check): Factor macro check into an inline function. (DECL_TEMPLATE_INFO): Use new check facility. (finish_concept_definition): New. Don't invalid concept declarations with invalid initializers. (find_template_parameters): New. (concept_definition_p): New. (concept_check_p): New. (variable_concept_check_p): New. (force_paren_expr_uneval): New. (ovl_iterator::using_p): A USING_DECL by itself was also introduced by a using-declaration. (struct tree_template_info): Use tree_base instead of tree_common. Add tmpl and args fields. (TI_TEMPLATE, TI_ARGS): Adjust. (DECLTYPE_FOR_INIT_CAPTURE): Remove. (CONSTR_CHECK, CONSTR_INFO, CONSTR_EXPR, CONSTR_CONTEXT): New. (ATOMIC_CONSTR_MAP, TRAIT_EXPR_LOCATION): New. (struct tree_trait_expr): Add locus field. (enum tsubst_flags): Add tf_norm as a hint to generate normalization context when diagnosing constraint failure. * cp-tree.def: Remove unused _CONSTR nodes and rename PRED_CONSTR to ATOMIC_CONSTR. (CONCEPT_DECL): New. * cxx-pretty-print.c: Remove constraint printing code. (pp_cxx_concept_definition): New. (pp_cxx_template_declaration): Print concept definitions. (pp_cxx_check_constraint): Update printing for concept definitions. (pp_cxx_nested_name_specifier): Fix a weird case where we're printing '::::' for concepts. (simple_type_specifier): Print requirements for placeholder types. (pp_cxx_constrained_type_spec): Print the associated requirements of a placeholder type. (pp_cxx_compound_requirement): Add space before the '->'. (pp_cxx_parameter_mapping): Print the parameter mapping. (pp_cxx_atomic_constraint): Use the function above. * decl.c (redeclaration_error_message): New error for concepts. (grokdeclarator): Check for and disallow decltype(auto) in parameter declarations. (grokfndecl): Don't normalize constraints. Add check for constraints on declaration. (grokvardecl): Don't normalize constraints. (grok_special_member_properties): Don't set TYPE_HAS_COMPLEX_*. (function_requirements_equivalent_p): New. Compare trailing requires clauses. Compare combined constraints in pre-C++20 mode. (decls_match): Compare trailing requires clauses. Compare template heads for function templates. Remove old constraint comparison. Simplify comparison of functions, function templates. (duplicate_function_template_decls): New. Refactor a nasty if condition into a single predicate. (require_deduced_type): Don't complain if we already complained about deduction failure. (finish_function): Perform auto deduction to ensure that constraints are checked even when functions contain no return statements. Only do auto deduction if we haven't previously seen any return statements. This prevents multiple diagnostics of the same error. (store_decomp_type): Remove. (cp_finish_decomp): Use hash_map_safe_put. * error.c: Remove constraint printing code. (dump_decl): Dump concept definitions. Handle wildcard declarations. (dump_template_decl): Likewise. (dump_type): Print associated requirements for placeholder types. (rebuild_concept_check): New. (maybe_print_single_constraint_context): New. (maybe_print_constraint_context): Recursively print nested contexts. * init.c (get_nsdmi): Use hash_map_safe_*. * lambda.c (maybe_add_lambda_conv_op): Bail if deduction failed. (add_capture): Copy parameter packs from init. (lambda_capture_field_type): Always use auto for init-capture. * logic.cc: Completely rewrite. (constraint_hash): New. (clause/ctor): Save atoms in the hash table. (replace): Save atoms during replacement. (insert): Save atoms during insertion. (contains): Only search the hash table for containment. (clause): Keep a hash of atomic constraints. (clause::clause): Explicitly copy the hash table when copying. (disjunction_p, conjunction_p, atomic_p, dnf_size, cnf_size): New. (diagnose_constraint_size): New. (subsumes_constraints_nonnull): Compare the sizes of normalized formula to determine the cheapest decomposition. * name-lookup.c (diagnose_name_conflict): Diagnose name issues with concepts. (matching_fn_p): Check constraints. (push_class_level_binding_1): Move overloaded functions case down, accept FUNCTION_DECL as target_decl. * parser.c (enum required_token): New required token for auto. (make_location): Add overload taking lexer as last parm. (cp_parser_required_error): Diagnose missing auto. (cp_parser_diagnose_ungrouped_constraint_plain): New. (cp_parser_diagnose_ungrouped_constraint_plain): New. (cp_parser_constraint_primary_expression): New. Tentatively parse the primary expression. If that fails tentatively parse a lower precedence expression in order to diagnose the error. (cp_parser_check_non_logical_constraint): New. Performs a trial parse of the right-hand-side of non-logical operators in order to generate good diagnostics. (cp_parser_constraint_logical_and_expression): New. (cp_parser_constraint_logical_or_expression): New. (cp_parser_requires_clause_expression): New. (cp_parser_requires_clause): Renamed to cp_parser_constraint_expression. (cp_parser_requires_clause_opt): Parse the requires-clause differently in -fconcepts and -std=c++2a modes. (cp_parser_requirement_list): Rename to cp_parser_requirement_seq. Rewrite so that semicolons are parsed along with requirements, not the sequence. (cp_parser_simple_requirement): Expect a semicolon at end. (cp_parser_compound_requirement): Expect a semicolon at end. Only allow trailing-return-type with -fconcepts-ts. (cp_parser_nested_requirement): Expect a semicolon at end. Parse constraint-expressions. (cp_parser_concept_definition): New. Don't fail parsing the concept definition if the initializer is ill-formed. Don't declare the concept before parsing the initializer. (cp_parser_constraint_expression): Declare earlier. (cp_parser_type_requirement): Current scope is not valid. (cp_parser_requires_expression): Commit to the tentative parse. (cp_parser_decl_specifier_seq): Warn when concept appears to be used as a decl-specifier. (cp_parser_template_declaration_after_parameters): Parse concept definitions. (cp_parser_template_id): Don't try to resolve a concept template-id yet. (cp_parser_template_id_expr): Resolve it as a concept check. (cp_parser_decl_specifier_seq): Warn on 'concept bool'. (cp_parser_type_parameter): Combine expressions not constraints. (cp_parser_explicit_template_declaration): Combine expressions not constraints. (cp_parser_maybe_concept_name): Removed. (cp_parser_simple_type_specifier): Handle an error condition of a bad constrained type specifier. Expect auto or decltype after a concept name. Also handle the case where we have a template-id as a concept check. (cp_parser_template_introduction): Diagnose errors on invalid introductions. Give up if it doesn't start with a concept name. Pedwarn if not -fconcepts-ts. (synthesize_implicit_template_parm): Don't do consistent binding. Use a new flag for constrained parameters. Combine expressions, not constraints. Fail if we get a placeholder in block scope. Placeholders that do not constrain types are not allowed in parameter declarations, so don't handle them. (cp_parser_placeholder_type_specifier): New. Implement parsing of placeholder type specifiers following a concept name or partial concept check. Disallow decltype(auto) parameters. (cp_parser_nested_name_specifier_opt): If the token is already CPP_NESTED_NAME_SPECIFIER, leave it alone. (cp_parser_id_expression, cp_parser_unqualified_id): Call cp_parser_template_id_expr. (cp_parser_placeholder_type_specifier): Add tentative parm. Don't expect a WILDCARD_DECL. (cp_parser_trait_expr): Pass trait_loc down. (cp_parser_postfix_expression): Do set location of dependent member call. * pt.c (finish_concept_definition): New. (push_template_decl_real): Handle concept definitions. (start_concept_definition): Let push_template_decl_real handle the creation of the template. (get_constraints): Return null if the table hasn't been initialized. (tsubst_copy_and_build): Build template-id expressions for concept checks. [TRAIT_EXPR]: Pass trait_loc down. (lookup_template_class_1): Add the template name to the constraint failure diagnostic. (lookup_and_finish_template_variable): Build concept checks with the correct arguments. (tsubst_function_decl): Don't substitute through constraints. Always associate constraints with functions. (template_parm_level_and_index): Make non-static. (for_each_template_parm_r): Handle requires expressions. (keep_template_parm): New. (find_template_parameters): New. (more_specialized_fn): Change how winners and losers are chosen. (make_constrained_auto): Don't normalize constraints. (template_parameters_equivalent_p): New. Compare template parameters. Add a comparison for implicitly vs. explicitly declared parameters. (template_parameter_lists_equivalent_p): New. Compare template parameter lists. (template_requirements_equivalent_p): New. (template_heads_equivalent_p): New. Compare template heads. (template_parameter_constraints_equivalent_p): New. (is_compatible_template_arg): Use weakly_subsumes. (maybe_new_partial_specialization): Use new constraint comparison for finding specializations. (process_partial_specialization): Pass main template as argument. (more_specialized_partial_spec): Don't immediately return when detecting a winner. (make_constrained_auto): Handle concept definitions. (do_auto_deduction): Update auto deduction for new concept model. Extract the function concept correctly; rename constr to check to reflect the kind of node. (tsubst): Adjust wildcard argument during substitution. [DECLTYPE_TYPE]: Remove init-capture handling. (tsubst_copy_and_build): Build concept checks, not template ids. Defer checks of function concepts. Handle concepts before variable templates. Handle calls to function concepts explicitly. (coerce_template_parms): Use concept_definition_p. Handle a deduction error where a potentially empty pack can be supplied after the last parameter of a concept. (finish_template_variable): Don't process concepts here. (instantiation_dependent_r): Use concept_check_p. (tsubst_template_args): Make non-static. (make_constrained_placeholder_type): New. Refactored from make_constrained_auto. (make_constrained_auto) Use make_constrained_placeholder_type. (make_constrained_decltype_auto) New. (tsubst_function_parms): New. (value_dependent_expression_p) [TEMPLATE_ID_EXPR]: Use concept_definition_p. (push_access_scope, pop_access_scope): No longer static. (tsubst_template_parm): Substitute TEMPLATE_PARM_CONSTRAINTS. (tsubst_friend_function): Use tsubst_constraint. Use generic_targs_for. (get_underlying_template) Use generic_targs_for. (uses_parameter_packs): Return tree. (gen_elem_of_pack_expansion_instantiation): Don't push local_specialization_stack. (prepend_one_capture): New. (tsubst_lambda_expr): Use prepend_one_capture. Don't touch local_specializations. (template_parms_level_to_args): No longer static. (add_outermost_template_args): Likewise. (find_template_parameter_info): New. Provide context for finding template parameters. (keep_template_parm): Don't keep parameters declared at depth levels greater than those of the template parameters of the source declaration. Don't propagate cv-qualified types. Return 0, so we find all template parameters, not the just first. (any_template_parm_r): New. Handle cases that are mishandled by for_each_template_parm_r. (generic_targs_for): Factor out of coerce_template_args_for_ttp. (tsubst_argument_pack): Factor out of tsubst_template_args. (constraint_sat_entry): Removed. (constraint_sat_hasher): Removed. (concept_spec_entry): Removed. (concept_spec_hasher): Removed. (constraint_memos): Removed. (concept_memos): Removed. (lookup_constraint_satisfaction): Removed. (memoize_constraint_satisfaction): Removed. (lookup_concept_satisfaction): Removed. (memoize_concept_satisfaction): Removed. (concept_expansions): Removed. (get_concept_expansion): Removed. (save_concept_expansion): Removed. (init_constraint_processing): Remove initialization of non-existing resources. (find_template_requirement): New. Search for the sub-requirement within the associated constraints. (convert_generic_types_to_packs): Also transform the associated constraint and update the current template requirements. (store_defaulted_ttp, lookup_defaulted_ttp): Remove. (add_defaults_to_ttp): Use hash_map_safe_*. * semantics.c (finish_call_expr): Diagnose calls to concepts. Handle concept checks explicitly. (finish_id_expression): Evaluate variable concepts as part of id-expression processing. Don't treat variable concepts as variables, and don't process function concepts as plain id-expressions. (force_paren_expr): Add even_uneval parm. (finish_trait_expr): Add location parm. * tree.c (special_memfn_p): New. (cp_expr_location): Handle TRAIT_EXPR. * typeck.c (check_return_expr): Actually use the diagnostic kind when performing return-type deduction. * typeck2.c (build_functional_cast): Don't rely on the location of 'auto'. gcc/testsuite/ * lib/target-supports.exp (check_effective_target_concepts): Check for std=c++2a. gcc/ * doc/invoke.texi: Document -fconcepts-ts. From-SVN: r276764 --- gcc/ChangeLog | 4 + gcc/c-family/ChangeLog | 14 + gcc/c-family/c-cppbuiltin.c | 8 +- gcc/c-family/c-opts.c | 11 + gcc/c-family/c.opt | 4 + gcc/cp/ChangeLog | 458 ++ gcc/cp/call.c | 54 +- gcc/cp/class.c | 3 + gcc/cp/config-lang.in | 1 + gcc/cp/constexpr.c | 22 +- gcc/cp/constraint.cc | 3769 +++++++++-------- gcc/cp/cp-objcp-common.c | 9 +- gcc/cp/cp-tree.def | 72 +- gcc/cp/cp-tree.h | 358 +- gcc/cp/cxx-pretty-print.c | 197 +- gcc/cp/decl.c | 172 +- gcc/cp/error.c | 202 +- gcc/cp/lambda.c | 4 +- gcc/cp/logic.cc | 1183 +++--- gcc/cp/name-lookup.c | 11 +- gcc/cp/parser.c | 951 +++-- gcc/cp/pt.c | 956 +++-- gcc/cp/search.c | 5 +- gcc/cp/semantics.c | 55 +- gcc/cp/typeck.c | 3 +- gcc/cp/typeck2.c | 5 +- gcc/doc/invoke.texi | 17 +- gcc/testsuite/ChangeLog | 5 + gcc/testsuite/g++.dg/concepts/alias1.C | 16 - gcc/testsuite/g++.dg/concepts/alias2.C | 14 - gcc/testsuite/g++.dg/concepts/alias3.C | 14 - gcc/testsuite/g++.dg/concepts/alias4.C | 20 - gcc/testsuite/g++.dg/concepts/auto1.C | 2 +- gcc/testsuite/g++.dg/concepts/auto3.C | 2 +- gcc/testsuite/g++.dg/concepts/auto4.C | 4 +- .../g++.dg/concepts/class-deduction1.C | 2 +- gcc/testsuite/g++.dg/concepts/class.C | 53 - gcc/testsuite/g++.dg/concepts/class1.C | 15 - gcc/testsuite/g++.dg/concepts/class2.C | 15 - gcc/testsuite/g++.dg/concepts/class3.C | 15 - gcc/testsuite/g++.dg/concepts/class4.C | 22 - gcc/testsuite/g++.dg/concepts/class5.C | 2 +- gcc/testsuite/g++.dg/concepts/class6.C | 4 +- gcc/testsuite/g++.dg/concepts/debug1.C | 8 +- gcc/testsuite/g++.dg/concepts/decl-diagnose.C | 8 +- .../g++.dg/concepts/deduction-constraint1.C | 2 +- gcc/testsuite/g++.dg/concepts/diagnostic1.C | 12 +- gcc/testsuite/g++.dg/concepts/disjunction1.C | 60 - gcc/testsuite/g++.dg/concepts/dr1430.C | 9 +- gcc/testsuite/g++.dg/concepts/equiv.C | 2 +- gcc/testsuite/g++.dg/concepts/equiv2.C | 12 +- .../g++.dg/concepts/explicit-inst4.C | 18 - .../g++.dg/concepts/explicit-spec3.C | 14 - gcc/testsuite/g++.dg/concepts/expression.C | 12 +- gcc/testsuite/g++.dg/concepts/expression2.C | 12 +- gcc/testsuite/g++.dg/concepts/expression3.C | 2 +- gcc/testsuite/g++.dg/concepts/feature-macro.C | 6 - gcc/testsuite/g++.dg/concepts/fn-concept1.C | 2 +- gcc/testsuite/g++.dg/concepts/fn-concept2.C | 2 +- .../g++.dg/concepts/fn-generic-member-ool.C | 1 - gcc/testsuite/g++.dg/concepts/fn1.C | 2 +- gcc/testsuite/g++.dg/concepts/fn10.C | 4 +- gcc/testsuite/g++.dg/concepts/fn2.C | 2 +- gcc/testsuite/g++.dg/concepts/fn3.C | 2 +- gcc/testsuite/g++.dg/concepts/fn4.C | 2 +- gcc/testsuite/g++.dg/concepts/fn5.C | 2 +- gcc/testsuite/g++.dg/concepts/fn6.C | 2 +- gcc/testsuite/g++.dg/concepts/fn7.C | 4 +- gcc/testsuite/g++.dg/concepts/fn8.C | 2 +- gcc/testsuite/g++.dg/concepts/fn9.C | 2 +- .../g++.dg/concepts/generic-fn-err.C | 8 +- gcc/testsuite/g++.dg/concepts/generic-fn.C | 2 +- gcc/testsuite/g++.dg/concepts/iconv1.C | 21 - gcc/testsuite/g++.dg/concepts/inherit-ctor1.C | 28 +- gcc/testsuite/g++.dg/concepts/inherit-ctor3.C | 9 +- gcc/testsuite/g++.dg/concepts/inherit-ctor4.C | 19 - gcc/testsuite/g++.dg/concepts/intro1.C | 2 +- gcc/testsuite/g++.dg/concepts/intro2.C | 2 +- gcc/testsuite/g++.dg/concepts/intro3.C | 2 +- gcc/testsuite/g++.dg/concepts/intro4.C | 12 +- gcc/testsuite/g++.dg/concepts/intro5.C | 9 +- gcc/testsuite/g++.dg/concepts/intro6.C | 2 +- gcc/testsuite/g++.dg/concepts/intro7.C | 6 +- gcc/testsuite/g++.dg/concepts/locations1.C | 4 +- gcc/testsuite/g++.dg/concepts/memtmpl1.C | 16 - .../g++.dg/concepts/partial-concept-id1.C | 33 +- .../g++.dg/concepts/partial-concept-id2.C | 2 +- gcc/testsuite/g++.dg/concepts/partial-spec5.C | 2 +- gcc/testsuite/g++.dg/concepts/placeholder1.C | 35 - gcc/testsuite/g++.dg/concepts/placeholder2.C | 23 +- gcc/testsuite/g++.dg/concepts/placeholder3.C | 6 +- gcc/testsuite/g++.dg/concepts/placeholder4.C | 6 +- gcc/testsuite/g++.dg/concepts/placeholder5.C | 8 +- gcc/testsuite/g++.dg/concepts/placeholder6.C | 7 +- gcc/testsuite/g++.dg/concepts/pr65634.C | 2 +- gcc/testsuite/g++.dg/concepts/pr65636.C | 2 +- gcc/testsuite/g++.dg/concepts/pr65681.C | 65 +- gcc/testsuite/g++.dg/concepts/pr65848.C | 2 +- gcc/testsuite/g++.dg/concepts/pr67249.C | 2 +- gcc/testsuite/g++.dg/concepts/pr67544.C | 3 +- gcc/testsuite/g++.dg/concepts/pr67595.C | 8 +- gcc/testsuite/g++.dg/concepts/pr67655.C | 2 +- gcc/testsuite/g++.dg/concepts/pr68434.C | 2 +- gcc/testsuite/g++.dg/concepts/pr71127.C | 7 +- gcc/testsuite/g++.dg/concepts/pr71128.C | 4 +- gcc/testsuite/g++.dg/concepts/pr71131.C | 7 +- gcc/testsuite/g++.dg/concepts/pr71385.C | 2 +- gcc/testsuite/g++.dg/concepts/pr84330.C | 7 +- gcc/testsuite/g++.dg/concepts/pr85065.C | 6 +- gcc/testsuite/g++.dg/concepts/req-neg1.C | 11 - gcc/testsuite/g++.dg/concepts/req1.C | 29 - gcc/testsuite/g++.dg/concepts/req10.C | 19 - gcc/testsuite/g++.dg/concepts/req11.C | 29 - gcc/testsuite/g++.dg/concepts/req12.C | 26 - gcc/testsuite/g++.dg/concepts/req13.C | 14 - gcc/testsuite/g++.dg/concepts/req16.C | 20 - gcc/testsuite/g++.dg/concepts/req18.C | 18 - gcc/testsuite/g++.dg/concepts/req19.C | 14 - gcc/testsuite/g++.dg/concepts/req2.C | 20 - gcc/testsuite/g++.dg/concepts/req20.C | 21 - gcc/testsuite/g++.dg/concepts/req3.C | 17 - gcc/testsuite/g++.dg/concepts/req4.C | 19 - gcc/testsuite/g++.dg/concepts/req5.C | 19 - gcc/testsuite/g++.dg/concepts/req6.C | 19 - gcc/testsuite/g++.dg/concepts/req7.C | 25 - gcc/testsuite/g++.dg/concepts/req8.C | 17 - gcc/testsuite/g++.dg/concepts/req9.C | 25 - .../g++.dg/concepts/template-parm1.C | 35 - .../g++.dg/concepts/template-parm10.C | 18 - .../g++.dg/concepts/template-parm11.C | 2 +- .../g++.dg/concepts/template-parm12.C | 4 +- .../g++.dg/concepts/template-parm2.C | 2 +- .../g++.dg/concepts/template-parm3.C | 2 +- .../g++.dg/concepts/template-parm4.C | 2 +- .../g++.dg/concepts/template-parm5.C | 20 - .../g++.dg/concepts/template-parm6.C | 42 - .../g++.dg/concepts/template-parm7.C | 45 - .../g++.dg/concepts/template-parm9.C | 19 - .../g++.dg/concepts/template-template-parm1.C | 2 +- gcc/testsuite/g++.dg/concepts/var-concept1.C | 2 +- gcc/testsuite/g++.dg/concepts/var-concept2.C | 2 +- gcc/testsuite/g++.dg/concepts/var-concept3.C | 15 +- gcc/testsuite/g++.dg/concepts/var-concept4.C | 2 +- gcc/testsuite/g++.dg/concepts/var-concept5.C | 6 +- gcc/testsuite/g++.dg/concepts/var-concept6.C | 2 +- gcc/testsuite/g++.dg/concepts/var-concept7.C | 4 +- gcc/testsuite/g++.dg/concepts/var-templ1.C | 2 +- gcc/testsuite/g++.dg/concepts/var-templ2.C | 2 +- gcc/testsuite/g++.dg/concepts/var-templ3.C | 2 +- gcc/testsuite/g++.dg/concepts/variadic1.C | 2 +- gcc/testsuite/g++.dg/concepts/variadic2.C | 9 +- gcc/testsuite/g++.dg/concepts/variadic3.C | 2 +- gcc/testsuite/g++.dg/concepts/variadic4.C | 11 +- gcc/testsuite/g++.dg/cpp0x/auto52.C | 2 +- .../g++.dg/cpp0x/lambda/lambda-err2.C | 2 +- gcc/testsuite/g++.dg/cpp2a/concepts-access1.C | 15 + gcc/testsuite/g++.dg/cpp2a/concepts-alias.C | 25 + gcc/testsuite/g++.dg/cpp2a/concepts-alias2.C | 12 + gcc/testsuite/g++.dg/cpp2a/concepts-class.C | 115 + gcc/testsuite/g++.dg/cpp2a/concepts-cmath.C | 4 + .../g++.dg/cpp2a/concepts-complete1.C | 11 + .../concepts-constrained-parm.C} | 5 +- .../g++.dg/cpp2a/concepts-decltype.C | 67 + gcc/testsuite/g++.dg/cpp2a/concepts-defarg1.C | 7 + gcc/testsuite/g++.dg/cpp2a/concepts-dep1.C | 5 + gcc/testsuite/g++.dg/cpp2a/concepts-dr1430.C | 14 + .../concepts-explicit-inst1.C} | 11 +- .../concepts-explicit-inst2.C} | 7 +- .../concepts-explicit-inst3.C} | 13 +- .../g++.dg/cpp2a/concepts-explicit-inst4.C | 17 + .../concepts-explicit-spec1.C} | 7 +- .../concepts-explicit-spec2.C} | 5 +- .../g++.dg/cpp2a/concepts-explicit-spec3.C | 13 + .../concepts-explicit-spec4.C} | 13 +- .../concepts-explicit-spec5.C} | 9 +- .../concepts-explicit-spec6.C} | 3 +- .../g++.dg/cpp2a/concepts-feature-macro.C | 5 + gcc/testsuite/g++.dg/cpp2a/concepts-fn1.C | 248 ++ gcc/testsuite/g++.dg/cpp2a/concepts-fn2.C | 111 + gcc/testsuite/g++.dg/cpp2a/concepts-fn3.C | 49 + gcc/testsuite/g++.dg/cpp2a/concepts-fnparm1.C | 10 + .../friend1.C => cpp2a/concepts-friend1.C} | 7 +- .../friend2.C => cpp2a/concepts-friend2.C} | 5 +- gcc/testsuite/g++.dg/cpp2a/concepts-friend3.C | 20 + gcc/testsuite/g++.dg/cpp2a/concepts-iconv1.C | 22 + .../concepts-inherit-ctor2.C} | 7 +- .../g++.dg/cpp2a/concepts-inherit-ctor4.C | 18 + .../g++.dg/cpp2a/concepts-inherit-ctor5.C | 18 + .../lambda1.C => cpp2a/concepts-lambda1.C} | 15 +- .../g++.dg/cpp2a/concepts-locations1.C | 22 + .../g++.dg/cpp2a/concepts-member-concept.C | 21 + .../concepts-memfun-err.C} | 16 +- .../memfun.C => cpp2a/concepts-memfun.C} | 45 +- .../g++.dg/cpp2a/concepts-memtmpl1.C | 17 + .../g++.dg/cpp2a/concepts-memtmpl2.C | 15 + gcc/testsuite/g++.dg/cpp2a/concepts-nested1.C | 13 + .../g++.dg/cpp2a/concepts-noexcept1.C | 25 + gcc/testsuite/g++.dg/cpp2a/concepts-p1141.C | 98 + .../concepts-partial-spec.C} | 2 +- .../concepts-partial-spec2.C} | 15 +- .../concepts-partial-spec3.C} | 2 +- .../concepts-partial-spec4.C} | 5 +- .../g++.dg/cpp2a/concepts-partial-spec5.C | 12 + .../concepts-partial-spec6.C} | 7 +- .../g++.dg/cpp2a/concepts-placeholder1.C | 14 + .../pr58500.C => cpp2a/concepts-pr58500.C} | 0 .../pr58534.C => cpp2a/concepts-pr58534.C} | 0 .../pr58535.C => cpp2a/concepts-pr58535.C} | 0 .../pr58536.C => cpp2a/concepts-pr58536.C} | 0 .../pr58548.C => cpp2a/concepts-pr58548.C} | 0 .../pr58549.C => cpp2a/concepts-pr58549.C} | 0 .../concepts-pr59200.C} | 3 +- .../pr60052.C => cpp2a/concepts-pr60052.C} | 0 .../pr60053.C => cpp2a/concepts-pr60053.C} | 0 .../pr60064.C => cpp2a/concepts-pr60064.C} | 0 .../pr60065.C => cpp2a/concepts-pr60065.C} | 0 .../pr60377.C => cpp2a/concepts-pr60377.C} | 0 .../pr60390.C => cpp2a/concepts-pr60390.C} | 0 .../pr60391.C => cpp2a/concepts-pr60391.C} | 0 .../pr60573.C => cpp2a/concepts-pr60573.C} | 0 .../pr65552.C => cpp2a/concepts-pr65552.C} | 8 +- .../pr65575.C => cpp2a/concepts-pr65575.C} | 11 +- gcc/testsuite/g++.dg/cpp2a/concepts-pr65634.C | 18 + gcc/testsuite/g++.dg/cpp2a/concepts-pr65636.C | 8 + gcc/testsuite/g++.dg/cpp2a/concepts-pr65848.C | 59 + .../pr65854.C => cpp2a/concepts-pr65854.C} | 11 +- .../pr66091.C => cpp2a/concepts-pr66091.C} | 5 +- gcc/testsuite/g++.dg/cpp2a/concepts-pr66844.C | 16 + gcc/testsuite/g++.dg/cpp2a/concepts-pr66962.C | 80 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67070.C | 51 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67147.C | 25 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67148.C | 121 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67178.C | 26 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67210.C | 10 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67217.C | 8 + .../g++.dg/cpp2a/concepts-pr67225-1.C | 32 + .../g++.dg/cpp2a/concepts-pr67225-2.C | 36 + .../g++.dg/cpp2a/concepts-pr67225-3.C | 21 + .../g++.dg/cpp2a/concepts-pr67225-4.C | 14 + .../g++.dg/cpp2a/concepts-pr67225-5.C | 17 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67319.C | 24 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67427.C | 22 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67654.C | 30 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67658.C | 14 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67684.C | 63 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67685.C | 7 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67692.C | 13 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67697.C | 15 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67719.C | 15 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67774.C | 26 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67825.C | 20 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67860.C | 61 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67862.C | 162 + gcc/testsuite/g++.dg/cpp2a/concepts-pr67969.C | 32 + .../g++.dg/cpp2a/concepts-pr68093-1.C | 13 + .../g++.dg/cpp2a/concepts-pr68093-2.C | 14 + gcc/testsuite/g++.dg/cpp2a/concepts-pr68372.C | 48 + gcc/testsuite/g++.dg/cpp2a/concepts-pr68434.C | 18 + .../pr68683.C => cpp2a/concepts-pr68683.C} | 8 +- gcc/testsuite/g++.dg/cpp2a/concepts-pr68812.C | 34 + gcc/testsuite/g++.dg/cpp2a/concepts-pr69235.C | 48 + .../pr71368.C => cpp2a/concepts-pr71368.C} | 12 +- gcc/testsuite/g++.dg/cpp2a/concepts-pr71385.C | 14 + .../pr71965.C => cpp2a/concepts-pr71965.C} | 11 +- .../memfun2.C => cpp2a/concepts-pr72415.C} | 2 +- gcc/testsuite/g++.dg/cpp2a/concepts-pr78752.C | 18 + gcc/testsuite/g++.dg/cpp2a/concepts-pr79759.C | 7 + .../pr80471.C => cpp2a/concepts-pr80471.C} | 0 gcc/testsuite/g++.dg/cpp2a/concepts-pr80746.C | 14 + gcc/testsuite/g++.dg/cpp2a/concepts-pr80773.C | 33 + gcc/testsuite/g++.dg/cpp2a/concepts-pr82507.C | 17 + gcc/testsuite/g++.dg/cpp2a/concepts-pr82740.C | 32 + gcc/testsuite/g++.dg/cpp2a/concepts-pr84140.C | 38 + gcc/testsuite/g++.dg/cpp2a/concepts-pr84551.C | 11 + .../concepts-pr84979-2.C} | 0 .../concepts-pr84979-3.C} | 0 .../pr84979.C => cpp2a/concepts-pr84979.C} | 0 .../pr84980.C => cpp2a/concepts-pr84980.C} | 4 +- .../pr85265.C => cpp2a/concepts-pr85265.C} | 3 +- .../concepts-pr85706.C} | 0 gcc/testsuite/g++.dg/cpp2a/concepts-pr85808.C | 19 + gcc/testsuite/g++.dg/cpp2a/concepts-pr86269.C | 19 + gcc/testsuite/g++.dg/cpp2a/concepts-pr87441.C | 10 + .../g++.dg/cpp2a/concepts-requires1.C | 71 + .../g++.dg/cpp2a/concepts-requires10.C | 32 + .../req17.C => cpp2a/concepts-requires11.C} | 10 +- .../g++.dg/cpp2a/concepts-requires12.C | 21 + .../g++.dg/cpp2a/concepts-requires13.C | 20 + .../g++.dg/cpp2a/concepts-requires14.C | 24 + .../g++.dg/cpp2a/concepts-requires15.C | 15 + .../g++.dg/cpp2a/concepts-requires16.C | 47 + .../g++.dg/cpp2a/concepts-requires17.C | 11 + .../g++.dg/cpp2a/concepts-requires2.C | 74 + .../g++.dg/cpp2a/concepts-requires3.C | 32 + .../g++.dg/cpp2a/concepts-requires4.C | 15 + .../g++.dg/cpp2a/concepts-requires5.C | 45 + .../g++.dg/cpp2a/concepts-requires6.C | 34 + .../req14.C => cpp2a/concepts-requires7.C} | 9 +- .../g++.dg/cpp2a/concepts-requires8.C | 17 + .../req15.C => cpp2a/concepts-requires9.C} | 3 +- gcc/testsuite/g++.dg/cpp2a/concepts-sfinae1.C | 10 + .../g++.dg/cpp2a/concepts-template-parm1.C | 19 + .../g++.dg/cpp2a/concepts-template-parm10.C | 10 + .../g++.dg/cpp2a/concepts-template-parm2.C | 15 + .../g++.dg/cpp2a/concepts-template-parm5.C | 9 + .../g++.dg/cpp2a/concepts-template-parm6.C | 44 + .../concepts-template-parm8.C} | 11 +- .../g++.dg/cpp2a/concepts-template-parm9.C | 18 + .../traits1.C => cpp2a/concepts-traits1.C} | 41 +- .../traits2.C => cpp2a/concepts-traits2.C} | 41 +- gcc/testsuite/g++.dg/cpp2a/concepts-ts1.C | 49 + gcc/testsuite/g++.dg/cpp2a/concepts-ts2.C | 260 ++ gcc/testsuite/g++.dg/cpp2a/concepts-ts3.C | 251 ++ gcc/testsuite/g++.dg/cpp2a/concepts-ts4.C | 34 + .../member-concept.C => cpp2a/concepts-ts5.C} | 4 +- gcc/testsuite/g++.dg/cpp2a/concepts-ts6.C | 72 + gcc/testsuite/g++.dg/cpp2a/concepts-using1.C | 17 + gcc/testsuite/g++.dg/cpp2a/concepts.C | 57 + gcc/testsuite/g++.dg/cpp2a/concepts1.C | 27 + gcc/testsuite/g++.dg/cpp2a/concepts2.C | 69 + gcc/testsuite/g++.dg/cpp2a/concepts3.C | 48 + gcc/testsuite/g++.dg/cpp2a/concepts4.C | 10 + gcc/testsuite/g++.dg/cpp2a/cond-triv2.C | 30 + gcc/testsuite/lib/g++-dg.exp | 2 +- gcc/testsuite/lib/target-supports.exp | 2 +- gcc/timevar.def | 5 +- 326 files changed, 9549 insertions(+), 4825 deletions(-) delete mode 100644 gcc/testsuite/g++.dg/concepts/alias1.C delete mode 100644 gcc/testsuite/g++.dg/concepts/alias2.C delete mode 100644 gcc/testsuite/g++.dg/concepts/alias3.C delete mode 100644 gcc/testsuite/g++.dg/concepts/alias4.C delete mode 100644 gcc/testsuite/g++.dg/concepts/class.C delete mode 100644 gcc/testsuite/g++.dg/concepts/class1.C delete mode 100644 gcc/testsuite/g++.dg/concepts/class2.C delete mode 100644 gcc/testsuite/g++.dg/concepts/class3.C delete mode 100644 gcc/testsuite/g++.dg/concepts/class4.C delete mode 100644 gcc/testsuite/g++.dg/concepts/disjunction1.C delete mode 100644 gcc/testsuite/g++.dg/concepts/explicit-inst4.C delete mode 100644 gcc/testsuite/g++.dg/concepts/explicit-spec3.C delete mode 100644 gcc/testsuite/g++.dg/concepts/feature-macro.C delete mode 100644 gcc/testsuite/g++.dg/concepts/iconv1.C delete mode 100644 gcc/testsuite/g++.dg/concepts/inherit-ctor4.C delete mode 100644 gcc/testsuite/g++.dg/concepts/memtmpl1.C delete mode 100644 gcc/testsuite/g++.dg/concepts/placeholder1.C delete mode 100644 gcc/testsuite/g++.dg/concepts/req-neg1.C delete mode 100644 gcc/testsuite/g++.dg/concepts/req1.C delete mode 100644 gcc/testsuite/g++.dg/concepts/req10.C delete mode 100644 gcc/testsuite/g++.dg/concepts/req11.C delete mode 100644 gcc/testsuite/g++.dg/concepts/req12.C delete mode 100644 gcc/testsuite/g++.dg/concepts/req13.C delete mode 100644 gcc/testsuite/g++.dg/concepts/req16.C delete mode 100644 gcc/testsuite/g++.dg/concepts/req18.C delete mode 100644 gcc/testsuite/g++.dg/concepts/req19.C delete mode 100644 gcc/testsuite/g++.dg/concepts/req2.C delete mode 100644 gcc/testsuite/g++.dg/concepts/req20.C delete mode 100644 gcc/testsuite/g++.dg/concepts/req3.C delete mode 100644 gcc/testsuite/g++.dg/concepts/req4.C delete mode 100644 gcc/testsuite/g++.dg/concepts/req5.C delete mode 100644 gcc/testsuite/g++.dg/concepts/req6.C delete mode 100644 gcc/testsuite/g++.dg/concepts/req7.C delete mode 100644 gcc/testsuite/g++.dg/concepts/req8.C delete mode 100644 gcc/testsuite/g++.dg/concepts/req9.C delete mode 100644 gcc/testsuite/g++.dg/concepts/template-parm1.C delete mode 100644 gcc/testsuite/g++.dg/concepts/template-parm10.C delete mode 100644 gcc/testsuite/g++.dg/concepts/template-parm5.C delete mode 100644 gcc/testsuite/g++.dg/concepts/template-parm6.C delete mode 100644 gcc/testsuite/g++.dg/concepts/template-parm7.C delete mode 100644 gcc/testsuite/g++.dg/concepts/template-parm9.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-access1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-alias.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-alias2.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-class.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-cmath.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-complete1.C rename gcc/testsuite/g++.dg/{concepts/constrained-parm.C => cpp2a/concepts-constrained-parm.C} (82%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-decltype.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-defarg1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-dep1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-dr1430.C rename gcc/testsuite/g++.dg/{concepts/explicit-inst1.C => cpp2a/concepts-explicit-inst1.C} (56%) rename gcc/testsuite/g++.dg/{concepts/explicit-inst2.C => cpp2a/concepts-explicit-inst2.C} (72%) rename gcc/testsuite/g++.dg/{concepts/explicit-inst3.C => cpp2a/concepts-explicit-inst3.C} (51%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-explicit-inst4.C rename gcc/testsuite/g++.dg/{concepts/explicit-spec1.C => cpp2a/concepts-explicit-spec1.C} (76%) rename gcc/testsuite/g++.dg/{concepts/explicit-spec2.C => cpp2a/concepts-explicit-spec2.C} (54%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec3.C rename gcc/testsuite/g++.dg/{concepts/explicit-spec4.C => cpp2a/concepts-explicit-spec4.C} (70%) rename gcc/testsuite/g++.dg/{concepts/explicit-spec5.C => cpp2a/concepts-explicit-spec5.C} (53%) rename gcc/testsuite/g++.dg/{concepts/explicit-spec6.C => cpp2a/concepts-explicit-spec6.C} (84%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-feature-macro.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-fn1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-fn2.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-fn3.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-fnparm1.C rename gcc/testsuite/g++.dg/{concepts/friend1.C => cpp2a/concepts-friend1.C} (71%) rename gcc/testsuite/g++.dg/{concepts/friend2.C => cpp2a/concepts-friend2.C} (66%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-friend3.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-iconv1.C rename gcc/testsuite/g++.dg/{concepts/inherit-ctor2.C => cpp2a/concepts-inherit-ctor2.C} (60%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor4.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor5.C rename gcc/testsuite/g++.dg/{concepts/lambda1.C => cpp2a/concepts-lambda1.C} (56%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-locations1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-member-concept.C rename gcc/testsuite/g++.dg/{concepts/memfun-err.C => cpp2a/concepts-memfun-err.C} (54%) rename gcc/testsuite/g++.dg/{concepts/memfun.C => cpp2a/concepts-memfun.C} (56%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-memtmpl1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-memtmpl2.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-nested1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-noexcept1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-p1141.C rename gcc/testsuite/g++.dg/{concepts/partial-spec.C => cpp2a/concepts-partial-spec.C} (84%) rename gcc/testsuite/g++.dg/{concepts/partial-spec2.C => cpp2a/concepts-partial-spec2.C} (67%) rename gcc/testsuite/g++.dg/{concepts/partial-spec3.C => cpp2a/concepts-partial-spec3.C} (82%) rename gcc/testsuite/g++.dg/{concepts/partial-spec4.C => cpp2a/concepts-partial-spec4.C} (87%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec5.C rename gcc/testsuite/g++.dg/{concepts/partial-spec6.C => cpp2a/concepts-partial-spec6.C} (72%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder1.C rename gcc/testsuite/g++.dg/{concepts/pr58500.C => cpp2a/concepts-pr58500.C} (100%) rename gcc/testsuite/g++.dg/{concepts/pr58534.C => cpp2a/concepts-pr58534.C} (100%) rename gcc/testsuite/g++.dg/{concepts/pr58535.C => cpp2a/concepts-pr58535.C} (100%) rename gcc/testsuite/g++.dg/{concepts/pr58536.C => cpp2a/concepts-pr58536.C} (100%) rename gcc/testsuite/g++.dg/{concepts/pr58548.C => cpp2a/concepts-pr58548.C} (100%) rename gcc/testsuite/g++.dg/{concepts/pr58549.C => cpp2a/concepts-pr58549.C} (100%) rename gcc/testsuite/g++.dg/{concepts/regress/alias-decl-42.C => cpp2a/concepts-pr59200.C} (75%) rename gcc/testsuite/g++.dg/{concepts/pr60052.C => cpp2a/concepts-pr60052.C} (100%) rename gcc/testsuite/g++.dg/{concepts/pr60053.C => cpp2a/concepts-pr60053.C} (100%) rename gcc/testsuite/g++.dg/{concepts/pr60064.C => cpp2a/concepts-pr60064.C} (100%) rename gcc/testsuite/g++.dg/{concepts/pr60065.C => cpp2a/concepts-pr60065.C} (100%) rename gcc/testsuite/g++.dg/{concepts/pr60377.C => cpp2a/concepts-pr60377.C} (100%) rename gcc/testsuite/g++.dg/{concepts/pr60390.C => cpp2a/concepts-pr60390.C} (100%) rename gcc/testsuite/g++.dg/{concepts/pr60391.C => cpp2a/concepts-pr60391.C} (100%) rename gcc/testsuite/g++.dg/{concepts/pr60573.C => cpp2a/concepts-pr60573.C} (100%) rename gcc/testsuite/g++.dg/{concepts/pr65552.C => cpp2a/concepts-pr65552.C} (63%) rename gcc/testsuite/g++.dg/{concepts/pr65575.C => cpp2a/concepts-pr65575.C} (63%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr65634.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr65636.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr65848.C rename gcc/testsuite/g++.dg/{concepts/pr65854.C => cpp2a/concepts-pr65854.C} (70%) rename gcc/testsuite/g++.dg/{concepts/pr66091.C => cpp2a/concepts-pr66091.C} (75%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr66844.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr66962.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67070.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67147.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67148.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67178.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67210.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67217.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-2.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-3.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-4.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-5.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67319.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67427.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67654.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67658.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67684.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67685.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67692.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67697.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67719.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67774.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67825.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67860.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67862.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr67969.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr68093-1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr68093-2.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr68372.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr68434.C rename gcc/testsuite/g++.dg/{concepts/pr68683.C => cpp2a/concepts-pr68683.C} (66%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr68812.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr69235.C rename gcc/testsuite/g++.dg/{concepts/pr71368.C => cpp2a/concepts-pr71368.C} (61%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr71385.C rename gcc/testsuite/g++.dg/{concepts/pr71965.C => cpp2a/concepts-pr71965.C} (77%) rename gcc/testsuite/g++.dg/{concepts/memfun2.C => cpp2a/concepts-pr72415.C} (94%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr78752.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr79759.C rename gcc/testsuite/g++.dg/{concepts/pr80471.C => cpp2a/concepts-pr80471.C} (100%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr80746.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr80773.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr82507.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr82740.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr84140.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr84551.C rename gcc/testsuite/g++.dg/{concepts/pr84979-2.C => cpp2a/concepts-pr84979-2.C} (100%) rename gcc/testsuite/g++.dg/{concepts/pr84979-3.C => cpp2a/concepts-pr84979-3.C} (100%) rename gcc/testsuite/g++.dg/{concepts/pr84979.C => cpp2a/concepts-pr84979.C} (100%) rename gcc/testsuite/g++.dg/{concepts/pr84980.C => cpp2a/concepts-pr84980.C} (65%) rename gcc/testsuite/g++.dg/{concepts/pr85265.C => cpp2a/concepts-pr85265.C} (70%) rename gcc/testsuite/g++.dg/{concepts/class-deduction2.C => cpp2a/concepts-pr85706.C} (100%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr85808.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr86269.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-pr87441.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-requires1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-requires10.C rename gcc/testsuite/g++.dg/{concepts/req17.C => cpp2a/concepts-requires11.C} (56%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-requires12.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-requires13.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-requires14.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-requires15.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-requires16.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-requires17.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-requires2.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-requires3.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-requires4.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-requires5.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-requires6.C rename gcc/testsuite/g++.dg/{concepts/req14.C => cpp2a/concepts-requires7.C} (52%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-requires8.C rename gcc/testsuite/g++.dg/{concepts/req15.C => cpp2a/concepts-requires9.C} (81%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-sfinae1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-template-parm1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-template-parm10.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-template-parm5.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C rename gcc/testsuite/g++.dg/{concepts/template-parm8.C => cpp2a/concepts-template-parm8.C} (50%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-template-parm9.C rename gcc/testsuite/g++.dg/{concepts/traits1.C => cpp2a/concepts-traits1.C} (60%) rename gcc/testsuite/g++.dg/{concepts/traits2.C => cpp2a/concepts-traits2.C} (59%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-ts1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-ts2.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-ts3.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-ts4.C rename gcc/testsuite/g++.dg/{concepts/member-concept.C => cpp2a/concepts-ts5.C} (78%) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-ts6.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-using1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts2.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts3.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts4.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/cond-triv2.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 330e13b2382..a02d2110c1a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,7 @@ +2019-10-08 Jason Merrill + + * doc/invoke.texi: Document -fconcepts-ts. + 2019-10-09 Richard Biener * tree-vect-loop.c (vect_is_simple_reduction): Simplify and diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 93077ff7c0e..3012f8b66fc 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,17 @@ +2019-10-08 Andrew Sutton + Jason Merrill + + Update the concepts implementation to conform to the C++20 + specification, improve compile times, and generally clean up + the implementation. + + * c-cppbuiltin.c (c_cpp_builtins): Use new feature test values for + concepts when -std=c++2a. Bump __cpp_concepts to 201907. + * c.opt: Add -Wconcepts-ts. + * c-opts.c (c_common_post_options): Warn when -fconcepts is used + with -std=c++2a. Disable warning for -fconcepts in C++20 mode. + (set_std_cxx2a): Enable concepts by default. + 2019-10-08 Joseph Myers * c-opts.c (c_common_post_options): Set diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c index 9e0ce428127..d9941e74e2e 100644 --- a/gcc/c-family/c-cppbuiltin.c +++ b/gcc/c-family/c-cppbuiltin.c @@ -992,7 +992,13 @@ c_cpp_builtins (cpp_reader *pfile) cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907"); } if (flag_concepts) - cpp_define (pfile, "__cpp_concepts=201507"); + { + if (cxx_dialect >= cxx2a) + /* FIXME: Update this to the value required by the IS. */ + cpp_define (pfile, "__cpp_concepts=201907"); + else + cpp_define (pfile, "__cpp_concepts=201507"); + } if (flag_tm) /* Use a value smaller than the 201505 specified in the TS, since we don't yet support atomic_cancel. */ diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c index 4ad24bd3ea0..0fffe60b140 100644 --- a/gcc/c-family/c-opts.c +++ b/gcc/c-family/c-opts.c @@ -1034,6 +1034,16 @@ c_common_post_options (const char **pfilename) if (warn_return_type == -1 && c_dialect_cxx ()) warn_return_type = 1; + /* C++2a is the final version of concepts. We still use -fconcepts + to know when concepts are enabled. Note that -fconcepts-ts can + be used to include additional features, although modified to + work with the standard. */ + if (cxx_dialect >= cxx2a) + flag_concepts = 1; + else if (flag_concepts) + /* For -std=c++17 -fconcepts, imply -fconcepts-ts. */ + flag_concepts_ts = 1; + if (num_in_fnames > 1) error ("too many filenames given; type %<%s %s%> for usage", progname, "--help"); @@ -1713,6 +1723,7 @@ set_std_cxx2a (int iso) flag_isoc94 = 1; flag_isoc99 = 1; flag_isoc11 = 1; + /* C++2a includes concepts. */ cxx_dialect = cxx2a; lang_hooks.name = "GNU C++17"; /* Pretend C++17 until standardization. */ } diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 88bbe2e2085..8f6867b855e 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -1427,6 +1427,10 @@ fconcepts C++ ObjC++ Var(flag_concepts) Enable support for C++ concepts. +fconcepts-ts +C++ ObjC++ Var(flag_concepts_ts) Init(0) +Enable certain features present in the Concepts TS. + fcond-mismatch C ObjC C++ ObjC++ Allow the arguments of the '?' operator to have different types. diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index d75ea5dc443..e633823ebae 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,461 @@ +2019-10-08 Andrew Sutton + Jason Merrill + + Update the concepts implementation to conform to the C++20 + specification, improve compile times, and generally clean up + the implementation. + * call.c (build_new_function_call): Don't evaluate concepts here. + (constraint_failure): Don't record the template. + (print_z_candidate): Don't extract the template. + * class.c (add_method): When overloading, hide ineligible special + member fns. + (check_methods): Set TYPE_HAS_COMPLEX_* here. + * constexpr.c (cxx_eval_constant_expression): Evaluate concepts. + (maybe_initialize_fundef_copies_table): Remove. + (get_fundef_copy): Use hash_map_safe_get_or_insert. + (clear_cv_and_fold_caches): Clear the satisfaction cache. + * constraint.cc (known_non_bool_p): New. + (parsing_constraint_expression_sentinel): Renamed from + expanding_constraint_sentinel. + (check_constraint_operands): New. + (check_constraint_atom): New. + (finish_constraint_binary_op): New. + (finish_constraint_or_expr): Likewise. + (finish_constraint_and_expr): Likewise. + (finish_constraint_primary_expr): Likewise. + (combine_constraint_expressions): New. + (finish_requires_expr): Add location parm. + (get_concept_definition): Return the initializer of concept definitions. + (get_template_head_requirements): New. + (get_trailing_function_requirements): New. + (deduce_constrained_parameter): Check if the identifier or template-id + is a concept definition. + (resolve_concept_definition_check): Removed. + (resolve_variable_concept_check): Removed. + (resolve_concept_check): New. + (resolve_constraint_check): Handle concept definitions. + converting arguments. + (function_concept_check_p): Removed. + (variable_concept_check_p): Removed. + (unpack_concept_check): New. + (get_concept_check_template): New. + (build_call_check): Moved and renamed to build_function_check. + (build_concept_check_arguments): make static. + (build_function_check): Always do overload resolution + in order to force conversion of template arguments (i.e., actually + check that the use of a concept is valid). + (build_standard_check): Renamed from build_real_concept_check. + (build_real_concept_check): Build checks for C++2a concepts by + (build_wildcard_concept_check): New. + (build_concept_check): Use build_real_concept_check. New overload. + (build_constraints): Save expressions, not normalized constraints. + (build_concept_id): New. Pass tf_warning_or_error. + (build_type_constraint): New. + (finish_type_constraints): New. + (associate_classtype_constraints): Also add constraints to union + types. Note the original declaration in errors. Don't return + error_mark_node in order to avoid an assertion later. + (push_down_pack_expansion): Remove. + (finish_shorthand_constraint): Make fold expressions, not naked + parameter packs. Always apply the constraint to each template argument. + (check_introduction_list): New. Fail if not enough + names are introduced. + (finish_template_introduction): Don't normalize constraints. Pass + tsubst flags. Check for insufficient introductions. + (placeholder_extract_concept_and_args): Handle the template-id case. + Unpack function concept checks correctly. + (tsubst_simple_requirement): Return errors if they occur. Don't + process as a template. + (tsubst_type_requirement): Likewise. + (type_deducible_p): New. Wrap the input expression in parens for the + purpose of deduction. + (expression_convertible_t): New. + (tsubst_compound_requirement): Use new deduction, conversion predicates. + (tsubst_nested_requirement): Return errors if they occur. Don't + process as a template. Instantiate and evaluate the nested requirement. + (tsubst_valid_expression_requirement): New. + (tsubst_simple_requirement): Use tsubst_valid_expression_requirement. + (tsubst_compound_requirement): Use tsubst_valid_expression_requirement. + (check_constaint_variables): New. + (tsubst_constraint_variables): Check that type substitutions are valid. + (tsubst_requires_expr): Likewise. Produce new requires-exprs during + template substitution. Copy the previous local specialization stack, + so references to non-local parameters can be found. Use cp_unevaluated. + (tsubst_constraint): New. Don't evaluate concept checks. + (subst_info): New. + (norm_info): New. Used to build a normalization tree for concept check + diagnostics. + (debug_parameter_mapping): New. + (debug_argument_list): New. + (expand_concept): Removed. + (normalize_logical_operation): Pass subst_info through call. + (normalize_pack_expansion): Remove. + (normalize_simple_requirement): Removed + (normalize_type_requirement): Removed + (normalize_compound_requirement): Removed + (normalize_nested_requirement): Removed + (normalize_requirement): Removed + (normalize_requirements): Removed + (normalize_requires_expression): Removed + (normalize_variable_concept_check): Removed. + (normalize_function_concept_check): Removed. + (normalize_concept_check): Merged all normalize_*_check here. + Substitute through written template arguments before normalizing the + definition. Only substitute the innermost template arguments. + (check_for_logical_overloads): Delete. + (map_arguments): New. Associate template parameters with arguments. + (build_parameter_mapping): New. Extract used parameters. + (normalize_expression): Rewrite. + (normalize_conjunction): Removed + (normalize_disjunction): Removed + (normalize_predicate_constraint): Removed + (normalize_parameterized_constraint): Removed + (normalized_map): New variable. + (get_normalized_constraints): New entry point for normalization. + Establishes a timer. + (get_normalized_constraints_from_info): New. + (get_normalized_constraints_from_decl): New. Turn on template processing + prior to normalization. Handle inheriting ctors. Build the + normalization arguments from the full set of template parameters of the + most general template. This guarantees that we have no concrete arguments + in the parameter mapping (e.g., from template members of class + templates). Cache normalizations. + (normalize_concept_definition): New. Cache normalizations. + (normalize_template_requirements): New. + (normalize_nontemplate_requirements): New. + (normalize_constraint_expression): New. + (tsubst_parameter_mapping): New. + (get_mapped_args): New. + (parameter_mapping_equivalent_p): New. Use template_args_equal. + (atomic_constraints_identical_p): New. + (hash_atomic_constraint): New. + (satisfying_constraint_p): New. Guard against recursive evaluation of + constraints during satisfaction. + (satisfy_conjunction): New. + (satisfy_disjunction): New. + (sat_entry): New class for hashing satisfaction results. + (sat_hasher): New hash traits. + (sat_cache): New. + (get_satisfaction): New. Returns cached satisfaction result. + (save_satisfaction): New. Caches a satisfaction result. + (clear_satisfaction_cache): New. + (satisfaction_cache): New. Helps manage satisfaction cache requests. + (decl_satisfied_cache): New. + (satisfy_atom): New. + (satisfy_constraint_r): New. + (satisfy_constraint): Use new satisfaction algorithm. + (evaluate_concept_check): New. + (evaluate_concept): Removed. + (evaluate_function_concept): Removed. + (evaluate_variable_concept): Removed. + (satisfy_constraint_expression): New. + (constraint_expression_satisfied_p): New. + (constraints_satisfied_p): Use strip_inheriting_ctors. Use + push_/pop_access_scope. + (more_constrained): Normalize before calling out to subsumption. Allow + classes as arguments. + (strictly_subsumes): Allow non-templates as arguments. Accept a new + template argument. + (weakly_subsumes): New. + (at_least_as_constrained): Removed. + (diagnose_other_expression): Removed. + (diagnose_predicate_constraint): Removed. + (diagnose_pack_expansion): Removed. + (diagnose_check_constraint): Removed. + (diagnose_logical_constraint): Removed. + (diagnose_expression_constraint): Removed. + (diagnose_type_constraint): Removed. + (diagnose_implicit_conversion_constraint): Removed. + (diagnose_argument_deduction_constraint): Removed. + (diagnose_exception_constraint): Removed. + (diagnose_parameterized_constraint): Removed. + (diagnose_argument_deduction_constraint): Removed. + (diagnose_argument_deduction_constraint): Removed. + (diagnose_argument_deduction_constraint): Removed. + (diagnose_trait_expr): New. + (diagnose_requires_expr): New. + (diagnose_atomic_constraint): New. + (diagnose_valid_expression) Stop wrongly diagnosing valid expressions. + Don't substitute as if in template decls. This causes substitution + to generate expressions that aren't suitable for use with the noexcept + routines. + (diagnose_valid_type) Likewise. + (diagnose_compound_requirement) Actually emit diagnostics for + the causes of errors.Call force_paren_expr_uneval. + (diagnose_declaration_constraints): Turn on template processing to + suppress certain analyses. + * cp-objcp-common.c (cp_common_init_ts): Make concepts typed. + (cp_get_debug_type): Use hash_map_safe_*. + * cp-tree.h: New function declarations for semantic actions, other + facilities. Remove declaration no longer used or needed. Remove + unused _CONSTR macros. + (LANG_DECL_HAS_MIN): Add CONCEPT_DECL. + (template_info_decl_check): Factor macro check into an inline function. + (DECL_TEMPLATE_INFO): Use new check facility. + (finish_concept_definition): New. Don't invalid concept declarations + with invalid initializers. + (find_template_parameters): New. + (concept_definition_p): New. + (concept_check_p): New. + (variable_concept_check_p): New. + (force_paren_expr_uneval): New. + (ovl_iterator::using_p): A USING_DECL by itself was also + introduced by a using-declaration. + (struct tree_template_info): Use tree_base instead of + tree_common. Add tmpl and args fields. + (TI_TEMPLATE, TI_ARGS): Adjust. + (DECLTYPE_FOR_INIT_CAPTURE): Remove. + (CONSTR_CHECK, CONSTR_INFO, CONSTR_EXPR, CONSTR_CONTEXT): New. + (ATOMIC_CONSTR_MAP, TRAIT_EXPR_LOCATION): New. + (struct tree_trait_expr): Add locus field. + (enum tsubst_flags): Add tf_norm as a hint to generate normalization + context when diagnosing constraint failure. + * cp-tree.def: Remove unused _CONSTR nodes and rename PRED_CONSTR + to ATOMIC_CONSTR. + (CONCEPT_DECL): New. + * cxx-pretty-print.c: Remove constraint printing code. + (pp_cxx_concept_definition): New. + (pp_cxx_template_declaration): Print concept definitions. + (pp_cxx_check_constraint): Update printing for concept definitions. + (pp_cxx_nested_name_specifier): Fix a weird + case where we're printing '::::' for concepts. + (simple_type_specifier): Print requirements for placeholder types. + (pp_cxx_constrained_type_spec): Print the associated requirements of + a placeholder type. + (pp_cxx_compound_requirement): Add space before the '->'. + (pp_cxx_parameter_mapping): Print the parameter mapping. + (pp_cxx_atomic_constraint): Use the function above. + * decl.c (redeclaration_error_message): New error for concepts. + (grokdeclarator): Check for and disallow decltype(auto) in parameter + declarations. + (grokfndecl): Don't normalize constraints. Add check for constraints + on declaration. + (grokvardecl): Don't normalize constraints. + (grok_special_member_properties): Don't set TYPE_HAS_COMPLEX_*. + (function_requirements_equivalent_p): New. Compare trailing + requires clauses. Compare combined constraints in pre-C++20 mode. + (decls_match): Compare trailing requires clauses. Compare template + heads for function templates. Remove old constraint comparison. + Simplify comparison of functions, function templates. + (duplicate_function_template_decls): New. Refactor a nasty if + condition into a single predicate. + (require_deduced_type): Don't complain if we already complained about + deduction failure. + (finish_function): Perform auto deduction to ensure that constraints + are checked even when functions contain no return statements. Only do + auto deduction if we haven't previously seen any return statements. + This prevents multiple diagnostics of the same error. + (store_decomp_type): Remove. + (cp_finish_decomp): Use hash_map_safe_put. + * error.c: Remove constraint printing code. + (dump_decl): Dump concept definitions. Handle wildcard declarations. + (dump_template_decl): Likewise. + (dump_type): Print associated requirements for placeholder + types. + (rebuild_concept_check): New. + (maybe_print_single_constraint_context): New. + (maybe_print_constraint_context): Recursively print nested contexts. + * init.c (get_nsdmi): Use hash_map_safe_*. + * lambda.c (maybe_add_lambda_conv_op): Bail if deduction failed. + (add_capture): Copy parameter packs from init. + (lambda_capture_field_type): Always use auto for init-capture. + * logic.cc: Completely rewrite. + (constraint_hash): New. + (clause/ctor): Save atoms in the hash table. + (replace): Save atoms during replacement. + (insert): Save atoms during insertion. + (contains): Only search the hash table for containment. + (clause): Keep a hash of atomic constraints. + (clause::clause): Explicitly copy the hash table when copying. + (disjunction_p, conjunction_p, atomic_p, dnf_size, cnf_size): New. + (diagnose_constraint_size): New. + (subsumes_constraints_nonnull): Compare the sizes of normalized formula + to determine the cheapest decomposition. + * name-lookup.c (diagnose_name_conflict): Diagnose name issues with + concepts. + (matching_fn_p): Check constraints. + (push_class_level_binding_1): Move overloaded functions case down, + accept FUNCTION_DECL as target_decl. + * parser.c (enum required_token): New required token for auto. + (make_location): Add overload taking lexer as last parm. + (cp_parser_required_error): Diagnose missing auto. + (cp_parser_diagnose_ungrouped_constraint_plain): New. + (cp_parser_diagnose_ungrouped_constraint_plain): New. + (cp_parser_constraint_primary_expression): New. Tentatively parse the + primary expression. If that fails tentatively parse a lower + precedence expression in order to diagnose the error. + (cp_parser_check_non_logical_constraint): New. Performs a trial + parse of the right-hand-side of non-logical operators in order to + generate good diagnostics. + (cp_parser_constraint_logical_and_expression): New. + (cp_parser_constraint_logical_or_expression): New. + (cp_parser_requires_clause_expression): New. + (cp_parser_requires_clause): Renamed to cp_parser_constraint_expression. + (cp_parser_requires_clause_opt): Parse the requires-clause differently + in -fconcepts and -std=c++2a modes. + (cp_parser_requirement_list): Rename to cp_parser_requirement_seq. + Rewrite so that semicolons are parsed + along with requirements, not the sequence. + (cp_parser_simple_requirement): Expect a semicolon at end. + (cp_parser_compound_requirement): Expect a semicolon at end. Only + allow trailing-return-type with -fconcepts-ts. + (cp_parser_nested_requirement): Expect a semicolon at end. Parse + constraint-expressions. + (cp_parser_concept_definition): New. Don't fail parsing the concept + definition if the initializer is ill-formed. Don't declare the concept + before parsing the initializer. + (cp_parser_constraint_expression): Declare earlier. + (cp_parser_type_requirement): Current scope is not valid. + (cp_parser_requires_expression): Commit to the tentative parse. + (cp_parser_decl_specifier_seq): Warn when concept appears to be used + as a decl-specifier. + (cp_parser_template_declaration_after_parameters): Parse concept + definitions. + (cp_parser_template_id): Don't try to resolve a concept template-id yet. + (cp_parser_template_id_expr): Resolve it as a concept check. + (cp_parser_decl_specifier_seq): Warn on 'concept bool'. + (cp_parser_type_parameter): Combine expressions not + constraints. + (cp_parser_explicit_template_declaration): Combine expressions not + constraints. + (cp_parser_maybe_concept_name): Removed. + (cp_parser_simple_type_specifier): Handle an error condition of + a bad constrained type specifier. Expect auto or decltype after + a concept name. Also handle the case where we have a template-id + as a concept check. + (cp_parser_template_introduction): Diagnose errors on invalid + introductions. Give up if it doesn't start with a concept name. + Pedwarn if not -fconcepts-ts. + (synthesize_implicit_template_parm): Don't do consistent binding. + Use a new flag for constrained parameters. Combine expressions, + not constraints. Fail if we get a placeholder in block scope. + Placeholders that do not constrain types are not allowed in parameter + declarations, so don't handle them. + (cp_parser_placeholder_type_specifier): New. Implement parsing of + placeholder type specifiers following a concept name or partial + concept check. Disallow decltype(auto) parameters. + (cp_parser_nested_name_specifier_opt): If the token is already + CPP_NESTED_NAME_SPECIFIER, leave it alone. + (cp_parser_id_expression, cp_parser_unqualified_id): Call + cp_parser_template_id_expr. + (cp_parser_placeholder_type_specifier): Add tentative parm. Don't + expect a WILDCARD_DECL. + (cp_parser_trait_expr): Pass trait_loc down. + (cp_parser_postfix_expression): Do set location of dependent member + call. + * pt.c (finish_concept_definition): New. + (push_template_decl_real): Handle concept definitions. + (start_concept_definition): Let push_template_decl_real handle the + creation of the template. + (get_constraints): Return null if the table hasn't been initialized. + (tsubst_copy_and_build): Build template-id expressions for concept + checks. + [TRAIT_EXPR]: Pass trait_loc down. + (lookup_template_class_1): Add the template name to the constraint + failure diagnostic. + (lookup_and_finish_template_variable): Build concept checks + with the correct arguments. + (tsubst_function_decl): Don't substitute through constraints. + Always associate constraints with functions. + (template_parm_level_and_index): Make non-static. + (for_each_template_parm_r): Handle requires expressions. + (keep_template_parm): New. + (find_template_parameters): New. + (more_specialized_fn): Change how winners and losers are chosen. + (make_constrained_auto): Don't normalize constraints. + (template_parameters_equivalent_p): New. Compare template + parameters. Add a comparison for implicitly vs. explicitly declared + parameters. + (template_parameter_lists_equivalent_p): New. Compare template + parameter lists. + (template_requirements_equivalent_p): New. + (template_heads_equivalent_p): New. Compare template heads. + (template_parameter_constraints_equivalent_p): New. + (is_compatible_template_arg): Use weakly_subsumes. + (maybe_new_partial_specialization): Use new constraint comparison + for finding specializations. + (process_partial_specialization): Pass main template as argument. + (more_specialized_partial_spec): Don't immediately return when + detecting a winner. + (make_constrained_auto): Handle concept definitions. + (do_auto_deduction): Update auto deduction for new concept model. + Extract the function concept correctly; rename constr to check to + reflect the kind of node. + (tsubst): Adjust wildcard argument during substitution. + [DECLTYPE_TYPE]: Remove init-capture handling. + (tsubst_copy_and_build): Build concept checks, not template ids. + Defer checks of function concepts. Handle concepts before variable + templates. Handle calls to function concepts explicitly. + (coerce_template_parms): Use concept_definition_p. Handle a deduction + error where a potentially empty pack can be supplied after the last + parameter of a concept. + (finish_template_variable): Don't process concepts here. + (instantiation_dependent_r): Use concept_check_p. + (tsubst_template_args): Make non-static. + (make_constrained_placeholder_type): New. Refactored from + make_constrained_auto. + (make_constrained_auto) Use make_constrained_placeholder_type. + (make_constrained_decltype_auto) New. + (tsubst_function_parms): New. + (value_dependent_expression_p) [TEMPLATE_ID_EXPR]: Use + concept_definition_p. + (push_access_scope, pop_access_scope): No longer static. + (tsubst_template_parm): Substitute TEMPLATE_PARM_CONSTRAINTS. + (tsubst_friend_function): Use tsubst_constraint. Use generic_targs_for. + (get_underlying_template) Use generic_targs_for. + (uses_parameter_packs): Return tree. + (gen_elem_of_pack_expansion_instantiation): Don't push + local_specialization_stack. + (prepend_one_capture): New. + (tsubst_lambda_expr): Use prepend_one_capture. Don't touch + local_specializations. + (template_parms_level_to_args): No longer static. + (add_outermost_template_args): Likewise. + (find_template_parameter_info): New. Provide context for finding + template parameters. + (keep_template_parm): Don't keep parameters declared at depth levels + greater than those of the template parameters of the source declaration. + Don't propagate cv-qualified types. Return 0, so we find all template + parameters, not the just first. + (any_template_parm_r): New. Handle cases that are mishandled by + for_each_template_parm_r. + (generic_targs_for): Factor out of coerce_template_args_for_ttp. + (tsubst_argument_pack): Factor out of tsubst_template_args. + (constraint_sat_entry): Removed. + (constraint_sat_hasher): Removed. + (concept_spec_entry): Removed. + (concept_spec_hasher): Removed. + (constraint_memos): Removed. + (concept_memos): Removed. + (lookup_constraint_satisfaction): Removed. + (memoize_constraint_satisfaction): Removed. + (lookup_concept_satisfaction): Removed. + (memoize_concept_satisfaction): Removed. + (concept_expansions): Removed. + (get_concept_expansion): Removed. + (save_concept_expansion): Removed. + (init_constraint_processing): Remove initialization of non-existing + resources. + (find_template_requirement): New. Search for the sub-requirement + within the associated constraints. + (convert_generic_types_to_packs): Also transform the associated + constraint and update the current template requirements. + (store_defaulted_ttp, lookup_defaulted_ttp): Remove. + (add_defaults_to_ttp): Use hash_map_safe_*. + * semantics.c (finish_call_expr): Diagnose calls to concepts. + Handle concept checks explicitly. + (finish_id_expression): Evaluate variable concepts as part of + id-expression processing. Don't treat variable concepts as variables, + and don't process function concepts as plain id-expressions. + (force_paren_expr): Add even_uneval parm. + (finish_trait_expr): Add location parm. + * tree.c (special_memfn_p): New. + (cp_expr_location): Handle TRAIT_EXPR. + * typeck.c (check_return_expr): Actually use the diagnostic kind + when performing return-type deduction. + * typeck2.c (build_functional_cast): Don't rely on the location of + 'auto'. + 2019-10-09 Paolo Carlini * decl.c (grok_ctor_properties): Use DECL_SOURCE_LOCATION. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 4ed424f3a6c..d36564d6527 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -717,24 +717,12 @@ inherited_ctor_rejection (void) return r; } -// Build a constraint failure record, saving information into the -// template_instantiation field of the rejection. If FN is not a template -// declaration, the TMPL member is the FN declaration and TARGS is empty. +/* Build a constraint failure record. */ static struct rejection_reason * -constraint_failure (tree fn) +constraint_failure (void) { struct rejection_reason *r = alloc_rejection (rr_constraint_failure); - if (tree ti = DECL_TEMPLATE_INFO (fn)) - { - r->u.template_instantiation.tmpl = TI_TEMPLATE (ti); - r->u.template_instantiation.targs = TI_ARGS (ti); - } - else - { - r->u.template_instantiation.tmpl = fn; - r->u.template_instantiation.targs = NULL_TREE; - } return r; } @@ -2251,10 +2239,9 @@ add_function_candidate (struct z_candidate **candidates, /* Second, for a function to be viable, its constraints must be satisfied. */ - if (flag_concepts && viable - && !constraints_satisfied_p (fn)) + if (flag_concepts && viable && !constraints_satisfied_p (fn)) { - reason = constraint_failure (fn); + reason = constraint_failure (); viable = false; } @@ -3729,11 +3716,7 @@ print_z_candidate (location_t loc, const char *msgstr, "class type is invalid"); break; case rr_constraint_failure: - { - tree tmpl = r->u.template_instantiation.tmpl; - tree args = r->u.template_instantiation.targs; - diagnose_constraints (cloc, tmpl, args); - } + diagnose_constraints (cloc, fn, NULL_TREE); break; case rr_inherited_ctor: inform (cloc, " an inherited constructor is not a candidate for " @@ -4532,25 +4515,7 @@ build_new_function_call (tree fn, vec **args, 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) - { - /* If overload resolution selects a specialization of a - function concept for non-dependent template arguments, - 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 && !processing_template_decl) - { - tree tmpl = DECL_TI_TEMPLATE (cand->fn); - tree targs = DECL_TI_ARGS (cand->fn); - tree decl = DECL_TEMPLATE_RESULT (tmpl); - if (DECL_DECLARED_CONCEPT_P (decl)) - return evaluate_function_concept (decl, targs); - } - - flags |= LOOKUP_EXPLICIT_TMPL_ARGS; - } + flags |= LOOKUP_EXPLICIT_TMPL_ARGS; result = build_over_call (cand, flags, complain); } @@ -10828,8 +10793,11 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn, return winner; } - // C++ Concepts - // or, if not that, F1 is more constrained than F2. + /* Concepts: ... or, if not that, F1 is more constrained than F2. + + FIXME: For function templates with no winner, this subsumption may + be computed a separate time. This needs to be validated, and if + so, the redundant check removed. */ if (flag_concepts && DECL_P (cand1->fn) && DECL_P (cand2->fn)) { winner = more_constrained (cand1->fn, cand2->fn); diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 4abcfaf4c1d..b6afdc487e7 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -7189,6 +7189,9 @@ finish_struct_1 (tree t) /* Finish debugging output for this type. */ rest_of_type_compilation (t, ! LOCAL_CLASS_P (t)); + /* Recalculate satisfaction that might depend on completeness. */ + clear_satisfaction_cache (); + if (TYPE_TRANSPARENT_AGGR (t)) { tree field = first_field (t); diff --git a/gcc/cp/config-lang.in b/gcc/cp/config-lang.in index 6e5f04677d6..adfe1b2415d 100644 --- a/gcc/cp/config-lang.in +++ b/gcc/cp/config-lang.in @@ -39,6 +39,7 @@ gtfiles="\ \$(srcdir)/c-family/c-common.c \$(srcdir)/c-family/c-format.c \ \$(srcdir)/c-family/c-cppbuiltin.c \$(srcdir)/c-family/c-pragma.c \ \$(srcdir)/cp/call.c \$(srcdir)/cp/class.c \$(srcdir)/cp/constexpr.c \ +\$(srcdir)/cp/constraint.cc \ \$(srcdir)/cp/cp-gimplify.c \ \$(srcdir)/cp/cp-lang.c \$(srcdir)/cp/cp-objcp-common.c \ \$(srcdir)/cp/decl.c \$(srcdir)/cp/decl2.c \ diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index f50cc04c293..ea502343e40 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -5523,7 +5523,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, '!requires (T t) { ... }' which is not transformed into a constraint. */ if (!processing_template_decl) - return evaluate_constraint_expression (t, NULL_TREE); + return satisfy_constraint_expression (t); else *non_constant_p = true; return t; @@ -5539,6 +5539,20 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, r = void_node; break; + case TEMPLATE_ID_EXPR: + { + /* We can evaluate template-id that refers to a concept only if + the template arguments are non-dependent. */ + if (!concept_definition_p (TREE_OPERAND (t, 0))) + internal_error ("unexpected template-id %qE", t); + + if (!processing_template_decl) + return satisfy_constraint_expression (t); + else + *non_constant_p = true; + return t; + } + case ASM_EXPR: if (!ctx->quiet) inline_asm_in_constexpr_error (cp_expr_loc_or_input_loc (t)); @@ -5980,13 +5994,15 @@ clear_cv_cache (void) cv_cache->empty (); } -/* Dispose of the whole CV_CACHE and FOLD_CACHE. */ +/* Dispose of the whole CV_CACHE, FOLD_CACHE, and satisfaction caches. */ void -clear_cv_and_fold_caches (void) +clear_cv_and_fold_caches (bool sat /*= true*/) { clear_cv_cache (); clear_fold_cache (); + if (sat) + clear_satisfaction_cache (); } /* Internal function handling expressions in templates for diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index c7a172cce4d..db4a81858f6 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -46,89 +46,189 @@ along with GCC; see the file COPYING3. If not see #include "toplev.h" #include "type-utils.h" +static tree satisfaction_value (tree t); + +/* When we're parsing or substuting a constraint expression, we have slightly + different expression semantics. In particular, we don't want to reduce a + concept-id to a satisfaction value. */ + +processing_constraint_expression_sentinel:: +processing_constraint_expression_sentinel () +{ + ++scope_chain->x_processing_constraint; +} + +processing_constraint_expression_sentinel:: +~processing_constraint_expression_sentinel () +{ + --scope_chain->x_processing_constraint; +} + +bool +processing_constraint_expression_p () +{ + return scope_chain->x_processing_constraint != 0; +} + /*--------------------------------------------------------------------------- - Operations on constraints + Constraint expressions ---------------------------------------------------------------------------*/ -/* Returns true if C is a constraint tree code. Note that ERROR_MARK - is a valid constraint. */ +/* Information provided to substitution. */ + +struct subst_info +{ + subst_info (tsubst_flags_t cmp, tree in) + : complain (cmp), in_decl (in) + { } + + /* True if we should not diagnose errors. */ + bool quiet() const + { + return complain == tf_none; + } + + /* True if we should diagnose errors. */ + bool noisy() const + { + return !quiet (); + } + + tsubst_flags_t complain; + tree in_decl; +}; + +/* True if T is known to be some type other than bool. Note that this + is false for dependent types and errors. */ static inline bool -constraint_p (tree_code c) +known_non_bool_p (tree t) { - return ((PRED_CONSTR <= c && c <= DISJ_CONSTR) - || c == EXPR_PACK_EXPANSION - || c == ERROR_MARK); + return (t && !WILDCARD_TYPE_P (t) && TREE_CODE (t) != BOOLEAN_TYPE); } -/* Returns true if T is a constraint. Note that error_mark_node - is a valid constraint. */ +static bool +check_constraint_atom (cp_expr expr) +{ + if (known_non_bool_p (TREE_TYPE (expr))) + { + error_at (expr.get_location (), + "constraint expression does not have type %"); + return false; + } -bool -constraint_p (tree t) + /* Check that we're using function concepts correctly. */ + if (concept_check_p (expr)) + { + tree id = unpack_concept_check (expr); + tree tmpl = TREE_OPERAND (id, 0); + if (OVL_P (tmpl) && TREE_CODE (expr) == TEMPLATE_ID_EXPR) + { + error_at (EXPR_LOC_OR_LOC (expr, input_location), + "function concept must be called"); + return false; + } + } + + return true; +} + +static bool +check_constraint_operands (location_t, cp_expr lhs, cp_expr rhs) { - return constraint_p (TREE_CODE (t)); + return check_constraint_atom (lhs) && check_constraint_atom (rhs); } -/* 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, +/* Validate the semantic properties of the constraint expression. */ + +static cp_expr +finish_constraint_binary_op (location_t loc, + tree_code code, + cp_expr lhs, + cp_expr rhs) +{ + gcc_assert (processing_constraint_expression_p ()); + if (lhs == error_mark_node || rhs == error_mark_node) + return error_mark_node; + if (!check_constraint_operands (loc, lhs, rhs)) + return error_mark_node; + tree overload; + tree expr = build_x_binary_op (loc, code, + lhs, TREE_CODE (lhs), + rhs, TREE_CODE (rhs), + &overload, tf_none); + /* When either operand is dependent, the overload set may be non-empty. */ + if (expr == error_mark_node) + return error_mark_node; + SET_EXPR_LOCATION (expr, loc); + return expr; +} - conjoin_constraints(a, NULL_TREE) == a +cp_expr +finish_constraint_or_expr (location_t loc, cp_expr lhs, cp_expr rhs) +{ + return finish_constraint_binary_op (loc, TRUTH_ORIF_EXPR, lhs, rhs); +} - and +cp_expr +finish_constraint_and_expr (location_t loc, cp_expr lhs, cp_expr rhs) +{ + return finish_constraint_binary_op (loc, TRUTH_ANDIF_EXPR, lhs, rhs); +} - conjoin_constraints (NULL_TREE, a) == a +cp_expr +finish_constraint_primary_expr (cp_expr expr) +{ + if (expr == error_mark_node) + return error_mark_node; + if (!check_constraint_atom (expr)) + return cp_expr (error_mark_node, expr.get_location ()); + return expr; +} - If both A and B are NULL_TREE, the result is also NULL_TREE. */ +/* Combine two constraint-expressions with a logical-and. */ 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 ? build_nt (CONJ_CONSTR, a, b) : a; - else if (b) - return b; - else - return NULL_TREE; +combine_constraint_expressions (tree lhs, tree rhs) +{ + processing_constraint_expression_sentinel pce; + if (!lhs) + return rhs; + if (!rhs) + return lhs; + return finish_constraint_and_expr (input_location, lhs, rhs); } -/* Transform the vector of expressions in the T into a conjunction - of requirements. T must be a TREE_VEC. */ +/* Extract the template-id from a concept check. For standard and variable + checks, this is simply T. For function concept checks, this is the + called function. */ tree -conjoin_constraints (tree t) +unpack_concept_check (tree t) { - gcc_assert (TREE_CODE (t) == TREE_VEC); - tree r = NULL_TREE; - for (int i = 0; i < TREE_VEC_LENGTH (t); ++i) - r = conjoin_constraints (r, TREE_VEC_ELT (t, i)); - return r; + gcc_assert (concept_check_p (t)); + + if (TREE_CODE (t) == CALL_EXPR) + t = CALL_EXPR_FN (t); + + gcc_assert (TREE_CODE (t) == TEMPLATE_ID_EXPR); + return t; } -/* Returns true if T is a call expression to a function - concept. */ +/* Extract the TEMPLATE_DECL from a concept check. */ -bool -function_concept_check_p (tree t) +tree +get_concept_check_template (tree t) { - gcc_assert (TREE_CODE (t) == CALL_EXPR); - tree fn = CALL_EXPR_FN (t); - if (fn != NULL_TREE - && TREE_CODE (fn) == TEMPLATE_ID_EXPR) - { - tree f1 = OVL_FIRST (TREE_OPERAND (fn, 0)); - if (TREE_CODE (f1) == TEMPLATE_DECL - && DECL_DECLARED_CONCEPT_P (DECL_TEMPLATE_RESULT (f1))) - return true; - } - return false; + tree id = unpack_concept_check (t); + tree tmpl = TREE_OPERAND (id, 0); + if (OVL_P (tmpl)) + tmpl = OVL_FIRST (tmpl); + return tmpl; } -/* Returns true if any of the arguments in the template - argument list is a wildcard or wildcard pack. */ +/* Returns true if any of the arguments in the template argument list is + a wildcard or wildcard pack. */ bool contains_wildcard_p (tree args) @@ -142,38 +242,6 @@ contains_wildcard_p (tree args) return false; } -/* Build a new call expression, but don't actually generate a - new function call. We just want the tree, not the semantics. */ - -inline tree -build_call_check (tree id) -{ - ++processing_template_decl; - vec *fargs = make_tree_vector(); - tree call = finish_call_expr (id, &fargs, false, false, tf_none); - release_tree_vector (fargs); - --processing_template_decl; - return call; -} - -/* Build an expression that will check a variable concept. If any - argument contains a wildcard, don't try to finish the variable - template because we can't substitute into a non-existent - declaration. */ - -tree -build_variable_check (tree id) -{ - gcc_assert (TREE_CODE (id) == TEMPLATE_ID_EXPR); - if (contains_wildcard_p (TREE_OPERAND (id, 1))) - return id; - - ++processing_template_decl; - tree var = finish_template_variable (id); - --processing_template_decl; - return var; -} - /*--------------------------------------------------------------------------- Resolution of qualified concept names ---------------------------------------------------------------------------*/ @@ -200,13 +268,12 @@ build_variable_check (tree id) the complete set of arguments substituted into the parameter list. */ static tree -resolve_constraint_check (tree ovl, tree args) +resolve_function_concept_overload (tree ovl, tree args) { int nerrs = 0; tree cands = NULL_TREE; for (lkp_iterator iter (ovl); iter; ++iter) { - // Get the next template overload. tree tmpl = *iter; if (TREE_CODE (tmpl) != TEMPLATE_DECL) continue; @@ -252,7 +319,7 @@ resolve_constraint_check (tree ovl, tree args) does not denote a constraint check, return NULL. */ tree -resolve_constraint_check (tree call) +resolve_function_concept_check (tree call) { gcc_assert (TREE_CODE (call) == CALL_EXPR); @@ -279,37 +346,40 @@ resolve_constraint_check (tree call) } tree args = TREE_OPERAND (target, 1); - return resolve_constraint_check (ovl, args); + return resolve_function_concept_overload (ovl, args); } -/* Returns a pair containing the checked variable concept - and its associated prototype parameter. The result - is a TREE_LIST whose TREE_VALUE is the variable concept - and whose TREE_PURPOSE is the prototype parameter. */ +/* Returns a pair containing the checked concept and its associated + prototype parameter. The result is a TREE_LIST whose TREE_VALUE + is the concept (non-template) and whose TREE_PURPOSE contains + the converted template arguments, including the deduced prototype + parameter (in position 0). */ tree -resolve_variable_concept_check (tree id) +resolve_concept_check (tree check) { + gcc_assert (concept_check_p (check)); + tree id = unpack_concept_check (check); tree tmpl = TREE_OPERAND (id, 0); - tree args = TREE_OPERAND (id, 1); - if (!variable_concept_p (tmpl)) - return NULL_TREE; + /* If this is an overloaded function concept, perform overload + resolution (this only happens when deducing prototype parameters + and template introductions). */ + if (TREE_CODE (tmpl) == OVERLOAD) + { + if (OVL_CHAIN (tmpl)) + return resolve_function_concept_check (check); + tmpl = OVL_FIRST (tmpl); + } - /* Make sure that we have the right parameters before - assuming that it works. Note that failing to deduce - will result in diagnostics. */ + tree args = TREE_OPERAND (id, 1); tree parms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl)); ++processing_template_decl; tree result = coerce_template_parms (parms, args, tmpl); --processing_template_decl; - if (result != error_mark_node) - { - tree decl = DECL_TEMPLATE_RESULT (tmpl); - return build_tree_list (result, decl); - } - else + if (result == error_mark_node) return error_mark_node; + return build_tree_list (result, DECL_TEMPLATE_RESULT (tmpl)); } /* Given a call expression or template-id expression to a concept EXPR @@ -321,14 +391,7 @@ resolve_variable_concept_check (tree id) bool deduce_constrained_parameter (tree expr, tree& check, tree& proto) { - tree info = NULL_TREE; - if (TREE_CODE (expr) == TEMPLATE_ID_EXPR) - info = resolve_variable_concept_check (expr); - else if (TREE_CODE (expr) == CALL_EXPR) - info = resolve_constraint_check (expr); - else - gcc_unreachable (); - + tree info = resolve_concept_check (expr); if (info && info != error_mark_node) { check = TREE_VALUE (info); @@ -338,6 +401,7 @@ deduce_constrained_parameter (tree expr, tree& check, tree& proto) proto = TREE_TYPE (arg); return true; } + check = proto = NULL_TREE; return false; } @@ -346,127 +410,39 @@ deduce_constrained_parameter (tree expr, tree& check, tree& proto) deduce the concept being checked and return the template arguments. Returns NULL_TREE if deduction fails. */ static tree -deduce_concept_introduction (tree expr) +deduce_concept_introduction (tree check) { - tree info = NULL_TREE; - if (TREE_CODE (expr) == TEMPLATE_ID_EXPR) - info = resolve_variable_concept_check (expr); - else if (TREE_CODE (expr) == CALL_EXPR) - info = resolve_constraint_check (expr); - else - gcc_unreachable (); - + tree info = resolve_concept_check (check); if (info && info != error_mark_node) return TREE_PURPOSE (info); return NULL_TREE; } -namespace { - -/*--------------------------------------------------------------------------- - Constraint implication learning ----------------------------------------------------------------------------*/ - -/* The implication context determines how we memoize concept checks. - Given two checks C1 and C2, the direction of implication depends - on whether we are learning implications of a conjunction or disjunction. - For example: - - template concept bool C = ...; - template concept bool D = C && true; - - From this, we can learn that D implies C. We cannot learn, - without further testing, that C does not imply D. If, for - example, C were defined as true, then these constraints would - be logically equivalent. - - In rare cases, we may start with a logical equivalence. For example: - - template concept bool C = ...; - template concept bool D = C; +/* Build a constrained placeholder type where SPEC is a type-constraint. + SPEC can be anything were concept_definition_p is true. - Here, we learn that C implies D and vice versa. */ - -enum implication_context -{ - conjunction_cxt, /* C1 implies C2. */ - disjunction_cxt, /* C2 implies C1. */ - equivalence_cxt /* C1 implies C2, C2 implies C1. */ -}; - -void learn_implications(tree, tree, implication_context); - -void -learn_implication (tree parent, tree child, implication_context cxt) -{ - switch (cxt) - { - case conjunction_cxt: - save_subsumption_result (parent, child, true); - break; - case disjunction_cxt: - save_subsumption_result (child, parent, true); - break; - case equivalence_cxt: - save_subsumption_result (parent, child, true); - save_subsumption_result (child, parent, true); - break; - } -} + If DECLTYPE_P is true, then the placeholder is decltype(auto). -void -learn_logical_operation (tree parent, tree constr, implication_context cxt) -{ - learn_implications (parent, TREE_OPERAND (constr, 0), cxt); - learn_implications (parent, TREE_OPERAND (constr, 1), cxt); -} + Returns a pair whose FIRST is the concept being checked and whose + SECOND is the prototype parameter. */ -void -learn_implications (tree parent, tree constr, implication_context cxt) +tree_pair +finish_type_constraints (tree spec, tree args, tsubst_flags_t complain) { - switch (TREE_CODE (constr)) - { - case CHECK_CONSTR: - return learn_implication (parent, constr, cxt); - - case CONJ_CONSTR: - if (cxt == disjunction_cxt) - return; - return learn_logical_operation (parent, constr, cxt); + gcc_assert (concept_definition_p (spec)); - case DISJ_CONSTR: - if (cxt == conjunction_cxt) - return; - return learn_logical_operation (parent, constr, cxt); - - default: - break; - } -} - -/* Quickly scan the top-level constraints of CONSTR to learn and - cache logical relations between concepts. The search does not - include conjunctions of disjunctions or vice versa. */ - -void -learn_implications (tree tmpl, tree args, tree constr) -{ - /* Don't memoize relations between non-dependent arguemnts. It's not - helpful. */ - if (!uses_template_parms (args)) - return; + /* Build an initial concept check. */ + tree check = build_type_constraint (spec, args, complain); + if (check == error_mark_node) + return std::make_pair (error_mark_node, NULL_TREE); - /* Build a check constraint for the purpose of caching. */ - tree parent = build_nt (CHECK_CONSTR, tmpl, args); + /* Extract the concept and prototype parameter from the check. */ + tree con; + tree proto; + if (!deduce_constrained_parameter (check, con, proto)) + return std::make_pair (error_mark_node, NULL_TREE); - /* Start learning based on the kind of the top-level contraint. */ - if (TREE_CODE (constr) == CONJ_CONSTR) - return learn_logical_operation (parent, constr, conjunction_cxt); - else if (TREE_CODE (constr) == DISJ_CONSTR) - return learn_logical_operation (parent, constr, disjunction_cxt); - else if (TREE_CODE (constr) == CHECK_CONSTR) - /* This is the rare concept alias case. */ - return learn_implication (parent, constr, equivalence_cxt); + return std::make_pair (con, proto); } /*--------------------------------------------------------------------------- @@ -475,7 +451,7 @@ learn_implications (tree tmpl, tree args, tree constr) /* Returns the expression of a function concept. */ -tree +static tree get_returned_expression (tree fn) { /* Extract the body of the function minus the return expression. */ @@ -492,594 +468,468 @@ get_returned_expression (tree fn) /* Returns the initializer of a variable concept. */ -tree +static tree get_variable_initializer (tree var) { tree init = DECL_INITIAL (var); if (!init) return error_mark_node; + if (BRACE_ENCLOSED_INITIALIZER_P (init) + && CONSTRUCTOR_NELTS (init) == 1) + init = CONSTRUCTOR_ELT (init, 0)->value; return init; } /* Returns the definition of a variable or function concept. */ -tree +static tree get_concept_definition (tree decl) { + if (TREE_CODE (decl) == OVERLOAD) + decl = OVL_FIRST (decl); + + if (TREE_CODE (decl) == TEMPLATE_DECL) + decl = DECL_TEMPLATE_RESULT (decl); + + if (TREE_CODE (decl) == CONCEPT_DECL) + return DECL_INITIAL (decl); if (VAR_P (decl)) return get_variable_initializer (decl); - else if (TREE_CODE (decl) == FUNCTION_DECL) + if (TREE_CODE (decl) == FUNCTION_DECL) return get_returned_expression (decl); gcc_unreachable (); } -int expansion_level = 0; +/*--------------------------------------------------------------------------- + Normalization of expressions + +This set of functions will transform an expression into a constraint +in a sequence of steps. +---------------------------------------------------------------------------*/ -class expanding_concept_sentinel +void +debug_parameter_mapping (tree map) { -public: - expanding_concept_sentinel () - { - ++expansion_level; - } + for (tree p = map; p; p = TREE_CHAIN (p)) + { + tree parm = TREE_VALUE (p); + tree arg = TREE_PURPOSE (p); + if (TYPE_P (parm)) + verbatim ("MAP %qD TO %qT", TEMPLATE_TYPE_DECL (parm), arg); + else + verbatim ("MAP %qD TO %qE", TEMPLATE_PARM_DECL (parm), arg); + // debug_tree (parm); + // debug_tree (arg); + } +} - ~expanding_concept_sentinel() - { - --expansion_level; - } -}; +void +debug_argument_list (tree args) +{ + for (int i = 0; i < TREE_VEC_LENGTH (args); ++i) + { + tree arg = TREE_VEC_ELT (args, i); + if (TYPE_P (arg)) + verbatim ("ARG %qT", arg); + else + verbatim ("ARG %qE", arg); + } +} +/* Associate each parameter in PARMS with its corresponding template + argument in ARGS. */ -} /* namespace */ +static tree +map_arguments (tree parms, tree args) +{ + for (tree p = parms; p; p = TREE_CHAIN (p)) + { + int level; + int index; + template_parm_level_and_index (TREE_VALUE (p), &level, &index); + TREE_PURPOSE (p) = TMPL_ARG (args, level, index); + } + return parms; +} -/* Returns true when a concept is being expanded. */ +/* Build the parameter mapping for EXPR using ARGS. */ -bool -expanding_concept() +static tree +build_parameter_mapping (tree expr, tree args, tree decl) { - return expansion_level > 0; + int depth = 0; + if (decl) + { + gcc_assert (TREE_CODE (decl) == TEMPLATE_DECL); + tree parms = DECL_TEMPLATE_PARMS (decl); + depth = TREE_INT_CST_LOW (TREE_PURPOSE (parms)); + } + tree parms = find_template_parameters (expr, depth); + tree map = map_arguments (parms, args); + return map; } -/* Expand a concept declaration (not a template) and its arguments to - a constraint defined by the concept's initializer or definition. */ +/* True if the parameter mappings of two atomic constraints are equivalent. */ -tree -expand_concept (tree decl, tree args) +static bool +parameter_mapping_equivalent_p (tree t1, tree t2) { - expanding_concept_sentinel sentinel; + tree map1 = ATOMIC_CONSTR_MAP (t1); + tree map2 = ATOMIC_CONSTR_MAP (t2); + while (map1 && map2) + { + tree arg1 = TREE_PURPOSE (map1); + tree arg2 = TREE_PURPOSE (map2); + if (!template_args_equal (arg1, arg2)) + return false; + map1 = TREE_CHAIN (map1); + map2 = TREE_CHAIN (map2); + } + return true; +} - if (TREE_CODE (decl) == TEMPLATE_DECL) - decl = DECL_TEMPLATE_RESULT (decl); - tree tmpl = DECL_TI_TEMPLATE (decl); +/* Provides additional context for normalization. */ - /* Check for a previous specialization. */ - if (tree spec = get_concept_expansion (tmpl, args)) - return spec; +struct norm_info : subst_info +{ + norm_info(tsubst_flags_t complain) + : subst_info (tf_warning_or_error | complain, NULL_TREE), + context() + {} - /* Substitute the arguments to form a new definition expression. */ - tree def = get_concept_definition (decl); + /* Construct a top-level context for DECL. */ - ++processing_template_decl; - tree result = tsubst_expr (def, args, tf_none, NULL_TREE, true); - --processing_template_decl; - if (result == error_mark_node) - return error_mark_node; + norm_info (tree in_decl, tsubst_flags_t complain) + : subst_info (tf_warning_or_error | complain, in_decl), + context (make_context (in_decl)) + {} - /* And lastly, normalize it, check for implications, and save - the specialization for later. */ - tree norm = normalize_expression (result); - learn_implications (tmpl, args, norm); - return save_concept_expansion (tmpl, args, norm); -} + bool generate_diagnostics() const + { + return complain & tf_norm; + } + tree make_context(tree in_decl) + { + if (generate_diagnostics ()) + return build_tree_list (NULL_TREE, in_decl); + return NULL_TREE; + } -/*--------------------------------------------------------------------------- - Stepwise normalization of expressions + void update_context(tree expr, tree args) + { + if (generate_diagnostics ()) + { + tree map = build_parameter_mapping (expr, args, in_decl); + context = tree_cons (map, expr, context); + } + in_decl = get_concept_check_template (expr); + } -This set of functions will transform an expression into a constraint -in a sequence of steps. Normalization does not not look into concept -definitions. ----------------------------------------------------------------------------*/ + /* Provides information about the source of a constraint. This is a + TREE_LIST whose VALUE is either a concept check or a constrained + declaration. The PURPOSE, for concept checks is a parameter mapping + for that check. */ -/* Transform a logical-or or logical-and expression into either - a conjunction or disjunction. */ + tree context; +}; -tree -normalize_logical_operation (tree t, tree_code c) -{ - tree t0 = normalize_expression (TREE_OPERAND (t, 0)); - tree t1 = normalize_expression (TREE_OPERAND (t, 1)); - return build_nt (c, t0, t1); -} +static tree normalize_expression (tree, tree, norm_info); -/* A simple requirement T introduces an expression constraint - for its expression. */ +/* Transform a logical-or or logical-and expression into either + a conjunction or disjunction. */ -inline tree -normalize_simple_requirement (tree t) +static tree +normalize_logical_operation (tree t, tree args, tree_code c, norm_info info) { - return build_nt (EXPR_CONSTR, TREE_OPERAND (t, 0)); -} + tree t0 = normalize_expression (TREE_OPERAND (t, 0), args, info); + tree t1 = normalize_expression (TREE_OPERAND (t, 1), args, info); -/* A type requirement T introduce a type constraint for its type. */ + /* Build a new info object for the constraint. */ + tree ci = info.generate_diagnostics() + ? build_tree_list (t, info.context) + : NULL_TREE; -inline tree -normalize_type_requirement (tree t) -{ - return build_nt (TYPE_CONSTR, TREE_OPERAND (t, 0)); + return build2 (c, ci, t0, t1); } -/* 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_compound_requirement (tree t) +static tree +normalize_concept_check (tree check, tree args, norm_info info) { - tree expr = TREE_OPERAND (t, 0); - tree constr = build_nt (EXPR_CONSTR, TREE_OPERAND (t, 0)); - - /* If a type is given, append an implicit conversion or - argument deduction constraint. */ - if (tree type = TREE_OPERAND (t, 1)) - { - tree type_constr; - /* TODO: We should be extracting a list of auto nodes - from type_uses_auto, not a single node */ - if (tree placeholder = type_uses_auto (type)) - type_constr = build_nt (DEDUCT_CONSTR, expr, type, placeholder); - else - type_constr = build_nt (ICONV_CONSTR, expr, type); - constr = conjoin_constraints (constr, type_constr); - } + tree id = unpack_concept_check (check); + tree tmpl = TREE_OPERAND (id, 0); + tree targs = TREE_OPERAND (id, 1); - /* If noexcept is present, append an exception constraint. */ - if (COMPOUND_REQ_NOEXCEPT_P (t)) + /* A function concept is wrapped in an overload. */ + if (TREE_CODE (tmpl) == OVERLOAD) { - tree except = build_nt (EXCEPT_CONSTR, expr); - constr = conjoin_constraints (constr, except); + /* TODO: Can we diagnose this error during parsing? */ + if (TREE_CODE (check) == TEMPLATE_ID_EXPR) + error_at (EXPR_LOC_OR_LOC (check, input_location), + "function concept must be called"); + tmpl = OVL_FIRST (tmpl); } - return constr; -} + /* Substitute through the arguments of the concept check. */ + targs = tsubst_template_args (targs, args, info.complain, info.in_decl); + if (targs == error_mark_node) + return error_mark_node; -/* A nested requirement T introduces a conjunction of constraints - corresponding to its constraint-expression. + /* Build the substitution for the concept definition. */ + tree parms = TREE_VALUE (DECL_TEMPLATE_PARMS (tmpl)); + /* Turn on template processing; coercing non-type template arguments + will automatically assume they're non-dependent. */ + ++processing_template_decl; + tree subst = coerce_template_parms (parms, targs, tmpl); + --processing_template_decl; + if (subst == error_mark_node) + return error_mark_node; - If the result of transforming T is error_mark_node, the resulting - constraint is a predicate constraint whose operand is also - error_mark_node. This preserves the constraint structure, but - will guarantee that the constraint is never satisfied. */ + /* The concept may have been ill-formed. */ + tree def = get_concept_definition (DECL_TEMPLATE_RESULT (tmpl)); + if (def == error_mark_node) + return error_mark_node; -inline tree -normalize_nested_requirement (tree t) -{ - return normalize_expression (TREE_OPERAND (t, 0)); + info.update_context (check, args); + return normalize_expression (def, subst, info); } -/* Transform a requirement T into one or more constraints. */ +/* The normal form of an atom depends on the expression. The normal + form of a function call to a function concept is a check constraint + for that concept. The normal form of a reference to a variable + concept is a check constraint for that concept. Otherwise, the + constraint is a predicate constraint. */ -tree -normalize_requirement (tree t) +static tree +normalize_atom (tree t, tree args, norm_info info) { - switch (TREE_CODE (t)) - { - case SIMPLE_REQ: - return normalize_simple_requirement (t); + /* Concept checks are not atomic. */ + if (concept_check_p (t)) + return normalize_concept_check (t, args, info); - case TYPE_REQ: - return normalize_type_requirement (t); + /* Build the parameter mapping for the atom. */ + tree map = build_parameter_mapping (t, args, info.in_decl); - case COMPOUND_REQ: - return normalize_compound_requirement (t); + /* Build a new info object for the atom. */ + tree ci = build_tree_list (t, info.context); - case NESTED_REQ: - return normalize_nested_requirement (t); + return build1 (ATOMIC_CONSTR, ci, map); +} + +/* Returns the normal form of an expression. */ +static tree +normalize_expression (tree t, tree args, norm_info info) +{ + if (!t) + return NULL_TREE; + + if (t == error_mark_node) + return error_mark_node; + + switch (TREE_CODE (t)) + { + case TRUTH_ANDIF_EXPR: + return normalize_logical_operation (t, args, CONJ_CONSTR, info); + case TRUTH_ORIF_EXPR: + return normalize_logical_operation (t, args, DISJ_CONSTR, info); default: - gcc_unreachable (); + return normalize_atom (t, args, info); } - return error_mark_node; } -/* Transform a sequence of requirements into a conjunction of - constraints. */ +/* Cache of the normalized form of constraints. Marked as deletable because it + can all be recalculated. */ +static GTY((deletable)) hash_map *normalized_map; -tree -normalize_requirements (tree t) +static tree +get_normalized_constraints (tree t, tree args, norm_info info) { - tree result = NULL_TREE; - for (; t; t = TREE_CHAIN (t)) - { - tree constr = normalize_requirement (TREE_VALUE (t)); - result = conjoin_constraints (result, constr); - } - return result; -} - -/* The normal form of a requires-expression is a parameterized - constraint having the same parameters and a conjunction of - constraints representing the normal form of requirements. */ - -tree -normalize_requires_expression (tree t) -{ - tree operand = normalize_requirements (TREE_OPERAND (t, 1)); - if (tree parms = TREE_OPERAND (t, 0)) - return build_nt (PARM_CONSTR, parms, operand); - else - return operand; + auto_timevar time (TV_CONSTRAINT_NORM); + return normalize_expression (t, args, info); } -/* For a template-id referring to a variable concept, returns - a check constraint. Otherwise, returns a predicate constraint. */ +/* Returns the normalized constraints from a constraint-info object + or NULL_TREE if the constraints are null. ARGS provide the initial + arguments for normalization and IN_DECL provides the declaration + to which the constraints belong. */ -tree -normalize_template_id_expression (tree t) +static tree +get_normalized_constraints_from_info (tree ci, tree args, tree in_decl, + bool diag = false) { - if (tree info = resolve_variable_concept_check (t)) - { - if (info == error_mark_node) - { - /* We get this when the template arguments don't match - the variable concept. */ - error ("invalid reference to concept %qE", t); - return error_mark_node; - } - - tree decl = TREE_VALUE (info); - tree args = TREE_PURPOSE (info); - return build_nt (CHECK_CONSTR, decl, args); - } - - /* Check that we didn't refer to a function concept like a variable. */ - tree fn = OVL_FIRST (TREE_OPERAND (t, 0)); - if (TREE_CODE (fn) == TEMPLATE_DECL - && DECL_DECLARED_CONCEPT_P (DECL_TEMPLATE_RESULT (fn))) - { - error_at (location_of (t), - "invalid reference to function concept %qD", fn); - return error_mark_node; - } - - return build_nt (PRED_CONSTR, t); -} - -/* For a call expression to a function concept, returns a check - constraint. Otherwise, returns a predicate constraint. */ + if (ci == NULL_TREE) + return NULL_TREE; -tree -normalize_call_expression (tree t) -{ - /* Try to resolve this function call as a concept. If not, then - it can be returned as a predicate constraint. */ - tree check = resolve_constraint_check (t); - if (!check) - return build_nt (PRED_CONSTR, t); - if (check == error_mark_node) - { - /* TODO: Improve diagnostics. We could report why the reference - is invalid. */ - error ("invalid reference to concept %qE", t); - return error_mark_node; - } + /* Substitution errors during normalization are fatal. */ + ++processing_template_decl; + norm_info info (in_decl, diag ? tf_norm : tf_none); + tree t = get_normalized_constraints (CI_ASSOCIATED_CONSTRAINTS (ci), + args, info); + --processing_template_decl; - tree fn = TREE_VALUE (check); - tree args = TREE_PURPOSE (check); - return build_nt (CHECK_CONSTR, fn, args); + return t; } -/* If T is a call to an overloaded && or || operator, diagnose that - as a non-SFINAEable error. Returns true if an error is emitted. - - TODO: It would be better to diagnose this at the point of definition, - if possible. Perhaps we should immediately do a first-pass normalization - of a concept definition to catch obvious non-dependent errors like - this. */ +/* Returns the normalized constraints for the declaration D. */ -bool -check_for_logical_overloads (tree t) +static tree +get_normalized_constraints_from_decl (tree d, bool diag = false) { - if (TREE_CODE (t) != CALL_EXPR) - return false; + tree tmpl; + tree decl; - tree fn = CALL_EXPR_FN (t); + /* For inherited constructors, consider the original declaration; + it has the correct template information attached. */ + d = strip_inheriting_ctors (d); - /* For member calls, try extracting the function from the - component ref. */ - if (TREE_CODE (fn) == COMPONENT_REF) + if (TREE_CODE (d) == TEMPLATE_DECL) { - fn = TREE_OPERAND (fn, 1); - if (TREE_CODE (fn) == BASELINK) - fn = BASELINK_FUNCTIONS (fn); + tmpl = d; + decl = DECL_TEMPLATE_RESULT (tmpl); } - - if (TREE_CODE (fn) != FUNCTION_DECL) - return false; - - if (DECL_OVERLOADED_OPERATOR_P (fn)) + else { - location_t loc = cp_expr_loc_or_input_loc (t); - error_at (loc, "constraint %qE, uses overloaded operator", t); - return true; + if (tree ti = DECL_TEMPLATE_INFO (d)) + tmpl = TI_TEMPLATE (ti); + else + tmpl = NULL_TREE; + decl = d; } - return false; -} + /* Get the most general template for the declaration, and compute + arguments from that. This ensures that the arguments used for + normalization are always template parameters and not arguments + used for outer specializations. For example: -/* The normal form of an atom depends on the expression. The normal - form of a function call to a function concept is a check constraint - for that concept. The normal form of a reference to a variable - concept is a check constraint for that concept. Otherwise, the - constraint is a predicate constraint. */ + template + struct S { + template requires C void f(U); + }; -tree -normalize_atom (tree t) -{ - /* We can get constraints pushed down through pack expansions, so - just return them. */ - if (constraint_p (t)) - return t; + S::f(0); - tree type = TREE_TYPE (t); - if (!type || type_unknown_p (t) || TREE_CODE (type) == TEMPLATE_TYPE_PARM) - ; - else if (!dependent_type_p (type)) - { - if (check_for_logical_overloads (t)) - return error_mark_node; + When we normalize the requirements for S::f, we want the + arguments to be {T, U}, not {int, U}. One reason for this is that + accepting the latter causes the template parameter level of U + to be reduced in a way that makes it overly difficult substitute + concrete arguments (i.e., eventually {int, int} during satisfaction. */ + if (tmpl) + { + if (DECL_LANG_SPECIFIC(tmpl) && !DECL_TEMPLATE_SPECIALIZATION (tmpl)) + tmpl = most_general_template (tmpl); + } - type = cv_unqualified (type); - if (!same_type_p (type, boolean_type_node)) - { - error ("predicate constraint %q+E does not have type %", t); - return error_mark_node; - } - } + /* If we're not diagnosing errors, use cached constraints, if any. */ + if (!diag) + if (tree *p = hash_map_safe_get (normalized_map, tmpl)) + return *p; - if (TREE_CODE (t) == TEMPLATE_ID_EXPR) - return normalize_template_id_expression (t); - if (TREE_CODE (t) == CALL_EXPR) - return normalize_call_expression (t); - return build_nt (PRED_CONSTR, t); -} + tree args = generic_targs_for (tmpl); + tree ci = get_constraints (decl); + tree norm = get_normalized_constraints_from_info (ci, args, tmpl, diag); -/* Push down the pack expansion EXP into the leaves of the constraint PAT. */ + if (!diag) + hash_map_safe_put (normalized_map, tmpl, norm); -tree -push_down_pack_expansion (tree exp, tree pat) -{ - switch (TREE_CODE (pat)) - { - case CONJ_CONSTR: - case DISJ_CONSTR: - { - pat = copy_node (pat); - TREE_OPERAND (pat, 0) - = push_down_pack_expansion (exp, TREE_OPERAND (pat, 0)); - TREE_OPERAND (pat, 1) - = push_down_pack_expansion (exp, TREE_OPERAND (pat, 1)); - return pat; - } - default: - { - exp = copy_node (exp); - SET_PACK_EXPANSION_PATTERN (exp, pat); - return exp; - } - } + return norm; } -/* Transform a pack expansion into a constraint. First we transform the - pattern of the pack expansion, then we push the pack expansion down into the - leaves of the constraint so that partial ordering will work. */ +/* Returns the normal form of TMPL's definition. */ -tree -normalize_pack_expansion (tree t) -{ - tree pat = normalize_expression (PACK_EXPANSION_PATTERN (t)); - return push_down_pack_expansion (t, pat); +static tree +normalize_concept_definition (tree tmpl) +{ + if (tree *p = hash_map_safe_get (normalized_map, tmpl)) + return *p; + gcc_assert (concept_definition_p (tmpl)); + if (OVL_P (tmpl)) + tmpl = OVL_FIRST (tmpl); + gcc_assert (TREE_CODE (tmpl) == TEMPLATE_DECL); + tree args = generic_targs_for (tmpl); + tree def = get_concept_definition (DECL_TEMPLATE_RESULT (tmpl)); + ++processing_template_decl; + norm_info info (tmpl, tf_none); + tree norm = get_normalized_constraints (def, args, info); + --processing_template_decl; + hash_map_safe_put (normalized_map, tmpl, norm); + return norm; } -/* Transform an expression into a constraint. */ - -tree -normalize_any_expression (tree t) -{ - switch (TREE_CODE (t)) - { - case TRUTH_ANDIF_EXPR: - return normalize_logical_operation (t, CONJ_CONSTR); - - case TRUTH_ORIF_EXPR: - return normalize_logical_operation (t, DISJ_CONSTR); - - case REQUIRES_EXPR: - return normalize_requires_expression (t); - - case BIND_EXPR: - return normalize_expression (BIND_EXPR_BODY (t)); - - case EXPR_PACK_EXPANSION: - return normalize_pack_expansion (t); - - default: - /* All other constraints are atomic. */ - return normalize_atom (t); - } -} +/* Returns the normal form of TMPL's requirements. */ -/* Transform a statement into an expression. */ -tree -normalize_any_statement (tree t) +static tree +normalize_template_requirements (tree tmpl, bool diag = false) { - switch (TREE_CODE (t)) - { - case RETURN_EXPR: - return normalize_expression (TREE_OPERAND (t, 0)); - default: - gcc_unreachable (); - } - return error_mark_node; + return get_normalized_constraints_from_decl (tmpl, diag); } -/* Reduction rules for the declaration T. */ +/* Returns the normal form of TMPL's requirements. */ -tree -normalize_any_declaration (tree t) +static tree +normalize_nontemplate_requirements (tree decl, bool diag = false) { - switch (TREE_CODE (t)) - { - case VAR_DECL: - return normalize_atom (t); - default: - gcc_unreachable (); - } - return error_mark_node; + return get_normalized_constraints_from_decl (decl, diag); } -/* Returns the normal form of a constraint expression. */ +/* Normalize an EXPR as a constraint. */ -tree -normalize_expression (tree t) +static tree +normalize_constraint_expression (tree expr, bool diag = false) { - if (!t) - return NULL_TREE; - - if (t == error_mark_node) - return error_mark_node; + if (!expr || expr == error_mark_node) + return expr; - switch (TREE_CODE_CLASS (TREE_CODE (t))) + /* For concept checks, use the supplied template arguments as those used + for normalization. Otherwise, there are no template arguments. */ + tree args; + if (concept_check_p (expr)) { - case tcc_unary: - case tcc_binary: - case tcc_expression: - case tcc_vl_exp: - return normalize_any_expression (t); - - case tcc_statement: - return normalize_any_statement (t); - - case tcc_declaration: - return normalize_any_declaration (t); - - case tcc_exceptional: - case tcc_constant: - case tcc_reference: - case tcc_comparison: - /* These are all atomic predicate constraints. */ - return normalize_atom (t); - - default: - /* Unhandled node kind. */ - gcc_unreachable (); + tree id = unpack_concept_check (expr); + args = TREE_OPERAND (id, 1); } - return error_mark_node; -} - - -/*--------------------------------------------------------------------------- - Constraint normalization ----------------------------------------------------------------------------*/ - -tree normalize_constraint (tree); - -/* 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 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. */ + else + args = NULL_TREE; -tree -normalize_predicate_constraint (tree t) -{ ++processing_template_decl; - tree expr = PRED_CONSTR_EXPR (t); - tree constr = normalize_expression (expr); + norm_info info (diag ? tf_norm : tf_none); + tree norm = get_normalized_constraints (expr, args, info); --processing_template_decl; - return constr; + return norm; } -/* The normal form of a parameterized constraint is the normal - form of its operand. */ +/* 17.4.1.2p2. Two constraints are identical if they are formed + from the same expression and the targets of the parameter mapping + are equivalent. */ -tree -normalize_parameterized_constraint (tree t) +bool +atomic_constraints_identical_p (tree t1, tree t2) { - tree parms = PARM_CONSTR_PARMS (t); - tree operand = normalize_constraint (PARM_CONSTR_OPERAND (t)); - return build_nt (PARM_CONSTR, parms, operand); -} + if (ATOMIC_CONSTR_EXPR (t1) != ATOMIC_CONSTR_EXPR (t2)) + return false; -/* Normalize the constraint T by reducing it so that it is - comprised of only conjunctions and disjunctions of atomic - constraints. */ + if (!parameter_mapping_equivalent_p (t1, t2)) + return false; -tree -normalize_constraint (tree t) -{ - if (!t) - return NULL_TREE; + return true; +} - if (t == error_mark_node) - return t; +hashval_t +hash_atomic_constraint (tree t) +{ + /* Hash the identity of the expression. */ + hashval_t val = htab_hash_pointer (ATOMIC_CONSTR_EXPR (t)); - switch (TREE_CODE (t)) + /* Hash the targets of the parameter map. */ + tree p = ATOMIC_CONSTR_MAP (t); + while (p) { - 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(); + val = iterative_hash_template_arg (TREE_PURPOSE (p), val); + p = TREE_CHAIN (p); } - return error_mark_node; -} - + return val; +} // -------------------------------------------------------------------------- // // Constraint Semantic Processing @@ -1093,7 +943,7 @@ current_template_constraints (void) { if (!current_template_parms) return NULL_TREE; - tree tmpl_constr = TEMPLATE_PARM_CONSTRAINTS (current_template_parms); + tree tmpl_constr = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms); return build_constraints (tmpl_constr, NULL_TREE); } @@ -1104,7 +954,7 @@ current_template_constraints (void) tree associate_classtype_constraints (tree type) { - if (!type || type == error_mark_node || TREE_CODE (type) != RECORD_TYPE) + if (!type || type == error_mark_node || !CLASS_TYPE_P (type)) return type; /* An explicit class template specialization has no template parameters. */ @@ -1124,9 +974,11 @@ associate_classtype_constraints (tree type) { if (!equivalent_constraints (ci, orig_ci)) { - // FIXME: Improve diagnostics. - error ("%qT does not match any declaration", type); - return error_mark_node; + error ("%qT does not match original declaration", type); + tree tmpl = CLASSTYPE_TI_TEMPLATE (type); + location_t loc = DECL_SOURCE_LOCATION (tmpl); + inform (loc, "original template declaration here"); + /* Fall through, so that we define the type anyway. */ } return type; } @@ -1135,17 +987,14 @@ associate_classtype_constraints (tree type) return type; } -namespace { +/* Create an empty constraint info block. */ -// Create an empty constraint info block. -inline tree_constraint_info* +static inline tree_constraint_info* build_constraint_info () { return (tree_constraint_info *)make_node (CONSTRAINT_INFO); } -} // namespace - /* Build a constraint-info object that contains the associated constraints of a declaration. This also includes the declaration's template requirements (TREQS) and any trailing requirements for a function @@ -1155,27 +1004,46 @@ build_constraint_info () this returns NULL_TREE, indicating an unconstrained declaration. */ tree -build_constraints (tree tmpl_reqs, tree decl_reqs) +build_constraints (tree tr, tree dr) { - gcc_assert (tmpl_reqs ? constraint_p (tmpl_reqs) : true); - gcc_assert (decl_reqs ? constraint_p (decl_reqs) : true); - - if (!tmpl_reqs && !decl_reqs) + if (!tr && !dr) return NULL_TREE; 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); + ci->template_reqs = tr; + ci->declarator_reqs = dr; + ci->associated_constr = combine_constraint_expressions (tr, dr); return (tree)ci; } -namespace { +/* Returns the template-head requires clause for the template + declaration T or NULL_TREE if none. */ + +tree +get_template_head_requirements (tree t) +{ + tree ci = get_constraints (t); + if (!ci) + return NULL_TREE; + return CI_TEMPLATE_REQS (ci); +} + +/* Returns the trailing requires clause of the declarator of + a template declaration T or NULL_TREE if none. */ + +tree +get_trailing_function_requirements (tree t) +{ + tree ci = get_constraints (t); + if (!ci) + return NULL_TREE; + return CI_DECLARATOR_REQS (ci); +} /* Construct a sequence of template arguments by prepending ARG to REST. Either ARG or REST may be null. */ -tree +static tree build_concept_check_arguments (tree arg, tree rest) { gcc_assert (rest ? TREE_CODE (rest) == TREE_VEC : true); @@ -1199,27 +1067,155 @@ build_concept_check_arguments (tree arg, tree rest) return args; } -} // namespace +/* Builds an id-expression of the form `C()` where C is a function + concept. */ + +static tree +build_function_check (tree tmpl, tree args, tsubst_flags_t /*complain*/) +{ + if (TREE_CODE (tmpl) == TEMPLATE_DECL) + { + /* If we just got a template, wrap it in an overload so it looks like any + other template-id. */ + tmpl = ovl_make (tmpl); + TREE_TYPE (tmpl) = boolean_type_node; + } + + /* Perform function concept resolution now so we always have a single + function of the overload set (even if we started with only one; the + resolution function converts template arguments). Note that we still + wrap this in an overload set so we don't upset other parts of the + compiler that expect template-ids referring to function concepts + to have an overload set. */ + tree info = resolve_function_concept_overload (tmpl, args); + if (info == error_mark_node) + return error_mark_node; + if (!info) + { + error ("no matching concepts for %qE", tmpl); + return error_mark_node; + } + args = TREE_PURPOSE (info); + tmpl = DECL_TI_TEMPLATE (TREE_VALUE (info)); + + /* Rebuild the singleton overload set; mark the type bool. */ + tmpl = ovl_make (tmpl, NULL_TREE); + TREE_TYPE (tmpl) = boolean_type_node; + + /* Build the id-expression around the overload set. */ + tree id = build2 (TEMPLATE_ID_EXPR, boolean_type_node, tmpl, args); + + /* Finally, build the call expression around the overload. */ + ++processing_template_decl; + vec *fargs = make_tree_vector (); + tree call = build_min_nt_call_vec (id, fargs); + release_tree_vector (fargs); + --processing_template_decl; + + return call; +} + +/* Builds an id-expression of the form `C` where C is a variable + concept. */ + +static tree +build_variable_check (tree tmpl, tree args, tsubst_flags_t complain) +{ + gcc_assert (variable_concept_p (tmpl)); + gcc_assert (TREE_CODE (tmpl) == TEMPLATE_DECL); + tree parms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl)); + args = coerce_template_parms (parms, args, tmpl, complain); + if (args == error_mark_node) + return error_mark_node; + return build2 (TEMPLATE_ID_EXPR, boolean_type_node, tmpl, args); +} + +/* Builds an id-expression of the form `C` where C is a standard + concept. */ + +static tree +build_standard_check (tree tmpl, tree args, tsubst_flags_t complain) +{ + gcc_assert (standard_concept_p (tmpl)); + gcc_assert (TREE_CODE (tmpl) == TEMPLATE_DECL); + tree parms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl)); + args = coerce_template_parms (parms, args, tmpl, complain); + if (args == error_mark_node) + return error_mark_node; + return build2 (TEMPLATE_ID_EXPR, boolean_type_node, tmpl, args); +} + +/* Construct an expression that checks TARGET using ARGS. */ -/* Construct an expression that checks the concept given by - TARGET. The TARGET must be: +tree +build_concept_check (tree target, tree args, tsubst_flags_t complain) +{ + return build_concept_check (target, NULL_TREE, args, complain); +} - - an OVERLOAD referring to one or more function concepts - - a BASELINK referring to an overload set of the above, or - - a TEMPLTATE_DECL referring to a variable concept. +/* Construct an expression that checks the concept given by DECL. If + concept_definition_p (DECL) is false, this returns null. */ - ARG and REST are the explicit template arguments for the - eventual concept check. */ tree -build_concept_check (tree target, tree arg, tree rest) +build_concept_check (tree decl, tree arg, tree rest, tsubst_flags_t complain) { + if (arg == NULL_TREE && rest == NULL_TREE) + { + tree id = build_nt (TEMPLATE_ID_EXPR, decl, rest); + error ("invalid use concept %qE", id); + return error_mark_node; + } + tree args = build_concept_check_arguments (arg, rest); - if (variable_template_p (target)) - return build_variable_check (lookup_template_variable (target, args)); - else - return build_call_check (lookup_template_function (target, args)); + + if (standard_concept_p (decl)) + return build_standard_check (decl, args, complain); + if (variable_concept_p (decl)) + return build_variable_check (decl, args, complain); + if (function_concept_p (decl)) + return build_function_check (decl, args, complain); + + return error_mark_node; +} + +/* Build a template-id that can participate in a concept check. */ + +static tree +build_concept_id (tree decl, tree args) +{ + tree check = build_concept_check (decl, args, tf_warning_or_error); + if (check == error_mark_node) + return error_mark_node; + return unpack_concept_check (check); +} + +/* Build a template-id that can participate in a concept check, preserving + the source location of the original template-id. */ + +tree +build_concept_id (tree expr) +{ + gcc_assert (TREE_CODE (expr) == TEMPLATE_ID_EXPR); + tree id = build_concept_id (TREE_OPERAND (expr, 0), TREE_OPERAND (expr, 1)); + protected_set_expr_location (id, cp_expr_location (expr)); + return id; } +/* Build as template-id with a placeholder that can be used as a + type constraint. + + Note that this will diagnose errors if the initial concept check + cannot be built. */ + +tree +build_type_constraint (tree decl, tree args, tsubst_flags_t complain) +{ + tree wildcard = build_nt (WILDCARD_DECL); + tree check = build_concept_check (decl, wildcard, args, complain); + if (check == error_mark_node) + return error_mark_node; + return unpack_concept_check (check); +} /* Returns a TYPE_DECL that contains sufficient information to build a template parameter of the same kind as PROTO and @@ -1240,17 +1236,13 @@ build_constrained_parameter (tree cnc, 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). - This will always return a CHECK_CONSTR. */ + 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) { @@ -1265,41 +1257,46 @@ finish_shorthand_constraint (tree decl, tree constr) tree con = CONSTRAINED_PARM_CONCEPT (constr); tree args = CONSTRAINED_PARM_EXTRA_ARGS (constr); - /* If the parameter declaration is variadic, but the concept - is not then we need to apply the concept to every element - in the pack. */ - bool is_proto_pack = template_parameter_pack_p (proto); - bool is_decl_pack = template_parameter_pack_p (decl); - bool apply_to_all_p = is_decl_pack && !is_proto_pack; + /* The TS lets use shorthand to constrain a pack of arguments, but the + standard does not. + + For the TS, consider: + + template struct s; + + If C is variadic (and because Ts is a pack), we associate the + constraint C. In all other cases, we associate + the constraint (C && ...). + + The standard behavior cannot be overridden by -fconcepts-ts. */ + bool variadic_concept_p = template_parameter_pack_p (proto); + bool declared_pack_p = template_parameter_pack_p (decl); + bool apply_to_each_p = (cxx_dialect >= cxx2a) ? true : !variadic_concept_p; /* Get the argument and overload used for the requirement and adjust it if we're going to expand later. */ tree arg = template_parm_to_arg (build_tree_list (NULL_TREE, decl)); - if (apply_to_all_p) + if (apply_to_each_p && declared_pack_p) arg = PACK_EXPANSION_PATTERN (TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg), 0)); - /* Build the concept check. If it the constraint needs to be - applied to all elements of the parameter pack, then make - the constraint an expansion. */ + /* Build the concept constraint-expression. */ tree tmpl = DECL_TI_TEMPLATE (con); - tree check = VAR_P (con) ? tmpl : ovl_make (tmpl); - check = build_concept_check (check, arg, args); + tree check = tmpl; + if (TREE_CODE (con) == FUNCTION_DECL) + check = ovl_make (tmpl); + check = build_concept_check (check, arg, args, tf_warning_or_error); - /* Make the check a pack expansion if needed. + /* Make the check a fold-expression if needed. */ + if (apply_to_each_p && declared_pack_p) + check = finish_left_unary_fold_expr (check, TRUTH_ANDIF_EXPR); - FIXME: We should be making a fold expression. */ - if (apply_to_all_p) - { - check = make_pack_expansion (check); - TREE_TYPE (check) = boolean_type_node; - } - - return normalize_expression (check); + return 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) { @@ -1309,94 +1306,219 @@ get_shorthand_constraints (tree parms) { tree parm = TREE_VEC_ELT (parms, i); tree constr = TEMPLATE_PARM_CONSTRAINTS (parm); - result = conjoin_constraints (result, constr); + result = combine_constraint_expressions (result, constr); } return result; } -// Returns and chains a new parameter for PARAMETER_LIST which will conform -// to the prototype given by SRC_PARM. The new parameter will have its -// identifier and location set according to IDENT and PARM_LOC respectively. +/* Get the deduced wildcard from a DEDUCED placeholder. If the deduced + wildcard is a pack, return the first argument of that pack. */ + static tree -process_introduction_parm (tree parameter_list, tree src_parm) +get_deduced_wildcard (tree wildcard) { - // If we have a pack, we should have a single pack argument which is the - // 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); + if (ARGUMENT_PACK_P (wildcard)) + wildcard = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (wildcard), 0); + gcc_assert (TREE_CODE (wildcard) == WILDCARD_DECL); + return wildcard; +} - // At this point we should have a wildcard, but we want to - // grab the associated decl from it. Also grab the stored - // identifier and location that should be chained to it in - // a PARM_DECL. - gcc_assert (TREE_CODE (src_parm) == WILDCARD_DECL); +/* Returns the prototype parameter for the nth deduced wildcard. */ - tree ident = DECL_NAME (src_parm); - location_t parm_loc = DECL_SOURCE_LOCATION (src_parm); +static tree +get_introduction_prototype (tree wildcards, int index) +{ + return TREE_TYPE (get_deduced_wildcard (TREE_VEC_ELT (wildcards, index))); +} - // If we expect a pack and the deduced template is not a pack, or if the - // template is using a pack and we didn't declare a pack, throw an error. - if (is_parameter_pack != WILDCARD_PACK_P (src_parm)) - { - 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); - } +/* Introduce a type template parameter. */ - src_parm = TREE_TYPE (src_parm); +static tree +introduce_type_template_parameter (tree wildcard, bool& non_type_p) +{ + non_type_p = false; + return finish_template_type_parm (class_type_node, DECL_NAME (wildcard)); +} + +/* Introduce a template template parameter. */ + +static tree +introduce_template_template_parameter (tree wildcard, bool& non_type_p) +{ + non_type_p = false; + begin_template_parm_list (); + current_template_parms = DECL_TEMPLATE_PARMS (TREE_TYPE (wildcard)); + end_template_parm_list (); + return finish_template_template_parm (class_type_node, DECL_NAME (wildcard)); +} + +/* Introduce a template non-type parameter. */ + +static tree +introduce_nontype_template_parameter (tree wildcard, bool& non_type_p) +{ + non_type_p = true; + tree parm = copy_decl (TREE_TYPE (wildcard)); + DECL_NAME (parm) = DECL_NAME (wildcard); + return parm; +} + +/* Introduce a single template parameter. */ + +static tree +build_introduced_template_parameter (tree wildcard, bool& non_type_p) +{ + tree proto = TREE_TYPE (wildcard); tree parm; - bool is_non_type; - if (TREE_CODE (src_parm) == TYPE_DECL) + if (TREE_CODE (proto) == TYPE_DECL) + parm = introduce_type_template_parameter (wildcard, non_type_p); + else if (TREE_CODE (proto) == TEMPLATE_DECL) + parm = introduce_template_template_parameter (wildcard, non_type_p); + else + parm = introduce_nontype_template_parameter (wildcard, non_type_p); + + /* Wrap in a TREE_LIST for process_template_parm. Note that introduced + parameters do not retain the defaults from the source parameter. */ + return build_tree_list (NULL_TREE, parm); +} + +/* Introduce a single template parameter. */ + +static tree +introduce_template_parameter (tree parms, tree wildcard) +{ + gcc_assert (!ARGUMENT_PACK_P (wildcard)); + tree proto = TREE_TYPE (wildcard); + location_t loc = DECL_SOURCE_LOCATION (wildcard); + + /* Diagnose the case where we have C{...Args}. */ + if (WILDCARD_PACK_P (wildcard)) { - is_non_type = false; - parm = finish_template_type_parm (class_type_node, ident); + tree id = DECL_NAME (wildcard); + error_at (loc, "%qE cannot be introduced with an ellipsis %<...%>", id); + inform (DECL_SOURCE_LOCATION (proto), "prototype declared here"); } - else if (TREE_CODE (src_parm) == TEMPLATE_DECL) + + bool non_type_p; + tree parm = build_introduced_template_parameter (wildcard, non_type_p); + return process_template_parm (parms, loc, parm, non_type_p, false); +} + +/* Introduce a template parameter pack. */ + +static tree +introduce_template_parameter_pack (tree parms, tree wildcard) +{ + bool non_type_p; + tree parm = build_introduced_template_parameter (wildcard, non_type_p); + location_t loc = DECL_SOURCE_LOCATION (wildcard); + return process_template_parm (parms, loc, parm, non_type_p, true); +} + +/* Introduce the nth template parameter. */ + +static tree +introduce_template_parameter (tree parms, tree wildcards, int& index) +{ + tree deduced = TREE_VEC_ELT (wildcards, index++); + return introduce_template_parameter (parms, deduced); +} + +/* Introduce either a template parameter pack or a list of template + parameters. */ + +static tree +introduce_template_parameters (tree parms, tree wildcards, int& index) +{ + /* If the prototype was a parameter, we better have deduced an + argument pack, and that argument must be the last deduced value + in the wildcard vector. */ + tree deduced = TREE_VEC_ELT (wildcards, index++); + gcc_assert (ARGUMENT_PACK_P (deduced)); + gcc_assert (index == TREE_VEC_LENGTH (wildcards)); + + /* Introduce each element in the pack. */ + tree args = ARGUMENT_PACK_ARGS (deduced); + for (int i = 0; i < TREE_VEC_LENGTH (args); ++i) { - is_non_type = false; - begin_template_parm_list (); - current_template_parms = DECL_TEMPLATE_PARMS (src_parm); - end_template_parm_list (); - parm = finish_template_template_parm (class_type_node, ident); + tree arg = TREE_VEC_ELT (args, i); + if (WILDCARD_PACK_P (arg)) + parms = introduce_template_parameter_pack (parms, arg); + else + parms = introduce_template_parameter (parms, arg); } + + return parms; +} + +/* Builds the template parameter list PARMS by chaining introduced + parameters from the WILDCARD vector. INDEX is the position of + the current parameter. */ + +static tree +process_introduction_parms (tree parms, tree wildcards, int& index) +{ + tree proto = get_introduction_prototype (wildcards, index); + if (template_parameter_pack_p (proto)) + return introduce_template_parameters (parms, wildcards, index); else - { - is_non_type = true; + return introduce_template_parameter (parms, wildcards, index); +} - // Since we don't have a declarator, so we can copy the source - // parameter and change the name and eventually the location. - parm = copy_decl (src_parm); - DECL_NAME (parm) = ident; - } +/* Ensure that all template parameters have been introduced for the concept + named in CHECK. If not, emit a diagnostic. - // Wrap in a TREE_LIST for process_template_parm. Introductions do not - // retain the defaults from the source template. - parm = build_tree_list (NULL_TREE, parm); + Note that implicitly introducing a parameter with a default argument + creates a case where a parameter is declared, but unnamed, making + it unusable in the definition. */ - return process_template_parm (parameter_list, parm_loc, parm, - is_non_type, is_parameter_pack); +static bool +check_introduction_list (tree intros, tree check) +{ + check = unpack_concept_check (check); + tree tmpl = TREE_OPERAND (check, 0); + if (OVL_P (tmpl)) + tmpl = OVL_FIRST (tmpl); + + tree parms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl); + if (TREE_VEC_LENGTH (intros) < TREE_VEC_LENGTH (parms)) + { + error_at (input_location, "all template parameters of %qD must " + "be introduced", tmpl); + return false; + } + + return true; } -/* Associates a constraint check to the current template based - on the introduction parameters. INTRO_LIST must be a TREE_VEC - of WILDCARD_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 a concept, C, this will form a check in the form of - C filling in any extra arguments needed by the - defaults deduced. +/* Associates a constraint check to the current template based on the + introduction parameters. INTRO_LIST must be a TREE_VEC of WILDCARD_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 a concept, C, this will form a check in the form of + C 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. */ - Returns NULL_TREE if no concept could be matched and - error_mark_node if an error occurred when matching. */ tree -finish_template_introduction (tree tmpl_decl, tree intro_list) +finish_template_introduction (tree tmpl_decl, + tree intro_list, + location_t intro_loc) { - /* Deduce the concept check. */ - tree expr = build_concept_check (tmpl_decl, NULL_TREE, intro_list); + /* Build a concept check to deduce the actual parameters. */ + tree expr = build_concept_check (tmpl_decl, intro_list, tf_none); if (expr == error_mark_node) - return NULL_TREE; + { + error_at (intro_loc, "cannot deduce template parameters from " + "introduction list"); + return error_mark_node; + } + + if (!check_introduction_list (intro_list, expr)) + return error_mark_node; tree parms = deduce_concept_introduction (expr); if (!parms) @@ -1406,9 +1528,15 @@ finish_template_introduction (tree tmpl_decl, tree intro_list) tree parm_list = NULL_TREE; begin_template_parm_list (); 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)); + for (int n = 0; n < nargs; ) + parm_list = process_introduction_parms (parm_list, parms, n); parm_list = end_template_parm_list (parm_list); + + /* Update the number of arguments to reflect the number of deduced + template parameter introductions. */ + nargs = TREE_VEC_LENGTH (parm_list); + + /* Determine if any errors occurred during matching. */ for (int i = 0; i < TREE_VEC_LENGTH (parm_list); ++i) if (TREE_VALUE (TREE_VEC_ELT (parm_list, i)) == error_mark_node) { @@ -1417,7 +1545,7 @@ finish_template_introduction (tree tmpl_decl, tree intro_list) } /* Build a concept check for our constraint. */ - tree check_args = make_tree_vec (TREE_VEC_LENGTH (parms)); + tree check_args = make_tree_vec (nargs); int n = 0; for (; n < TREE_VEC_LENGTH (parm_list); ++n) { @@ -1431,22 +1559,33 @@ finish_template_introduction (tree tmpl_decl, tree intro_list) for (; n < TREE_VEC_LENGTH (parms); ++n) TREE_VEC_ELT (check_args, n) = TREE_VEC_ELT (parms, n); - /* Associate the constraint. */ - tree check = build_concept_check (tmpl_decl, NULL_TREE, check_args); - tree constr = normalize_expression (check); - TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = constr; + /* Associate the constraint. */ + tree check = build_concept_check (tmpl_decl, + check_args, + tf_warning_or_error); + TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = check; return parm_list; } -/* Given the predicate constraint T from a constrained-type-specifier, extract +/* Given the concept check T from a constrained-type-specifier, extract its TMPL and ARGS. FIXME why do we need two different forms of constrained-type-specifier? */ void placeholder_extract_concept_and_args (tree t, tree &tmpl, tree &args) { + if (concept_check_p (t)) + { + t = unpack_concept_check (t); + tmpl = TREE_OPERAND (t, 0); + if (TREE_CODE (tmpl) == OVERLOAD) + tmpl = OVL_FIRST (tmpl); + args = TREE_OPERAND (t, 1); + return; + } + if (TREE_CODE (t) == TYPE_DECL) { /* A constrained parameter. Build a constraint check @@ -1457,20 +1596,10 @@ placeholder_extract_concept_and_args (tree t, tree &tmpl, tree &args) placeholder_extract_concept_and_args (check, tmpl, args); return; } - - if (TREE_CODE (t) == CHECK_CONSTR) - { - tree decl = CHECK_CONSTR_CONCEPT (t); - tmpl = DECL_TI_TEMPLATE (decl); - args = CHECK_CONSTR_ARGS (t); - return; - } - - gcc_unreachable (); } /* Returns true iff the placeholders C1 and C2 are equivalent. C1 - and C2 can be either CHECK_CONSTR or TEMPLATE_TYPE_PARM. */ + and C2 can be either TEMPLATE_TYPE_PARM or template-ids. */ bool equivalent_placeholder_constraints (tree c1, tree c2) @@ -1515,7 +1644,7 @@ equivalent_placeholder_constraints (tree c1, tree c2) return true; } -/* Return a hash value for the placeholder PRED_CONSTR C. */ +/* Return a hash value for the placeholder ATOMIC_CONSTR C. */ hashval_t hash_placeholder_constraint (tree c) @@ -1532,178 +1661,215 @@ hash_placeholder_constraint (tree c) return val; } -/*--------------------------------------------------------------------------- - Constraint substitution ----------------------------------------------------------------------------*/ +/* Substitute through the simple requirement. */ -/* The following functions implement substitution rules for constraints. - Substitution without checking constraints happens only in the - instantiation of class templates. For example: +static tree +tsubst_valid_expression_requirement (tree t, tree args, subst_info info) +{ + return tsubst_expr (t, args, info.complain, info.in_decl, false); +} - template struct S { - void f(T) requires C2; - void g(T) requires T::value; - }; - S s; // error instantiating S::g(T) +/* Substitute through the simple requirement. */ - When we instantiate S, we substitute into its member declarations, - including their constraints. However, those constraints are not - checked. Substituting int into C2 yields C2, and substituting - into T::value yields a substitution failure, making the program - ill-formed. +static tree +tsubst_simple_requirement (tree t, tree args, subst_info info) +{ + tree t0 = TREE_OPERAND (t, 0); + tree expr = tsubst_valid_expression_requirement (t0, args, info); + if (expr == error_mark_node) + return error_mark_node; + return finish_simple_requirement (EXPR_LOCATION (t), expr); +} - Note that we only ever substitute into the associated constraints - of a declaration. That is, substitution is defined only for predicate - constraints and conjunctions. */ +/* Substitute through the type requirement. */ -/* 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) +static tree +tsubst_type_requirement (tree t, tree args, subst_info info) { - 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); + tree t0 = TREE_OPERAND (t, 0); + tree type = tsubst (t0, args, info.complain, info.in_decl); + if (type == error_mark_node) + return error_mark_node; + return finish_type_requirement (EXPR_LOCATION (t), type); } -/* Substitute into a check constraint. */ +/* True if TYPE can be deduced from EXPR. -tree -tsubst_check_constraint (tree t, tree args, - tsubst_flags_t complain, tree in_decl) + FIXME: C++20 compound requirement constraints should be normalized and then + satisfied rather than substituted. */ + +static bool +type_deducible_p (tree expr, tree type, tree placeholder, tree args, + subst_info info) { - tree decl = CHECK_CONSTR_CONCEPT (t); - tree tmpl = DECL_TI_TEMPLATE (decl); - tree targs = CHECK_CONSTR_ARGS (t); + /* Make sure deduction is performed against ( EXPR ), so that + references are preserved in the result. */ + expr = force_paren_expr_uneval (expr); - /* Substitute through by building an template-id expression - and then substituting into that. */ - tree expr = build_nt (TEMPLATE_ID_EXPR, tmpl, targs); - ++processing_template_decl; - tree result = tsubst_expr (expr, args, complain, in_decl, false); - --processing_template_decl; + /* Replace the constraints with the instantiated constraints. This + substitutes args into any template parameters in the trailing + result type. */ + tree saved_constr = PLACEHOLDER_TYPE_CONSTRAINTS (placeholder); + PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) + = tsubst_constraint (saved_constr, + args, + info.complain | tf_partial, + info.in_decl); - if (result == error_mark_node) - return error_mark_node; + /* Temporarily unlink the canonical type. */ + tree saved_type = TYPE_CANONICAL (placeholder); + TYPE_CANONICAL (placeholder) = NULL_TREE; - /* Extract the results and rebuild the check constraint. */ - decl = DECL_TEMPLATE_RESULT (TREE_OPERAND (result, 0)); - args = TREE_OPERAND (result, 1); + tree deduced_type + = do_auto_deduction (type, + expr, + placeholder, + info.complain, + adc_requirement); + + PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) = saved_constr; + TYPE_CANONICAL (placeholder) = saved_type; + + if (deduced_type == error_mark_node) + return false; - return build_nt (CHECK_CONSTR, decl, args); + return true; } -/* Substitute into the conjunction of constraints. Returns - error_mark_node if substitution into either operand fails. */ +/* True if EXPR can not be converted to TYPE. */ -tree -tsubst_logical_operator (tree t, tree args, - tsubst_flags_t complain, tree in_decl) +static bool +expression_convertible_p (tree expr, tree type, subst_info info) { - tree t0 = TREE_OPERAND (t, 0); - tree r0 = tsubst_constraint (t0, args, complain, in_decl); - if (r0 == error_mark_node) - return error_mark_node; - tree t1 = TREE_OPERAND (t, 1); - tree r1 = tsubst_constraint (t1, args, complain, in_decl); - if (r1 == error_mark_node) - return error_mark_node; - return build_nt (TREE_CODE (t), r0, r1); + tree conv = + perform_direct_initialization_if_possible (type, expr, false, + info.complain); + if (conv == error_mark_node) + return false; + if (conv == NULL_TREE) + { + if (info.complain & tf_error) + { + location_t loc = EXPR_LOC_OR_LOC (expr, input_location); + error_at (loc, "cannot convert %qE to %qT", expr, type); + } + return false; + } + return true; } -namespace { -/* Substitute ARGS into the expression constraint T. */ +/* Substitute through the compound requirement. */ -tree -tsubst_expr_constr (tree t, tree args, tsubst_flags_t complain, tree in_decl) +static tree +tsubst_compound_requirement (tree t, tree args, subst_info info) { - cp_unevaluated guard; - tree expr = EXPR_CONSTR_EXPR (t); - tree ret = tsubst_expr (expr, args, complain, in_decl, false); - if (ret == error_mark_node) + tree t0 = TREE_OPERAND (t, 0); + tree t1 = TREE_OPERAND (t, 1); + tree expr = tsubst_valid_expression_requirement (t0, args, info); + if (expr == error_mark_node) return error_mark_node; - return build_nt (EXPR_CONSTR, ret); -} -/* Substitute ARGS into the type constraint T. */ + /* Check the noexcept condition. */ + bool noexcept_p = COMPOUND_REQ_NOEXCEPT_P (t); + if (noexcept_p && !expr_noexcept_p (expr, tf_none)) + return error_mark_node; -tree -tsubst_type_constr (tree t, tree args, tsubst_flags_t complain, tree in_decl) -{ - tree type = TYPE_CONSTR_TYPE (t); - tree ret = tsubst (type, args, complain, in_decl); - if (ret == error_mark_node) + /* Substitute through the type expression, if any. */ + tree type = tsubst (t1, args, info.complain, info.in_decl); + if (type == error_mark_node) return error_mark_node; - return build_nt (TYPE_CONSTR, ret); -} -/* Substitute ARGS into the implicit conversion constraint T. */ + /* Check expression against the result type. */ + if (type) + { + if (tree placeholder = type_uses_auto (type)) + { + if (!type_deducible_p (expr, type, placeholder, args, info)) + return error_mark_node; + } + else if (!expression_convertible_p (expr, type, info)) + return error_mark_node; + } -tree -tsubst_implicit_conversion_constr (tree t, tree args, tsubst_flags_t complain, - tree in_decl) -{ - cp_unevaluated guard; - tree expr = ICONV_CONSTR_EXPR (t); - tree type = ICONV_CONSTR_TYPE (t); - tree new_expr = tsubst_expr (expr, args, complain, in_decl, false); - if (new_expr == error_mark_node) - return error_mark_node; - tree new_type = tsubst (type, args, complain, in_decl); - if (new_type == error_mark_node) - return error_mark_node; - return build_nt (ICONV_CONSTR, new_expr, new_type); + return finish_compound_requirement (EXPR_LOCATION (t), + expr, type, noexcept_p); } -/* Substitute ARGS into the argument deduction constraint T. */ - -tree -tsubst_argument_deduction_constr (tree t, tree args, tsubst_flags_t complain, - tree in_decl) -{ - cp_unevaluated guard; - tree expr = DEDUCT_CONSTR_EXPR (t); - tree pattern = DEDUCT_CONSTR_PATTERN (t); - tree autos = DEDUCT_CONSTR_PLACEHOLDER(t); - tree new_expr = tsubst_expr (expr, args, complain, in_decl, false); - if (new_expr == error_mark_node) - return error_mark_node; - /* It seems like substituting through the pattern will not affect the - placeholders. We should (?) be able to reuse the existing list - without any problems. If not, then we probably want to create a - new list of placeholders and then instantiate the pattern using - those. */ - tree new_pattern = tsubst (pattern, args, complain, in_decl); - if (new_pattern == error_mark_node) +static tree +tsubst_nested_requirement (tree t, tree args, subst_info info) +{ + tree t0 = TREE_OPERAND (t, 0); + tree expr = tsubst_expr (t0, args, info.complain, info.in_decl, false); + if (expr == error_mark_node) return error_mark_node; - return build_nt (DEDUCT_CONSTR, new_expr, new_pattern, autos); + + /* Ensure that concrete results are satisfied. */ + if (!uses_template_parms (args)) + { + /* FIXME satisfy_constraint_expression (t0, args, info) */ + + /* [17.4.1.2] ... lvalue-to-value conversion is performed as necessary, + and EXPR shall be a constant expression of type bool. */ + tree result = force_rvalue (expr, tf_error); + if (result == error_mark_node) + return error_mark_node; + + /* FIXME: The expression must have boolean type. */ + if (cv_unqualified (TREE_TYPE (result)) != boolean_type_node) + return error_mark_node; + + /* Compute the value of the expression. */ + result = satisfaction_value (cxx_constant_value (result)); + if (result == error_mark_node || result == boolean_false_node) + return error_mark_node; + } + + return finish_nested_requirement (EXPR_LOCATION (t), expr); } -/* Substitute ARGS into the exception constraint T. */ +/* Substitute ARGS into the requirement T. */ -tree -tsubst_exception_constr (tree t, tree args, tsubst_flags_t complain, - tree in_decl) +static tree +tsubst_requirement (tree t, tree args, subst_info info) { - cp_unevaluated guard; - tree expr = EXCEPT_CONSTR_EXPR (t); - tree ret = tsubst_expr (expr, args, complain, in_decl, false); - if (ret == error_mark_node) - return error_mark_node; - return build_nt (EXCEPT_CONSTR, ret); + iloc_sentinel loc_s (cp_expr_location (t)); + switch (TREE_CODE (t)) + { + case SIMPLE_REQ: + return tsubst_simple_requirement (t, args, info); + case TYPE_REQ: + return tsubst_type_requirement (t, args, info); + case COMPOUND_REQ: + return tsubst_compound_requirement (t, args, info); + case NESTED_REQ: + return tsubst_nested_requirement (t, args, info); + default: + break; + } + gcc_unreachable (); } -/* 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. */ +/* Substitute ARGS into the list of requirements T. Note that + substitution failures here result in ill-formed programs. */ -tree +static tree +tsubst_requirement_body (tree t, tree args, subst_info info) +{ + tree result = NULL_TREE; + while (t) + { + tree req = tsubst_requirement (TREE_VALUE (t), args, info); + if (req == error_mark_node) + return error_mark_node; + result = tree_cons (NULL_TREE, req, result); + t = TREE_CHAIN (t); + } + return nreverse (result); +} + +static tree declare_constraint_vars (tree parms, tree vars) { tree s = vars; @@ -1723,6 +1889,24 @@ declare_constraint_vars (tree parms, tree vars) return vars; } +/* Substitute through as if checking function parameter types. This + will diagnose common parameter type errors. Returns error_mark_node + if an error occurred. */ + +static tree +check_constaint_variables (tree t, tree args, subst_info info) +{ + tree types = NULL_TREE; + tree p = t; + while (p && !VOID_TYPE_P (p)) + { + types = tree_cons (NULL_TREE, TREE_TYPE (p), types); + p = TREE_CHAIN (p); + } + types = chainon (nreverse (types), void_list_node); + return tsubst_function_parms (types, args, info.complain, info.in_decl); +} + /* A subroutine of tsubst_parameterized_constraint. Substitute ARGS into the parameter list T, producing a sequence of constraint variables, declared in the current scope. @@ -1731,171 +1915,81 @@ declare_constraint_vars (tree parms, tree vars) prior to calling this function since this substitution will declare the substituted parameters. */ -tree -tsubst_constraint_variables (tree t, tree args, - tsubst_flags_t complain, tree in_decl) +static tree +tsubst_constraint_variables (tree t, tree args, subst_info info) { + /* Perform a trial substitution to check for type errors. */ + tree parms = check_constaint_variables (t, args, info); + if (parms == error_mark_node) + return error_mark_node; + /* Clear cp_unevaluated_operand across tsubst so that we get a proper chain of PARM_DECLs. */ int saved_unevaluated_operand = cp_unevaluated_operand; cp_unevaluated_operand = 0; - tree vars = tsubst (t, args, complain, in_decl); + tree vars = tsubst (t, args, info.complain, info.in_decl); cp_unevaluated_operand = saved_unevaluated_operand; if (vars == error_mark_node) return error_mark_node; return declare_constraint_vars (t, vars); } -/* Substitute ARGS into the parameterized constraint T. */ +/* Substitute ARGS into the requires-expression T. [8.4.7]p6. The + substitution of template arguments into a requires-expression + may result in the formation of invalid types or expressions + in its requirements ... In such cases, the expression evaluates + to false; it does not cause the program to be ill-formed. -tree -tsubst_parameterized_constraint (tree t, tree args, - tsubst_flags_t complain, tree in_decl) -{ - local_specialization_stack stack; - tree vars = tsubst_constraint_variables (PARM_CONSTR_PARMS (t), - args, complain, in_decl); - if (vars == error_mark_node) - return error_mark_node; - tree expr = tsubst_constraint (PARM_CONSTR_OPERAND (t), args, - complain, in_decl); - if (expr == error_mark_node) - return error_mark_node; - return build_nt (PARM_CONSTR, vars, expr); -} - -/* 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. */ + However, there are cases where substitution must produce a + new requires-expression, that is not a template constraint. + For example: -inline tree -tsubst_simple_requirement (tree t, tree args, - tsubst_flags_t complain, tree in_decl) -{ - ++processing_template_decl; - tree expr = tsubst_expr (TREE_OPERAND (t, 0), args, complain, in_decl, false); - --processing_template_decl; - return finish_simple_requirement (expr); -} + template + class X { + template + static constexpr bool var = requires (U u) { T::fn(u); }; + }; -/* 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) -{ - ++processing_template_decl; - tree type = tsubst (TREE_OPERAND (t, 0), args, complain, in_decl); - --processing_template_decl; - return finish_type_requirement (type); -} - -/* 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. */ + In the instantiation of X (assuming Y defines fn), then the + instantiated requires-expression would include Y::fn(u). If any + substitution in the requires-expression fails, we can immediately + fold the expression to false, as would be the case e.g., when + instantiation X. */ tree -tsubst_compound_requirement (tree t, tree args, - tsubst_flags_t complain, tree in_decl) +tsubst_requires_expr (tree t, tree args, + tsubst_flags_t complain, tree in_decl) { - ++processing_template_decl; - tree expr = tsubst_expr (TREE_OPERAND (t, 0), args, complain, in_decl, false); - tree type = tsubst (TREE_OPERAND (t, 1), args, complain, in_decl); - --processing_template_decl; - bool noexcept_p = COMPOUND_REQ_NOEXCEPT_P (t); - return finish_compound_requirement (expr, type, noexcept_p); -} + local_specialization_stack stack (lss_copy); -/* Substitute ARGS into the nested requirement T. */ + subst_info info (complain, in_decl); -tree -tsubst_nested_requirement (tree t, tree args, - tsubst_flags_t complain, tree in_decl) -{ - ++processing_template_decl; - tree expr = tsubst_expr (TREE_OPERAND (t, 0), args, complain, in_decl, false); - --processing_template_decl; - return finish_nested_requirement (expr); -} - -/* Substitute ARGS into the requirement 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_requirement_body (tree t, tree args, - tsubst_flags_t complain, tree in_decl) -{ - 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); - } - /* Ensure that the order of constraints is the same as the original. */ - return nreverse (r); -} - -} /* 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_requires_expr (tree t, tree args, - tsubst_flags_t complain, tree in_decl) -{ - local_specialization_stack stack; + /* A requires-expression is an unevaluated context. */ + cp_unevaluated u; tree parms = TREE_OPERAND (t, 0); if (parms) { - parms = tsubst_constraint_variables (parms, args, complain, in_decl); + parms = tsubst_constraint_variables (parms, args, info); if (parms == error_mark_node) - return error_mark_node; + return boolean_false_node; } tree reqs = TREE_OPERAND (t, 1); - reqs = tsubst_requirement_body (reqs, args, complain, in_decl); + reqs = tsubst_requirement_body (reqs, args, info); if (reqs == error_mark_node) - return error_mark_node; + return boolean_false_node; + + /* In certain cases, produce a new requires-expression. + Otherwise the value of the expression is true. */ + if (processing_template_decl && uses_template_parms (args)) + return finish_requires_expr (cp_expr_location (t), parms, reqs); - return finish_requires_expr (parms, reqs); + return boolean_true_node; } /* Substitute ARGS into the constraint information CI, producing a new - constraint record. */ + constraint record. */ tree tsubst_constraint_info (tree t, tree args, @@ -1904,378 +1998,418 @@ tsubst_constraint_info (tree t, tree args, 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 tr = tsubst_constraint (CI_TEMPLATE_REQS (t), args, complain, in_decl); + tree dr = tsubst_constraint (CI_DECLARATOR_REQS (t), args, complain, in_decl); + return build_constraints (tr, dr); +} - tree decl_constr = NULL_TREE; - if (tree r = CI_DECLARATOR_REQS (t)) - decl_constr = tsubst_constraint (r, args, complain, in_decl); +/* Substitute through a parameter mapping, in order to get the actual + arguments used to instantiate an atomic constraint. This may fail + if the substitution into arguments produces something ill-formed. */ - return build_constraints (tmpl_constr, decl_constr); -} +static tree +tsubst_parameter_mapping (tree map, tree args, subst_info info) +{ + if (!map) + return NULL_TREE; + + tsubst_flags_t complain = info.complain; + tree in_decl = info.in_decl; -/* Substitute ARGS into the constraint T. */ + tree result = NULL_TREE; + for (tree p = map; p; p = TREE_CHAIN (p)) + { + if (p == error_mark_node) + return error_mark_node; + tree parm = TREE_VALUE (p); + tree arg = TREE_PURPOSE (p); + tree new_arg = NULL_TREE; + if (TYPE_P (arg)) + { + /* If a template parameter is declared with a placeholder, we can + get those in the argument list if decltype is applied to the + placeholder. For example: + + template + requires C + void f() { } + + The normalized argument for C will be an auto type, so we'll + need to deduce the actual argument from the corresponding + initializer (whatever argument is provided for T), and use + that result in the instantiated parameter mapping. */ + if (tree auto_node = type_uses_auto (arg)) + { + int level; + int index; + template_parm_level_and_index (parm, &level, &index); + tree init = TMPL_ARG (args, level, index); + new_arg = do_auto_deduction (arg, init, auto_node, + complain, adc_variable_type, + make_tree_vec (0)); + } + } + else if (ARGUMENT_PACK_P (arg)) + new_arg = tsubst_argument_pack (arg, args, complain, in_decl); + if (!new_arg) + new_arg = tsubst_template_arg (arg, args, complain, in_decl); + if (new_arg == error_mark_node) + return error_mark_node; + + result = tree_cons (new_arg, parm, result); + } + return nreverse (result); +} tree -tsubst_constraint (tree t, tree args, tsubst_flags_t complain, tree in_decl) +tsubst_parameter_mapping (tree map, tree args, tsubst_flags_t complain, tree in_decl) { - if (t == NULL_TREE || t == error_mark_node) - return t; - switch (TREE_CODE (t)) - { - case PRED_CONSTR: - return tsubst_predicate_constraint (t, args, complain, in_decl); - case CHECK_CONSTR: - return tsubst_check_constraint (t, args, complain, in_decl); - case CONJ_CONSTR: - case DISJ_CONSTR: - return tsubst_logical_operator (t, args, complain, in_decl); - case PARM_CONSTR: - return tsubst_parameterized_constraint (t, args, complain, in_decl); - case EXPR_CONSTR: - return tsubst_expr_constr (t, args, complain, in_decl); - case TYPE_CONSTR: - return tsubst_type_constr (t, args, complain, in_decl); - case ICONV_CONSTR: - return tsubst_implicit_conversion_constr (t, args, complain, in_decl); - case DEDUCT_CONSTR: - return tsubst_argument_deduction_constr (t, args, complain, in_decl); - case EXCEPT_CONSTR: - return tsubst_exception_constr (t, args, complain, in_decl); - default: - gcc_unreachable (); - } - return error_mark_node; + return tsubst_parameter_mapping (map, args, subst_info (complain, in_decl)); } /*--------------------------------------------------------------------------- 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 satisfy_constraint_1 (tree, tree, tsubst_flags_t, tree); +/* Hash functions for satisfaction entries. */ -/* Check the constraint pack expansion. */ - -tree -satisfy_pack_expansion (tree t, tree args, - tsubst_flags_t complain, tree in_decl) +struct GTY((for_user)) sat_entry { - /* Get the vector of satisfaction results. - gen_elem_of_pack_expansion_instantiation will check that each element of - the expansion is satisfied. */ - tree exprs = tsubst_pack_expansion (t, args, complain, in_decl); + tree constr; + tree args; + tree result; +}; - if (exprs == error_mark_node) - return boolean_false_node; +struct sat_hasher : ggc_ptr_hash +{ + static hashval_t hash (sat_entry *e) + { + hashval_t value = hash_atomic_constraint (e->constr); + return iterative_hash_template_arg (e->args, value); + } - /* TODO: It might be better to normalize each expanded term - and evaluate them separately. That would provide better - opportunities for diagnostics. */ - for (int i = 0; i < TREE_VEC_LENGTH (exprs); ++i) - if (TREE_VEC_ELT (exprs, i) != boolean_true_node) - return boolean_false_node; - return boolean_true_node; -} + static bool equal (sat_entry *e1, sat_entry *e2) + { + if (!atomic_constraints_identical_p (e1->constr, e2->constr)) + return false; + return template_args_equal (e1->args, e2->args); + } +}; -/* 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]). +/* Cache the result of satisfy_atom. */ +static GTY((deletable)) hash_table *sat_cache; - 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. */ +/* Cache the result of constraints_satisfied_p. */ +static GTY((deletable)) hash_map *decl_satisfied_cache; -tree -satisfy_predicate_constraint (tree t, tree args, - tsubst_flags_t complain, tree in_decl) +static tree +get_satisfaction (tree constr, tree args) { - tree expr = TREE_OPERAND (t, 0); - - /* We should never have a naked pack expansion in a predicate constraint. */ - gcc_assert (TREE_CODE (expr) != EXPR_PACK_EXPANSION); - - /* If substitution into the expression fails, the constraint - is not satisfied. */ - expr = tsubst_expr (expr, args, complain, in_decl, false); - if (expr == error_mark_node) - return boolean_false_node; + if (!sat_cache) + return NULL_TREE; + sat_entry elt = { constr, args, NULL_TREE }; + sat_entry* found = sat_cache->find (&elt); + if (found) + return found->result; + else + return NULL_TREE; +} - /* 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 (expr)); - if (!same_type_p (type, boolean_type_node)) - { - error_at (cp_expr_loc_or_input_loc (expr), - "constraint %qE does not have type %qT", - expr, boolean_type_node); - return boolean_false_node; - } +static void +save_satisfaction (tree constr, tree args, tree result) +{ + if (!sat_cache) + sat_cache = hash_table::create_ggc (31); + sat_entry elt = {constr, args, result}; + sat_entry** slot = sat_cache->find_slot (&elt, INSERT); + sat_entry* entry = ggc_alloc (); + *entry = elt; + *slot = entry; +} - return cxx_constant_value (expr); +void +clear_satisfaction_cache () +{ + if (sat_cache) + sat_cache->empty (); + if (decl_satisfied_cache) + decl_satisfied_cache->empty (); } -/* A concept check constraint like C is satisfied if substituting ARGS - into CARGS succeeds and C is satisfied for the resulting arguments. */ +/* A tool to help manage satisfaction caching in satisfy_constraint_r. + Note the cache is only used when not diagnosing errors. */ -tree -satisfy_check_constraint (tree t, tree args, - tsubst_flags_t complain, tree in_decl) +struct satisfaction_cache { - tree decl = CHECK_CONSTR_CONCEPT (t); - tree tmpl = DECL_TI_TEMPLATE (decl); - tree cargs = CHECK_CONSTR_ARGS (t); + satisfaction_cache (tree constr, tree args, tsubst_flags_t complain) + : constr(constr), args(args), complain(complain) + { } - /* Instantiate the concept check arguments. */ - tree targs = tsubst (cargs, args, tf_none, NULL_TREE); - if (targs == error_mark_node) - return boolean_false_node; + tree get () + { + if (complain == tf_none) + return get_satisfaction (constr, args); + return NULL_TREE; + } - /* Search for a previous value. */ - if (tree prev = lookup_concept_satisfaction (tmpl, targs)) - return prev; + tree save (tree result) + { + if (complain == tf_none) + save_satisfaction (constr, args, result); + return result; + } - /* Expand the concept; failure here implies non-satisfaction. */ - tree def = expand_concept (decl, targs); - if (def == error_mark_node) - return memoize_concept_satisfaction (tmpl, args, boolean_false_node); + tree constr; + tree args; + tsubst_flags_t complain; +}; - /* Recursively satisfy the constraint. */ - tree result = satisfy_constraint_1 (def, targs, complain, in_decl); - return memoize_concept_satisfaction (tmpl, targs, result); -} +static int satisfying_constraint = 0; -/* Check an expression constraint. The constraint is satisfied if - substitution succeeds ([temp.constr.expr]). +/* Returns true if we are currently satisfying a constraint. - Note that the expression is unevaluated. */ + This is used to guard against recursive calls to evaluate_concept_check + during template argument substitution. -tree -satisfy_expression_constraint (tree t, tree args, - tsubst_flags_t complain, tree in_decl) -{ - cp_unevaluated guard; - deferring_access_check_sentinel deferring; + TODO: Do we need this now that we fully normalize prior to evaluation? + I think not. */ - 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; - if (!perform_deferred_access_checks (tf_none)) - return boolean_false_node; - return boolean_true_node; +bool +satisfying_constraint_p () +{ + return satisfying_constraint; } -/* Check a type constraint. The constraint is satisfied if - substitution succeeds. */ +/* Substitute ARGS into constraint-expression T during instantiation of + a member of a class template. */ -inline tree -satisfy_type_constraint (tree t, tree args, - tsubst_flags_t complain, tree in_decl) +tree +tsubst_constraint (tree t, tree args, tsubst_flags_t complain, tree in_decl) { - deferring_access_check_sentinel deferring; - tree type = TYPE_CONSTR_TYPE (t); - gcc_assert (TYPE_P (type) || type == error_mark_node); - tree check = tsubst (type, args, complain, in_decl); - if (error_operand_p (check)) - return boolean_false_node; - if (!perform_deferred_access_checks (complain)) - return boolean_false_node; - return boolean_true_node; + /* We also don't want to evaluate concept-checks when substituting the + constraint-expressions of a declaration. */ + processing_constraint_expression_sentinel s; + tree expr = tsubst_expr (t, args, complain, in_decl, false); + return expr; } -/* Check an implicit conversion constraint. */ +static tree satisfy_constraint_r (tree, tree, subst_info info); -tree -satisfy_implicit_conversion_constraint (tree t, tree args, - tsubst_flags_t complain, tree in_decl) -{ - /* 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. */ - tree expr = - tsubst_expr (ICONV_CONSTR_EXPR (t), args, complain, in_decl, false); - if (expr == error_mark_node) - return boolean_false_node; +/* Compute the satisfaction of a conjunction. */ - /* Get the transformed target type. */ - tree type = tsubst (ICONV_CONSTR_TYPE (t), args, complain, in_decl); - if (type == error_mark_node) - return boolean_false_node; - - /* Attempt the conversion as a direct initialization - of the form TYPE = EXPR. */ - tree conv = - perform_direct_initialization_if_possible (type, expr, false, complain); - if (conv == NULL_TREE || conv == error_mark_node) - return boolean_false_node; - else - return boolean_true_node; +static tree +satisfy_conjunction (tree t, tree args, subst_info info) +{ + tree lhs = satisfy_constraint_r (TREE_OPERAND (t, 0), args, info); + if (lhs == error_mark_node || lhs == boolean_false_node) + return lhs; + return satisfy_constraint_r (TREE_OPERAND (t, 1), args, info); } -/* Check an argument deduction constraint. */ +/* Compute the satisfaction of a disjunction. */ -tree -satisfy_argument_deduction_constraint (tree t, tree args, - tsubst_flags_t complain, tree in_decl) +static tree +satisfy_disjunction (tree t, tree args, subst_info info) { - /* Substitute through the expression. */ - tree expr = DEDUCT_CONSTR_EXPR (t); - tree init = tsubst_expr (expr, args, complain, in_decl, false); - if (expr == error_mark_node) - return boolean_false_node; + /* Evaluate the operands quietly. */ + subst_info quiet (tf_none, NULL_TREE); - /* Perform auto or decltype(auto) deduction to get the result. */ - tree pattern = DEDUCT_CONSTR_PATTERN (t); - tree placeholder = DEDUCT_CONSTR_PLACEHOLDER (t); - tree constr = PLACEHOLDER_TYPE_CONSTRAINTS (placeholder); - tree type_canonical = TYPE_CANONICAL (placeholder); - PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) - = tsubst_constraint (constr, args, complain|tf_partial, in_decl); - TYPE_CANONICAL (placeholder) = NULL_TREE; - tree type = do_auto_deduction (pattern, init, placeholder, - complain, adc_requirement); - PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) = constr; - TYPE_CANONICAL (placeholder) = type_canonical; - if (type == error_mark_node) - return boolean_false_node; + /* Register the constraint for diagnostics, if needed. */ + diagnosing_failed_constraint failure (t, args, info.noisy ()); - return boolean_true_node; + tree lhs = satisfy_constraint_r (TREE_OPERAND (t, 0), args, quiet); + if (lhs == boolean_true_node) + return boolean_true_node; + tree rhs = satisfy_constraint_r (TREE_OPERAND (t, 1), args, quiet); + if (rhs != boolean_true_node && info.noisy ()) + { + location_t loc = cp_expr_location (CONSTR_EXPR (t)); + inform (loc, "neither operand of the disjunction is satisfied"); + /* TODO: Replay the LHS and RHS to find failures in both branches. */ + // satisfy_constraint_r (TREE_OPERAND (t, 0), args, info); + // satisfy_constraint_r (TREE_OPERAND (t, 1), args, info); + } + return rhs; } -/* Check an exception constraint. An exception constraint for an - expression e is satisfied when noexcept(e) is true. */ +/* Ensures that T is a truth value and not (accidentally, as sometimes + happens) an integer value. */ tree -satisfy_exception_constraint (tree t, tree args, - tsubst_flags_t complain, tree in_decl) +satisfaction_value (tree t) { - 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; - - if (expr_noexcept_p (check, complain)) + if (t == error_mark_node) + return t; + if (t == boolean_true_node || t == integer_one_node) return boolean_true_node; - else + if (t == boolean_false_node || t == integer_zero_node) return boolean_false_node; + + /* Anything else should be invalid. */ + gcc_assert (false); } -/* Check a parameterized constraint. */ +/* Build a new template argument list with template arguments corresponding + to the parameters used in an atomic constraint. */ tree -satisfy_parameterized_constraint (tree t, tree args, - tsubst_flags_t complain, tree in_decl) +get_mapped_args (tree map) { - 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 satisfy_constraint_1 (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. + /* If there's no map, then there are no arguments. */ + if (!map) + return NULL_TREE; - FIXME: Check that this wouldn't result in a user-defined - operator. Note that this error is partially diagnosed in - satisfy_predicate_constraint. It would be nice to diagnose - the overload, but I don't think it's strictly necessary. */ + /* Find the mapped parameter with the highest level. */ + int count = 0; + for (tree p = map; p; p = TREE_CHAIN (p)) + { + int level; + int index; + template_parm_level_and_index (TREE_VALUE (p), &level, &index); + if (level > count) + count = level; + } -tree -satisfy_conjunction (tree t, tree args, tsubst_flags_t complain, tree in_decl) -{ - tree t0 = satisfy_constraint_1 (TREE_OPERAND (t, 0), args, complain, in_decl); - if (t0 == boolean_false_node) - return boolean_false_node; - return satisfy_constraint_1 (TREE_OPERAND (t, 1), args, complain, in_decl); -} + /* Place each argument at its corresponding position in the argument + list. Note that the list will be sparse (not all arguments supplied), + but instantiation is guaranteed to only use the parameters in the + mapping, so null arguments would never be used. */ + auto_vec< auto_vec > lists (count); + lists.quick_grow_cleared (count); + for (tree p = map; p; p = TREE_CHAIN (p)) + { + int level; + int index; + template_parm_level_and_index (TREE_VALUE (p), &level, &index); + + /* Insert the argument into its corresponding position. */ + auto_vec &list = lists[level - 1]; + if (index >= (int)list.length ()) + list.safe_grow_cleared (index + 1); + list[index] = TREE_PURPOSE (p); + } -/* Check that the disjunction of constraints is satisfied. Note - that if the left operand is satisfied, the right operand is not - checked. */ + /* Build the actual argument list. */ + tree args = make_tree_vec (lists.length ()); + for (unsigned i = 0; i != lists.length (); ++i) + { + auto_vec &list = lists[i]; + tree level = make_tree_vec (list.length ()); + for (unsigned j = 0; j < list.length(); ++j) + TREE_VEC_ELT (level, j) = list[j]; + SET_TMPL_ARGS_LEVEL (args, i + 1, level); + } + SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args, 0); -tree -satisfy_disjunction (tree t, tree args, tsubst_flags_t complain, tree in_decl) -{ - tree t0 = satisfy_constraint_1 (TREE_OPERAND (t, 0), args, complain, in_decl); - if (t0 == boolean_true_node) - return boolean_true_node; - return satisfy_constraint_1 (TREE_OPERAND (t, 1), args, complain, in_decl); + return args; } -/* Dispatch to an appropriate satisfaction routine depending on the - tree code of T. */ +static void diagnose_atomic_constraint (tree, tree, subst_info); -tree -satisfy_constraint_1 (tree t, tree args, tsubst_flags_t complain, tree in_decl) -{ - gcc_assert (!processing_template_decl); +/* Compute the satisfaction of an atomic constraint. */ - if (!t) - return boolean_false_node; +static tree +satisfy_atom (tree t, tree args, subst_info info) +{ + satisfaction_cache cache (t, args, info.complain); + if (tree r = cache.get ()) + return r; - if (t == error_mark_node) - return boolean_false_node; + /* Perform substitution quietly. */ + subst_info quiet (tf_none, NULL_TREE); - switch (TREE_CODE (t)) - { - case PRED_CONSTR: - return satisfy_predicate_constraint (t, args, complain, in_decl); + /* In case there is a diagnostic, we want to establish the context + prior to printing errors. If no errors occur, this context is + removed before returning. */ + diagnosing_failed_constraint failure (t, args, info.noisy ()); - case CHECK_CONSTR: - return satisfy_check_constraint (t, args, complain, in_decl); + /* Instantiate the parameter mapping, so that we map directly to + the arguments provided to the instantiation. */ + tree map = tsubst_parameter_mapping (ATOMIC_CONSTR_MAP (t), args, quiet); + if (map == error_mark_node) + { + /* If instantiation of the parameter mapping fails, the program + is ill-formed. */ + if (info.noisy()) + tsubst_parameter_mapping (ATOMIC_CONSTR_MAP (t), args, info); + return cache.save (boolean_false_node); + } - case EXPR_CONSTR: - return satisfy_expression_constraint (t, args, complain, in_decl); + /* Rebuild the argument vector from the parameter mapping. */ + args = get_mapped_args (map); - case TYPE_CONSTR: - return satisfy_type_constraint (t, args, complain, in_decl); + /* Apply the parameter mapping (i.e., just substitute). */ + tree expr = ATOMIC_CONSTR_EXPR (t); + tree result = tsubst_expr (expr, args, quiet.complain, quiet.in_decl, false); + if (result == error_mark_node) + { + /* If substitution results in an invalid type or expression, the constraint + is not satisfied. Replay the substitution. */ + if (info.noisy ()) + tsubst_expr (expr, args, info.complain, info.in_decl, false); + return cache.save (boolean_false_node); + } - case ICONV_CONSTR: - return satisfy_implicit_conversion_constraint (t, args, complain, in_decl); + location_t loc = cp_expr_location (expr); - case DEDUCT_CONSTR: - return satisfy_argument_deduction_constraint (t, args, complain, in_decl); + /* [17.4.1.2] ... lvalue-to-value conversion is performed as necessary, + and EXPR shall be a constant expression of type bool. */ + result = force_rvalue (result, tf_error); + if (result == error_mark_node) + { + if (info.noisy ()) + inform (loc, "cannot convert constraint to rvalue"); + return cache.save (error_mark_node); + } + if (!same_type_p (TREE_TYPE (result), boolean_type_node)) + { + if (info.noisy ()) + inform (loc, "constraint does not have type %"); + return cache.save (error_mark_node); + } - case EXCEPT_CONSTR: - return satisfy_exception_constraint (t, args, complain, in_decl); + /* Compute the value of the constraint. */ + result = satisfaction_value (cxx_constant_value (result)); + if (result == boolean_false_node && info.noisy ()) + diagnose_atomic_constraint (t, args, info); - case PARM_CONSTR: - return satisfy_parameterized_constraint (t, args, complain, in_decl); + return cache.save (result); +} - case CONJ_CONSTR: - return satisfy_conjunction (t, args, complain, in_decl); +/* Determine if the normalized constraint T is satisfied. + Returns boolean_true_node if the expression/constraint is + satisfied, boolean_false_node if not, and error_mark_node + if the there was an error evaluating the constraint. - case DISJ_CONSTR: - return satisfy_disjunction (t, args, complain, in_decl); + The parameter mapping of atomic constraints is simply the + set of template arguments that will be substituted into + the expression, regardless of template parameters appearing + withing. Whether a template argument is used in the atomic + constraint only matters for subsumption. */ - case EXPR_PACK_EXPANSION: - return satisfy_pack_expansion (t, args, complain, in_decl); +static tree +satisfy_constraint_r (tree t, tree args, subst_info info) +{ + if (t == error_mark_node) + return error_mark_node; - default: - gcc_unreachable (); - } - return boolean_false_node; + switch (TREE_CODE (t)) + { + case CONJ_CONSTR: + return satisfy_conjunction (t, args, info); + case DISJ_CONSTR: + return satisfy_disjunction (t, args, info); + case ATOMIC_CONSTR: + return satisfy_atom (t, args, info); + default: + gcc_unreachable (); + } } -/* Check that the constraint is satisfied, according to the rules - for that constraint. Note that each satisfy_* function returns - true or false, depending on whether it is satisfied or not. */ +/* Check that the normalized constraint T is satisfied for ARGS. */ -tree -satisfy_constraint (tree t, tree args) +static tree +satisfy_constraint (tree t, tree args, subst_info info) { auto_timevar time (TV_CONSTRAINT_SAT); @@ -2283,146 +2417,199 @@ satisfy_constraint (tree t, tree args) to non-dependent terms, so we want to ensure full checking here. */ processing_template_decl_sentinel proc (true); - /* Avoid early exit in tsubst and tsubst_copy from null args; since earlier - substitution was done with processing_template_decl forced on, there will - be expressions that still need semantic processing, possibly buried in - decltype or a template argument. */ + /* We need to check access during satisfaction. */ + deferring_access_check_sentinel acs (dk_no_deferred); + + /* Avoid early exit in tsubst and tsubst_copy from null args. */ if (args == NULL_TREE) args = make_tree_vec (1); - return satisfy_constraint_1 (t, args, tf_none, NULL_TREE); + return satisfy_constraint_r (t, args, info); } -/* Check the associated constraints in CI against the given - ARGS, returning true when the constraints are satisfied - and false otherwise. */ +/* Check the normalized constraints T against ARGS, returning a satisfaction + value (either true, false, or error). */ -tree -satisfy_associated_constraints (tree ci, tree args) +static tree +satisfy_associated_constraints (tree t, tree args, subst_info info) { /* If there are no constraints then this is trivially satisfied. */ - if (!ci) + if (!t) return boolean_true_node; /* If any arguments depend on template parameters, we can't - check constraints. */ + check constraints. Pretend they're satisfied for now. */ if (args && uses_template_parms (args)) return boolean_true_node; - /* Check if we've seen a previous result. */ - if (tree prev = lookup_constraint_satisfaction (ci, args)) - return prev; - - /* Actually test for satisfaction. */ - tree result = satisfy_constraint (CI_ASSOCIATED_CONSTRAINTS (ci), args); - return memoize_constraint_satisfaction (ci, args, result); + return satisfy_constraint (t, args, info); } -} /* namespace */ +/* Evaluate EXPR as a constraint expression using ARGS, returning a + satisfaction value. */ -/* Evaluate the given constraint, returning boolean_true_node - if the constraint is satisfied and boolean_false_node - otherwise. */ - -tree -evaluate_constraints (tree constr, tree args) +static tree +satisfy_constraint_expression (tree expr, tree args, subst_info info) { - gcc_assert (constraint_p (constr)); - return satisfy_constraint (constr, args); + /* Normalize the expression before satisfaction testing. */ + tree norm; + if (args == NULL_TREE && concept_check_p (expr)) + { + tree id = unpack_concept_check (expr); + args = TREE_OPERAND (id, 1); + tree tmpl = get_concept_check_template (id); + norm = normalize_concept_definition (tmpl); + } + else + norm = normalize_constraint_expression (expr); + return satisfy_constraint (norm, args, info); } -/* Evaluate the function concept FN by substituting its own args - into its definition and evaluating that as the result. Returns - boolean_true_node if the constraints are satisfied and - boolean_false_node otherwise. */ +/* Used to evaluate concept checks and requires-expressions during + constant expression evaluation. */ tree -evaluate_function_concept (tree fn, tree args) +satisfy_constraint_expression (tree expr) { - tree constr = build_nt (CHECK_CONSTR, fn, args); - return satisfy_constraint (constr, args); + subst_info info (tf_none, NULL_TREE); + return satisfy_constraint_expression (expr, NULL_TREE, info); } -/* Evaluate the variable concept VAR by substituting its own args into - its initializer and checking the resulting constraint. Returns - boolean_true_node if the constraints are satisfied and - boolean_false_node otherwise. */ +/* True if T is satisfied for ARGS. */ -tree -evaluate_variable_concept (tree var, tree args) +static bool +constraint_expression_satisfied_p (tree t, tree args, subst_info info) { - tree constr = build_nt (CHECK_CONSTR, var, args); - return satisfy_constraint (constr, args); + tree r = satisfy_constraint_expression (t, args, info); + return r == boolean_true_node; } -/* 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) +static bool +constraints_satisfied_p (tree t, subst_info info) { - tree constr = normalize_expression (expr); - return satisfy_constraint (constr, args); -} + if (!DECL_P (t)) + return constraint_expression_satisfied_p (t, NULL_TREE, info); -/* Returns true if the DECL's constraints are satisfied. - This is used in cases where a declaration is formed but - before it is used (e.g., overload resolution). */ + /* For inherited constructors, consider the original declaration; + it has the correct template information attached. */ + if (flag_new_inheriting_ctors) + t = strip_inheriting_ctors (t); + + /* Update the declaration for diagnostics. */ + info.in_decl = t; + + if (info.quiet ()) + if (bool *p = hash_map_safe_get (decl_satisfied_cache, t)) + return *p; -bool -constraints_satisfied_p (tree decl) -{ /* Get the constraints to check for satisfaction. This depends on whether we're looking at a template specialization or not. */ - tree ci; + tree norm = NULL_TREE; tree args = NULL_TREE; - if (tree ti = DECL_TEMPLATE_INFO (decl)) + tree ti = DECL_TEMPLATE_INFO (t); + if (ti) { tree tmpl = TI_TEMPLATE (ti); - ci = get_constraints (tmpl); - int depth = TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (tmpl)); - args = get_innermost_template_args (TI_ARGS (ti), depth); + norm = normalize_template_requirements (tmpl, info.noisy ()); + + /* The initial parameter mapping is the complete set of + template arguments substituted into the declaration. */ + args = TI_ARGS (ti); } else { - ci = get_constraints (decl); + /* These should be empty until we allow constraints on non-templates. */ + norm = normalize_nontemplate_requirements (t, info.noisy ()); + } + + bool r = true; + if (norm) + { + push_access_scope (t); + tree eval = satisfy_associated_constraints (norm, args, info); + pop_access_scope (t); + r = (eval == boolean_true_node); } - tree eval = satisfy_associated_constraints (ci, args); - return eval == boolean_true_node; + if (info.quiet ()) + hash_map_safe_put (decl_satisfied_cache, t, r); + + return r; } -/* Returns true if the constraints are satisfied by ARGS. - Here, T can be either a constraint or a constrained - declaration. */ +/* Returns true if the T's constraints are satisfied, of if T is an expression, + if T is satisfied. This is used in cases where the arguments can be + determined from the declaration or expression. + + Note that T is typically a template specialization. */ bool -constraints_satisfied_p (tree t, tree args) +constraints_satisfied_p (tree t) { - tree eval; - if (constraint_p (t)) - eval = evaluate_constraints (t, args); - else - eval = satisfy_associated_constraints (get_constraints (t), args); - return eval == boolean_true_node; + subst_info info (tf_none, NULL_TREE); + return constraints_satisfied_p (t, info); } -namespace +/* Returns true if the expression or constrained declaration T is + satisfied by ARGS. In this case, we don't have a specialization + where we can cache the results (e.g., alias templates). */ + +static bool +constraints_satisfied_p (tree t, tree args, subst_info info) { + if (!DECL_P (t)) + return constraint_expression_satisfied_p (t, args, info); + + /* Update the declaration for diagnostics. */ + info.in_decl = t; + + gcc_assert (TREE_CODE (t) == TEMPLATE_DECL); + if (tree norm = normalize_template_requirements (t, info.noisy ())) + { + tree pattern = DECL_TEMPLATE_RESULT (t); + push_access_scope (pattern); + tree eval = satisfy_associated_constraints (norm, args, info); + pop_access_scope (pattern); + return eval == boolean_true_node; + } -/* Normalize EXPR and determine if the resulting constraint is - satisfied by ARGS. Returns true if and only if the constraint - is satisfied. This is used extensively by diagnostics to - determine causes for failure. */ + return true; +} -inline bool -constraint_expression_satisfied_p (tree expr, tree args) +bool +constraints_satisfied_p (tree t, tree args) { - return evaluate_constraint_expression (expr, args) == boolean_true_node; + subst_info info (tf_none, NULL); + return constraints_satisfied_p (t, args, info); } -} /* namespace */ +/* Evaluate a concept check of the form C, returning either TRUE + or FALSE. If ARGS contains any template parameters, this returns the + check. If satisfaction yields a hard error, diagnose the error. */ + +tree +evaluate_concept_check (tree check, tsubst_flags_t complain) +{ + /* FIXME we ought to be able to pass complain into subst_info rather + than repeat satisfaction, but currently that will complain about + non-satisfaction as well as errors. */ + if (check == error_mark_node) + return error_mark_node; + + gcc_assert (concept_check_p (check)); + + subst_info info (tf_none, NULL_TREE); + tree result = satisfy_constraint_expression (check, NULL_TREE, info); + if (result == error_mark_node && (complain & tf_error)) + { + location_t loc = cp_expr_loc_or_input_loc (check); + error_at (loc, "concept satisfaction failed"); + info.complain = complain; + satisfy_constraint_expression (check, NULL_TREE, info); + } + + return result; +} /*--------------------------------------------------------------------------- Semantic analysis of requires-expressions @@ -2430,8 +2617,9 @@ constraint_expression_satisfied_p (tree expr, tree args) /* 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) +finish_requires_expr (location_t loc, tree parms, tree reqs) { /* Modify the declared parameters by removing their context so they don't refer to the enclosing scope and explicitly @@ -2446,41 +2634,53 @@ finish_requires_expr (tree parms, tree reqs) tree r = build_min (REQUIRES_EXPR, boolean_type_node, parms, reqs); TREE_SIDE_EFFECTS (r) = false; TREE_CONSTANT (r) = true; + SET_EXPR_LOCATION (r, loc); return r; } -/* Construct a requirement for the validity of EXPR. */ +/* Construct a requirement for the validity of EXPR. */ + tree -finish_simple_requirement (tree expr) +finish_simple_requirement (location_t loc, tree expr) { - return build_nt (SIMPLE_REQ, expr); + tree r = build_nt (SIMPLE_REQ, expr); + SET_EXPR_LOCATION (r, loc); + return r; } -/* Construct a requirement for the validity of TYPE. */ +/* Construct a requirement for the validity of TYPE. */ + tree -finish_type_requirement (tree type) +finish_type_requirement (location_t loc, tree type) { - return build_nt (TYPE_REQ, type); + tree r = build_nt (TYPE_REQ, type); + SET_EXPR_LOCATION (r, loc); + return r; } /* 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. */ + NOEXCEPT_P is true iff the noexcept keyword was specified. */ + tree -finish_compound_requirement (tree expr, tree type, bool noexcept_p) +finish_compound_requirement (location_t loc, tree expr, tree type, bool noexcept_p) { tree req = build_nt (COMPOUND_REQ, expr, type); + SET_EXPR_LOCATION (req, loc); COMPOUND_REQ_NOEXCEPT_P (req) = noexcept_p; return req; } -/* Finish a nested requirement. */ +/* Finish a nested requirement. */ + tree -finish_nested_requirement (tree expr) +finish_nested_requirement (location_t loc, tree expr) { - return build_nt (NESTED_REQ, expr); + tree r = build_nt (NESTED_REQ, expr); + SET_EXPR_LOCATION (r, loc); + return r; } /* Check that FN satisfies the structural requirements of a @@ -2500,7 +2700,7 @@ check_function_concept (tree fn) if (TREE_CODE (body) == CLEANUP_POINT_EXPR) body = TREE_OPERAND (body, 0); - /* Check that the definition is written correctly. */ + /* Check that the definition is written correctly. */ if (TREE_CODE (body) != RETURN_EXPR) { location_t loc = DECL_SOURCE_LOCATION (fn); @@ -2580,108 +2780,93 @@ subsumes_constraints (tree a, tree b) return subsumes (a, b); } -/* Returns true when the the constraints in A subsume those in B, but - the constraints in B do not subsume the constraints in A. */ +/* Returns true when the the constraints in CI (with arguments + ARGS) strictly subsume the associated constraints of TMPL. */ + +bool +strictly_subsumes (tree ci, tree args, tree tmpl) +{ + tree n1 = get_normalized_constraints_from_info (ci, args, NULL_TREE); + tree n2 = get_normalized_constraints_from_decl (tmpl); + + return subsumes (n1, n2) && !subsumes (n2, n1); +} + +/* REturns true when the constraints in CI (with arguments ARGS) subsume + the associated constraints of TMPL. */ bool -strictly_subsumes (tree a, tree b) +weakly_subsumes (tree ci, tree args, tree tmpl) { - return subsumes (a, b) && !subsumes (b, a); + tree n1 = get_normalized_constraints_from_info (ci, args, NULL_TREE); + tree n2 = get_normalized_constraints_from_decl (tmpl); + + return subsumes (n1, n2); } /* 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. */ + Returns 1 if D1 is more constrained than D2, -1 if D2 is more constrained + than D1, and 0 otherwise. */ int more_constrained (tree d1, tree d2) { - tree c1 = get_constraints (d1); - tree c2 = get_constraints (d2); + tree n1 = get_normalized_constraints_from_decl (d1); + tree n2 = get_normalized_constraints_from_decl (d2); + int winner = 0; - if (subsumes_constraints (c1, c2)) + if (subsumes (n1, n2)) ++winner; - if (subsumes_constraints (c2, c1)) + if (subsumes (n2, n1)) --winner; 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. */ - -bool -at_least_as_constrained (tree d1, tree d2) -{ - tree c1 = get_constraints (d1); - tree c2 = get_constraints (d2); - return subsumes_constraints (c1, c2); -} - - /*--------------------------------------------------------------------------- Constraint diagnostics - -FIXME: Normalize expressions into constraints before evaluating them. -This should be the general pattern for all such diagnostics. ---------------------------------------------------------------------------*/ -/* The number of detailed constraint failures. */ - -int constraint_errors = 0; - -/* Do not generate errors after diagnosing this number of constraint - failures. - - FIXME: This is a really arbitrary number. Provide better control of - constraint diagnostics with a command line option. */ - -int constraint_thresh = 20; - - -/* Returns true if we should elide the diagnostic for a constraint failure. - This is the case when the number of errors has exceeded the pre-configured - threshold. */ - -inline bool -elide_constraint_failure_p () -{ - bool ret = constraint_thresh <= constraint_errors; - ++constraint_errors; - return ret; -} - -/* Returns the number of undiagnosed errors. */ - -inline int -undiagnosed_constraint_failures () -{ - return constraint_errors - constraint_thresh; +/* Returns the best location to diagnose a constraint error. */ + +static location_t +get_constraint_error_location (tree t) +{ + /* If we have a specific location give it. */ + tree expr = CONSTR_EXPR (t); + if (location_t loc = cp_expr_location (expr)) + return loc; + + /* If the constraint is normalized from a requires-clause, give + the location as that of the constrained declaration. */ + tree cxt = CONSTR_CONTEXT (t); + tree src = TREE_VALUE (cxt); + if (!src) + /* TODO: This only happens for constrained non-template declarations. */ + return input_location; + if (DECL_P (src)) + return DECL_SOURCE_LOCATION (src); + + /* Otherwise, give the location as the defining concept. */ + gcc_assert (concept_check_p (src)); + tree id = unpack_concept_check (src); + tree tmpl = TREE_OPERAND (id, 0); + if (OVL_P (tmpl)) + tmpl = OVL_FIRST (tmpl); + return DECL_SOURCE_LOCATION (tmpl); } -/* The diagnosis of constraints performs a combination of normalization - and satisfaction testing. We recursively walk through the conjunction or - disjunction of associated constraints, testing each sub-constraint in - turn. */ - -namespace { - -void diagnose_constraint (location_t, tree, tree, tree); - -/* Emit a specific diagnostics for a failed trait. */ +/* Emit a diagnostic for a failed trait. */ void -diagnose_trait_expression (location_t loc, tree, tree cur, tree args) +diagnose_trait_expr (tree expr, tree args) { - if (constraint_expression_satisfied_p (cur, args)) - return; - if (elide_constraint_failure_p()) - return; + location_t loc = cp_expr_location (expr); - tree expr = PRED_CONSTR_EXPR (cur); + /* Build a "fake" version of the instantiated trait, so we can + get the instantiated types from result. */ ++processing_template_decl; expr = tsubst_expr (expr, args, tf_none, NULL_TREE, false); --processing_template_decl; @@ -2758,390 +2943,224 @@ diagnose_trait_expression (location_t loc, tree, tree cur, tree args) } } -/* Diagnose the expression of a predicate constraint. */ - -void -diagnose_other_expression (location_t loc, tree, tree cur, tree args) +static tree +diagnose_valid_expression (tree expr, tree args, tree in_decl) { - if (constraint_expression_satisfied_p (cur, args)) - return; - if (elide_constraint_failure_p()) - return; - inform (loc, "%qE evaluated to false", cur); -} + tree result = tsubst_expr (expr, args, tf_none, in_decl, false); + if (result != error_mark_node) + return result; -/* Do our best to infer meaning from predicates. */ + location_t loc = cp_expr_loc_or_input_loc (expr); + inform (loc, "the required expression %qE is invalid", expr); -inline void -diagnose_predicate_constraint (location_t loc, tree orig, tree cur, tree args) -{ - if (TREE_CODE (PRED_CONSTR_EXPR (cur)) == TRAIT_EXPR) - diagnose_trait_expression (loc, orig, cur, args); - else - diagnose_other_expression (loc, orig, cur, args); -} + /* TODO: Replay the substitution to diagnose the error? */ + // tsubst_expr (expr, args, tf_error, in_decl, false); -/* Diagnose a failed pack expansion, possibly containing constraints. */ + return error_mark_node; +} -void -diagnose_pack_expansion (location_t loc, tree, tree cur, tree args) +static tree +diagnose_valid_type (tree type, tree args, tree in_decl) { - if (constraint_expression_satisfied_p (cur, args)) - return; - if (elide_constraint_failure_p()) - return; - - /* Make sure that we don't have naked packs that we don't expect. */ - if (!same_type_p (TREE_TYPE (cur), boolean_type_node)) - { - inform (loc, "invalid pack expansion in constraint %qE", cur); - return; - } + tree result = tsubst (type, args, tf_none, in_decl); + if (result != error_mark_node) + return result; - inform (loc, "in the expansion of %qE", cur); + location_t loc = cp_expr_loc_or_input_loc (type); + inform (loc, "the required type %qT is invalid", type); - /* Get the vector of expanded arguments. Note that n must not - be 0 since this constraint is not satisfied. */ - ++processing_template_decl; - tree exprs = tsubst_pack_expansion (cur, args, tf_none, NULL_TREE); - --processing_template_decl; - if (exprs == error_mark_node) - { - /* TODO: This error message could be better. */ - inform (loc, " substitution failure occurred during expansion"); - return; - } + /* TODO: Replay the substitution to diagnose the error? */ + // tsubst (type, args, tf_error, in_decl); - /* Check each expanded constraint separately. */ - int n = TREE_VEC_LENGTH (exprs); - for (int i = 0; i < n; ++i) - { - tree expr = TREE_VEC_ELT (exprs, i); - if (!constraint_expression_satisfied_p (expr, args)) - inform (loc, " %qE was not satisfied", expr); - } + return error_mark_node; } -/* Diagnose a potentially unsatisfied concept check constraint DECL. - Parameters are as for diagnose_constraint. */ - -void -diagnose_check_constraint (location_t loc, tree orig, tree cur, tree args) +static void +diagnose_simple_requirement (tree req, tree args, tree in_decl) { - if (constraints_satisfied_p (cur, args)) - return; - - tree decl = CHECK_CONSTR_CONCEPT (cur); - tree cargs = CHECK_CONSTR_ARGS (cur); - tree tmpl = DECL_TI_TEMPLATE (decl); - tree check = build_nt (CHECK_CONSTR, decl, cargs); - - /* Instantiate the concept check arguments. */ - tree targs = tsubst (cargs, args, tf_none, NULL_TREE); - if (targs == error_mark_node) - { - if (elide_constraint_failure_p ()) - return; - inform (loc, "invalid use of the concept %qE", check); - tsubst (cargs, args, tf_warning_or_error, NULL_TREE); - return; - } - - tree sub = build_tree_list (tmpl, targs); - /* Update to the expanded definitions. */ - cur = expand_concept (decl, targs); - if (cur == error_mark_node) - { - if (elide_constraint_failure_p ()) - return; - inform (loc, "in the expansion of concept %<%E %S%>", check, sub); - cur = get_concept_definition (decl); - tsubst_expr (cur, targs, tf_warning_or_error, NULL_TREE, false); - return; - } - - orig = get_concept_definition (CHECK_CONSTR_CONCEPT (orig)); - orig = normalize_expression (orig); - - location_t dloc = DECL_SOURCE_LOCATION (decl); - inform (dloc, "within %qS", sub); - diagnose_constraint (dloc, orig, cur, targs); + diagnose_valid_expression (TREE_OPERAND (req, 0), args, in_decl); } -/* Diagnose a potentially unsatisfied conjunction or disjunction. Parameters - are as for diagnose_constraint. */ - -void -diagnose_logical_constraint (location_t loc, tree orig, tree cur, tree args) +static void +diagnose_compound_requirement (tree req, tree args, tree in_decl) { - tree t0 = TREE_OPERAND (cur, 0); - tree t1 = TREE_OPERAND (cur, 1); - if (!constraints_satisfied_p (t0, args)) - diagnose_constraint (loc, TREE_OPERAND (orig, 0), t0, args); - else if (TREE_CODE (orig) == TRUTH_ORIF_EXPR) + tree expr = TREE_OPERAND (req, 0); + expr = diagnose_valid_expression (expr, args, in_decl); + if (expr == error_mark_node) return; - if (!constraints_satisfied_p (t1, args)) - diagnose_constraint (loc, TREE_OPERAND (orig, 1), t1, args); -} -/* Diagnose a potential expression constraint failure. */ + location_t loc = cp_expr_loc_or_input_loc (expr); -void -diagnose_expression_constraint (location_t loc, tree orig, tree cur, tree args) -{ - if (constraints_satisfied_p (cur, args)) - return; - if (elide_constraint_failure_p()) + /* Check the noexcept condition. */ + if (COMPOUND_REQ_NOEXCEPT_P (req) && !expr_noexcept_p (expr, tf_none)) + inform (loc, "%qE is not %", expr); + + tree type = TREE_OPERAND (req, 1); + type = diagnose_valid_type (type, args, in_decl); + if (type == error_mark_node) return; - tree expr = EXPR_CONSTR_EXPR (orig); - inform (loc, "the required expression %qE would be ill-formed", expr); + if (type) + { + subst_info quiet (tf_none, in_decl); + subst_info noisy (tf_error, in_decl); - // TODO: We should have a flag that controls this substitution. - // I'm finding it very useful for resolving concept check errors. + /* Check the expression against the result type. */ + if (tree placeholder = type_uses_auto (type)) + { + if (!type_deducible_p (expr, type, placeholder, args, quiet)) + { + tree orig_expr = TREE_OPERAND (req, 0); + inform (loc, "type deduction from %qE failed", orig_expr); + + /* Further explain the reason for the error. */ + type_deducible_p (expr, type, placeholder, args, noisy); + } + } + else if (!expression_convertible_p (expr, type, quiet)) + { + tree orig_expr = TREE_OPERAND (req, 0); + inform (loc, "cannot convert %qE to %qT", orig_expr, type); - // inform (input_location, "==== BEGIN DUMP ===="); - // tsubst_expr (EXPR_CONSTR_EXPR (orig), args, tf_warning_or_error, NULL_TREE, false); - // inform (input_location, "==== END DUMP ===="); + /* Further explain the reason for the error. */ + expression_convertible_p (expr, type, noisy); + } + } } -/* Diagnose a potentially failed type constraint. */ - -void -diagnose_type_constraint (location_t loc, tree orig, tree cur, tree args) +static void +diagnose_type_requirement (tree req, tree args, tree in_decl) { - if (constraints_satisfied_p (cur, args)) - return; - if (elide_constraint_failure_p()) - return; - - tree type = TYPE_CONSTR_TYPE (orig); - inform (loc, "the required type %qT would be ill-formed", type); + tree type = TREE_OPERAND (req, 0); + diagnose_valid_type (type, args, in_decl); } -/* Diagnose a potentially unsatisfied conversion constraint. */ - -void -diagnose_implicit_conversion_constraint (location_t loc, tree orig, tree cur, - tree args) +static void +diagnose_nested_requirement (tree req, tree args) { - if (constraints_satisfied_p (cur, args)) - return; - - /* The expression and type will previously have been substituted into, - and therefore may already be an error. Also, we will have already - diagnosed substitution failures into an expression since this must be - part of a compound requirement. */ - tree expr = ICONV_CONSTR_EXPR (cur); - if (error_operand_p (expr)) + tree expr = TREE_OPERAND (req, 0); + if (constraints_satisfied_p (expr, args)) return; + location_t loc = cp_expr_location (expr); + inform (loc, "nested requirement %qE is not satisfied", expr); - /* Don't elide a previously diagnosed failure. */ - if (elide_constraint_failure_p()) - return; - - tree type = ICONV_CONSTR_TYPE (cur); - if (error_operand_p (type)) - { - inform (loc, "substitution into type %qT failed", - ICONV_CONSTR_TYPE (orig)); - return; - } - - inform(loc, "%qE is not implicitly convertible to %qT", expr, type); + /* TODO: Replay the substitution to diagnose the error? */ + // subst_info noisy (tf_warning_or_error, NULL_TREE); + // constraints_satisfied_p (expr, args, noisy); } -/* Diagnose an argument deduction constraint. */ - -void -diagnose_argument_deduction_constraint (location_t loc, tree orig, tree cur, - tree args) +static void +diagnose_requirement (tree req, tree args, tree in_decl) { - if (constraints_satisfied_p (cur, args)) - return; - - /* The expression and type will previously have been substituted into, - and therefore may already be an error. Also, we will have already - diagnosed substution failures into an expression since this must be - part of a compound requirement. */ - tree expr = DEDUCT_CONSTR_EXPR (cur); - if (error_operand_p (expr)) - return; - - /* Don't elide a previously diagnosed failure. */ - if (elide_constraint_failure_p ()) - return; - - tree pattern = DEDUCT_CONSTR_PATTERN (cur); - if (error_operand_p (pattern)) + iloc_sentinel loc_s (cp_expr_location (req)); + switch (TREE_CODE (req)) { - inform (loc, "substitution into type %qT failed", - DEDUCT_CONSTR_PATTERN (orig)); - return; + case SIMPLE_REQ: + return diagnose_simple_requirement (req, args, in_decl); + case COMPOUND_REQ: + return diagnose_compound_requirement (req, args, in_decl); + case TYPE_REQ: + return diagnose_type_requirement (req, args, in_decl); + case NESTED_REQ: + return diagnose_nested_requirement (req, args); + default: + gcc_unreachable (); } - - inform (loc, "unable to deduce placeholder type %qT from %qE", - pattern, expr); } -/* Diagnose an exception constraint. */ - -void -diagnose_exception_constraint (location_t loc, tree orig, tree cur, tree args) +static void +diagnose_requires_expr (tree expr, tree args, tree in_decl) { - if (constraints_satisfied_p (cur, args)) - return; - if (elide_constraint_failure_p ()) - return; + local_specialization_stack stack (lss_copy); + tree parms = TREE_OPERAND (expr, 0); + tree body = TREE_OPERAND (expr, 1); - /* Rebuild a noexcept expression. */ - tree expr = EXCEPT_CONSTR_EXPR (cur); - if (error_operand_p (expr)) + cp_unevaluated u; + subst_info info (tf_warning_or_error, NULL_TREE); + tree vars = tsubst_constraint_variables (parms, args, info); + if (vars == error_mark_node) return; - inform (loc, "%qE evaluated to false", EXCEPT_CONSTR_EXPR (orig)); + tree p = body; + while (p) + { + tree req = TREE_VALUE (p); + diagnose_requirement (req, args, in_decl); + p = TREE_CHAIN (p); + } } -/* Diagnose a potentially unsatisfied parameterized constraint. */ +/* Diagnose a substitution failure in the atomic constraint T. Note that + ARGS have been previously instantiated through the parameter map. */ -void -diagnose_parameterized_constraint (location_t loc, tree orig, tree cur, - tree args) +static void +diagnose_atomic_constraint (tree t, tree args, subst_info info) { - if (constraints_satisfied_p (cur, args)) - return; - - local_specialization_stack stack; - tree parms = PARM_CONSTR_PARMS (cur); - tree vars = tsubst_constraint_variables (parms, args, tf_warning_or_error, - NULL_TREE); - if (vars == error_mark_node) + /* If the constraint is already ill-formed, we've previously diagnosed + the reason. We should still say why the constraints aren't satisfied. */ + if (t == error_mark_node) { - if (elide_constraint_failure_p ()) - return; - - /* TODO: Check which variable failed and use orig to diagnose - that substitution error. */ - inform (loc, "failed to instantiate constraint variables"); + location_t loc; + if (info.in_decl) + loc = DECL_SOURCE_LOCATION (info.in_decl); + else + loc = input_location; + inform (loc, "invalid constraints"); return; } - /* TODO: It would be better write these in a list. */ - while (vars) - { - inform (loc, " with %q#D", vars); - vars = TREE_CHAIN (vars); - } - orig = PARM_CONSTR_OPERAND (orig); - cur = PARM_CONSTR_OPERAND (cur); - return diagnose_constraint (loc, orig, cur, args); -} - -/* Diagnose the constraint CUR for the given ARGS. This is only ever invoked - on the associated constraints, so we can only have conjunctions of - predicate constraints. The ORIGinal (dependent) constructs follow - the current constraints to enable better diagnostics. Note that ORIG - and CUR must be the same kinds of node, except when CUR is an error. */ + location_t loc = get_constraint_error_location (t); + iloc_sentinel loc_s (loc); -void -diagnose_constraint (location_t loc, tree orig, tree cur, tree args) -{ - switch (TREE_CODE (cur)) + /* Generate better diagnostics for certain kinds of expressions. */ + tree expr = ATOMIC_CONSTR_EXPR (t); + STRIP_ANY_LOCATION_WRAPPER (expr); + switch (TREE_CODE (expr)) { - case EXPR_CONSTR: - diagnose_expression_constraint (loc, orig, cur, args); - break; - - case TYPE_CONSTR: - diagnose_type_constraint (loc, orig, cur, args); - break; - - case ICONV_CONSTR: - diagnose_implicit_conversion_constraint (loc, orig, cur, args); - break; - - case DEDUCT_CONSTR: - diagnose_argument_deduction_constraint (loc, orig, cur, args); - break; - - case EXCEPT_CONSTR: - diagnose_exception_constraint (loc, orig, cur, args); - break; - - case CONJ_CONSTR: - case DISJ_CONSTR: - diagnose_logical_constraint (loc, orig, cur, args); - break; - - case PRED_CONSTR: - diagnose_predicate_constraint (loc, orig, cur, args); - break; - - case PARM_CONSTR: - diagnose_parameterized_constraint (loc, orig, cur, args); - break; - - case CHECK_CONSTR: - diagnose_check_constraint (loc, orig, cur, args); + case TRAIT_EXPR: + diagnose_trait_expr (expr, args); break; - - case EXPR_PACK_EXPANSION: - diagnose_pack_expansion (loc, orig, cur, args); + case REQUIRES_EXPR: + diagnose_requires_expr (expr, args, info.in_decl); break; - - case ERROR_MARK: - /* TODO: Can we improve the diagnostic with the original? */ - inform (input_location, "ill-formed constraint"); + case INTEGER_CST: + /* This must be either 0 or false. */ + inform (loc, "%qE is never satisfied", expr); break; - default: - gcc_unreachable (); - break; + inform (loc, "the expression %qE evaluated to %", expr); } } -/* Diagnose the reason(s) why ARGS do not satisfy the constraints - of declaration DECL. */ - -void -diagnose_declaration_constraints (location_t loc, tree decl, tree args) +diagnosing_failed_constraint:: +diagnosing_failed_constraint (tree t, tree args, bool diag) + : diagnosing_error (diag) { - inform (loc, " constraints not satisfied"); - - /* Constraints are attached to the template. */ - if (tree ti = DECL_TEMPLATE_INFO (decl)) - { - decl = TI_TEMPLATE (ti); - if (!args) - args = TI_ARGS (ti); - } - - /* Recursively diagnose the associated constraints. */ - tree ci = get_constraints (decl); - tree t = CI_ASSOCIATED_CONSTRAINTS (ci); - diagnose_constraint (loc, t, t, args); + if (diagnosing_error) + current_failed_constraint = tree_cons (args, t, current_failed_constraint); } -} // namespace +diagnosing_failed_constraint:: +~diagnosing_failed_constraint () +{ + if (diagnosing_error && current_failed_constraint) + current_failed_constraint = TREE_CHAIN (current_failed_constraint); +} -/* Emit diagnostics detailing the failure ARGS to satisfy the - constraints of T. Here, T can be either a constraint - or a declaration. */ +/* Emit diagnostics detailing the failure ARGS to satisfy the constraints + of T. Here, T can be either a constraint or a declaration. */ void diagnose_constraints (location_t loc, tree t, tree args) { - constraint_errors = 0; + inform (loc, "constraints not satisfied"); - if (constraint_p (t)) - diagnose_constraint (loc, t, t, args); - else if (DECL_P (t)) - diagnose_declaration_constraints (loc, t, args); + /* Replay satisfaction, but diagnose errors. */ + subst_info info (tf_warning_or_error, NULL_TREE); + if (!args) + constraints_satisfied_p (t, info); else - gcc_unreachable (); - - /* Note the number of elided failures. */ - int n = undiagnosed_constraint_failures (); - if (n > 0) - inform (loc, "... and %d more constraint errors not shown", n); + constraints_satisfied_p (t, args, info); } + +#include "gt-cp-constraint.h" diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c index 0a72231456b..f38b4e8cfcb 100644 --- a/gcc/cp/cp-objcp-common.c +++ b/gcc/cp/cp-objcp-common.c @@ -381,6 +381,7 @@ cp_common_init_ts (void) /* New decls. */ MARK_TS_DECL_COMMON (TEMPLATE_DECL); MARK_TS_DECL_COMMON (WILDCARD_DECL); + MARK_TS_DECL_COMMON (CONCEPT_DECL); MARK_TS_DECL_NON_COMMON (USING_DECL); @@ -458,17 +459,11 @@ cp_common_init_ts (void) MARK_TS_EXP (CHECK_CONSTR); MARK_TS_EXP (COMPOUND_REQ); MARK_TS_EXP (CONJ_CONSTR); - MARK_TS_EXP (DEDUCT_CONSTR); MARK_TS_EXP (DISJ_CONSTR); - MARK_TS_EXP (EXCEPT_CONSTR); - MARK_TS_EXP (EXPR_CONSTR); - MARK_TS_EXP (ICONV_CONSTR); + MARK_TS_EXP (ATOMIC_CONSTR); MARK_TS_EXP (NESTED_REQ); - MARK_TS_EXP (PARM_CONSTR); - MARK_TS_EXP (PRED_CONSTR); MARK_TS_EXP (REQUIRES_EXPR); MARK_TS_EXP (SIMPLE_REQ); - MARK_TS_EXP (TYPE_CONSTR); MARK_TS_EXP (TYPE_REQ); c_common_init_ts (); diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def index 4db1d316124..845a7e251f3 100644 --- a/gcc/cp/cp-tree.def +++ b/gcc/cp/cp-tree.def @@ -507,6 +507,11 @@ DEFTREECODE (OMP_DEPOBJ, "omp_depobj", tcc_statement, 2) /* Extensions for Concepts. */ +/* Concept definition. This is not entirely different than a VAR_DECL + except that a) it must be a template, and b) doesn't have the wide + range of value and linkage options available to variables. */ +DEFTREECODE (CONCEPT_DECL, "concept_decl", tcc_declaration, 0) + /* Used to represent information associated with constrained declarations. */ DEFTREECODE (CONSTRAINT_INFO, "constraint_info", tcc_exceptional, 0) @@ -541,10 +546,24 @@ DEFTREECODE (NESTED_REQ, "nested_req", tcc_expression, 1) 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. +/* An atomic constraint evaluates an expression E. The operand of the + constraint is its parameter mapping. The actual expression is stored + in the context. + + ATOMIC_CONSTR_INFO provides source info to support diagnostics. + ATOMIC_CONSTR_EXPR has the expression to be evaluated. + ATOMIC_CONSTR_PARMS is the parameter mapping for the atomic constraint + and is stored in the type field. */ +DEFTREECODE (ATOMIC_CONSTR, "atomic_constr", tcc_expression, 1) + +/* The conjunction and disjunction of two constraints, respectively. + Operands are accessed using TREE_OPERAND. The third operand provides + source info for diagnostics. - PRED_CONSTR_EXPR has the expression to be evaluated. */ -DEFTREECODE (PRED_CONSTR, "pred_constr", tcc_expression, 1) + CONJ_CONSTR_INFO and DISJ_CONSTR_INFO provide access to the source + information of constraints, which is stored in the TREE_TYPE. */ +DEFTREECODE (CONJ_CONSTR, "conj_constr", tcc_expression, 2) +DEFTREECODE (DISJ_CONSTR, "disj_constr", tcc_expression, 2) /* A check constraint represents the checking of a concept C. It has two operands: the template defining the concept @@ -554,53 +573,6 @@ DEFTREECODE (PRED_CONSTR, "pred_constr", tcc_expression, 1) CHECK_CONSTR_ARGUMENTS are the template arguments */ DEFTREECODE (CHECK_CONSTR, "check_constr", tcc_expression, 2) -/* 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 pattern T. - DEDUCT_CONSTR_PLACEHOLDERS has the list of placeholder nodes in T. */ -DEFTREECODE (DEDUCT_CONSTR, "deduct_constr", tcc_expression, 3) - -/* 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: mode:c diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index b82b5808197..9ff617be2d4 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -1522,9 +1522,37 @@ check_constraint_info (tree t) #define PLACEHOLDER_TYPE_CONSTRAINTS(NODE) \ DECL_SIZE_UNIT (TYPE_NAME (NODE)) -/* The expression evaluated by the predicate constraint. */ -#define PRED_CONSTR_EXPR(NODE) \ - TREE_OPERAND (TREE_CHECK (NODE, PRED_CONSTR), 0) +/* Valid for any normalized constraint. */ +#define CONSTR_CHECK(NODE) \ + TREE_CHECK3 (NODE, ATOMIC_CONSTR, CONJ_CONSTR, DISJ_CONSTR) + +/* The CONSTR_INFO stores normalization data for a constraint. It refers to + the original expression and the expression or declaration + from which the constraint was normalized. + + This is TREE_LIST whose TREE_PURPOSE is the original expression and whose + TREE_VALUE is a list of contexts. */ +#define CONSTR_INFO(NODE) \ + TREE_TYPE (CONSTR_CHECK (NODE)) + +/* The expression evaluated by the constraint. */ +#define CONSTR_EXPR(NODE) \ + TREE_PURPOSE (CONSTR_INFO (NODE)) + +/* The expression or declaration from which this constraint was normalized. + This is a TREE_LIST whose TREE_VALUE is either a template-id expression + denoting a concept check or the declaration introducing the constraint. + These are chained to other context objects. */ +#define CONSTR_CONTEXT(NODE) \ + TREE_VALUE (CONSTR_INFO (NODE)) + +/* The parameter mapping for an atomic constraint. */ +#define ATOMIC_CONSTR_MAP(NODE) \ + TREE_OPERAND (TREE_CHECK (NODE, ATOMIC_CONSTR), 0) + +/* The expression of an atomic constraint. */ +#define ATOMIC_CONSTR_EXPR(NODE) \ + CONSTR_EXPR (ATOMIC_CONSTR_CHECK (NODE)) /* The concept of a concept check. */ #define CHECK_CONSTR_CONCEPT(NODE) \ @@ -1534,46 +1562,6 @@ check_constraint_info (tree t) #define CHECK_CONSTR_ARGS(NODE) \ TREE_OPERAND (TREE_CHECK (NODE, CHECK_CONSTR), 1) -/* 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) - -/* In an argument deduction constraint, the list of placeholder nodes. */ -#define DEDUCT_CONSTR_PLACEHOLDER(NODE) \ - TREE_OPERAND (TREE_CHECK (NODE, DEDUCT_CONSTR), 2) - -/* 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) - /* Whether a PARM_DECL represents a local parameter in a requires-expression. */ #define CONSTRAINT_VAR_P(NODE) \ @@ -1663,6 +1651,7 @@ struct GTY(()) saved_scope { int x_processing_template_decl; int x_processing_specialization; + int x_processing_constraint; int suppress_location_wrappers; BOOL_BITFIELD x_processing_explicit_instantiation : 1; BOOL_BITFIELD need_pop_function_context : 1; @@ -2627,7 +2616,8 @@ struct GTY(()) lang_decl_base { || TREE_CODE (NODE) == CONST_DECL \ || TREE_CODE (NODE) == TYPE_DECL \ || TREE_CODE (NODE) == TEMPLATE_DECL \ - || TREE_CODE (NODE) == USING_DECL) + || TREE_CODE (NODE) == USING_DECL \ + || TREE_CODE (NODE) == CONCEPT_DECL) /* DECL_LANG_SPECIFIC for the above codes. */ @@ -3358,6 +3348,33 @@ struct GTY(()) lang_decl { #define TEMPLATE_DECL_COMPLEX_ALIAS_P(NODE) \ DECL_LANG_FLAG_2 (TEMPLATE_DECL_CHECK (NODE)) +/* Returns t iff the node can have a TEMPLATE_INFO field. */ + +inline tree +template_info_decl_check (const_tree t, const char* f, int l, const char* fn) +{ + switch (TREE_CODE (t)) + { + case VAR_DECL: + case FUNCTION_DECL: + case FIELD_DECL: + case TYPE_DECL: + case CONCEPT_DECL: + case TEMPLATE_DECL: + return const_cast(t); + default: + break; + } + tree_check_failed (t, f, l, fn, + VAR_DECL, FUNCTION_DECL, FIELD_DECL, TYPE_DECL, + CONCEPT_DECL, TEMPLATE_DECL, 0); + gcc_unreachable (); +} + + +#define TEMPLATE_INFO_DECL_CHECK(NODE) \ + template_info_decl_check ((NODE), __FILE__, __LINE__, __FUNCTION__) + /* Nonzero for a type which is an alias for another type; i.e, a type which declaration was written 'using name-of-type = another-type'. */ @@ -3367,8 +3384,8 @@ struct GTY(()) lang_decl { && TREE_CODE (TYPE_NAME (NODE)) == TYPE_DECL \ && TYPE_DECL_ALIAS_P (TYPE_NAME (NODE))) -/* If non-NULL for a VAR_DECL, FUNCTION_DECL, TYPE_DECL or - TEMPLATE_DECL, the entity is either a template specialization (if +/* If non-NULL for a VAR_DECL, FUNCTION_DECL, TYPE_DECL, TEMPLATE_DECL, + or CONCEPT_DECL, the entity is either a template specialization (if DECL_USE_TEMPLATE is nonzero) or the abstract instance of the template itself. @@ -3387,7 +3404,7 @@ struct GTY(()) lang_decl { global function f. In this case, DECL_TEMPLATE_INFO for S::f will be non-NULL, but DECL_USE_TEMPLATE will be zero. */ #define DECL_TEMPLATE_INFO(NODE) \ - (DECL_LANG_SPECIFIC (VAR_TEMPL_TYPE_FIELD_OR_FUNCTION_DECL_CHECK (NODE)) \ + (DECL_LANG_SPECIFIC (TEMPLATE_INFO_DECL_CHECK (NODE)) \ ->u.min.template_info) /* For a lambda capture proxy, its captured variable. */ @@ -5222,6 +5239,8 @@ enum tsubst_flags { declaration. */ tf_no_cleanup = 1 << 10, /* Do not build a cleanup (build_target_expr and friends) */ + tf_norm = 1 << 11, /* Build diagnostic information during + constraint normalization. */ /* Convenient substitution flags combinations. */ tf_warning_or_error = tf_warning | tf_error }; @@ -6121,43 +6140,6 @@ class_of_this_parm (const_tree fntype) return TREE_TYPE (type_of_this_parm (fntype)); } -/* True iff T is a variable template declaration. */ -inline bool -variable_template_p (tree t) -{ - if (TREE_CODE (t) != TEMPLATE_DECL) - return false; - if (!PRIMARY_TEMPLATE_P (t)) - return false; - if (tree r = DECL_TEMPLATE_RESULT (t)) - return VAR_P (r); - return false; -} - -/* True iff T is a variable concept definition. That is, T is - a variable template declared with the concept specifier. */ -inline bool -variable_concept_p (tree t) -{ - if (TREE_CODE (t) != TEMPLATE_DECL) - return false; - if (tree r = DECL_TEMPLATE_RESULT (t)) - return VAR_P (r) && DECL_DECLARED_CONCEPT_P (r); - return false; -} - -/* True iff T is a concept definition. That is, T is a variable or function - template declared with the concept specifier. */ -inline bool -concept_template_p (tree t) -{ - if (TREE_CODE (t) != TEMPLATE_DECL) - return false; - if (tree r = DECL_TEMPLATE_RESULT (t)) - return VAR_OR_FUNCTION_DECL_P (r) && DECL_DECLARED_CONCEPT_P (r); - return false; -} - /* A parameter list indicating for a function with no parameters, e.g "int f(void)". */ extern cp_parameter_declarator *no_parameters; @@ -6614,6 +6596,9 @@ extern void finish_eh_spec_block (tree, tree); extern tree build_eh_type_type (tree); extern tree cp_protect_cleanup_actions (void); extern tree create_try_catch_expr (tree, tree); +extern tree template_parms_to_args (tree); +extern tree template_parms_level_to_args (tree); +extern tree generic_targs_for (tree); /* in expr.c */ extern tree cplus_expand_constant (tree); @@ -6747,6 +6732,8 @@ extern void maybe_show_extern_c_location (void); extern bool literal_integer_zerop (const_tree); /* in pt.c */ +extern void push_access_scope (tree); +extern void pop_access_scope (tree); extern bool check_template_shadow (tree); extern bool check_auto_in_tmpl_args (tree, tree); extern tree get_innermost_template_args (tree, int); @@ -6766,6 +6753,8 @@ extern int num_template_headers_for_class (tree); extern void check_template_variable (tree); extern tree make_auto (void); extern tree make_decltype_auto (void); +extern tree make_constrained_auto (tree, tree); +extern tree make_constrained_decltype_auto (tree, tree); extern tree make_template_placeholder (tree); extern bool template_placeholder_p (tree); extern tree do_auto_deduction (tree, tree, tree, @@ -6792,6 +6781,7 @@ extern bool check_default_tmpl_args (tree, tree, bool, bool, int); extern tree push_template_decl (tree); extern tree push_template_decl_real (tree, bool); extern tree add_inherited_template_parms (tree, tree); +extern void template_parm_level_and_index (tree, int*, int*); extern bool redeclare_class_template (tree, tree, tree); extern tree lookup_template_class (tree, tree, tree, tree, int, tsubst_flags_t); @@ -6816,6 +6806,7 @@ extern bool always_instantiate_p (tree); extern bool maybe_instantiate_noexcept (tree, tsubst_flags_t = tf_warning_or_error); extern tree instantiate_decl (tree, bool, bool); extern int comp_template_parms (const_tree, const_tree); +extern bool template_heads_equivalent_p (const_tree, const_tree); extern bool builtin_pack_fn_p (tree); extern tree uses_parameter_packs (tree); extern bool template_parameter_pack_p (const_tree); @@ -6844,7 +6835,11 @@ 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 tsubst_pack_expansion (tree, tree, tsubst_flags_t, tree); +extern tree tsubst_pack_expansion (tree, tree, tsubst_flags_t, tree); +extern tree tsubst_argument_pack (tree, tree, tsubst_flags_t, tree); +extern tree tsubst_template_args (tree, tree, tsubst_flags_t, tree); +extern tree tsubst_template_arg (tree, tree, tsubst_flags_t, tree); +extern tree tsubst_function_parms (tree, tree, tsubst_flags_t, tree); extern tree most_general_template (tree); extern tree get_mostly_instantiated_function_type (tree); extern bool problematic_instantiation_changed (void); @@ -6913,6 +6908,7 @@ extern bool deduction_guide_p (const_tree); extern bool copy_guide_p (const_tree); extern bool template_guide_p (const_tree); extern void store_explicit_specifier (tree, tree); +extern tree add_outermost_template_args (tree, tree); /* in rtti.c */ /* A vector of all tinfo decls that haven't been emitted yet. */ @@ -7077,7 +7073,9 @@ extern tree finish_asm_stmt (location_t, int, tree, tree, extern tree finish_label_stmt (tree); extern void finish_label_decl (tree); extern cp_expr finish_parenthesized_expr (cp_expr); -extern tree force_paren_expr (tree); +extern tree force_paren_expr (tree, bool = false); +inline tree force_paren_expr_uneval (tree t) +{ return force_paren_expr (t, true); } extern tree maybe_undo_parenthesized_ref (tree); extern tree maybe_strip_ref_conversion (tree); extern tree finish_non_static_data_member (tree, tree, tree); @@ -7661,69 +7659,97 @@ typedef void cp_binding_oracle_function (enum cp_oracle_request, tree identifier extern cp_binding_oracle_function *cp_binding_oracle; +/* Set during diagnostics to record the failed constraint. This is a + TREE_LIST whose VALUE is the constraint and whose PURPOSE are the + instantiation arguments Defined in pt.c. */ + +extern tree current_failed_constraint; + +/* An RAII class to manage the failed constraint. */ + +struct diagnosing_failed_constraint +{ + diagnosing_failed_constraint (tree, tree, bool); + ~diagnosing_failed_constraint (); + + bool diagnosing_error; +}; + /* in constraint.cc */ -extern void init_constraint_processing (); -extern bool constraint_p (tree); -extern tree conjoin_constraints (tree, tree); -extern tree conjoin_constraints (tree); + +extern void init_constraint_processing (); +extern cp_expr finish_constraint_or_expr (location_t, cp_expr, cp_expr); +extern cp_expr finish_constraint_and_expr (location_t, cp_expr, cp_expr); +extern cp_expr finish_constraint_primary_expr (cp_expr); +extern tree finish_concept_definition (cp_expr, tree); +extern tree combine_constraint_expressions (tree, tree); extern tree get_constraints (tree); extern void set_constraints (tree, tree); extern void remove_constraints (tree); extern tree current_template_constraints (void); extern tree associate_classtype_constraints (tree); extern tree build_constraints (tree, tree); +extern tree get_template_head_requirements (tree); +extern tree get_trailing_function_requirements (tree); extern tree get_shorthand_constraints (tree); -extern tree build_concept_check (tree, tree, tree = NULL_TREE); + +extern tree build_concept_id (tree); +extern tree build_type_constraint (tree, tree, tsubst_flags_t); +extern tree build_concept_check (tree, tree, tsubst_flags_t); +extern tree build_concept_check (tree, tree, tree, tsubst_flags_t); + +extern tree_pair finish_type_constraints (tree, tree, tsubst_flags_t); extern tree build_constrained_parameter (tree, tree, tree = NULL_TREE); -extern tree make_constrained_auto (tree, tree); extern void placeholder_extract_concept_and_args (tree, tree&, tree&); extern bool equivalent_placeholder_constraints (tree, tree); extern hashval_t hash_placeholder_constraint (tree); extern bool deduce_constrained_parameter (tree, tree&, tree&); extern tree resolve_constraint_check (tree); extern tree check_function_concept (tree); -extern tree finish_template_introduction (tree, tree); +extern tree finish_template_introduction (tree, tree, location_t loc); 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_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_requires_expr (location_t, tree, tree); +extern tree finish_simple_requirement (location_t, tree); +extern tree finish_type_requirement (location_t, tree); +extern tree finish_compound_requirement (location_t, tree, tree, bool); +extern tree finish_nested_requirement (location_t, tree); extern void check_constrained_friend (tree, tree); extern tree tsubst_requires_expr (tree, tree, tsubst_flags_t, tree); extern tree tsubst_constraint (tree, tree, tsubst_flags_t, tree); extern tree tsubst_constraint_info (tree, tree, tsubst_flags_t, tree); -extern bool function_concept_check_p (tree); -extern tree normalize_expression (tree); -extern tree expand_concept (tree, tree); -extern bool expanding_concept (); -extern tree evaluate_constraints (tree, tree); -extern tree evaluate_function_concept (tree, tree); -extern tree evaluate_variable_concept (tree, tree); -extern tree evaluate_constraint_expression (tree, tree); +extern tree tsubst_parameter_mapping (tree, tree, tsubst_flags_t, tree); +extern tree get_mapped_args (tree); + +struct processing_constraint_expression_sentinel +{ + processing_constraint_expression_sentinel (); + ~processing_constraint_expression_sentinel (); +}; + +extern bool processing_constraint_expression_p (); + +extern tree unpack_concept_check (tree); +extern tree evaluate_concept_check (tree, tsubst_flags_t); +extern tree satisfy_constraint_expression (tree); extern bool constraints_satisfied_p (tree); extern bool constraints_satisfied_p (tree, tree); -extern tree lookup_constraint_satisfaction (tree, tree); -extern tree memoize_constraint_satisfaction (tree, tree, tree); -extern tree lookup_concept_satisfaction (tree, tree); -extern tree memoize_concept_satisfaction (tree, tree, tree); -extern tree get_concept_expansion (tree, tree); -extern tree save_concept_expansion (tree, tree, tree); +extern void clear_satisfaction_cache (); extern bool* lookup_subsumption_result (tree, tree); extern bool save_subsumption_result (tree, tree, bool); - +extern tree find_template_parameters (tree, int); extern bool equivalent_constraints (tree, tree); extern bool equivalently_constrained (tree, tree); extern bool subsumes_constraints (tree, tree); -extern bool strictly_subsumes (tree, tree); +extern bool strictly_subsumes (tree, tree, tree); +extern bool weakly_subsumes (tree, tree, tree); extern int more_constrained (tree, tree); - +extern bool atomic_constraints_identical_p (tree, tree); +extern hashval_t hash_atomic_constraint (tree); extern void diagnose_constraints (location_t, tree, tree); /* in logic.cc */ -extern tree decompose_conclusions (tree); extern bool subsumes (tree, tree); /* In class.c */ @@ -7773,7 +7799,7 @@ extern bool var_in_maybe_constexpr_fn (tree); extern void explain_invalid_constexpr_fn (tree); extern vec cx_error_context (void); extern tree fold_sizeof_expr (tree); -extern void clear_cv_and_fold_caches (void); +extern void clear_cv_and_fold_caches (bool = true); extern tree unshare_constructor (tree CXX_MEM_STAT_INFO); /* In cp-ubsan.c */ @@ -7820,6 +7846,104 @@ null_node_p (const_tree expr) return expr == null_node; } +/* True iff T is a variable template declaration. */ +inline bool +variable_template_p (tree t) +{ + if (TREE_CODE (t) != TEMPLATE_DECL) + return false; + if (!PRIMARY_TEMPLATE_P (t)) + return false; + if (tree r = DECL_TEMPLATE_RESULT (t)) + return VAR_P (r); + return false; +} + +/* True iff T is a standard concept definition. This will return + true for both the template and underlying declaration. */ + +inline bool +standard_concept_p (tree t) +{ + if (TREE_CODE (t) == TEMPLATE_DECL) + t = DECL_TEMPLATE_RESULT (t); + return TREE_CODE (t) == CONCEPT_DECL; +} + +/* True iff T is a variable concept definition. This will return + true for both the template and the underlying declaration. */ + +inline bool +variable_concept_p (tree t) +{ + if (TREE_CODE (t) == TEMPLATE_DECL) + t = DECL_TEMPLATE_RESULT (t); + return VAR_P (t) && DECL_DECLARED_CONCEPT_P (t); +} + +/* True iff T is a function concept definition or an overload set + containing multiple function concepts. This will return true for + both the template and the underlying declaration. */ + +inline bool +function_concept_p (tree t) +{ + if (TREE_CODE (t) == OVERLOAD) + t = OVL_FIRST (t); + if (TREE_CODE (t) == TEMPLATE_DECL) + t = DECL_TEMPLATE_RESULT (t); + return TREE_CODE (t) == FUNCTION_DECL && DECL_DECLARED_CONCEPT_P (t); +} + +/* True iff T is a standard, variable, or function concept. */ + +inline bool +concept_definition_p (tree t) +{ + if (t == error_mark_node) + return false; + + /* Adjust for function concept overloads. */ + if (TREE_CODE (t) == OVERLOAD) + t = OVL_FIRST (t); + + /* See through templates. */ + if (TREE_CODE (t) == TEMPLATE_DECL) + t = DECL_TEMPLATE_RESULT (t); + + /* The obvious and easy case. */ + if (TREE_CODE (t) == CONCEPT_DECL) + return true; + + /* Definitely not a concept. */ + if (!VAR_OR_FUNCTION_DECL_P (t)) + return false; + if (!DECL_LANG_SPECIFIC (t)) + return false; + + return DECL_DECLARED_CONCEPT_P (t); +} + +/* Same as above, but for const trees. */ + +inline bool +concept_definition_p (const_tree t) +{ + return concept_definition_p (const_cast (t)); +} + +/* True if t is an expression that checks a concept. */ + +inline bool +concept_check_p (const_tree t) +{ + if (TREE_CODE (t) == CALL_EXPR) + t = CALL_EXPR_FN (t); + if (t && TREE_CODE (t) == TEMPLATE_ID_EXPR) + return concept_definition_p (TREE_OPERAND (t, 0)); + return false; +} + #if CHECKING_P namespace selftest { extern void run_cp_tests (void); diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c index 038f3009039..2a129a3bff7 100644 --- a/gcc/cp/cxx-pretty-print.c +++ b/gcc/cp/cxx-pretty-print.c @@ -38,6 +38,7 @@ static void pp_cxx_typeid_expression (cxx_pretty_printer *, tree); static void pp_cxx_unary_left_fold_expression (cxx_pretty_printer *, tree); static void pp_cxx_unary_right_fold_expression (cxx_pretty_printer *, tree); static void pp_cxx_binary_fold_expression (cxx_pretty_printer *, tree); +static void pp_cxx_concept_definition (cxx_pretty_printer *, tree); static inline void @@ -237,7 +238,14 @@ pp_cxx_template_keyword_if_needed (cxx_pretty_printer *pp, tree scope, tree t) static void pp_cxx_nested_name_specifier (cxx_pretty_printer *pp, tree t) { - if (!SCOPE_FILE_SCOPE_P (t) && t != pp->enclosing_scope) + /* FIXME: When diagnosing references to concepts (especially as types?) + we end up adding too many '::' to the name. This is partially due + to the fact that pp->enclosing_namespace is null. */ + if (t == global_namespace) + { + pp_cxx_colon_colon (pp); + } + else if (!SCOPE_FILE_SCOPE_P (t) && t != pp->enclosing_scope) { tree scope = get_containing_scope (t); pp_cxx_nested_name_specifier (pp, scope); @@ -1214,7 +1222,7 @@ cxx_pretty_printer::expression (tree t) } } break; - + case LAMBDA_EXPR: pp_cxx_ws_string (this, ""); break; @@ -1223,14 +1231,8 @@ cxx_pretty_printer::expression (tree t) pp_cxx_trait_expression (this, t); break; - case PRED_CONSTR: + case ATOMIC_CONSTR: case CHECK_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); @@ -1349,6 +1351,8 @@ cxx_pretty_printer::simple_type_specifier (tree t) case TEMPLATE_PARM_INDEX: case BOUND_TEMPLATE_TEMPLATE_PARM: pp_cxx_unqualified_id (this, t); + if (tree c = PLACEHOLDER_TYPE_CONSTRAINTS (t)) + pp_cxx_constrained_type_spec (this, c); break; case TYPENAME_TYPE: @@ -1876,7 +1880,7 @@ pp_cxx_template_argument_list (cxx_pretty_printer *pp, tree t) { if (argpack) arg = TREE_VEC_ELT (argpack, idx); - + if (need_comma) pp_cxx_separate_with (pp, ','); else @@ -2302,24 +2306,28 @@ pp_cxx_canonical_template_parameter (cxx_pretty_printer *pp, tree parm) void pp_cxx_constrained_type_spec (cxx_pretty_printer *pp, tree c) { - tree t, a; + pp_cxx_whitespace (pp); + pp_cxx_left_bracket (pp); + pp->translate_string ("requires"); + pp_cxx_whitespace (pp); if (c == error_mark_node) { - pp_cxx_ws_string(pp, ""); + pp_cxx_ws_string(pp, ""); return; } + tree t, a; placeholder_extract_concept_and_args (c, t, a); pp->id_expression (t); - if (TREE_VEC_LENGTH (a) > 1) - { - pp_cxx_begin_template_argument_list (pp); - tree args = make_tree_vec (TREE_VEC_LENGTH (a) - 1); - for (int i = TREE_VEC_LENGTH (a) - 1; i > 0; --i) - TREE_VEC_ELT (args, i-1) = TREE_VEC_ELT (a, i); - pp_cxx_template_argument_list (pp, args); - ggc_free (args); - pp_cxx_end_template_argument_list (pp); - } + pp_cxx_begin_template_argument_list (pp); + pp_cxx_ws_string (pp, ""); + pp_cxx_separate_with (pp, ','); + tree args = make_tree_vec (TREE_VEC_LENGTH (a) - 1); + for (int i = 0; i < TREE_VEC_LENGTH (a) - 1; ++i) + TREE_VEC_ELT (args, i) = TREE_VEC_ELT (a, i + 1); + pp_cxx_template_argument_list (pp, args); + ggc_free (args); + pp_cxx_end_template_argument_list (pp); + pp_cxx_right_bracket (pp); } /* @@ -2358,6 +2366,8 @@ pp_cxx_template_declaration (cxx_pretty_printer *pp, tree t) if (TREE_CODE (t) == FUNCTION_DECL && DECL_SAVED_TREE (t)) pp_cxx_function_definition (pp, t); + else if (TREE_CODE (t) == CONCEPT_DECL) + pp_cxx_concept_definition (pp, t); else pp_cxx_simple_declaration (pp, t); } @@ -2374,6 +2384,17 @@ pp_cxx_explicit_instantiation (cxx_pretty_printer *pp, tree t) pp_unsupported_tree (pp, t); } +static void +pp_cxx_concept_definition (cxx_pretty_printer *pp, tree t) +{ + pp_cxx_unqualified_id (pp, DECL_NAME (t)); + pp_cxx_whitespace (pp); + pp_cxx_ws_string (pp, "="); + pp_cxx_whitespace (pp); + pp->expression (DECL_INITIAL (t)); + pp_cxx_semicolon (pp); +} + /* declaration: block-declaration @@ -2841,6 +2862,7 @@ pp_cxx_compound_requirement (cxx_pretty_printer *pp, tree t) if (tree type = TREE_OPERAND (t, 1)) { + pp_cxx_whitespace (pp); pp_cxx_ws_string (pp, "->"); pp->type_id (type); } @@ -2857,12 +2879,6 @@ pp_cxx_nested_requirement (cxx_pretty_printer *pp, tree t) pp_cxx_semicolon (pp); } -void -pp_cxx_predicate_constraint (cxx_pretty_printer *pp, tree t) -{ - pp->expression (TREE_OPERAND (t, 0)); -} - void pp_cxx_check_constraint (cxx_pretty_printer *pp, tree t) { @@ -2871,7 +2887,9 @@ pp_cxx_check_constraint (cxx_pretty_printer *pp, tree t) tree args = CHECK_CONSTR_ARGS (t); tree id = build_nt (TEMPLATE_ID_EXPR, tmpl, args); - if (VAR_P (decl)) + if (TREE_CODE (decl) == CONCEPT_DECL) + pp->expression (id); + else if (VAR_P (decl)) pp->expression (id); else if (TREE_CODE (decl) == FUNCTION_DECL) { @@ -2884,77 +2902,60 @@ pp_cxx_check_constraint (cxx_pretty_printer *pp, tree t) gcc_unreachable (); } -void -pp_cxx_expression_constraint (cxx_pretty_printer *pp, tree t) -{ - pp_string (pp, "expression (TREE_OPERAND (t, 0)); - pp_cxx_right_paren (pp); - pp_string (pp, ">"); -} +/* Output the "[with ...]" clause for a parameter mapping of an atomic + constraint. */ -void -pp_cxx_type_constraint (cxx_pretty_printer *pp, tree t) +static void +pp_cxx_parameter_mapping (cxx_pretty_printer *pp, tree map) { - pp_string (pp, "type_id (TREE_OPERAND (t, 0)); - pp_string (pp, ">"); -} + for (tree p = map; p; p = TREE_CHAIN (p)) + { + tree parm = TREE_VALUE (p); + tree arg = TREE_PURPOSE (p); -void -pp_cxx_implicit_conversion_constraint (cxx_pretty_printer *pp, tree t) -{ - pp_string (pp, "expression (ICONV_CONSTR_EXPR (t)); - pp_cxx_right_paren (pp); - pp_cxx_ws_string (pp, "to"); - pp->type_id (ICONV_CONSTR_TYPE (t)); - pp_string (pp, ">"); -} + if (TYPE_P (parm)) + pp->type_id (parm); + else + pp_cxx_tree_identifier (pp, DECL_NAME (TEMPLATE_PARM_DECL (parm))); -void -pp_cxx_argument_deduction_constraint (cxx_pretty_printer *pp, tree t) -{ - pp_string (pp, "expression (DEDUCT_CONSTR_EXPR (t)); - pp_cxx_right_paren (pp); - pp_cxx_ws_string (pp, "as"); - pp->expression (DEDUCT_CONSTR_PATTERN (t)); - pp_string (pp, ">"); -} + pp_cxx_whitespace (pp); + pp_equal (pp); + pp_cxx_whitespace (pp); -void -pp_cxx_exception_constraint (cxx_pretty_printer *pp, tree t) -{ - pp_cxx_ws_string (pp, "noexcept"); - pp_cxx_whitespace (pp); - pp_cxx_left_paren (pp); - pp->expression (TREE_OPERAND (t, 0)); - pp_cxx_right_paren (pp); + if (TYPE_P (arg) || DECL_TEMPLATE_TEMPLATE_PARM_P (arg)) + pp->type_id (arg); + else + pp->expression (arg); + + if (TREE_CHAIN (p) != NULL_TREE) + pp_cxx_separate_with (pp, ';'); + } } void -pp_cxx_parameterized_constraint (cxx_pretty_printer *pp, tree t) +pp_cxx_atomic_constraint (cxx_pretty_printer *pp, tree t) { - pp_left_paren (pp); - pp_string (pp, "expression (ATOMIC_CONSTR_EXPR (t)); + + /* Emit the parameter mapping. */ + tree map = ATOMIC_CONSTR_MAP (t); + if (map && map != error_mark_node) { - pp_cxx_parameter_declaration_clause (pp, parms); pp_cxx_whitespace (pp); - } - pp_cxx_constraint (pp, PARM_CONSTR_OPERAND (t)); - pp_string (pp, ">"); + pp_cxx_left_bracket (pp); + pp->translate_string ("with"); + pp_cxx_whitespace (pp); + pp_cxx_parameter_mapping (pp, map); + pp_cxx_right_bracket (pp); + } } void pp_cxx_conjunction (cxx_pretty_printer *pp, tree t) { pp_cxx_constraint (pp, TREE_OPERAND (t, 0)); - pp_string (pp, " and "); + pp_string (pp, " /\\ "); pp_cxx_constraint (pp, TREE_OPERAND (t, 1)); } @@ -2962,7 +2963,7 @@ void pp_cxx_disjunction (cxx_pretty_printer *pp, tree t) { pp_cxx_constraint (pp, TREE_OPERAND (t, 0)); - pp_string (pp, " or "); + pp_string (pp, " \\/ "); pp_cxx_constraint (pp, TREE_OPERAND (t, 1)); } @@ -2974,38 +2975,14 @@ pp_cxx_constraint (cxx_pretty_printer *pp, tree t) switch (TREE_CODE (t)) { - case PRED_CONSTR: - pp_cxx_predicate_constraint (pp, t); + case ATOMIC_CONSTR: + pp_cxx_atomic_constraint (pp, t); break; case CHECK_CONSTR: pp_cxx_check_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; diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 72c02afa105..6f4c589b4b1 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -920,6 +920,31 @@ determine_local_discriminator (tree decl) } + +/* Returns true if functions FN1 and FN2 have equivalent trailing + requires clauses. */ + +static bool +function_requirements_equivalent_p (tree newfn, tree oldfn) +{ + /* In the concepts TS, the combined constraints are compared. */ + if (cxx_dialect < cxx2a) + { + tree ci1 = get_constraints (oldfn); + tree ci2 = get_constraints (newfn); + tree req1 = ci1 ? CI_ASSOCIATED_CONSTRAINTS (ci1) : NULL_TREE; + tree req2 = ci2 ? CI_ASSOCIATED_CONSTRAINTS (ci2) : NULL_TREE; + return cp_tree_equal (req1, req2); + } + + /* Compare only trailing requirements. */ + tree reqs1 = get_trailing_function_requirements (newfn); + tree reqs2 = get_trailing_function_requirements (oldfn); + if ((reqs1 != NULL_TREE) != (reqs2 != NULL_TREE)) + return false; + return cp_tree_equal (reqs1, reqs2); +} + /* Subroutine of duplicate_decls: return truthvalue of whether or not types of these decls match. @@ -999,6 +1024,12 @@ decls_match (tree newdecl, tree olddecl, bool record_versions /* = true */) else types_match = 0; + /* Two function declarations match if either has a requires-clause + then both have a requires-clause and their constraints-expressions + are equivalent. */ + if (types_match && flag_concepts) + types_match = function_requirements_equivalent_p (newdecl, olddecl); + /* The decls dont match if they correspond to two different versions of the same function. Disallow extern "C" functions to be versions for now. */ @@ -1013,23 +1044,21 @@ decls_match (tree newdecl, tree olddecl, bool record_versions /* = true */) } else if (TREE_CODE (newdecl) == TEMPLATE_DECL) { + if (!template_heads_equivalent_p (newdecl, olddecl)) + return 0; + tree oldres = DECL_TEMPLATE_RESULT (olddecl); tree newres = DECL_TEMPLATE_RESULT (newdecl); if (TREE_CODE (newres) != TREE_CODE (oldres)) return 0; - if (!comp_template_parms (DECL_TEMPLATE_PARMS (newdecl), - DECL_TEMPLATE_PARMS (olddecl))) - return 0; - - if (TREE_CODE (DECL_TEMPLATE_RESULT (newdecl)) == TYPE_DECL) - types_match = (same_type_p (TREE_TYPE (oldres), TREE_TYPE (newres)) - && equivalently_constrained (olddecl, newdecl)); + /* Two template types match if they are the same. Otherwise, compare + the underlying declarations. */ + if (TREE_CODE (newres) == TYPE_DECL) + types_match = same_type_p (TREE_TYPE (newres), TREE_TYPE (oldres)); else - // We don't need to check equivalently_constrained for variable and - // function templates because we check it on the results. - types_match = decls_match (oldres, newres); + types_match = decls_match (newres, oldres); } else { @@ -1057,11 +1086,6 @@ decls_match (tree newdecl, tree olddecl, bool record_versions /* = true */) COMPARE_REDECLARATION); } - // Normal functions can be constrained, as can variable partial - // specializations. - if (types_match && VAR_OR_FUNCTION_DECL_P (newdecl)) - types_match = equivalently_constrained (newdecl, olddecl); - return types_match; } @@ -1336,6 +1360,46 @@ merge_attribute_bits (tree newdecl, tree olddecl) && lookup_attribute ("gnu_inline", \ DECL_ATTRIBUTES (fn))) +/* A subroutine of duplicate_decls. Emits a diagnostic when newdecl + ambiguates olddecl. Returns true if an error occurs. */ + +static bool +duplicate_function_template_decls (tree newdecl, tree olddecl) +{ + + tree newres = DECL_TEMPLATE_RESULT (newdecl); + tree oldres = DECL_TEMPLATE_RESULT (olddecl); + /* Function template declarations can be differentiated by parameter + and return type. */ + if (compparms (TYPE_ARG_TYPES (TREE_TYPE (oldres)), + TYPE_ARG_TYPES (TREE_TYPE (newres))) + && same_type_p (TREE_TYPE (TREE_TYPE (newdecl)), + TREE_TYPE (TREE_TYPE (olddecl)))) + { + /* ... and also by their template-heads and requires-clauses. */ + if (template_heads_equivalent_p (newdecl, olddecl) + && function_requirements_equivalent_p (newres, oldres)) + { + error ("ambiguating new declaration %q+#D", newdecl); + inform (DECL_SOURCE_LOCATION (olddecl), + "old declaration %q#D", olddecl); + return true; + } + + /* FIXME: The types are the same but the are differences + in either the template heads or function requirements. + We should be able to diagnose a set of common errors + stemming from these declarations. For example: + + template requires C void f(...); + template void f(...) requires C; + + These are functionally equivalent but not equivalent. */ + } + + return false; +} + /* If NEWDECL is a redeclaration of OLDDECL, merge the declarations. If the redeclaration is invalid, a diagnostic is issued, and the error_mark_node is returned. Otherwise, OLDDECL is returned. @@ -1644,11 +1708,14 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend) if (TREE_CODE (newdecl) == TEMPLATE_DECL) { + tree oldres = DECL_TEMPLATE_RESULT (olddecl); + tree newres = DECL_TEMPLATE_RESULT (newdecl); + /* The name of a class template may not be declared to refer to any other template, class, function, object, namespace, value, or type in the same scope. */ - if (TREE_CODE (DECL_TEMPLATE_RESULT (olddecl)) == TYPE_DECL - || TREE_CODE (DECL_TEMPLATE_RESULT (newdecl)) == TYPE_DECL) + if (TREE_CODE (oldres) == TYPE_DECL + || TREE_CODE (newres) == TYPE_DECL) { error_at (newdecl_loc, "conflicting declaration of template %q#D", newdecl); @@ -1656,24 +1723,13 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend) "previous declaration %q#D", olddecl); return error_mark_node; } - else if (TREE_CODE (DECL_TEMPLATE_RESULT (olddecl)) == FUNCTION_DECL - && TREE_CODE (DECL_TEMPLATE_RESULT (newdecl)) == FUNCTION_DECL - && compparms (TYPE_ARG_TYPES (TREE_TYPE (DECL_TEMPLATE_RESULT (olddecl))), - TYPE_ARG_TYPES (TREE_TYPE (DECL_TEMPLATE_RESULT (newdecl)))) - && comp_template_parms (DECL_TEMPLATE_PARMS (newdecl), - DECL_TEMPLATE_PARMS (olddecl)) - /* Template functions can be disambiguated by - return type. */ - && same_type_p (TREE_TYPE (TREE_TYPE (newdecl)), - TREE_TYPE (TREE_TYPE (olddecl))) - /* Template functions can also be disambiguated by - constraints. */ - && equivalently_constrained (olddecl, newdecl)) + + else if (TREE_CODE (oldres) == FUNCTION_DECL + && TREE_CODE (newres) == FUNCTION_DECL) { - error_at (newdecl_loc, "ambiguating new declaration %q#D", - newdecl); - inform (olddecl_loc, - "old declaration %q#D", olddecl); + if (duplicate_function_template_decls (newdecl, olddecl)) + return error_mark_node; + return NULL_TREE; } else if (check_concept_refinement (olddecl, newdecl)) return error_mark_node; @@ -2916,6 +2972,9 @@ redeclaration_error_message (tree newdecl, tree olddecl) return NULL; } + if (TREE_CODE (DECL_TEMPLATE_RESULT (newdecl)) == CONCEPT_DECL) + return G_("redefinition of %q#D"); + if (TREE_CODE (DECL_TEMPLATE_RESULT (newdecl)) != FUNCTION_DECL || (DECL_TEMPLATE_RESULT (newdecl) == DECL_TEMPLATE_RESULT (olddecl))) @@ -8985,12 +9044,12 @@ grokfndecl (tree ctype, tree tmpl_reqs = NULL_TREE; if (processing_template_decl > template_class_depth (ctype)) tmpl_reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms); - - /* Adjust the required expression into a constraint. */ - if (decl_reqs) - decl_reqs = normalize_expression (decl_reqs); - tree ci = build_constraints (tmpl_reqs, decl_reqs); + if (concept_p && ci) + { + error_at (location, "a function concept cannot be constrained"); + ci = NULL_TREE; + } set_constraints (decl, ci); } @@ -9630,12 +9689,18 @@ grokvardecl (tree type, if (!same_type_ignoring_top_level_qualifiers_p (type, boolean_type_node)) error_at (declspecs->locations[ds_type_spec], "concept must have type %"); + if (TEMPLATE_PARMS_CONSTRAINTS (current_template_parms)) + { + error_at (location, "a variable concept cannot be constrained"); + TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = NULL_TREE; + } } else if (flag_concepts && processing_template_decl > template_class_depth (scope)) { tree reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms); tree ci = build_constraints (reqs, NULL_TREE); + set_constraints (decl, ci); } @@ -12543,12 +12608,18 @@ grokdeclarator (const cp_declarator *declarator, if (ctype || in_namespace) error ("cannot use %<::%> in parameter declaration"); - if (type_uses_auto (type) - && !(cxx_dialect >= cxx17 && template_parm_flag)) + tree auto_node = type_uses_auto (type); + if (auto_node && !(cxx_dialect >= cxx17 && template_parm_flag)) { if (cxx_dialect >= cxx14) - error_at (typespec_loc, - "% parameter not permitted in this context"); + { + if (decl_context == PARM && AUTO_IS_DECLTYPE (auto_node)) + error_at (typespec_loc, + "cannot declare a parameter with %"); + else + error_at (typespec_loc, + "% parameter not permitted in this context"); + } else error_at (typespec_loc, "parameter declared %"); type = error_mark_node; @@ -16362,8 +16433,17 @@ finish_function (bool inline_p) if (!processing_template_decl && FNDECL_USED_AUTO (fndecl) && TREE_TYPE (fntype) == DECL_SAVED_AUTO_RETURN_TYPE (fndecl)) { - if (is_auto (DECL_SAVED_AUTO_RETURN_TYPE (fndecl))) + if (is_auto (DECL_SAVED_AUTO_RETURN_TYPE (fndecl)) + && !current_function_returns_value + && !current_function_returns_null) { + /* We haven't applied return type deduction because we haven't + seen any return statements. Do that now. */ + tree node = type_uses_auto (DECL_SAVED_AUTO_RETURN_TYPE (fndecl)); + do_auto_deduction (DECL_SAVED_AUTO_RETURN_TYPE (fndecl), + void_node, node, tf_warning_or_error, + adc_return_type); + apply_deduced_return_type (fndecl, void_type_node); fntype = TREE_TYPE (fndecl); } @@ -16953,7 +17033,9 @@ require_deduced_type (tree decl, tsubst_flags_t complain) { if (undeduced_auto_decl (decl)) { - if (complain & tf_error) + if (TREE_NO_WARNING (decl) && seen_error ()) + /* We probably already complained about deduction failure. */; + else if (complain & tf_error) error ("use of %qD before deduction of %", decl); return false; } diff --git a/gcc/cp/error.c b/gcc/cp/error.c index 56201345429..1fd87d2abea 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -98,6 +98,7 @@ static void print_instantiation_full_context (diagnostic_context *); static void print_instantiation_partial_context (diagnostic_context *, struct tinst_level *, location_t); +static void maybe_print_constraint_context (diagnostic_context *); static void cp_diagnostic_starter (diagnostic_context *, diagnostic_info *); static void cp_print_error_function (diagnostic_context *, diagnostic_info *); @@ -545,9 +546,7 @@ dump_type (cxx_pretty_printer *pp, tree t, int flags) case TEMPLATE_TYPE_PARM: pp_cxx_cv_qualifier_seq (pp, t); - if (tree c = PLACEHOLDER_TYPE_CONSTRAINTS (t)) - pp_cxx_constrained_type_spec (pp, c); - else if (template_placeholder_p (t)) + if (template_placeholder_p (t)) { t = TREE_TYPE (CLASS_PLACEHOLDER_TEMPLATE (t)); pp_cxx_tree_identifier (pp, TYPE_IDENTIFIER (t)); @@ -558,6 +557,9 @@ dump_type (cxx_pretty_printer *pp, tree t, int flags) else pp_cxx_canonical_template_parameter (pp, TEMPLATE_TYPE_PARM_INDEX (t)); + /* If this is a constrained placeholder, add the requirements. */ + if (tree c = PLACEHOLDER_TYPE_CONSTRAINTS (t)) + pp_cxx_constrained_type_spec (pp, c); break; /* This is not always necessary for pointers and such, but doing this @@ -1284,6 +1286,15 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags) dump_template_decl (pp, t, flags); break; + case CONCEPT_DECL: + pp_cxx_ws_string (pp, "concept"); + dump_decl_name (pp, DECL_NAME (t), flags); + break; + + case WILDCARD_DECL: + pp_string (pp, ""); + break; + case TEMPLATE_ID_EXPR: { tree name = TREE_OPERAND (t, 0); @@ -1448,7 +1459,9 @@ dump_template_decl (cxx_pretty_printer *pp, tree t, int flags) else if (DECL_TEMPLATE_RESULT (t) && (VAR_P (DECL_TEMPLATE_RESULT (t)) /* Alias template. */ - || DECL_TYPE_TEMPLATE_P (t))) + || DECL_TYPE_TEMPLATE_P (t) + /* Concept definition. &*/ + || TREE_CODE (DECL_TEMPLATE_RESULT (t)) == CONCEPT_DECL)) dump_decl (pp, DECL_TEMPLATE_RESULT (t), flags | TFF_TEMPLATE_NAME); else { @@ -2082,6 +2095,7 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags) case TEMPLATE_DECL: case NAMESPACE_DECL: case LABEL_DECL: + case WILDCARD_DECL: case OVERLOAD: case TYPE_DECL: case IDENTIFIER_NODE: @@ -2848,18 +2862,14 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags) pp_cxx_nested_requirement (cxx_pp, t); break; - case PRED_CONSTR: + case ATOMIC_CONSTR: case CHECK_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 (cxx_pp, t); - break; + { + pp_cxx_constraint (cxx_pp, t); + break; + } case PLACEHOLDER_EXPR: pp_string (pp, M_("*this")); @@ -3326,6 +3336,7 @@ cp_diagnostic_starter (diagnostic_context *context, cp_print_error_function (context, diagnostic); maybe_print_instantiation_context (context); maybe_print_constexpr_context (context); + maybe_print_constraint_context (context); pp_set_prefix (context->printer, diagnostic_build_prefix (context, diagnostic)); } @@ -3650,6 +3661,171 @@ maybe_print_constexpr_context (diagnostic_context *context) } +static void +print_location (diagnostic_context *context, location_t loc) +{ + expanded_location xloc = expand_location (loc); + if (context->show_column) + pp_verbatim (context->printer, _("%r%s:%d:%d:%R "), + "locus", xloc.file, xloc.line, xloc.column); + else + pp_verbatim (context->printer, _("%r%s:%d:%R "), + "locus", xloc.file, xloc.line); +} + +/* Instantiate the concept check for the purpose of diagnosing an error. */ + +static tree +rebuild_concept_check (tree expr, tree map, tree args) +{ + /* Instantiate the parameter mapping for the template-id. */ + map = tsubst_parameter_mapping (map, args, tf_none, NULL_TREE); + if (map == error_mark_node) + return error_mark_node; + args = get_mapped_args (map); + + /* Rebuild the template id using substituted arguments. Substituting + directly through the expression will trigger recursive satisfaction, + so don't do that. */ + tree id = unpack_concept_check (expr); + args = tsubst_template_args (TREE_OPERAND (id, 1), args, tf_none, NULL_TREE); + if (args == error_mark_node) + return error_mark_node; + return build_nt (TEMPLATE_ID_EXPR, TREE_OPERAND (id, 0), args); +} + +static void +print_constrained_decl_info (diagnostic_context *context, tree decl) +{ + print_location (context, DECL_SOURCE_LOCATION (decl)); + pp_verbatim (context->printer, "required by the constraints of %q#D\n", decl); +} + +static void +print_concept_check_info (diagnostic_context *context, tree expr, tree map, tree args) +{ + gcc_assert (concept_check_p (expr)); + + tree id = unpack_concept_check (expr); + tree tmpl = TREE_OPERAND (id, 0); + if (OVL_P (tmpl)) + tmpl = OVL_FIRST (tmpl); + tree check = rebuild_concept_check (expr, map, args); + if (check == error_mark_node) + check = expr; + + print_location (context, DECL_SOURCE_LOCATION (tmpl)); + pp_verbatim (context->printer, "required for the satisfaction of %qE\n", check); +} + +/* Diagnose the entry point into the satisfaction error. Returns the next + context, if any. */ + +static tree +print_constraint_context_head (diagnostic_context *context, tree cxt, tree args) +{ + tree src = TREE_VALUE (cxt); + if (!src) + { + print_location (context, input_location); + pp_verbatim (context->printer, "required for constraint satisfaction\n"); + return NULL_TREE; + } + if (DECL_P (src)) + { + print_constrained_decl_info (context, src); + return NULL_TREE; + } + else + { + print_concept_check_info (context, src, TREE_PURPOSE (cxt), args); + return TREE_CHAIN (cxt); + } +} + +static void +print_requires_expression_info (diagnostic_context *context, tree constr, tree args) +{ + + tree expr = ATOMIC_CONSTR_EXPR (constr); + tree map = ATOMIC_CONSTR_MAP (constr); + map = tsubst_parameter_mapping (map, args, tf_none, NULL_TREE); + if (map == error_mark_node) + return; + args = get_mapped_args (map); + + print_location (context, cp_expr_loc_or_input_loc (expr)); + pp_verbatim (context->printer, "in requirements "); + + tree parms = TREE_OPERAND (expr, 0); + if (parms) + pp_verbatim (context->printer, "with "); + while (parms) + { + tree next = TREE_CHAIN (parms); + + TREE_CHAIN (parms) = NULL_TREE; + cp_unevaluated u; + tree p = tsubst (parms, args, tf_none, NULL_TREE); + pp_verbatim (context->printer, "%q#D", p); + TREE_CHAIN (parms) = next; + + if (next) + pp_separate_with_comma ((cxx_pretty_printer *)context->printer); + + parms = next; + } + + pp_verbatim (context->printer, "\n"); +} + +void +maybe_print_single_constraint_context (diagnostic_context *context, tree failed) +{ + if (!failed) + return; + + tree constr = TREE_VALUE (failed); + if (!constr || constr == error_mark_node) + return; + tree cxt = CONSTR_CONTEXT (constr); + if (!cxt) + return; + tree args = TREE_PURPOSE (failed); + + /* Print the stack of requirements. */ + cxt = print_constraint_context_head (context, cxt, args); + while (cxt && !DECL_P (TREE_VALUE (cxt))) + { + tree expr = TREE_VALUE (cxt); + tree map = TREE_PURPOSE (cxt); + print_concept_check_info (context, expr, map, args); + cxt = TREE_CHAIN (cxt); + } + + /* For certain constraints, we can provide additional context. */ + if (TREE_CODE (constr) == ATOMIC_CONSTR + && TREE_CODE (ATOMIC_CONSTR_EXPR (constr)) == REQUIRES_EXPR) + print_requires_expression_info (context, constr, args); +} + +void +maybe_print_constraint_context (diagnostic_context *context) +{ + if (!current_failed_constraint) + return; + + tree cur = current_failed_constraint; + + /* Recursively print nested contexts. */ + current_failed_constraint = TREE_CHAIN (current_failed_constraint); + if (current_failed_constraint) + maybe_print_constraint_context (context); + + /* Print this context. */ + maybe_print_single_constraint_context (context, cur); +} + /* Return true iff TYPE_A and TYPE_B are template types that are meaningful to compare. */ diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index c4fed168269..b503e9743cf 100644 --- a/gcc/cp/lambda.c +++ b/gcc/cp/lambda.c @@ -1040,9 +1040,9 @@ maybe_add_lambda_conv_op (tree type) bool const generic_lambda_p = generic_lambda_fn_p (callop); - if (!generic_lambda_p && DECL_INITIAL (callop) == NULL_TREE) + if (!generic_lambda_p && undeduced_auto_decl (callop)) { - /* If the op() wasn't instantiated due to errors, give up. */ + /* If the op() wasn't deduced due to errors, give up. */ gcc_assert (errorcount || sorrycount); return; } diff --git a/gcc/cp/logic.cc b/gcc/cp/logic.cc index 13cc3212436..2d4abaf6edd 100644 --- a/gcc/cp/logic.cc +++ b/gcc/cp/logic.cc @@ -47,729 +47,736 @@ along with GCC; see the file COPYING3. If not see #include "toplev.h" #include "type-utils.h" -namespace { +/* Hash functions for atomic constrains. */ -// Helper algorithms - -template -inline I -next (I iter) +struct constraint_hash : default_hash_traits { - return ++iter; -} + static hashval_t hash (tree t) + { + return hash_atomic_constraint (t); + } -template -inline bool -any_p (I first, I last, P pred) -{ - while (first != last) - { - if (pred(*first)) - return true; - ++first; - } - return false; -} + static bool equal (tree t1, tree t2) + { + return atomic_constraints_identical_p (t1, t2); + } +}; -bool prove_implication (tree, tree); +/* A conjunctive or disjunctive clause. -/*--------------------------------------------------------------------------- - Proof state ----------------------------------------------------------------------------*/ + Each clause maintains an iterator that refers to the current + term, which is used in the linear decomposition of a formula + into CNF or DNF. */ -struct term_entry +struct clause { - tree t; -}; + typedef std::list::iterator iterator; + typedef std::list::const_iterator const_iterator; -/* Hashing function and equality for constraint entries. */ + /* Initialize a clause with an initial term. */ -struct term_hasher : ggc_ptr_hash -{ - static hashval_t hash (term_entry *e) + clause (tree t) { - return iterative_hash_template_arg (e->t, 0); + m_terms.push_back (t); + if (TREE_CODE (t) == ATOMIC_CONSTR) + m_set.add (t); + + m_current = m_terms.begin (); } - static bool equal (term_entry *e1, term_entry *e2) + /* Create a copy of the current term. The current + iterator is set to point to the same position in the + copied list of terms. */ + + clause (clause const& c) + : m_terms (c.m_terms), m_set (c.m_set), m_current (m_terms.begin ()) { - return cp_tree_equal (e1->t, e2->t); + std::advance (m_current, std::distance (c.begin (), c.current ())); } -}; -/* A term list is a list of atomic constraints. It is used - to maintain the lists of assumptions and conclusions in a - proof goal. + /* Returns true when all terms are atoms. */ - 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. */ -class term_list -{ -public: - typedef std::list::iterator iterator; + bool done () const + { + return m_current == end (); + } - term_list (); - term_list (tree); + /* Advance to the next term. */ - bool includes (tree); - iterator insert (iterator, tree); - iterator push_back (tree); - iterator erase (iterator); - iterator replace (iterator, tree); - iterator replace (iterator, tree, tree); + void advance () + { + gcc_assert (!done ()); + ++m_current; + } - iterator begin() { return seq.begin(); } - iterator end() { return seq.end(); } + /* Replaces the current term at position ITER with T. If + T is an atomic constraint that already appears in the + clause, remove but do not replace ITER. Returns a pair + containing an iterator to the replace object or past + the erased object and a boolean value which is true if + an object was erased. */ - std::list seq; - hash_table tab; -}; + std::pair replace (iterator iter, tree t) + { + gcc_assert (TREE_CODE (*iter) != ATOMIC_CONSTR); + if (TREE_CODE (t) == ATOMIC_CONSTR) + { + if (m_set.add (t)) + return std::make_pair (m_terms.erase (iter), true); + } + *iter = t; + return std::make_pair (iter, false); + } -inline -term_list::term_list () - : seq(), tab (11) -{ -} + /* Inserts T before ITER in the list of terms. If T has + already is an atomic constraint that already appears in + the clause, no action is taken, and the current iterator + is returned. Returns a pair of an iterator to the inserted + object or ITER if no insertion occurred and a boolean + value which is true if an object was inserted. */ -/* Initialize a term list with an initial term. */ + std::pair insert (iterator iter, tree t) + { + if (TREE_CODE (t) == ATOMIC_CONSTR) + { + if (m_set.add (t)) + return std::make_pair (iter, false); + } + return std::make_pair (m_terms.insert (iter, t), true); + } -inline -term_list::term_list (tree t) - : seq (), tab (11) -{ - push_back (t); -} + /* Replaces the current term with T. In the case where the + current term is erased (because T is redundant), update + the position of the current term to the next term. */ -/* Returns true if T is the in the tree. */ + void replace (tree t) + { + m_current = replace (m_current, t).first; + } -inline bool -term_list::includes (tree t) -{ - term_entry ent = {t}; - return tab.find (&ent); -} + /* Replace the current term with T1 and T2, in that order. */ -/* Append a term to the list. */ -inline term_list::iterator -term_list::push_back (tree t) -{ - return insert (end(), t); -} + void replace (tree t1, tree t2) + { + /* Replace the current term with t1. Ensure that iter points + to the term before which t2 will be inserted. Update the + current term as needed. */ + std::pair rep = replace (m_current, t1); + if (rep.second) + m_current = rep.first; + else + ++rep.first; -/* Insert a new (unseen) term T into the list before the proposition - indicated by ITER. Returns the iterator to the newly inserted - element. */ + /* Insert the t2. Make this the current term if we erased + the prior term. */ + std::pair ins = insert (rep.first, t2); + if (rep.second && ins.second) + m_current = ins.first; + } -term_list::iterator -term_list::insert (iterator iter, tree t) -{ - gcc_assert (!includes (t)); - iter = seq.insert (iter, t); - term_entry ent = {t}; - term_entry** slot = tab.find_slot (&ent, INSERT); - term_entry* ptr = ggc_alloc (); - *ptr = ent; - *slot = ptr; - return iter; -} + /* Returns true if the clause contains the term T. */ -/* Remove an existing term from the list. Returns an iterator referring - to the element after the removed term. This may be end(). */ + bool contains (tree t) + { + gcc_assert (TREE_CODE (t) == ATOMIC_CONSTR); + return m_set.contains (t); + } -term_list::iterator -term_list::erase (iterator iter) -{ - gcc_assert (includes (*iter)); - term_entry ent = {*iter}; - tab.remove_elt (&ent); - iter = seq.erase (iter); - return iter; -} -/* Replace the given term with that specified. If the term has - been previously seen, do not insert the term. Returns the - first iterator past the current term. */ + /* Returns an iterator to the first clause in the formula. */ -term_list::iterator -term_list::replace (iterator iter, tree t) -{ - iter = erase (iter); - if (!includes (t)) - insert (iter, t); - return iter; -} + iterator begin () + { + return m_terms.begin (); + } + /* Returns an iterator to the first clause in the formula. */ -/* Replace the term at the given position by the supplied T1 - followed by t2. This is used in certain logical operators to - load a list of assumptions or conclusions. */ + const_iterator begin () const + { + return m_terms.begin (); + } -term_list::iterator -term_list::replace (iterator iter, tree t1, tree t2) -{ - iter = erase (iter); - if (!includes (t1)) - insert (iter, t1); - if (!includes (t2)) - insert (iter, t2); - return iter; -} + /* Returns an iterator past the last clause in the formula. */ + + iterator end () + { + return m_terms.end (); + } -/* 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). */ + /* Returns an iterator past the last clause in the formula. */ -class proof_goal -{ -public: - term_list assumptions; - term_list conclusions; + const_iterator end () const + { + return m_terms.end (); + } + + /* Returns the current iterator. */ + + const_iterator current () const + { + return m_current; + } + + std::list m_terms; /* The list of terms. */ + hash_set m_set; /* The set of atomic constraints. */ + iterator m_current; /* The current term. */ }; + /* 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. */ -class proof_state : public std::list +struct formula { -public: - proof_state (); + typedef std::list::iterator iterator; + typedef std::list::const_iterator const_iterator; - iterator branch (iterator i); - iterator discharge (iterator i); -}; + /* Construct a formula with an initial formula in a + single clause. */ -/* Initialize the state with a single empty goal, and set that goal - as the current subgoal. */ - -inline -proof_state::proof_state () - : std::list (1) -{ } + formula (tree t) + { + /* This should call emplace_back(). There's a an extra copy being + invoked by using push_back(). */ + m_clauses.push_back (t); + m_current = m_clauses.begin (); + } + /* Returns true when all clauses are atomic. */ + bool done () const + { + return m_current == end (); + } -/* Branch the current goal by creating a new subgoal, returning a - reference to the new object. This does not update the current goal. */ + /* Advance to the next term. */ + void advance () + { + gcc_assert (!done ()); + ++m_current; + } -inline proof_state::iterator -proof_state::branch (iterator i) -{ - gcc_assert (i != end()); - proof_goal& g = *i; - return insert (++i, g); -} + /* Insert a copy of clause into the formula. This corresponds + to a distribution of one logical operation over the other. */ -/* Discharge the current goal, setting it equal to the - next non-satisfied goal. */ + clause& branch () + { + gcc_assert (!done ()); + m_clauses.push_back (*m_current); + return m_clauses.back (); + } -inline proof_state::iterator -proof_state::discharge (iterator i) -{ - gcc_assert (i != end()); - return erase (i); -} + /* Returns the position of the current clause. */ + iterator current () + { + return m_current; + } -/*--------------------------------------------------------------------------- - Debugging ----------------------------------------------------------------------------*/ + /* Returns an iterator to the first clause in the formula. */ -// void -// debug (term_list& ts) -// { -// for (term_list::iterator i = ts.begin(); i != ts.end(); ++i) -// verbatim (" # %E", *i); -// } -// -// void -// debug (proof_goal& g) -// { -// debug (g.assumptions); -// verbatim (" |-"); -// debug (g.conclusions); -// } + iterator begin () + { + return m_clauses.begin (); + } -/*--------------------------------------------------------------------------- - Atomicity of constraints ----------------------------------------------------------------------------*/ + /* Returns an iterator to the first clause in the formula. */ -/* Returns true if T is not an atomic constraint. */ + const_iterator begin () const + { + return m_clauses.begin (); + } -bool -non_atomic_constraint_p (tree t) -{ - switch (TREE_CODE (t)) - { - case PRED_CONSTR: - case EXPR_CONSTR: - case TYPE_CONSTR: - case ICONV_CONSTR: - case DEDUCT_CONSTR: - case EXCEPT_CONSTR: - /* A pack expansion isn't atomic, but it can't decompose to prove an - atom, so it shouldn't cause analyze_atom to return undecided. */ - case EXPR_PACK_EXPANSION: - return false; - case CHECK_CONSTR: - case PARM_CONSTR: - case CONJ_CONSTR: - case DISJ_CONSTR: - return true; - default: - gcc_unreachable (); - } -} + /* Returns an iterator past the last clause in the formula. */ -/* Returns true if any constraints in T are not atomic. */ + iterator end () + { + return m_clauses.end (); + } -bool -any_non_atomic_constraints_p (term_list& t) -{ - return any_p (t.begin(), t.end(), non_atomic_constraint_p); -} + /* Returns an iterator past the last clause in the formula. */ -/*--------------------------------------------------------------------------- - Proof validations ----------------------------------------------------------------------------*/ + const_iterator end () const + { + return m_clauses.end (); + } -enum proof_result -{ - invalid, - valid, - undecided + std::list m_clauses; /* The list of clauses. */ + iterator m_current; /* The current clause. */ }; -proof_result check_term (term_list&, tree); - - -proof_result -analyze_atom (term_list& ts, tree t) +void +debug (clause& c) { - /* FIXME: Hook into special cases, if any. */ - /* - term_list::iterator iter = ts.begin(); - term_list::iterator end = ts.end(); - while (iter != end) - { - ++iter; - } - */ - - if (non_atomic_constraint_p (t)) - return undecided; - if (any_non_atomic_constraints_p (ts)) - return undecided; - return invalid; + for (clause::iterator i = c.begin(); i != c.end(); ++i) + verbatim (" # %E", *i); } -/* Search for a pack expansion in the list of assumptions that would - make this expansion valid. */ - -proof_result -analyze_pack (term_list& ts, tree t) +void +debug (formula& f) { - tree c1 = normalize_expression (PACK_EXPANSION_PATTERN (t)); - term_list::iterator iter = ts.begin(); - term_list::iterator end = ts.end(); - while (iter != end) + for (formula::iterator i = f.begin(); i != f.end(); ++i) { - if (TREE_CODE (*iter) == TREE_CODE (t)) - { - tree c2 = normalize_expression (PACK_EXPANSION_PATTERN (*iter)); - if (prove_implication (c2, c1)) - return valid; - else - return invalid; - } - ++iter; + verbatim ("((("); + debug (*i); + verbatim (")))"); } - return invalid; } -/* Search for concept checks in TS that we know subsume T. */ +/* The logical rules used to analyze a logical formula. The + "left" and "right" refer to the position of formula in a + sequent (as in sequent calculus). */ -proof_result -search_known_subsumptions (term_list& ts, tree t) +enum rules { - for (term_list::iterator i = ts.begin(); i != ts.end(); ++i) - if (TREE_CODE (*i) == CHECK_CONSTR) - { - if (bool* b = lookup_subsumption_result (*i, t)) - return *b ? valid : invalid; - } - return undecided; -} + left, right +}; -/* Determine if the terms in TS provide sufficient support for proving - the proposition T. If any term in TS is a concept check that is known - to subsume T, then the proof is valid. Otherwise, we have to expand T - and continue searching for support. */ +/* Distribution counting. */ -proof_result -analyze_check (term_list& ts, tree t) +static inline bool +disjunction_p (tree t) { - proof_result r = search_known_subsumptions (ts, t); - if (r != undecided) - return r; - - tree tmpl = CHECK_CONSTR_CONCEPT (t); - tree args = CHECK_CONSTR_ARGS (t); - tree c = expand_concept (tmpl, args); - return check_term (ts, c); + return TREE_CODE (t) == DISJ_CONSTR; } -/* Recursively check constraints of the parameterized constraint. */ - -proof_result -analyze_parameterized (term_list& ts, tree t) +static inline bool +conjunction_p (tree t) { - return check_term (ts, PARM_CONSTR_OPERAND (t)); + return TREE_CODE (t) == CONJ_CONSTR; } -proof_result -analyze_conjunction (term_list& ts, tree t) +static inline bool +atomic_p (tree t) { - proof_result r = check_term (ts, TREE_OPERAND (t, 0)); - if (r == invalid || r == undecided) - return r; - return check_term (ts, TREE_OPERAND (t, 1)); + return TREE_CODE (t) == ATOMIC_CONSTR; } -proof_result -analyze_disjunction (term_list& ts, tree t) -{ - proof_result r = check_term (ts, TREE_OPERAND (t, 0)); - if (r == valid) - return r; - return check_term (ts, TREE_OPERAND (t, 1)); -} +/* Recursively count the number of clauses produced when converting T + to DNF. Returns a pair containing the number of clauses and a bool + value signifying that the the tree would be rewritten as a result of + distributing. In general, a conjunction for which this flag is set + is considered a disjunction for the purpose of counting. */ -proof_result -analyze_term (term_list& ts, tree t) +static std::pair +dnf_size_r (tree t) { - switch (TREE_CODE (t)) - { - case CHECK_CONSTR: - return analyze_check (ts, t); + if (atomic_p (t)) + /* Atomic constraints produce no clauses. */ + return std::make_pair (0, false); - case PARM_CONSTR: - return analyze_parameterized (ts, t); + /* For compound constraints, recursively count clauses and unpack + the results. */ + tree lhs = TREE_OPERAND (t, 0); + tree rhs = TREE_OPERAND (t, 1); + std::pair p1 = dnf_size_r (lhs); + std::pair p2 = dnf_size_r (rhs); + int n1 = p1.first, n2 = p2.first; + bool d1 = p1.second, d2 = p2.second; - case CONJ_CONSTR: - return analyze_conjunction (ts, t); - case DISJ_CONSTR: - return analyze_disjunction (ts, t); - - case PRED_CONSTR: - case EXPR_CONSTR: - case TYPE_CONSTR: - case ICONV_CONSTR: - case DEDUCT_CONSTR: - case EXCEPT_CONSTR: - return analyze_atom (ts, t); - - case EXPR_PACK_EXPANSION: - return analyze_pack (ts, t); - - case ERROR_MARK: - /* Encountering an error anywhere in a constraint invalidates - the proof, since the constraint is ill-formed. */ - return invalid; - default: - gcc_unreachable (); + if (disjunction_p (t)) + { + /* Matches constraints of the form P \/ Q. Disjunctions contribute + linearly to the number of constraints. When both P and Q are + disjunctions, clauses are added. When only one of P and Q + is a disjunction, an additional clause is produced. When neither + P nor Q are disjunctions, two clauses are produced. */ + if (disjunction_p (lhs)) + { + if (disjunction_p (rhs) || (conjunction_p (rhs) && d2)) + /* Both P and Q are disjunctions. */ + return std::make_pair (n1 + n2, d1 | d2); + else + /* Only LHS is a disjunction. */ + return std::make_pair (1 + n1 + n2, d1 | d2); + gcc_unreachable (); + } + if (conjunction_p (lhs)) + { + if ((disjunction_p (rhs) && d1) || (conjunction_p (rhs) && d1 && d2)) + /* Both P and Q are disjunctions. */ + return std::make_pair (n1 + n2, d1 | d2); + if (disjunction_p (rhs) + || (conjunction_p (rhs) && d1 != d2) + || (atomic_p (rhs) && d1)) + /* Either LHS or RHS is a disjunction. */ + return std::make_pair (1 + n1 + n2, d1 | d2); + else + /* Neither LHS nor RHS is a disjunction. */ + return std::make_pair (2, false); + } + if (atomic_p (lhs)) + { + if (disjunction_p (rhs) || (conjunction_p (rhs) && d2)) + /* Only RHS is a disjunction. */ + return std::make_pair (1 + n1 + n2, d1 | d2); + else + /* Neither LHS nor RHS is a disjunction. */ + return std::make_pair (2, false); + } + } + else /* conjunction_p (t) */ + { + /* Matches constraints of the form P /\ Q, possibly resulting + in the distribution of one side over the other. When both + P and Q are disjunctions, the number of clauses are multiplied. + When only one of P and Q is a disjunction, the the number of + clauses are added. Otherwise, neither side is a disjunction and + no clauses are created. */ + if (disjunction_p (lhs)) + { + if (disjunction_p (rhs) || (conjunction_p (rhs) && d2)) + /* Both P and Q are disjunctions. */ + return std::make_pair (n1 * n2, true); + else + /* Only LHS is a disjunction. */ + return std::make_pair (n1 + n2, true); + gcc_unreachable (); + } + if (conjunction_p (lhs)) + { + if ((disjunction_p (rhs) && d1) || (conjunction_p (rhs) && d1 && d2)) + /* Both P and Q are disjunctions. */ + return std::make_pair (n1 * n2, true); + if (disjunction_p (rhs) + || (conjunction_p (rhs) && d1 != d2) + || (atomic_p (rhs) && d1)) + /* Either LHS or RHS is a disjunction. */ + return std::make_pair (n1 + n2, true); + else + /* Neither LHS nor RHS is a disjunction. */ + return std::make_pair (0, false); + } + if (atomic_p (lhs)) + { + if (disjunction_p (rhs) || (conjunction_p (rhs) && d2)) + /* Only RHS is a disjunction. */ + return std::make_pair (n1 + n2, true); + else + /* Neither LHS nor RHS is a disjunction. */ + return std::make_pair (0, false); + } } + gcc_unreachable (); } -/* Check if a single term can be proven from a set of assumptions. - If the proof is not valid, then it is incomplete when either - the given term is non-atomic or any term in the list of assumptions - is not-atomic. */ +/* Recursively count the number of clauses produced when converting T + to CNF. Returns a pair containing the number of clauses and a bool + value signifying that the the tree would be rewritten as a result of + distributing. In general, a disjunction for which this flag is set + is considered a conjunction for the purpose of counting. */ -proof_result -check_term (term_list& ts, tree t) +static std::pair +cnf_size_r (tree t) { - /* Try the easy way; search for an equivalent term. */ - if (ts.includes (t)) - return valid; + if (atomic_p (t)) + /* Atomic constraints produce no clauses. */ + return std::make_pair (0, false); - /* The hard way; actually consider what the term means. */ - return analyze_term (ts, t); -} + /* For compound constraints, recursively count clauses and unpack + the results. */ + tree lhs = TREE_OPERAND (t, 0); + tree rhs = TREE_OPERAND (t, 1); + std::pair p1 = cnf_size_r (lhs); + std::pair p2 = cnf_size_r (rhs); + int n1 = p1.first, n2 = p2.first; + bool d1 = p1.second, d2 = p2.second; -/* Check to see if any term is proven by the assumptions in the - proof goal. The proof is valid if the proof of any term is valid. - If validity cannot be determined, but any particular - check was undecided, then this goal is undecided. */ - -proof_result -check_goal (proof_goal& g) -{ - term_list::iterator iter = g.conclusions.begin (); - term_list::iterator end = g.conclusions.end (); - bool incomplete = false; - while (iter != end) + if (disjunction_p (t)) { - proof_result r = check_term (g.assumptions, *iter); - if (r == valid) - return r; - if (r == undecided) - incomplete = true; - ++iter; + /* Matches constraints of the form P \/ Q, possibly resulting + in the distribution of one side over the other. When both + P and Q are conjunctions, the number of clauses are multiplied. + When only one of P and Q is a conjunction, the the number of + clauses are added. Otherwise, neither side is a conjunction and + no clauses are created. */ + if (disjunction_p (lhs)) + { + if ((disjunction_p (rhs) && d1 && d2) || (conjunction_p (rhs) && d1)) + /* Both P and Q are conjunctions. */ + return std::make_pair (n1 * n2, true); + if ((disjunction_p (rhs) && d1 != d2) + || conjunction_p (rhs) + || (atomic_p (rhs) && d1)) + /* Either LHS or RHS is a conjunction. */ + return std::make_pair (n1 + n2, true); + else + /* Neither LHS nor RHS is a conjunction. */ + return std::make_pair (0, false); + gcc_unreachable (); + } + if (conjunction_p (lhs)) + { + if ((disjunction_p (rhs) && d2) || conjunction_p (rhs)) + /* Both LHS and RHS are conjunctions. */ + return std::make_pair (n1 * n2, true); + else + /* Only LHS is a conjunction. */ + return std::make_pair (n1 + n2, true); + } + if (atomic_p (lhs)) + { + if ((disjunction_p (rhs) && d2) || conjunction_p (rhs)) + /* Only RHS is a disjunction. */ + return std::make_pair (n1 + n2, true); + else + /* Neither LHS nor RHS is a disjunction. */ + return std::make_pair (0, false); + } } - - /* Was the proof complete? */ - if (incomplete) - return undecided; - else - return invalid; -} - -/* Check if the the proof is valid. This is the case when all - goals can be discharged. If any goal is invalid, then the - entire proof is invalid. Otherwise, the proof is undecided. */ - -proof_result -check_proof (proof_state& p) -{ - proof_state::iterator iter = p.begin(); - proof_state::iterator end = p.end(); - while (iter != end) + else /* conjunction_p (t) */ { - proof_result r = check_goal (*iter); - if (r == invalid) - return r; - if (r == valid) - iter = p.discharge (iter); - else - ++iter; + /* Matches constraints of the form P /\ Q. Conjunctions contribute + linearly to the number of constraints. When both P and Q are + conjunctions, clauses are added. When only one of P and Q + is a conjunction, an additional clause is produced. When neither + P nor Q are conjunctions, two clauses are produced. */ + if (disjunction_p (lhs)) + { + if ((disjunction_p (rhs) && d1 && d2) || (conjunction_p (rhs) && d1)) + /* Both P and Q are conjunctions. */ + return std::make_pair (n1 + n2, d1 | d2); + if ((disjunction_p (rhs) && d1 != d2) + || conjunction_p (rhs) + || (atomic_p (rhs) && d1)) + /* Either LHS or RHS is a conjunction. */ + return std::make_pair (1 + n1 + n2, d1 | d2); + else + /* Neither LHS nor RHS is a conjunction. */ + return std::make_pair (2, false); + gcc_unreachable (); + } + if (conjunction_p (lhs)) + { + if ((disjunction_p (rhs) && d2) || conjunction_p (rhs)) + /* Both LHS and RHS are conjunctions. */ + return std::make_pair (n1 + n2, d1 | d2); + else + /* Only LHS is a conjunction. */ + return std::make_pair (1 + n1 + n2, d1 | d2); + } + if (atomic_p (lhs)) + { + if ((disjunction_p (rhs) && d2) || conjunction_p (rhs)) + /* Only RHS is a disjunction. */ + return std::make_pair (1 + n1 + n2, d1 | d2); + else + /* Neither LHS nor RHS is a disjunction. */ + return std::make_pair (2, false); + } } - - /* If all goals are discharged, then the proof is valid. */ - if (p.empty()) - return valid; - else - return undecided; + gcc_unreachable (); } -/*--------------------------------------------------------------------------- - Left logical rules ----------------------------------------------------------------------------*/ +/* Count the number conjunctive clauses that would be created + when rewriting T to DNF. */ -term_list::iterator -load_check_assumption (term_list& ts, term_list::iterator i) +static int +dnf_size (tree t) { - tree decl = CHECK_CONSTR_CONCEPT (*i); - tree tmpl = DECL_TI_TEMPLATE (decl); - tree args = CHECK_CONSTR_ARGS (*i); - return ts.replace(i, expand_concept (tmpl, args)); + std::pair result = dnf_size_r (t); + return result.first == 0 ? 1 : result.first; } -term_list::iterator -load_parameterized_assumption (term_list& ts, term_list::iterator i) -{ - return ts.replace(i, PARM_CONSTR_OPERAND(*i)); -} -term_list::iterator -load_conjunction_assumption (term_list& ts, term_list::iterator i) +/* Count the number disjunctive clauses that would be created + when rewriting T to CNF. */ + +static int +cnf_size (tree t) { - tree t1 = TREE_OPERAND (*i, 0); - tree t2 = TREE_OPERAND (*i, 1); - return ts.replace(i, t1, t2); + std::pair result = cnf_size_r (t); + return result.first == 0 ? 1 : result.first; } -/* Examine the terms in the list, and apply left-logical rules to move - terms into the set of assumptions. */ + +/* A left-conjunction is replaced by its operands. */ void -load_assumptions (proof_goal& g) +replace_term (clause& c, tree t) { - term_list::iterator iter = g.assumptions.begin(); - term_list::iterator end = g.assumptions.end(); - while (iter != end) - { - switch (TREE_CODE (*iter)) - { - case CHECK_CONSTR: - iter = load_check_assumption (g.assumptions, iter); - break; - case PARM_CONSTR: - iter = load_parameterized_assumption (g.assumptions, iter); - break; - case CONJ_CONSTR: - iter = load_conjunction_assumption (g.assumptions, iter); - break; - default: - ++iter; - break; - } - } + tree t1 = TREE_OPERAND (t, 0); + tree t2 = TREE_OPERAND (t, 1); + return c.replace (t1, t2); } -/* In each subgoal, load constraints into the assumption set. */ +/* Create a new clause in the formula by copying the current + clause. In the current clause, the term at CI is replaced + by the first operand, and in the new clause, it is replaced + by the second. */ void -load_assumptions(proof_state& p) +branch_clause (formula& f, clause& c1, tree t) { - proof_state::iterator iter = p.begin(); - while (iter != p.end()) - { - load_assumptions (*iter); - ++iter; - } + tree t1 = TREE_OPERAND (t, 0); + tree t2 = TREE_OPERAND (t, 1); + clause& c2 = f.branch (); + c1.replace (t1); + c2.replace (t2); } -void -explode_disjunction (proof_state& p, proof_state::iterator gi, term_list::iterator ti1) -{ - tree t1 = TREE_OPERAND (*ti1, 0); - tree t2 = TREE_OPERAND (*ti1, 1); +/* Decompose t1 /\ t2 according to the rules R. */ - /* Erase the current term from the goal. */ - proof_goal& g1 = *gi; - proof_goal& g2 = *p.branch (gi); +inline void +decompose_conjuntion (formula& f, clause& c, tree t, rules r) +{ + if (r == left) + replace_term (c, t); + else + branch_clause (f, c, t); +} - /* Get an iterator to the equivalent position in th enew goal. */ - int n = std::distance (g1.assumptions.begin (), ti1); - term_list::iterator ti2 = g2.assumptions.begin (); - std::advance (ti2, n); +/* Decompose t1 \/ t2 according to the rules R. */ - /* Replace the disjunction in both branches. */ - g1.assumptions.replace (ti1, t1); - g2.assumptions.replace (ti2, t2); +inline void +decompose_disjunction (formula& f, clause& c, tree t, rules r) +{ + if (r == right) + replace_term (c, t); + else + branch_clause (f, c, t); } +/* An atomic constraint is already decomposed. */ +inline void +decompose_atom (clause& c) +{ + c.advance (); +} -/* Search the assumptions of the goal for the first disjunction. */ +/* Decompose a term of clause C (in formula F) according to the + logical rules R. */ -bool -explode_goal (proof_state& p, proof_state::iterator gi) +void +decompose_term (formula& f, clause& c, tree t, rules r) { - term_list& ts = gi->assumptions; - term_list::iterator ti = ts.begin(); - term_list::iterator end = ts.end(); - while (ti != end) + switch (TREE_CODE (t)) { - if (TREE_CODE (*ti) == DISJ_CONSTR) - { - explode_disjunction (p, gi, ti); - return true; - } - else ++ti; + case CONJ_CONSTR: + return decompose_conjuntion (f, c, t, r); + case DISJ_CONSTR: + return decompose_disjunction (f, c, t, r); + default: + return decompose_atom (c); } - return false; } -/* Search for the first goal with a disjunction, and then branch - creating a clone of that subgoal. */ +/* Decompose C (in F) using the logical rules R until it + is comprised of only atomic constraints. */ void -explode_assumptions (proof_state& p) +decompose_clause (formula& f, clause& c, rules r) { - proof_state::iterator iter = p.begin(); - proof_state::iterator end = p.end(); - while (iter != end) - { - if (explode_goal (p, iter)) - return; - ++iter; - } + while (!c.done ()) + decompose_term (f, c, *c.current (), r); + f.advance (); } +/* Decompose the logical formula F according to the logical + rules determined by R. The result is a formula containing + clauses that contain only atomic terms. */ -/*--------------------------------------------------------------------------- - Right logical rules ----------------------------------------------------------------------------*/ - -term_list::iterator -load_disjunction_conclusion (term_list& g, term_list::iterator i) +void +decompose_formula (formula& f, rules r) { - tree t1 = TREE_OPERAND (*i, 0); - tree t2 = TREE_OPERAND (*i, 1); - return g.replace(i, t1, t2); + while (!f.done ()) + decompose_clause (f, *f.current (), r); } -/* Apply logical rules to the right hand side. This will load the - conclusion set with all tpp-level disjunctions. */ +/* Fully decomposing T into a list of sequents, each comprised of + a list of atomic constraints, as if T were an antecedent. */ -void -load_conclusions (proof_goal& g) +static formula +decompose_antecedents (tree t) { - term_list::iterator iter = g.conclusions.begin(); - term_list::iterator end = g.conclusions.end(); - while (iter != end) - { - if (TREE_CODE (*iter) == DISJ_CONSTR) - iter = load_disjunction_conclusion (g.conclusions, iter); - else - ++iter; - } + formula f (t); + decompose_formula (f, left); + return f; } -void -load_conclusions (proof_state& p) +/* Fully decomposing T into a list of sequents, each comprised of + a list of atomic constraints, as if T were a consequent. */ + +static formula +decompose_consequents (tree t) { - proof_state::iterator iter = p.begin(); - while (iter != p.end()) - { - load_conclusions (*iter); - ++iter; - } + formula f (t); + decompose_formula (f, right); + return f; } +static bool derive_proof (clause&, tree, rules); -/*--------------------------------------------------------------------------- - High-level proof tactics ----------------------------------------------------------------------------*/ +/* Derive a proof of both operands of T. */ -/* Given two constraints A and C, try to derive a proof that - A implies C. */ +static bool +derive_proof_for_both_operands (clause& c, tree t, rules r) +{ + if (!derive_proof (c, TREE_OPERAND (t, 0), r)) + return false; + return derive_proof (c, TREE_OPERAND (t, 1), r); +} -bool -prove_implication (tree a, tree c) +/* Derive a proof of either operand of T. */ + +static bool +derive_proof_for_either_operand (clause& c, tree t, rules r) { - /* Quick accept. */ - if (cp_tree_equal (a, c)) + if (derive_proof (c, TREE_OPERAND (t, 0), r)) return true; + return derive_proof (c, TREE_OPERAND (t, 1), r); +} - /* Build the initial proof state. */ - proof_state proof; - proof_goal& goal = proof.front(); - goal.assumptions.push_back(a); - goal.conclusions.push_back(c); - - /* Perform an initial right-expansion in the off-chance that the right - hand side contains disjunctions. */ - load_conclusions (proof); +/* Derive a proof of the atomic constraint T in clause C. */ - int step_max = 1 << 10; - int step_count = 0; /* FIXME: We shouldn't have this. */ - std::size_t branch_limit = 1024; /* FIXME: This needs to be configurable. */ - while (step_count < step_max && proof.size() < branch_limit) - { - /* Determine if we can prove that the assumptions entail the - conclusions. If so, we're done. */ - load_assumptions (proof); +static bool +derive_atomic_proof (clause& c, tree t) +{ + return c.contains (t); +} - /* Can we solve the proof based on this? */ - proof_result r = check_proof (proof); - if (r != undecided) - return r == valid; +/* Derive a proof of T from the terms in C. */ - /* If not, then we need to dig into disjunctions. */ - explode_assumptions (proof); +static bool +derive_proof (clause& c, tree t, rules r) +{ + switch (TREE_CODE (t)) + { + case CONJ_CONSTR: + if (r == left) + return derive_proof_for_both_operands (c, t, r); + else + return derive_proof_for_either_operand (c, t, r); + case DISJ_CONSTR: + if (r == left) + return derive_proof_for_either_operand (c, t, r); + else + return derive_proof_for_both_operands (c, t, r); + default: + return derive_atomic_proof (c, t); + } +} - ++step_count; - } +/* Derive a proof of T from disjunctive clauses in F. */ - if (step_count == step_max) - error ("subsumption failed to resolve"); +static bool +derive_proofs (formula& f, tree t, rules r) +{ + for (formula::iterator i = f.begin(); i != f.end(); ++i) + if (!derive_proof (*i, t, r)) + return false; + return true; +} - if (proof.size() == branch_limit) - error ("exceeded maximum number of branches"); +/* The largest number of clauses in CNF or DNF we accept as input + for subsumption. This an upper bound of 2^16 expressions. */ +static int max_problem_size = 16; +static inline bool +diagnose_constraint_size (tree t) +{ + error_at (input_location, "%qE exceeds the maximum constraint complexity", t); return false; } @@ -777,31 +784,51 @@ prove_implication (tree a, tree c) This is done by deriving a proof of the conclusions on the RIGHT from the assumptions on the LEFT assumptions. */ -bool -subsumes_constraints_nonnull (tree left, tree right) +static bool +subsumes_constraints_nonnull (tree lhs, tree rhs) { - gcc_assert (check_constraint_info (left)); - gcc_assert (check_constraint_info (right)); - auto_timevar time (TV_CONSTRAINT_SUB); - tree a = CI_ASSOCIATED_CONSTRAINTS (left); - tree c = CI_ASSOCIATED_CONSTRAINTS (right); - return prove_implication (a, c); -} -} /* namespace */ + int n1 = dnf_size (lhs); + int n2 = cnf_size (rhs); + + /* Make sure we haven't exceeded the largest acceptable problem. */ + if (std::min (n1, n2) >= max_problem_size) + { + if (n1 < n2) + diagnose_constraint_size (lhs); + else + diagnose_constraint_size (rhs); + return false; + } + + /* Decompose the smaller of the two formulas, and recursively + check the implication using the larger. Note that for + constraints that are largely comprised of conjunctions the + it will usually be the case that n1 <= n2. */ + if (n1 <= n2) + { + formula dnf = decompose_antecedents (lhs); + return derive_proofs (dnf, rhs, left); + } + else + { + formula cnf = decompose_consequents (rhs); + return derive_proofs (cnf, lhs, right); + } +} /* Returns true if the LEFT constraints subsume the RIGHT constraints. */ bool -subsumes (tree left, tree right) +subsumes (tree lhs, tree rhs) { - if (left == right) + if (lhs == rhs) return true; - if (!left) + if (!lhs || lhs == error_mark_node) return false; - if (!right) + if (!rhs || rhs == error_mark_node) return true; - return subsumes_constraints_nonnull (left, right); + return subsumes_constraints_nonnull (lhs, rhs); } diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index ff6d5ee6984..57ab129c9ec 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -2268,7 +2268,13 @@ diagnose_name_conflict (tree decl, tree bval) && (TREE_CODE (decl) != TYPE_DECL || DECL_ARTIFICIAL (decl) == DECL_ARTIFICIAL (bval)) && CP_DECL_CONTEXT (decl) == CP_DECL_CONTEXT (bval)) - error ("redeclaration of %q#D", decl); + { + if (concept_definition_p (decl)) + error ("redeclaration of %q#D with different template parameters", + decl); + else + error ("redeclaration of %q#D", decl); + } else error ("%q#D conflicts with a previous declaration", decl); @@ -2334,6 +2340,9 @@ matching_fn_p (tree one, tree two) return false; } + if (!equivalently_constrained (one, two)) + return false; + return true; } diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index c61e0b2cb9d..b6e738c67d6 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -169,6 +169,7 @@ enum required_token { RT_TRY, /* try */ RT_CATCH, /* catch */ RT_THROW, /* throw */ + RT_AUTO, /* auto */ RT_LABEL, /* __label__ */ RT_AT_TRY, /* @try */ RT_AT_SYNCHRONIZED, /* @synchronized */ @@ -2196,6 +2197,8 @@ static tree cp_parser_type_specifier int *, bool *); static tree cp_parser_simple_type_specifier (cp_parser *, cp_decl_specifier_seq *, cp_parser_flags); +static tree cp_parser_placeholder_type_specifier + (cp_parser *, location_t, tree, bool); static tree cp_parser_type_name (cp_parser *, bool); static tree cp_parser_nonclass_name @@ -2369,6 +2372,8 @@ static tree cp_parser_type_parameter (cp_parser *, bool *); static tree cp_parser_template_id (cp_parser *, bool, bool, enum tag_types, bool); +static tree cp_parser_template_id_expr + (cp_parser *, bool, bool, bool); static tree cp_parser_template_name (cp_parser *, bool, bool, bool, enum tag_types, bool *); static tree cp_parser_template_argument_list @@ -2444,7 +2449,9 @@ static void cp_parser_label_declaration /* Concept Extensions */ -static tree cp_parser_requires_clause +static tree cp_parser_concept_definition + (cp_parser *); +static tree cp_parser_constraint_expression (cp_parser *); static tree cp_parser_requires_clause_opt (cp_parser *); @@ -2454,7 +2461,7 @@ static tree cp_parser_requirement_parameter_list (cp_parser *); static tree cp_parser_requirement_body (cp_parser *); -static tree cp_parser_requirement_list +static tree cp_parser_requirement_seq (cp_parser *); static tree cp_parser_requirement (cp_parser *); @@ -2687,11 +2694,6 @@ static bool cp_parser_init_statement_p static bool cp_parser_skip_to_closing_square_bracket (cp_parser *); -/* Concept-related syntactic transformations */ - -static tree cp_parser_maybe_concept_name (cp_parser *, tree); -static tree cp_parser_maybe_partial_concept_id (cp_parser *, tree, tree); - // -------------------------------------------------------------------------- // // Unevaluated Operand Guard // @@ -4874,6 +4876,8 @@ class token_pair m_open_loc); } + location_t open_location () const { return m_open_loc; } + private: location_t m_open_loc; }; @@ -4948,7 +4952,7 @@ cp_parser_statement_expr (cp_parser *parser) This returns the tree code corresponding to the matched operator as an int. When the current token matches a compound assignment - opertor, the resulting tree code is the negative value of the + operator, the resulting tree code is the negative value of the non-assignment operator. */ static int @@ -5904,11 +5908,10 @@ cp_parser_id_expression (cp_parser *parser, cp_parser_parse_tentatively (parser); /* Try a template-id. */ - id = cp_parser_template_id (parser, - /*template_keyword_p=*/false, - /*check_dependency_p=*/true, - none_type, - declarator_p); + id = cp_parser_template_id_expr (parser, + /*template_keyword_p=*/false, + /*check_dependency_p=*/true, + declarator_p); /* If that worked, we're done. */ if (cp_parser_parse_definitely (parser)) return id; @@ -5983,10 +5986,9 @@ cp_parser_unqualified_id (cp_parser* parser, template-id. */ cp_parser_parse_tentatively (parser); /* Try a template-id. */ - id = cp_parser_template_id (parser, template_keyword_p, - check_dependency_p, - none_type, - declarator_p); + id = cp_parser_template_id_expr (parser, template_keyword_p, + check_dependency_p, + declarator_p); /* If it worked, we're done. */ if (cp_parser_parse_definitely (parser)) return id; @@ -5995,10 +5997,9 @@ cp_parser_unqualified_id (cp_parser* parser, } case CPP_TEMPLATE_ID: - return cp_parser_template_id (parser, template_keyword_p, - check_dependency_p, - none_type, - declarator_p); + return cp_parser_template_id_expr (parser, template_keyword_p, + check_dependency_p, + declarator_p); case CPP_COMPL: { @@ -6239,10 +6240,9 @@ cp_parser_unqualified_id (cp_parser* parser, /* This could be a template-id, so we try that first. */ cp_parser_parse_tentatively (parser); /* Try a template-id. */ - id = cp_parser_template_id (parser, template_keyword_p, - /*check_dependency_p=*/true, - none_type, - declarator_p); + id = cp_parser_template_id_expr (parser, template_keyword_p, + /*check_dependency_p=*/true, + declarator_p); /* If that worked, we're done. */ if (cp_parser_parse_definitely (parser)) return id; @@ -9638,6 +9638,8 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p, current.lhs = error_mark_node; else { + current.lhs.maybe_add_location_wrapper (); + rhs.maybe_add_location_wrapper (); current.lhs = build_min (current.tree_type, TREE_CODE_CLASS (current.tree_type) @@ -14059,10 +14061,26 @@ cp_parser_decl_specifier_seq (cp_parser* parser, case RID_CONCEPT: ds = ds_concept; cp_lexer_consume_token (parser->lexer); + + /* Warn for concept as a decl-specifier. We'll rewrite these as + concept declarations later. */ + if (!flag_concepts_ts) + { + cp_token *next = cp_lexer_peek_token (parser->lexer); + if (next->keyword == RID_BOOL) + pedwarn (next->location, 0, "the % keyword is not " + "allowed in a C++20 concept definition"); + else + pedwarn (token->location, 0, "C++20 concept definition syntax " + "is % = %> "); + } + /* In C++20 a concept definition is just 'concept name = expr;' - Support that syntax by pretending we've seen 'bool'. */ + Support that syntax as a TS extension by pretending we've seen + the 'bool' specifier. */ if (cp_lexer_next_token_is (parser->lexer, CPP_NAME) - && cp_lexer_nth_token_is (parser->lexer, 2, CPP_EQ)) + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_EQ) + && !decl_specs->any_type_specifiers_p) { cp_parser_set_decl_spec_type (decl_specs, boolean_type_node, token, /*type_definition*/false); @@ -15800,15 +15818,15 @@ get_unqualified_id (cp_declarator *declarator) return NULL_TREE; } -/* Returns true if DECL represents a constrained-parameter. */ +/* Returns true if TYPE would declare a constrained constrained-parameter. */ static inline bool -is_constrained_parameter (tree decl) +is_constrained_parameter (tree type) { - return (decl - && TREE_CODE (decl) == TYPE_DECL - && CONSTRAINED_PARM_CONCEPT (decl) - && DECL_P (CONSTRAINED_PARM_CONCEPT (decl))); + return (type + && TREE_CODE (type) == TYPE_DECL + && CONSTRAINED_PARM_CONCEPT (type) + && DECL_P (CONSTRAINED_PARM_CONCEPT (type))); } /* Returns true if PARM declares a constrained-parameter. */ @@ -15894,8 +15912,8 @@ cp_parser_constrained_template_template_parm (cp_parser *parser, declarator. */ static tree -constrained_non_type_template_parm (bool *is_non_type, - cp_parameter_declarator *parm) +cp_parser_constrained_non_type_template_parm (bool *is_non_type, + cp_parameter_declarator *parm) { *is_non_type = true; cp_declarator *decl = parm->declarator; @@ -15912,20 +15930,13 @@ constrained_non_type_template_parm (bool *is_non_type, static tree finish_constrained_parameter (cp_parser *parser, cp_parameter_declarator *parmdecl, - bool *is_non_type, - bool *is_parameter_pack) + bool *is_non_type) { tree decl = parmdecl->decl_specifiers.type; tree id = get_unqualified_id (parmdecl->declarator); tree def = parmdecl->default_argument; tree proto = DECL_INITIAL (decl); - /* A template parameter constrained by a variadic concept shall also - be declared as a template parameter pack. */ - bool is_variadic = template_parameter_pack_p (proto); - if (is_variadic && !*is_parameter_pack) - cp_parser_error (parser, "variadic constraint introduced without %<...%>"); - /* Build the parameter. Return an error if the declarator was invalid. */ tree parm; if (TREE_CODE (proto) == TYPE_DECL) @@ -15934,7 +15945,7 @@ finish_constrained_parameter (cp_parser *parser, parm = cp_parser_constrained_template_template_parm (parser, proto, id, parmdecl); else - parm = constrained_non_type_template_parm (is_non_type, parmdecl); + parm = cp_parser_constrained_non_type_template_parm (is_non_type, parmdecl); if (parm == error_mark_node) return error_mark_node; @@ -15949,14 +15960,13 @@ finish_constrained_parameter (cp_parser *parser, /* Returns true if the parsed type actually represents the declaration of a type template-parameter. */ -static inline bool +static bool declares_constrained_type_template_parameter (tree type) { return (is_constrained_parameter (type) && TREE_CODE (TREE_TYPE (type)) == TEMPLATE_TYPE_PARM); } - /* Returns true if the parsed type actually represents the declaration of a template template-parameter. */ @@ -16132,12 +16142,11 @@ cp_parser_template_parameter (cp_parser* parser, bool *is_non_type, cp_lexer_consume_token (parser->lexer); } - // The parameter may have been constrained. + /* The parameter may have been constrained type parameter. */ if (is_constrained_parameter (parameter_declarator)) return finish_constrained_parameter (parser, parameter_declarator, - is_non_type, - is_parameter_pack); + is_non_type); // Now we're sure that the parameter is a non-type parameter. *is_non_type = true; @@ -16263,8 +16272,8 @@ cp_parser_type_parameter (cp_parser* parser, bool *is_parameter_pack) if (flag_concepts) { tree reqs = get_shorthand_constraints (current_template_parms); - if (tree r = cp_parser_requires_clause_opt (parser)) - reqs = conjoin_constraints (reqs, normalize_expression (r)); + if (tree dreqs = cp_parser_requires_clause_opt (parser)) + reqs = combine_constraint_expressions (reqs, dreqs); TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs; } @@ -16370,6 +16379,7 @@ cp_parser_template_id (cp_parser *parser, /* If the next token corresponds to a template-id, there is no need to reparse it. */ cp_token *token = cp_lexer_peek_token (parser->lexer); + if (token->type == CPP_TEMPLATE_ID) { cp_lexer_consume_token (parser->lexer); @@ -16512,7 +16522,7 @@ cp_parser_template_id (cp_parser *parser, = make_location (token->location, token->location, parser->lexer); /* Check for concepts autos where they don't belong. We could - identify types in some cases of idnetifier TEMPL, looking ahead + identify types in some cases of identifier TEMPL, looking ahead for a CPP_SCOPE, but that would buy us nothing: we accept auto in types. We reject them in functions, but if what we have is an identifier, even with none_type we can't conclude it's NOT a @@ -16538,11 +16548,13 @@ cp_parser_template_id (cp_parser *parser, template_id = finish_template_type (templ, arguments, entering_scope); } - /* A template-like identifier may be a partial concept id. */ - else if (flag_concepts - && (template_id = (cp_parser_maybe_partial_concept_id - (parser, templ, arguments)))) - return template_id; + else if (concept_definition_p (templ)) + { + /* The caller will decide whether this is a concept check or type + constraint. */ + template_id = build2_loc (combined_loc, TEMPLATE_ID_EXPR, + boolean_type_node, templ, arguments); + } else if (variable_template_p (templ)) { template_id = lookup_template_variable (templ, arguments); @@ -16599,6 +16611,23 @@ cp_parser_template_id (cp_parser *parser, return template_id; } +/* Like cp_parser_template_id, called in non-type context. */ + +static tree +cp_parser_template_id_expr (cp_parser *parser, + bool template_keyword_p, + bool check_dependency_p, + bool is_declaration) +{ + tree x = cp_parser_template_id (parser, template_keyword_p, check_dependency_p, + none_type, is_declaration); + if (TREE_CODE (x) == TEMPLATE_ID_EXPR + && concept_check_p (x)) + /* We didn't check the arguments in cp_parser_template_id; do that now. */ + return build_concept_id (x); + return x; +} + /* Parse a template-name. template-name: @@ -17019,11 +17048,7 @@ cp_parser_template_argument (cp_parser* parser) /*check_dependency=*/true, /*ambiguous_decls=*/NULL, argument_start_token->location); - /* Handle a constrained-type-specifier for a non-type template - parameter. */ - if (tree decl = cp_parser_maybe_concept_name (parser, argument)) - argument = decl; - else if (TREE_CODE (argument) != TEMPLATE_DECL + if (TREE_CODE (argument) != TEMPLATE_DECL && TREE_CODE (argument) != UNBOUND_CLASS_TEMPLATE) cp_parser_error (parser, "expected template-name"); } @@ -17772,7 +17797,7 @@ cp_parser_simple_type_specifier (cp_parser* parser, else if (!flag_concepts) pedwarn (token->location, 0, "use of % in parameter declaration " - "only available with %<-fconcepts%>"); + "only available with %<-fconcepts-ts%>"); } else type = make_auto (); @@ -17888,6 +17913,10 @@ cp_parser_simple_type_specifier (cp_parser* parser, if (flags & CP_PARSER_FLAGS_OPTIONAL) cp_parser_parse_tentatively (parser); + /* Remember current tentative parsing state -- if we know we need + a type, we can give better diagnostics here. */ + bool tent = cp_parser_parsing_tentatively (parser); + token = cp_lexer_peek_token (parser->lexer); /* Look for the optional `::' operator. */ @@ -17942,13 +17971,44 @@ cp_parser_simple_type_specifier (cp_parser* parser, type = NULL_TREE; } + if (!type && flag_concepts && decl_specs) + { + /* Try for a type-constraint with template arguments. We check + decl_specs here to avoid trying this for a functional cast. */ + + cp_parser_parse_tentatively (parser); + + type = cp_parser_template_id (parser, + /*template_keyword_p=*/false, + /*check_dependency_p=*/true, + none_type, + /*is_declaration=*/false); + if (type && concept_check_p (type)) + { + location_t loc = EXPR_LOCATION (type); + type = cp_parser_placeholder_type_specifier (parser, loc, + type, tent); + if (tent && type == error_mark_node) + /* Perhaps it's a concept-check expression. */ + cp_parser_simulate_error (parser); + } + else + cp_parser_simulate_error (parser); + + if (!cp_parser_parse_definitely (parser)) + type = NULL_TREE; + } + if (!type && cxx_dialect >= cxx17) { - /* Try class template argument deduction. */ + /* Try class template argument deduction or type-constraint without + template arguments. */ tree name = cp_parser_identifier (parser); if (name && TREE_CODE (name) == IDENTIFIER_NODE && parser->scope != error_mark_node) { + location_t loc + = cp_lexer_previous_token (parser->lexer)->location; tree tmpl = cp_parser_lookup_name (parser, name, none_type, /*is_template=*/false, @@ -17960,6 +18020,9 @@ cp_parser_simple_type_specifier (cp_parser* parser, && (DECL_CLASS_TEMPLATE_P (tmpl) || DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl))) type = make_template_placeholder (tmpl); + else if (flag_concepts && tmpl && concept_definition_p (tmpl)) + type = cp_parser_placeholder_type_specifier (parser, loc, + tmpl, tent); else { type = error_mark_node; @@ -18031,6 +18094,140 @@ cp_parser_simple_type_specifier (cp_parser* parser, return type; } +/* Parse the remainder of a placholder-type-specifier. + + placeholder-type-specifier: + type-constraint_opt auto + type-constraint_opt decltype(auto) + + The raw form of the constraint is parsed in cp_parser_simple_type_specifier + and passed as TMPL. This function converts TMPL to an actual type-constraint, + parses the placeholder type, and performs some contextual syntactic analysis. + + LOC provides the location of the template name. + + TENTATIVE is true if the type-specifier parsing is tentative; in that case, + don't give an error if TMPL isn't a valid type-constraint, as the template-id + might actually be a concept-check, + + Note that the Concepts TS allows the auto or decltype(auto) to be + omitted in a constrained-type-specifier. */ + +tree +cp_parser_placeholder_type_specifier (cp_parser *parser, location_t loc, + tree tmpl, bool tentative) +{ + if (tmpl == error_mark_node) + return error_mark_node; + + tree orig_tmpl = tmpl; + + /* Get the arguments as written for subsequent analysis. */ + tree args = NULL_TREE; + if (TREE_CODE (tmpl) == TEMPLATE_ID_EXPR) + { + args = TREE_OPERAND (tmpl, 1); + tmpl = TREE_OPERAND (tmpl, 0); + } + if (args == NULL_TREE) + /* A concept-name with no arguments can't be an expression. */ + tentative = false; + + tsubst_flags_t complain = tentative ? tf_none : tf_warning_or_error; + + /* Get the concept and prototype parameter for the constraint. */ + tree_pair info = finish_type_constraints (tmpl, args, complain); + tree con = info.first; + tree proto = info.second; + if (con == error_mark_node) + return error_mark_node; + + /* As per the standard, require auto or decltype(auto), except in some + cases (template parameter lists, -fconcepts-ts enabled). */ + cp_token *placeholder = NULL, *open_paren = NULL, *close_paren = NULL; + if (cxx_dialect >= cxx2a) + { + if (cp_lexer_next_token_is_keyword (parser->lexer, RID_AUTO)) + placeholder = cp_lexer_consume_token (parser->lexer); + else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_DECLTYPE)) + { + placeholder = cp_lexer_consume_token (parser->lexer); + open_paren = cp_parser_require (parser, CPP_OPEN_PAREN, + RT_OPEN_PAREN); + cp_parser_require_keyword (parser, RID_AUTO, RT_AUTO); + close_paren = cp_parser_require (parser, CPP_CLOSE_PAREN, + RT_CLOSE_PAREN, + open_paren->location); + } + } + + /* A type constraint constrains a contextually determined type or type + parameter pack. However, the the Concepts TS does allow concepts + to introduce non-type and template template parameters. */ + if (TREE_CODE (proto) != TYPE_DECL) + { + if (!flag_concepts_ts + || !processing_template_parmlist) + { + error_at (loc, "%qE does not constrain a type", DECL_NAME (con)); + inform (DECL_SOURCE_LOCATION (con), "concept defined here"); + return error_mark_node; + } + } + + /* In a template parameter list, a type-parameter can be introduced + by type-constraints alone. */ + if (processing_template_parmlist && !placeholder) + return build_constrained_parameter (con, proto, args); + + /* Diagnose issues placeholder issues. */ + if (!flag_concepts_ts + && !parser->in_result_type_constraint_p + && !placeholder) + { + tree id = build_nt (TEMPLATE_ID_EXPR, tmpl, args); + tree expr = DECL_P (orig_tmpl) ? DECL_NAME (con) : id; + error_at (input_location, + "expected % or % after %qE", expr); + /* Fall through. This is an error of omission. */ + } + else if (parser->in_result_type_constraint_p && placeholder) + { + /* A trailing return type only allows type-constraints. */ + error_at (input_location, + "unexpected placeholder in constrained result type"); + } + + /* In a parameter-declaration-clause, a placeholder-type-specifier + results in an invented template parameter. */ + if (parser->auto_is_implicit_function_template_parm_p) + { + if (placeholder && token_is_decltype (placeholder)) + { + location_t loc = make_location (placeholder->location, + placeholder->location, + close_paren->location); + error_at (loc, "cannot declare a parameter with %"); + return error_mark_node; + } + tree parm = build_constrained_parameter (con, proto, args); + return synthesize_implicit_template_parm (parser, parm); + } + + /* Determine if the type should be deduced using template argument + deduction or decltype deduction. Note that the latter is always + used for type-constraints in trailing return types. */ + bool decltype_p = placeholder + ? placeholder->keyword == RID_DECLTYPE + : parser->in_result_type_constraint_p; + + /* Otherwise, this is the type of a variable or return type. */ + if (decltype_p) + return make_constrained_decltype_auto (con, args); + else + return make_constrained_auto (con, args); +} + /* Parse a type-name. type-name: @@ -18103,8 +18300,6 @@ cp_parser_type_name (cp_parser* parser, bool typename_keyword_p) && TREE_CODE (type_decl) == TYPE_DECL && TYPE_DECL_ALIAS_P (type_decl)) gcc_assert (DECL_TEMPLATE_INSTANTIATION (type_decl)); - else if (is_constrained_parameter (type_decl)) - /* Don't do anything. */ ; else cp_parser_simulate_error (parser); @@ -18116,105 +18311,6 @@ cp_parser_type_name (cp_parser* parser, bool typename_keyword_p) return type_decl; } -/* Check if DECL and ARGS can form a constrained-type-specifier. - If ARGS is non-null, we try to form a concept check of the - form DECL where ? is a wildcard that matches any - kind of template argument. If ARGS is NULL, then we try to - form a concept check of the form DECL. */ - -static tree -cp_parser_maybe_constrained_type_specifier (cp_parser *parser, - tree decl, tree args) -{ - gcc_assert (args ? TREE_CODE (args) == TREE_VEC : true); - - /* If we a constrained-type-specifier cannot be deduced. */ - if (parser->prevent_constrained_type_specifiers) - return NULL_TREE; - - /* A constrained type specifier can only be found in an - overload set or as a reference to a template declaration. - - FIXME: This might be masking a bug. It's possible that - that the deduction below is causing template specializations - to be formed with the wildcard as an argument. */ - if (TREE_CODE (decl) != OVERLOAD && TREE_CODE (decl) != TEMPLATE_DECL) - return NULL_TREE; - - /* Try to build a call expression that evaluates the - concept. This can fail if the overload set refers - only to non-templates. */ - tree placeholder = build_nt (WILDCARD_DECL); - tree check = build_concept_check (decl, placeholder, args); - if (check == error_mark_node) - return NULL_TREE; - - /* Deduce the checked constraint and the prototype parameter. - - FIXME: In certain cases, failure to deduce should be a - diagnosable error. */ - tree conc; - tree proto; - if (!deduce_constrained_parameter (check, conc, proto)) - return NULL_TREE; - - /* In template parameter scope, this results in a constrained - parameter. Return a descriptor of that parm. */ - if (processing_template_parmlist) - return build_constrained_parameter (conc, proto, args); - - /* In a parameter-declaration-clause, constrained-type - specifiers result in invented template parameters. */ - if (parser->auto_is_implicit_function_template_parm_p) - { - tree x = build_constrained_parameter (conc, proto, args); - return synthesize_implicit_template_parm (parser, x); - } - else - { - /* Otherwise, we're in a context where the constrained - type name is deduced and the constraint applies - after deduction. */ - return make_constrained_auto (conc, args); - } - - return NULL_TREE; -} - -/* If DECL refers to a concept, return a TYPE_DECL representing - the result of using the constrained type specifier in the - current context. DECL refers to a concept if - - - it is an overload set containing a function concept taking a single - type argument, or - - - it is a variable concept taking a single type argument. */ - -static tree -cp_parser_maybe_concept_name (cp_parser* parser, tree decl) -{ - if (flag_concepts - && (TREE_CODE (decl) == OVERLOAD - || BASELINK_P (decl) - || variable_concept_p (decl))) - return cp_parser_maybe_constrained_type_specifier (parser, decl, NULL_TREE); - else - return NULL_TREE; -} - -/* Check if DECL and ARGS form a partial-concept-id. If so, - assign ID to the resulting constrained placeholder. - - Returns true if the partial-concept-id designates a placeholder - and false otherwise. Note that *id is set to NULL_TREE in - this case. */ - -static tree -cp_parser_maybe_partial_concept_id (cp_parser *parser, tree decl, tree args) -{ - return cp_parser_maybe_constrained_type_specifier (parser, decl, args); -} - /* Parse a non-class type-name, that is, either an enum-name, a typedef-name, or a concept-name. @@ -18245,10 +18341,6 @@ cp_parser_nonclass_name (cp_parser* parser) type_decl = strip_using_decl (type_decl); - /* If we found an overload set, then it may refer to a concept-name. */ - if (tree decl = cp_parser_maybe_concept_name (parser, type_decl)) - type_decl = decl; - if (TREE_CODE (type_decl) != TYPE_DECL && (objc_is_id (identifier) || objc_is_class_name (identifier))) { @@ -26885,31 +26977,280 @@ cp_parser_label_declaration (cp_parser* parser) cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); } +// -------------------------------------------------------------------------- // +// Concept definitions + +static tree +cp_parser_concept_definition (cp_parser *parser) +{ + gcc_assert (cp_lexer_next_token_is_keyword (parser->lexer, RID_CONCEPT)); + cp_lexer_consume_token (parser->lexer); + + cp_expr id = cp_parser_identifier (parser); + if (id == error_mark_node) + { + cp_parser_skip_to_end_of_statement (parser); + cp_parser_consume_semicolon_at_end_of_statement (parser); + return NULL_TREE; + } + + if (!cp_parser_require (parser, CPP_EQ, RT_EQ)) + { + cp_parser_skip_to_end_of_statement (parser); + cp_parser_consume_semicolon_at_end_of_statement (parser); + return error_mark_node; + } + + processing_constraint_expression_sentinel parsing_constraint; + tree init = cp_parser_constraint_expression (parser); + if (init == error_mark_node) + cp_parser_skip_to_end_of_statement (parser); + + /* Consume the trailing ';'. Diagnose the problem if it isn't there, + but continue as if it were. */ + cp_parser_consume_semicolon_at_end_of_statement (parser); + + return finish_concept_definition (id, init); +} + // -------------------------------------------------------------------------- // // Requires Clause -// Parse a requires clause. -// -// requires-clause: -// 'requires' logical-or-expression -// -// The required logical-or-expression must be a constant expression. Note -// that we don't check that the expression is constepxr here. We defer until -// we analyze constraints and then, we only check atomic constraints. +/* Diagnose an expression that should appear in ()'s within a requires-clause + and suggest where to place those parentheses. */ + +static void +cp_parser_diagnose_ungrouped_constraint_plain (location_t loc) +{ + error_at (loc, "expression after % must be enclosed " + "in parentheses"); +} + +static void +cp_parser_diagnose_ungrouped_constraint_rich (location_t loc) +{ + gcc_rich_location richloc (loc); + richloc.add_fixit_insert_before ("("); + richloc.add_fixit_insert_after (")"); + error_at (&richloc, "expression after % must be enclosed " + "in parentheses"); +} + +/* Parse a primary expression within a constraint. */ + +static cp_expr +cp_parser_constraint_primary_expression (cp_parser *parser) +{ + cp_parser_parse_tentatively (parser); + cp_id_kind idk; + location_t loc = input_location; + cp_expr expr = cp_parser_primary_expression (parser, + /*address_p=*/false, + /*cast_p=*/false, + /*template_arg_p=*/false, + &idk); + expr.maybe_add_location_wrapper (); + if (expr != error_mark_node) + expr = finish_constraint_primary_expr (expr); + if (cp_parser_parse_definitely (parser)) + return expr; + + /* Retry the parse at a lower precedence. If that succeeds, diagnose the + error, but return the expression as if it were valid. */ + cp_parser_parse_tentatively (parser); + expr = cp_parser_simple_cast_expression (parser); + if (cp_parser_parse_definitely (parser)) + { + cp_parser_diagnose_ungrouped_constraint_rich (expr.get_location()); + return expr; + } + + /* Otherwise, something has gone wrong, but we can't generate a more + meaningful diagnostic or recover. */ + cp_parser_diagnose_ungrouped_constraint_plain (loc); + return error_mark_node; +} + +/* Examine the token following EXPR. If it is an operator in a non-logical + binary expression, diagnose that as an error. Returns ERROR_MARK_NODE. */ + +static cp_expr +cp_parser_check_non_logical_constraint (cp_parser *parser, cp_expr lhs) +{ + cp_token *token = cp_lexer_peek_token (parser->lexer); + switch (token->type) + { + default: + return lhs; + + /* Arithmetic operators. */ + case CPP_PLUS: + case CPP_MINUS: + case CPP_MULT: + case CPP_DIV: + case CPP_MOD: + /* Bitwise operators. */ + case CPP_AND: + case CPP_OR: + case CPP_XOR: + case CPP_RSHIFT: + case CPP_LSHIFT: + /* Relational operators. */ + /* FIXME: Handle '<=>'. */ + case CPP_EQ_EQ: + case CPP_NOT_EQ: + case CPP_LESS: + case CPP_GREATER: + case CPP_LESS_EQ: + case CPP_GREATER_EQ: + /* Conditional operator */ + case CPP_QUERY: + /* Pointer-to-member. */ + case CPP_DOT_STAR: + case CPP_DEREF_STAR: + /* Assignment operators. */ + case CPP_PLUS_EQ: + case CPP_MINUS_EQ: + case CPP_MULT_EQ: + case CPP_DIV_EQ: + case CPP_MOD_EQ: + case CPP_AND_EQ: + case CPP_OR_EQ: + case CPP_XOR_EQ: + case CPP_RSHIFT_EQ: + case CPP_LSHIFT_EQ: + break; + + case CPP_EQ: { + /* An equal sign may be part of the the definition of a function, + and not an assignment operator, when parsing the expression + for a trailing requires-clause. For example: + + template + struct S { + S() requires C = default; + } + + This is not an error. */ + if (cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_DELETE) + || cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_DEFAULT)) + return lhs; + + break; + } + } + + /* Try to parse the RHS as either the remainder of a conditional-expression + or a logical-or-expression so we can form a good diagnostic. */ + cp_parser_parse_tentatively (parser); + cp_expr rhs; + if (token->type == CPP_QUERY) + rhs = cp_parser_question_colon_clause (parser, lhs); + else + { + cp_lexer_consume_token (parser->lexer); + rhs = cp_parser_binary_expression (parser, false, false, false, + PREC_NOT_OPERATOR, NULL); + } + + /* If we couldn't parse the RHS, then emit the best diagnostic we can. */ + if (!cp_parser_parse_definitely (parser)) + { + cp_parser_diagnose_ungrouped_constraint_plain (token->location); + return error_mark_node; + } + + /* Otherwise, emit a fixit for the complete binary expression. */ + location_t loc = make_location (token->location, + lhs.get_start(), + rhs.get_finish()); + cp_parser_diagnose_ungrouped_constraint_rich (loc); + return error_mark_node; +} + +/* Parse a constraint-logical-and-expression. + + constraint-logical-and-expression: + primary-expression + constraint-logical-and-expression '&&' primary-expression */ + +static cp_expr +cp_parser_constraint_logical_and_expression (cp_parser *parser) +{ + cp_expr lhs = cp_parser_constraint_primary_expression (parser); + while (cp_lexer_next_token_is (parser->lexer, CPP_AND_AND)) + { + cp_token *op = cp_lexer_consume_token (parser->lexer); + tree rhs = cp_parser_constraint_primary_expression (parser); + lhs = finish_constraint_and_expr (op->location, lhs, rhs); + } + return cp_parser_check_non_logical_constraint (parser, lhs); +} + +/* Parse a constraint-logical-or-expression. + + constraint-logical-or-expression: + constraint-logical-and-expression + constraint-logical-or-expression '||' constraint-logical-and-expression */ + +static cp_expr +cp_parser_constraint_logical_or_expression (cp_parser *parser) +{ + cp_expr lhs = cp_parser_constraint_logical_and_expression (parser); + while (cp_lexer_next_token_is (parser->lexer, CPP_OR_OR)) + { + cp_token *op = cp_lexer_consume_token (parser->lexer); + cp_expr rhs = cp_parser_constraint_logical_and_expression (parser); + lhs = finish_constraint_or_expr (op->location, lhs, rhs); + } + return cp_parser_check_non_logical_constraint (parser, lhs); +} + +/* Parse the expression after a requires-clause. This has a different grammar + than that in the concepts TS. */ + static tree -cp_parser_requires_clause (cp_parser *parser) +cp_parser_requires_clause_expression (cp_parser *parser) { - // Parse the requires clause so that it is not automatically folded. + processing_constraint_expression_sentinel parsing_constraint; ++processing_template_decl; - tree expr = cp_parser_binary_expression (parser, false, false, - PREC_NOT_OPERATOR, NULL); + cp_expr expr = cp_parser_constraint_logical_or_expression (parser); if (check_for_bare_parameter_packs (expr)) expr = error_mark_node; --processing_template_decl; return expr; } -// Optionally parse a requires clause: +/* Parse a expression after a requires clause. + + constraint-expression: + logical-or-expression + + The required logical-or-expression must be a constant expression. Note + that we don't check that the expression is constepxr here. We defer until + we analyze constraints and then, we only check atomic constraints. */ + +static tree +cp_parser_constraint_expression (cp_parser *parser) +{ + processing_constraint_expression_sentinel parsing_constraint; + ++processing_template_decl; + cp_expr expr = cp_parser_binary_expression (parser, false, true, + PREC_NOT_OPERATOR, NULL); + if (check_for_bare_parameter_packs (expr)) + expr = error_mark_node; + --processing_template_decl; + expr.maybe_add_location_wrapper (); + return expr; +} + +/* Optionally parse a requires clause: + + requires-clause: + `requires` constraint-logical-or-expression. + [ConceptsTS] + `requires constraint-expression. */ + static tree cp_parser_requires_clause_opt (cp_parser *parser) { @@ -26920,17 +27261,21 @@ cp_parser_requires_clause_opt (cp_parser *parser) && tok->u.value == ridpointers[RID_REQUIRES]) { error_at (cp_lexer_peek_token (parser->lexer)->location, - "% only available with %<-fconcepts%>"); + "% only available with " + "%<-std=c++2a%> or %<-fconcepts%>"); /* Parse and discard the requires-clause. */ cp_lexer_consume_token (parser->lexer); - cp_parser_requires_clause (parser); + cp_parser_constraint_expression (parser); } return NULL_TREE; } cp_lexer_consume_token (parser->lexer); - return cp_parser_requires_clause (parser); -} + if (!flag_concepts_ts) + return cp_parser_requires_clause_expression (parser); + else + return cp_parser_constraint_expression (parser); +} /*--------------------------------------------------------------------------- Requires expressions @@ -26940,6 +27285,7 @@ cp_parser_requires_clause_opt (cp_parser *parser) requirement-expression: 'requires' requirement-parameter-list [opt] requirement-body */ + static tree cp_parser_requires_expression (cp_parser *parser) { @@ -26957,6 +27303,9 @@ cp_parser_requires_expression (cp_parser *parser) return error_mark_node; } + /* This is definitely a requires-expression. */ + cp_parser_commit_to_tentative_parse (parser); + tree parms, reqs; { /* Local parameters are delared as variables within the scope @@ -26997,13 +27346,15 @@ cp_parser_requires_expression (cp_parser *parser) /* This needs to happen after pop_bindings_and_leave_scope, as it reverses the parm chain. */ grokparms (parms, &parms); - return finish_requires_expr (parms, reqs); + loc = make_location (loc, loc, parser->lexer); + return finish_requires_expr (loc, parms, reqs); } /* Parse a parameterized requirement. requirement-parameter-list: '(' parameter-declaration-clause ')' */ + static tree cp_parser_requirement_parameter_list (cp_parser *parser) { @@ -27031,7 +27382,7 @@ cp_parser_requirement_body (cp_parser *parser) if (!braces.require_open (parser)) return error_mark_node; - tree reqs = cp_parser_requirement_list (parser); + tree reqs = cp_parser_requirement_seq (parser); if (!braces.require_close (parser)) return error_mark_node; @@ -27039,34 +27390,28 @@ cp_parser_requirement_body (cp_parser *parser) return reqs; } -/* Parse a list of requirements. +/* Parse a sequence of requirements. - requirement-list: + requirement-seq: requirement - requirement-list ';' requirement[opt] */ + requirement-seq requirement */ + static tree -cp_parser_requirement_list (cp_parser *parser) +cp_parser_requirement_seq (cp_parser *parser) { tree result = NULL_TREE; - while (true) + do { tree req = cp_parser_requirement (parser); - if (req == error_mark_node) - return error_mark_node; - - result = tree_cons (NULL_TREE, req, result); - - /* 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 (req != error_mark_node) + result = tree_cons (NULL_TREE, req, result); + } while (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_BRACE)); - /* Stop processing at the end of the list. */ - if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE)) - break; - } + /* If there are no valid requirements, this is not a valid expression. */ + if (!result) + return error_mark_node; - /* Reverse the order of requirements so they are analyzed in - declaration order. */ + /* Reverse the order of requirements so they are analyzed in order. */ return nreverse (result); } @@ -27077,6 +27422,7 @@ cp_parser_requirement_list (cp_parser *parser) compound-requirement type-requirement nested-requirement */ + static tree cp_parser_requirement (cp_parser *parser) { @@ -27094,17 +27440,26 @@ cp_parser_requirement (cp_parser *parser) simple-requirement: expression ';' */ + static tree cp_parser_simple_requirement (cp_parser *parser) { - tree expr = cp_parser_expression (parser, NULL, false, false); + location_t start = cp_lexer_peek_token (parser->lexer)->location; + cp_expr expr = cp_parser_expression (parser, NULL, false, false); + if (expr == error_mark_node) + cp_parser_skip_to_end_of_statement (parser); + + cp_parser_consume_semicolon_at_end_of_statement (parser); + if (!expr || expr == error_mark_node) return error_mark_node; - if (!cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON)) - return error_mark_node; + /* Sometimes we don't get locations, so use the cached token location + as a reasonable approximation. */ + if (expr.get_location() == UNKNOWN_LOCATION) + expr.set_location (start); - return finish_simple_requirement (expr); + return finish_simple_requirement (expr.get_location (), expr); } /* Parse a type requirement @@ -27115,16 +27470,18 @@ cp_parser_simple_requirement (cp_parser *parser) required-type-name: type-name 'template' [opt] simple-template-id */ + static tree cp_parser_type_requirement (cp_parser *parser) { - cp_lexer_consume_token (parser->lexer); + cp_token *start_tok = cp_lexer_consume_token (parser->lexer); + location_t loc = cp_lexer_peek_token (parser->lexer)->location; // Save the scope before parsing name specifiers. tree saved_scope = parser->scope; tree saved_object_scope = parser->object_scope; tree saved_qualifying_scope = parser->qualifying_scope; - cp_parser_global_scope_opt (parser, /*current_scope_valid_p=*/true); + cp_parser_global_scope_opt (parser, /*current_scope_valid_p=*/false); cp_parser_nested_name_specifier_opt (parser, /*typename_keyword_p=*/true, /*check_dependency_p=*/false, @@ -27156,18 +27513,20 @@ cp_parser_type_requirement (cp_parser *parser) if (type == error_mark_node) cp_parser_skip_to_end_of_statement (parser); - if (!cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON)) - return error_mark_node; + cp_parser_consume_semicolon_at_end_of_statement (parser); + if (type == error_mark_node) return error_mark_node; - return finish_type_requirement (type); + loc = make_location (loc, start_tok->location, parser->lexer); + return finish_type_requirement (loc, type); } /* Parse a compound requirement compound-requirement: '{' expression '}' 'noexcept' [opt] trailing-return-type [opt] ';' */ + static tree cp_parser_compound_requirement (cp_parser *parser) { @@ -27176,12 +27535,26 @@ cp_parser_compound_requirement (cp_parser *parser) if (!braces.require_open (parser)) return error_mark_node; + cp_token *expr_token = cp_lexer_peek_token (parser->lexer); + tree expr = cp_parser_expression (parser, NULL, false, false); - if (!expr || expr == error_mark_node) - return error_mark_node; + if (expr == error_mark_node) + cp_parser_skip_to_closing_brace (parser); if (!braces.require_close (parser)) - return error_mark_node; + { + cp_parser_skip_to_end_of_statement (parser); + cp_parser_consume_semicolon_at_end_of_statement (parser); + return error_mark_node; + } + + /* If the expression was invalid, skip the remainder of the requirement. */ + if (!expr || expr == error_mark_node) + { + cp_parser_skip_to_end_of_statement (parser); + cp_parser_consume_semicolon_at_end_of_statement (parser); + return error_mark_node; + } /* Parse the optional noexcept. */ bool noexcept_p = false; @@ -27196,29 +27569,69 @@ cp_parser_compound_requirement (cp_parser *parser) if (cp_lexer_next_token_is (parser->lexer, CPP_DEREF)) { cp_lexer_consume_token (parser->lexer); + cp_token *tok = cp_lexer_peek_token (parser->lexer); + bool saved_result_type_constraint_p = parser->in_result_type_constraint_p; parser->in_result_type_constraint_p = true; + /* C++2a allows either a type-id or a type-constraint. Parsing + a type-id will subsume the parsing for a type-constraint but + allow for more syntactic forms (e.g., const C*). */ type = cp_parser_trailing_type_id (parser); parser->in_result_type_constraint_p = saved_result_type_constraint_p; if (type == error_mark_node) return error_mark_node; + + location_t type_loc = make_location (tok->location, tok->location, + parser->lexer); + + /* Check that we haven't written something like 'const C*'. */ + if (type_uses_auto (type)) + { + if (!is_auto (type)) + { + error_at (type_loc, + "result type is not a plain type-constraint"); + cp_parser_consume_semicolon_at_end_of_statement (parser); + return error_mark_node; + } + } + else if (!flag_concepts_ts) + /* P1452R2 removed the trailing-return-type option. */ + error_at (type_loc, + "return-type-requirement is not a type-constraint"); } - return finish_compound_requirement (expr, type, noexcept_p); + location_t loc = make_location (expr_token->location, + braces.open_location (), + parser->lexer); + + cp_parser_consume_semicolon_at_end_of_statement (parser); + + if (expr == error_mark_node || type == error_mark_node) + return error_mark_node; + + return finish_compound_requirement (loc, expr, type, noexcept_p); } /* Parse a nested requirement. This is the same as a requires clause. nested-requirement: requires-clause */ + static tree cp_parser_nested_requirement (cp_parser *parser) { - cp_lexer_consume_token (parser->lexer); - tree req = cp_parser_requires_clause (parser); + gcc_assert (cp_lexer_next_token_is_keyword (parser->lexer, RID_REQUIRES)); + cp_token *tok = cp_lexer_consume_token (parser->lexer); + location_t loc = cp_lexer_peek_token (parser->lexer)->location; + tree req = cp_parser_constraint_expression (parser); + if (req == error_mark_node) + cp_parser_skip_to_end_of_statement (parser); + loc = make_location (loc, tok->location, parser->lexer); + cp_parser_consume_semicolon_at_end_of_statement (parser); if (req == error_mark_node) return error_mark_node; - return finish_nested_requirement (req); + return finish_nested_requirement (loc, req); } /* Support Functions */ @@ -28145,6 +28558,11 @@ cp_parser_template_declaration_after_parameters (cp_parser* parser, else if (cxx_dialect >= cxx11 && cp_lexer_next_token_is_keyword (parser->lexer, RID_USING)) decl = cp_parser_alias_declaration (parser); + else if (cxx_dialect >= cxx2a /* Implies flag_concept. */ + && cp_lexer_next_token_is_keyword (parser->lexer, RID_CONCEPT) + && !cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_BOOL)) + /* Allow 'concept bool' to be handled as per the TS. */ + decl = cp_parser_concept_definition (parser); else { /* There are no access checks when parsing a template, as we do not @@ -28264,6 +28682,8 @@ cp_parser_template_introduction (cp_parser* parser, bool member_p) tree saved_object_scope = parser->object_scope; tree saved_qualifying_scope = parser->qualifying_scope; + cp_token *start_token = cp_lexer_peek_token (parser->lexer); + /* Look for the optional `::' operator. */ cp_parser_global_scope_opt (parser, /*current_scope_valid_p=*/false); @@ -28285,12 +28705,14 @@ cp_parser_template_introduction (cp_parser* parser, bool member_p) parser->object_scope = saved_object_scope; parser->qualifying_scope = saved_qualifying_scope; - if (concept_name == error_mark_node) + if (concept_name == error_mark_node + || (seen_error () && !concept_definition_p (tmpl_decl))) cp_parser_simulate_error (parser); /* Look for opening brace for introduction. */ matching_braces braces; braces.require_open (parser); + location_t open_loc = input_location; if (!cp_parser_parse_definitely (parser)) return false; @@ -28321,15 +28743,26 @@ cp_parser_template_introduction (cp_parser* parser, bool member_p) } /* Build and associate the constraint. */ - tree parms = finish_template_introduction (tmpl_decl, introduction_list); + location_t introduction_loc = make_location (open_loc, + start_token->location, + parser->lexer); + tree parms = finish_template_introduction (tmpl_decl, + introduction_list, + introduction_loc); if (parms && parms != error_mark_node) { + if (!flag_concepts_ts) + pedwarn (introduction_loc, 0, "template-introductions" + " are not part of C++20 concepts [-fconcepts-ts]"); + cp_parser_template_declaration_after_parameters (parser, parms, member_p); return true; } - error_at (token->location, "no matching concept for template-introduction"); + if (parms == NULL_TREE) + error_at (token->location, "no matching concept for template-introduction"); + return true; } @@ -28397,8 +28830,8 @@ cp_parser_explicit_template_declaration (cp_parser* parser, bool member_p) if (flag_concepts) { tree reqs = get_shorthand_constraints (current_template_parms); - if (tree r = cp_parser_requires_clause_opt (parser)) - reqs = conjoin_constraints (reqs, normalize_expression (r)); + if (tree treqs = cp_parser_requires_clause_opt (parser)) + reqs = combine_constraint_expressions (reqs, treqs); TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs; } @@ -29635,6 +30068,9 @@ cp_parser_required_error (cp_parser *parser, case RT_THROW: gmsgid = G_("expected %"); break; + case RT_AUTO: + gmsgid = G_("expected %"); + break; case RT_LABEL: gmsgid = G_("expected %<__label__%>"); break; @@ -41910,36 +42346,15 @@ make_generic_type_name () static tree synthesize_implicit_template_parm (cp_parser *parser, tree constr) { - gcc_assert (current_binding_level->kind == sk_function_parms); - - /* Before committing to modifying any scope, if we're in an - implicit template scope, and we're trying to synthesize a - constrained parameter, try to find a previous parameter with - the same name. This is the same-type rule for abbreviated - function templates. - - NOTE: We can generate implicit parameters when tentatively - parsing a nested name specifier, only to reject that parse - later. However, matching the same template-id as part of a - direct-declarator should generate an identical template - parameter, so this rule will merge them. */ - if (parser->implicit_template_scope && constr) - { - tree t = parser->implicit_template_parms; - while (t) - { - if (equivalent_placeholder_constraints (TREE_TYPE (t), constr)) - { - tree d = TREE_VALUE (t); - if (TREE_CODE (d) == PARM_DECL) - /* Return the TEMPLATE_PARM_INDEX. */ - d = DECL_INITIAL (d); - return d; - } - t = TREE_CHAIN (t); - } + /* A requires-clause is not a function and cannot have placeholders. */ + if (current_binding_level->kind == sk_block) + { + error ("placeholder type not allowed in this context"); + return error_mark_node; } + gcc_assert (current_binding_level->kind == sk_function_parms); + /* We are either continuing a function template that already contains implicit template parameters, creating a new fully-implicit function template, or extending an existing explicit function template with implicit template @@ -42050,18 +42465,9 @@ synthesize_implicit_template_parm (cp_parser *parser, tree constr) tree synth_tmpl_parm; bool non_type = false; - if (proto == NULL_TREE || TREE_CODE (proto) == TYPE_DECL) - synth_tmpl_parm - = finish_template_type_parm (class_type_node, synth_id); - else if (TREE_CODE (proto) == TEMPLATE_DECL) - synth_tmpl_parm - = finish_constrained_template_template_parm (proto, synth_id); - else - { - synth_tmpl_parm = copy_decl (proto); - DECL_NAME (synth_tmpl_parm) = synth_id; - non_type = true; - } + /* Synthesize the type template parameter. */ + gcc_assert(!proto || TREE_CODE (proto) == TYPE_DECL); + synth_tmpl_parm = finish_template_type_parm (class_type_node, synth_id); /* Attach the constraint to the parm before processing. */ tree node = build_tree_list (NULL_TREE, synth_tmpl_parm); @@ -42073,6 +42479,13 @@ synthesize_implicit_template_parm (cp_parser *parser, tree constr) /*non_type=*/non_type, /*param_pack=*/false); + /* Mark the synthetic declaration "virtual". This is used when + comparing template-heads to determine if whether an abbreviated + function template is equivalent to an explicit template. + + Note that DECL_ARTIFICIAL is used elsewhere for template parameters. */ + DECL_VIRTUAL_P (TREE_VALUE (new_parm)) = true; + // Chain the new parameter to the list of implicit parameters. if (parser->implicit_template_parms) parser->implicit_template_parms @@ -42111,7 +42524,7 @@ synthesize_implicit_template_parm (cp_parser *parser, tree constr) if (tree req = TEMPLATE_PARM_CONSTRAINTS (tree_last (new_parm))) { tree reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms); - reqs = conjoin_constraints (reqs, req); + reqs = combine_constraint_expressions (reqs, req); TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs; } diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 6310e7b399b..84464436991 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -135,8 +135,6 @@ enum template_base_result { tbr_success }; -static void push_access_scope (tree); -static void pop_access_scope (tree); static bool resolve_overloaded_unification (tree, tree, tree, tree, unification_kind_t, int, bool); @@ -153,7 +151,6 @@ static tree coerce_innermost_template_parms (tree, tree, tree, tsubst_flags_t, bool, bool); static void tsubst_enum (tree, tree, tree); static tree add_to_template_args (tree, tree); -static tree add_outermost_template_args (tree, tree); static bool check_instantiated_args (tree, tree, tsubst_flags_t); static int check_non_deducible_conversion (tree, tree, int, int, struct conversion **, bool); @@ -183,12 +180,9 @@ static int can_complete_type_without_circularity (tree); static tree get_bindings (tree, tree, tree, bool); static int template_decl_level (tree); static int check_cv_quals_for_unify (int, tree, tree); -static void template_parm_level_and_index (tree, int*, int*); static int unify_pack_expansion (tree, tree, tree, tree, unification_kind_t, bool, bool); static tree copy_template_args (tree); -static tree tsubst_template_arg (tree, tree, tsubst_flags_t, tree); -static tree tsubst_template_args (tree, tree, tsubst_flags_t, tree); static tree tsubst_template_parms (tree, tree, tsubst_flags_t); static tree most_specialized_partial_spec (tree, tsubst_flags_t); static tree tsubst_aggr_type (tree, tree, tsubst_flags_t, tree, int); @@ -232,7 +226,7 @@ static tree enclosing_instantiation_of (tree tctx); template, VAR_DECL for static member variable, or TYPE_DECL for alias template (needed by instantiate_decl). */ -static void +void push_access_scope (tree t) { gcc_assert (VAR_OR_FUNCTION_DECL_P (t) @@ -255,7 +249,7 @@ push_access_scope (tree t) /* Restore the scope set up by push_access_scope. T is the node we are processing. */ -static void +void pop_access_scope (tree t) { if (TREE_CODE (t) == FUNCTION_DECL) @@ -582,7 +576,7 @@ add_to_template_args (tree args, tree extra_args) template arguments used to attain the full instantiation from the partial instantiation. */ -static tree +tree add_outermost_template_args (tree args, tree extra_args) { tree new_args; @@ -3305,6 +3299,153 @@ comp_template_parms (const_tree parms1, const_tree parms2) return 1; } +/* Returns true if two template parameters are declared with + equivalent constraints. */ + +static bool +template_parameter_constraints_equivalent_p (const_tree parm1, const_tree parm2) +{ + tree req1 = TREE_TYPE (parm1); + tree req2 = TREE_TYPE (parm2); + if (!req1 != !req2) + return false; + if (req1) + return cp_tree_equal (req1, req2); + return true; +} + +/* Returns true when two template parameters are equivalent. */ + +static bool +template_parameters_equivalent_p (const_tree parm1, const_tree parm2) +{ + tree decl1 = TREE_VALUE (parm1); + tree decl2 = TREE_VALUE (parm2); + + /* If either of the template parameters are invalid, assume + they match for the sake of error recovery. */ + if (error_operand_p (decl1) || error_operand_p (decl2)) + return true; + + /* ... they declare parameters of the same kind. */ + if (TREE_CODE (decl1) != TREE_CODE (decl2)) + return false; + + /* ... one parameter was introduced by a parameter declaration, then + both are. This case arises as a result of eagerly rewriting declarations + during parsing. */ + if (DECL_VIRTUAL_P (decl1) != DECL_VIRTUAL_P (decl2)) + return false; + + /* ... if either declares a pack, they both do. */ + if (template_parameter_pack_p (decl1) != template_parameter_pack_p (decl2)) + return false; + + if (TREE_CODE (decl1) == PARM_DECL) + { + /* ... if they declare non-type parameters, the types are equivalent. */ + if (!same_type_p (TREE_TYPE (decl1), TREE_TYPE (decl2))) + return false; + } + else if (TREE_CODE (decl2) == TEMPLATE_DECL) + { + /* ... if they declare template template parameters, their template + parameter lists are equivalent. */ + if (!template_heads_equivalent_p (decl1, decl2)) + return false; + } + + /* ... if they are declared with a qualified-concept name, they both + are, and those names are equivalent. */ + return template_parameter_constraints_equivalent_p (parm1, parm2); +} + +/* Returns true if two template parameters lists are equivalent. + Two template parameter lists are equivalent if they have the + same length and their corresponding parameters are equivalent. + + PARMS1 and PARMS2 are TREE_LISTs containing TREE_VECs: the + data structure returned by DECL_TEMPLATE_PARMS. + + This is generally the same implementation as comp_template_parms + except that it also the concept names and arguments used to + introduce parameters. */ + +static bool +template_parameter_lists_equivalent_p (const_tree parms1, const_tree parms2) +{ + if (parms1 == parms2) + return true; + + const_tree p1 = parms1; + const_tree p2 = parms2; + while (p1 != NULL_TREE && p2 != NULL_TREE) + { + tree list1 = TREE_VALUE (p1); + tree list2 = TREE_VALUE (p2); + + if (TREE_VEC_LENGTH (list1) != TREE_VEC_LENGTH (list2)) + return 0; + + for (int i = 0; i < TREE_VEC_LENGTH (list2); ++i) + { + tree parm1 = TREE_VEC_ELT (list1, i); + tree parm2 = TREE_VEC_ELT (list2, i); + if (!template_parameters_equivalent_p (parm1, parm2)) + return false; + } + + p1 = TREE_CHAIN (p1); + p2 = TREE_CHAIN (p2); + } + + if ((p1 != NULL_TREE) != (p2 != NULL_TREE)) + return false; + + return true; +} + +/* Return true if the requires-clause of the template parameter lists are + equivalent and false otherwise. */ +static bool +template_requirements_equivalent_p (const_tree parms1, const_tree parms2) +{ + tree req1 = TEMPLATE_PARMS_CONSTRAINTS (parms1); + tree req2 = TEMPLATE_PARMS_CONSTRAINTS (parms2); + if ((req1 != NULL_TREE) != (req2 != NULL_TREE)) + return false; + if (!cp_tree_equal (req1, req2)) + return false; + return true; +} + +/* Returns true if two template heads are equivalent. 17.6.6.1p6: + Two template heads are equivalent if their template parameter + lists are equivalent and their requires clauses are equivalent. + + In pre-C++20, this is equivalent to calling comp_template_parms + for the template parameters of TMPL1 and TMPL2. */ + +bool +template_heads_equivalent_p (const_tree tmpl1, const_tree tmpl2) +{ + tree parms1 = DECL_TEMPLATE_PARMS (tmpl1); + tree parms2 = DECL_TEMPLATE_PARMS (tmpl2); + + /* Don't change the matching rules for pre-C++20. */ + if (cxx_dialect < cxx2a) + return comp_template_parms (parms1, parms2); + + /* ... have the same number of template parameters, and their + corresponding parameters are equivalent. */ + if (!template_parameter_lists_equivalent_p (parms1, parms2)) + return false; + + /* ... if either has a requires-clause, they both do and their + corresponding constraint-expressions are equivalent. */ + return template_requirements_equivalent_p (parms1, parms2); +} + /* Determine whether PARM is a parameter pack. */ bool @@ -4572,7 +4713,7 @@ template_parm_to_arg (tree t) /* Given a single level of template parameters (a TREE_VEC), return it as a set of template arguments. */ -static tree +tree template_parms_level_to_args (tree parms) { tree a = copy_node (parms); @@ -4590,7 +4731,7 @@ template_parms_level_to_args (tree parms) arguments. The template parameters are represented as a TREE_VEC, in the form documented in cp-tree.h for template arguments. */ -static tree +tree template_parms_to_args (tree parms) { tree header; @@ -4626,6 +4767,26 @@ current_template_args (void) return template_parms_to_args (current_template_parms); } +/* Return the fully generic arguments for of TMPL, i.e. what + current_template_args would be while parsing it. */ + +tree +generic_targs_for (tree tmpl) +{ + if (tmpl == NULL_TREE) + return NULL_TREE; + if (DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl) + || DECL_TEMPLATE_SPECIALIZATION (tmpl)) + /* DECL_TEMPLATE_RESULT doesn't have the arguments we want. For a template + template parameter, it has no TEMPLATE_INFO; for a partial + specialization, it has the arguments for the primary template, and we + want the arguments for the partial specialization. */; + else if (tree result = DECL_TEMPLATE_RESULT (tmpl)) + if (tree ti = get_template_info (result)) + return TI_ARGS (ti); + return template_parms_to_args (DECL_TEMPLATE_PARMS (tmpl)); +} + /* Update the declared TYPE by doing any lookups which were thought to be dependent, but are not now that we know the SCOPE of the declarator. */ @@ -4855,7 +5016,7 @@ process_partial_specialization (tree decl) if (comp_template_args (inner_args, INNERMOST_TEMPLATE_ARGS (main_args)) && (!flag_concepts || !strictly_subsumes (current_template_constraints (), - get_constraints (maintmpl)))) + inner_args, maintmpl))) { if (!flag_concepts) error ("partial specialization %q+D does not specialize " @@ -5564,6 +5725,8 @@ push_template_decl_real (tree decl, bool is_friend) gcc_assert (!DECL_ARTIFICIAL (decl)); else if (VAR_P (decl)) /* C++14 variable template. */; + else if (TREE_CODE (decl) == CONCEPT_DECL) + /* C++2a concept definitions. */; else { error ("template declaration of %q#D", decl); @@ -5966,6 +6129,19 @@ redeclare_class_template (tree type, tree parms, tree cons) return false; } + /* The parameters can be declared to introduce different + constraints. */ + tree p1 = TREE_VEC_ELT (tmpl_parms, i); + tree p2 = TREE_VEC_ELT (parms, i); + if (!template_parameter_constraints_equivalent_p (p1, p2)) + { + error ("declaration of template parameter %q+#D with different " + "constraints", parm); + inform (DECL_SOURCE_LOCATION (tmpl_parm), + "original declaration appeared here"); + return false; + } + if (tmpl_default != NULL_TREE && parm_default != NULL_TREE) { /* We have in [temp.param]: @@ -5999,13 +6175,18 @@ redeclare_class_template (tree type, tree parms, tree cons) TEMPLATE_TYPE_PARM_FOR_CLASS (TREE_TYPE (parm)) = true; } - // Cannot redeclare a class template with a different set of constraints. - if (!equivalent_constraints (get_constraints (tmpl), cons)) + tree ci = get_constraints (tmpl); + tree req1 = ci ? CI_TEMPLATE_REQS (ci) : NULL_TREE; + tree req2 = cons ? CI_TEMPLATE_REQS (cons) : NULL_TREE; + + /* Two classes with different constraints declare different entities. */ + if (!cp_tree_equal (req1, req2)) { error_at (input_location, "redeclaration %q#D with different " "constraints", tmpl); inform (DECL_SOURCE_LOCATION (tmpl), "original declaration appeared here"); + return false; } return true; @@ -6214,8 +6395,7 @@ get_underlying_template (tree tmpl) != num_innermost_template_parms (underlying))) break; - tree alias_args = INNERMOST_TEMPLATE_ARGS - (template_parms_to_args (DECL_TEMPLATE_PARMS (tmpl))); + tree alias_args = INNERMOST_TEMPLATE_ARGS (generic_targs_for (tmpl)); if (!comp_template_args (TI_ARGS (tinfo), alias_args)) break; @@ -7322,14 +7502,7 @@ coerce_template_args_for_ttp (tree templ, tree arglist, tree outer = DECL_CONTEXT (templ); if (outer) - { - if (DECL_TEMPLATE_SPECIALIZATION (outer)) - /* We want arguments for the partial specialization, not arguments for - the primary template. */ - outer = template_parms_to_args (DECL_TEMPLATE_PARMS (outer)); - else - outer = TI_ARGS (get_template_info (DECL_TEMPLATE_RESULT (outer))); - } + outer = generic_targs_for (outer); else if (current_template_parms) { /* This is an argument of the current template, so we haven't set @@ -7787,8 +7960,6 @@ is_compatible_template_arg (tree parm, tree arg) if (parm_cons == NULL_TREE) return true; - tree arg_cons = get_constraints (arg); - /* If the template parameter is constrained, we need to rewrite its constraints in terms of the ARG's template parameters. This ensures that all of the template parameter types will have the same depth. @@ -7796,17 +7967,18 @@ is_compatible_template_arg (tree parm, tree arg) Note that this is only valid when coerce_template_template_parm is true for the innermost template parameters of PARM and ARG. In other words, because coercion is successful, this conversion will be valid. */ + tree new_args = NULL_TREE; if (parm_cons) { - tree args = template_parms_to_args (DECL_TEMPLATE_PARMS (arg)); - parm_cons = tsubst_constraint_info (parm_cons, - INNERMOST_TEMPLATE_ARGS (args), + tree aparms = DECL_INNERMOST_TEMPLATE_PARMS (arg); + new_args = template_parms_level_to_args (aparms); + parm_cons = tsubst_constraint_info (parm_cons, new_args, tf_none, NULL_TREE); if (parm_cons == error_mark_node) return false; } - return subsumes (parm_cons, arg_cons); + return weakly_subsumes (parm_cons, new_args, arg); } // Convert a placeholder argument into a binding to the original @@ -8426,7 +8598,7 @@ coerce_template_parms (tree parms, template (DR 1430). */ else if (in_decl && (DECL_ALIAS_TEMPLATE_P (in_decl) - || concept_template_p (in_decl)) + || concept_definition_p (in_decl)) && variadic_args_p && nargs - variadic_args_p < nparms - variadic_p) { @@ -8600,6 +8772,7 @@ coerce_template_parms (tree parms, if (arg == error_mark_node) lost++; + TREE_VEC_ELT (new_inner_args, arg_idx - pack_adjust) = arg; } @@ -8617,6 +8790,32 @@ coerce_template_parms (tree parms, /* We had some pack expansion arguments that will only work if the packs are empty, but wait until instantiation time to complain. See variadic-ttp3.C. */ + + /* Except that we can't provide empty packs to alias templates or + concepts when there are no corresponding parameters. Basically, + we can get here with this: + + template concept C = true; + + template + requires C + void f(); + + When parsing C, we try to form a concept check of + C. Without the extra check for substituting an empty + pack past the last parameter, we can accept the check as valid. + + FIXME: This may be valid for alias templates (but I doubt it). + + FIXME: The error could be better also. */ + if (in_decl && concept_definition_p (in_decl)) + { + if (complain & tf_error) + error_at (location_of (TREE_VEC_ELT (args, arg_idx)), + "too many arguments"); + return error_mark_node; + } + int len = nparms + (nargs - arg_idx); tree args = make_tree_vec (len); int i = 0; @@ -9452,7 +9651,7 @@ lookup_template_class_1 (tree d1, tree arglist, tree in_decl, tree context, if (complain & tf_error) { auto_diagnostic_group d; - error ("template constraint failure"); + error ("template constraint failure for %qD", gen_tmpl); diagnose_constraints (input_location, gen_tmpl, arglist); } return error_mark_node; @@ -9785,13 +9984,12 @@ lookup_template_class (tree d1, tree arglist, tree in_decl, tree context, tree lookup_template_variable (tree templ, tree arglist) { + if (flag_concepts && variable_concept_p (templ)) + return build_concept_check (templ, arglist, tf_none); + /* The type of the expression is NULL_TREE since the template-id could refer to an explicit or partial specialization. */ - tree type = NULL_TREE; - if (flag_concepts && variable_concept_p (templ)) - /* Except that concepts are always bool. */ - type = boolean_type_node; - return build2 (TEMPLATE_ID_EXPR, type, templ, arglist); + return build2 (TEMPLATE_ID_EXPR, NULL_TREE, templ, arglist); } /* Instantiate a variable declaration from a TEMPLATE_ID_EXPR for use. */ @@ -9802,12 +10000,6 @@ finish_template_variable (tree var, tsubst_flags_t complain) tree templ = TREE_OPERAND (var, 0); tree arglist = TREE_OPERAND (var, 1); - /* We never want to return a VAR_DECL for a variable concept, since they - aren't instantiated. In a template, leave the TEMPLATE_ID_EXPR alone. */ - bool concept_p = flag_concepts && variable_concept_p (templ); - if (concept_p && processing_template_decl) - return var; - tree tmpl_args = DECL_TI_ARGS (DECL_TEMPLATE_RESULT (templ)); arglist = add_outermost_template_args (tmpl_args, arglist); @@ -9828,19 +10020,6 @@ finish_template_variable (tree var, tsubst_flags_t complain) return error_mark_node; } - /* 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. */ - if (concept_p) - { - tree decl = DECL_TEMPLATE_RESULT (templ); - return evaluate_variable_concept (decl, arglist); - } - return instantiate_template (templ, arglist, complain); } @@ -10041,6 +10220,26 @@ for_each_template_parm_r (tree *tp, int *walk_subtrees, void *d) return error_mark_node; break; + case SCOPE_REF: + if (pfd->include_nondeduced_p) + WALK_SUBTREE (TREE_OPERAND (t, 0)); + break; + + case REQUIRES_EXPR: + { + if (!fn) + return error_mark_node; + + /* Recursively walk the type of each constraint variable. */ + tree p = TREE_OPERAND (t, 0); + while (p) + { + WALK_SUBTREE (TREE_TYPE (p)); + p = TREE_CHAIN (p); + } + } + break; + default: break; } @@ -10104,6 +10303,119 @@ for_each_template_parm (tree t, tree_fn_t fn, void* data, return result; } +struct find_template_parameter_info +{ + explicit find_template_parameter_info (int d) + : max_depth (d) + {} + + hash_set visited; + hash_set parms; + int max_depth; +}; + +/* Appends the declaration of T to the list in DATA. */ + +static int +keep_template_parm (tree t, void* data) +{ + find_template_parameter_info *ftpi = (find_template_parameter_info*)data; + + /* Template parameters declared within the expression are not part of + the parameter mapping. For example, in this concept: + + template + concept C = requires { } -> same_as; + + the return specifier same_as declares a new decltype parameter + that must not be part of the parameter mapping. The same is true + for generic lambda parameters, lambda template parameters, etc. */ + int level; + int index; + template_parm_level_and_index (t, &level, &index); + if (level > ftpi->max_depth) + return 0; + + /* Arguments like const T yield parameters like const T. This means that + a template-id like X would yield two distinct parameters: + T and const T. Adjust types to their unqualified versions. */ + if (TYPE_P (t)) + t = TYPE_MAIN_VARIANT (t); + ftpi->parms.add (t); + + return 0; +} + +/* Ensure that we recursively examine certain terms that are not normally + visited in for_each_template_parm_r. */ + +static int +any_template_parm_r (tree t, void *data) +{ + find_template_parameter_info *ftpi = (find_template_parameter_info*)data; + +#define WALK_SUBTREE(NODE) \ + do \ + { \ + for_each_template_parm (NODE, keep_template_parm, data, \ + &ftpi->visited, true, \ + any_template_parm_r); \ + } \ + while (0) + + switch (TREE_CODE (t)) + { + case RECORD_TYPE: + case UNION_TYPE: + case ENUMERAL_TYPE: + /* Search for template parameters in type aliases. */ + if (alias_template_specialization_p (t)) + { + tree tinfo = TYPE_ALIAS_TEMPLATE_INFO (t); + WALK_SUBTREE (TI_ARGS (tinfo)); + } + break; + + case TEMPLATE_TYPE_PARM: + /* Type constraints of a placeholder type may contain parameters. */ + if (is_auto (t)) + if (tree constr = PLACEHOLDER_TYPE_CONSTRAINTS (t)) + WALK_SUBTREE (constr); + break; + + case TEMPLATE_ID_EXPR: + /* Search through references to variable templates. */ + WALK_SUBTREE (TREE_OPERAND (t, 0)); + WALK_SUBTREE (TREE_OPERAND (t, 1)); + break; + + case CONSTRUCTOR: + if (TREE_TYPE (t)) + WALK_SUBTREE (TREE_TYPE (t)); + break; + default: + break; + } + + /* Keep walking. */ + return 0; +} + +/* Returns a list of unique template parameters found within T. */ + +tree +find_template_parameters (tree t, int depth) +{ + find_template_parameter_info ftpi (depth); + for_each_template_parm (t, keep_template_parm, &ftpi, &ftpi.visited, + /*include_nondeduced*/true, any_template_parm_r); + tree list = NULL_TREE; + for (hash_set::iterator iter = ftpi.parms.begin(); + iter != ftpi.parms.end(); ++iter) + list = tree_cons (NULL_TREE, *iter, list); + return list; +} + /* Returns true if T depends on any template parameter. */ int @@ -10431,6 +10743,30 @@ tsubst_friend_function (tree decl, tree args) DECL_USE_TEMPLATE (DECL_TEMPLATE_RESULT (new_friend)) = 0; DECL_SAVED_TREE (DECL_TEMPLATE_RESULT (new_friend)) = DECL_SAVED_TREE (DECL_TEMPLATE_RESULT (decl)); + + /* Attach the template requirements to the new declaration + for declaration matching. We need to rebuild the requirements + so that parameter levels match. */ + if (tree ci = get_constraints (decl)) + { + tree parms = DECL_TEMPLATE_PARMS (new_friend); + tree args = generic_targs_for (new_friend); + tree treqs = tsubst_constraint (CI_TEMPLATE_REQS (ci), args, + tf_warning_or_error, NULL_TREE); + tree freqs = tsubst_constraint (CI_DECLARATOR_REQS (ci), args, + tf_warning_or_error, NULL_TREE); + + /* Update the constraints -- these won't really be valid for + checking, but that's not what we need them for. These ensure + that the declared function can find the friend during + declaration matching. */ + tree new_ci = get_constraints (new_friend); + CI_TEMPLATE_REQS (new_ci) = treqs; + CI_DECLARATOR_REQS (new_ci) = freqs; + + /* Also update the template parameter list. */ + TEMPLATE_PARMS_CONSTRAINTS (parms) = treqs; + } } /* The mangled name for the NEW_FRIEND is incorrect. The function @@ -11529,7 +11865,7 @@ instantiate_class_template (tree type) return ret; } -static tree +tree tsubst_template_arg (tree t, tree args, tsubst_flags_t complain, tree in_decl) { tree r; @@ -11547,6 +11883,7 @@ tsubst_template_arg (tree t, tree args, tsubst_flags_t complain, tree in_decl) if (!(complain & tf_warning)) --c_inhibit_evaluation_warnings; } + return r; } @@ -11770,14 +12107,6 @@ gen_elem_of_pack_expansion_instantiation (tree pattern, t = tsubst_decl (pattern, args, complain); else if (pattern == error_mark_node) t = error_mark_node; - else if (constraint_p (pattern)) - { - if (processing_template_decl) - t = tsubst_constraint (pattern, args, complain, in_decl); - else - t = (constraints_satisfied_p (pattern, args) - ? boolean_true_node : boolean_false_node); - } else if (!TYPE_P (pattern)) t = tsubst_expr (pattern, args, complain, in_decl, /*integral_constant_expression_p=*/false); @@ -12257,7 +12586,6 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, { int idx; template_parm_level_and_index (parm_pack, &level, &idx); - if (level <= levels) arg_pack = TMPL_ARG (args, level, idx); } @@ -12311,6 +12639,10 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, /* We can't substitute for this parameter pack. We use a flag as well as the missing_level counter because function parameter packs don't have a level. */ + if (!(processing_template_decl || is_auto (parm_pack))) + { + gcc_unreachable (); + } gcc_assert (processing_template_decl || is_auto (parm_pack)); unsubstituted_packs = true; } @@ -12519,9 +12851,33 @@ copy_template_args (tree t) return new_vec; } +/* Substitute ARGS into the *_ARGUMENT_PACK orig_arg. */ + +tree +tsubst_argument_pack (tree orig_arg, tree args, tsubst_flags_t complain, + tree in_decl) +{ + /* Substitute into each of the arguments. */ + tree new_arg = TYPE_P (orig_arg) + ? cxx_make_type (TREE_CODE (orig_arg)) + : make_node (TREE_CODE (orig_arg)); + + tree pack_args = tsubst_template_args (ARGUMENT_PACK_ARGS (orig_arg), + args, complain, in_decl); + if (pack_args == error_mark_node) + new_arg = error_mark_node; + else + SET_ARGUMENT_PACK_ARGS (new_arg, pack_args); + + if (TREE_CODE (new_arg) == NONTYPE_ARGUMENT_PACK) + TREE_CONSTANT (new_arg) = TREE_CONSTANT (orig_arg); + + return new_arg; +} + /* Substitute ARGS into the vector or list of template arguments T. */ -static tree +tree tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl) { tree orig_t = t; @@ -12556,22 +12912,7 @@ tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl) expanded_len_adjust += TREE_VEC_LENGTH (new_arg) - 1; } else if (ARGUMENT_PACK_P (orig_arg)) - { - /* Substitute into each of the arguments. */ - new_arg = TYPE_P (orig_arg) - ? cxx_make_type (TREE_CODE (orig_arg)) - : make_node (TREE_CODE (orig_arg)); - - tree pack_args = tsubst_template_args (ARGUMENT_PACK_ARGS (orig_arg), - args, complain, in_decl); - if (pack_args == error_mark_node) - new_arg = error_mark_node; - else - SET_ARGUMENT_PACK_ARGS (new_arg, pack_args); - - if (TREE_CODE (new_arg) == NONTYPE_ARGUMENT_PACK) - TREE_CONSTANT (new_arg) = TREE_CONSTANT (orig_arg); - } + new_arg = tsubst_argument_pack (orig_arg, args, complain, in_decl); else new_arg = tsubst_template_arg (orig_arg, args, complain, in_decl); @@ -12704,6 +13045,7 @@ tsubst_template_parm (tree t, tree args, tsubst_flags_t complain) default_value = TREE_PURPOSE (t); parm_decl = TREE_VALUE (t); + tree constraint = TEMPLATE_PARM_CONSTRAINTS (t); parm_decl = tsubst (parm_decl, args, complain, NULL_TREE); if (TREE_CODE (parm_decl) == PARM_DECL @@ -12711,8 +13053,11 @@ tsubst_template_parm (tree t, tree args, tsubst_flags_t complain) parm_decl = error_mark_node; default_value = tsubst_template_arg (default_value, args, complain, NULL_TREE); + constraint = tsubst_constraint (constraint, args, complain, NULL_TREE); - return build_tree_list (default_value, parm_decl); + tree r = build_tree_list (default_value, parm_decl); + TEMPLATE_PARM_CONSTRAINTS (r) = constraint; + return r; } /* Substitute the ARGS into the indicated aggregate (or enumeration) @@ -13138,14 +13483,11 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain, && !grok_op_properties (r, /*complain=*/false)) return error_mark_node; - /* When instantiating a constrained member, substitute - into the constraints to create a new constraint. */ + /* Associate the constraints directly with the instantiation. We + don't substitute through the constraints; that's only done when + they are checked. */ if (tree ci = get_constraints (t)) - if (member) - { - ci = tsubst_constraint_info (ci, argvec, complain, NULL_TREE); - set_constraints (r, ci); - } + set_constraints (r, ci); if (DECL_FRIEND_P (t) && DECL_FRIEND_CONTEXT (t)) SET_DECL_FRIEND_CONTEXT (r, @@ -14001,6 +14343,17 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain) return r; } +/* Substitute into the complete parameter type list PARMS. */ + +tree +tsubst_function_parms (tree parms, + tree args, + tsubst_flags_t complain, + tree in_decl) +{ + return tsubst_arg_types (parms, args, NULL_TREE, complain, in_decl); +} + /* Substitute into the ARG_TYPES of a function type. If END is a TREE_CHAIN, leave it and any following types un-substituted. */ @@ -14565,6 +14918,14 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) if (code == TEMPLATE_TYPE_PARM) { int quals; + + /* When building concept checks for the purpose of + deducing placeholders, we can end up with wildcards + where types are expected. Adjust this to the deduced + value. */ + if (TREE_CODE (arg) == WILDCARD_DECL) + arg = TREE_TYPE (TREE_TYPE (arg)); + gcc_assert (TYPE_P (arg)); quals = cp_type_quals (arg) | cp_type_quals (t); @@ -14696,10 +15057,10 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) if (TREE_CODE (t) == TEMPLATE_TYPE_PARM) { - /* Propagate constraints on placeholders. */ + /* Propagate constraints on placeholders since they are + only instantiated during satisfaction. */ if (tree constr = PLACEHOLDER_TYPE_CONSTRAINTS (t)) - PLACEHOLDER_TYPE_CONSTRAINTS (r) - = tsubst_constraint (constr, args, complain, in_decl); + PLACEHOLDER_TYPE_CONSTRAINTS (r) = constr; else if (tree pl = CLASS_PLACEHOLDER_TEMPLATE (t)) { pl = tsubst_copy (pl, args, complain, in_decl); @@ -18347,6 +18708,28 @@ tsubst_copy_and_build (tree t, RETURN (templ); } + if (concept_definition_p (templ)) + { + tree check = build_concept_check (templ, targs, complain); + if (check == error_mark_node) + RETURN (error_mark_node); + + tree id = unpack_concept_check (check); + + /* If we built a function concept check, return the underlying + template-id. So we can evaluate it as a function call. */ + if (function_concept_p (TREE_OPERAND (id, 0))) + RETURN (id); + + /* Evaluate the concept, if needed. */ + tree args = TREE_OPERAND (id, 1); + if (!uses_template_parms (args) + && !processing_constraint_expression_p ()) + RETURN (evaluate_concept_check (check, complain)); + + RETURN (check); + } + if (variable_template_p (templ)) RETURN (lookup_and_finish_template_variable (templ, targs, complain)); @@ -19111,6 +19494,25 @@ tsubst_copy_and_build (tree t, /*fn_p=*/NULL, complain)); } + else if (concept_check_p (function)) + { + /* FUNCTION is a template-id referring to a concept definition. */ + tree id = unpack_concept_check (function); + tree tmpl = TREE_OPERAND (id, 0); + tree args = TREE_OPERAND (id, 1); + + /* Calls to standard and variable concepts should have been + previously diagnosed. */ + gcc_assert (function_concept_p (tmpl)); + + /* Ensure the result is wrapped as a call expression. */ + ret = build_concept_check (tmpl, args, tf_warning_or_error); + + /* Possibly evaluate the check if it is non-dependent. */ + if (!uses_template_parms (args) + && !processing_constraint_expression_p ()) + ret = evaluate_concept_check (ret, complain); + } else ret = finish_call_expr (function, &call_args, /*disallow_virtual=*/qualified_p, @@ -23202,10 +23604,11 @@ more_specialized_fn (tree pat1, tree pat2, int len) constrained template. */ if (!lose1 && !lose2) { - tree c1 = get_constraints (DECL_TEMPLATE_RESULT (pat1)); - tree c2 = get_constraints (DECL_TEMPLATE_RESULT (pat2)); - lose1 = !subsumes_constraints (c1, c2); - lose2 = !subsumes_constraints (c2, c1); + int winner = more_constrained (decl1, decl2); + if (winner > 0) + lose2 = true; + else if (winner < 0) + lose1 = true; } /* All things being equal, if the next argument is a pack expansion @@ -23277,7 +23680,7 @@ more_specialized_partial_spec (tree tmpl, tree pat1, tree pat2) /* If both deductions succeed, the partial ordering selects the more constrained template. */ if (!winner && any_deductions) - return more_constrained (tmpl1, tmpl2); + winner = more_constrained (tmpl1, tmpl2); /* In the case of a tie where at least one of the templates has a parameter pack at the end, the template with the most @@ -25712,7 +26115,7 @@ value_dependent_expression_p (tree expression) } case TEMPLATE_ID_EXPR: - return variable_concept_p (TREE_OPERAND (expression, 0)); + return concept_definition_p (TREE_OPERAND (expression, 0)); case CONSTRUCTOR: { @@ -26112,14 +26515,14 @@ instantiation_dependent_r (tree *tp, int *walk_subtrees, return *tp; case CALL_EXPR: - /* Treat calls to function concepts as dependent. */ - if (function_concept_check_p (*tp)) + /* Treat concept checks as dependent. */ + if (concept_check_p (*tp)) return *tp; break; case TEMPLATE_ID_EXPR: - /* And variable concepts. */ - if (variable_concept_p (TREE_OPERAND (*tp, 0))) + /* Treat concept checks as dependent. */ + if (concept_check_p (*tp)) return *tp; break; @@ -26657,10 +27060,10 @@ build_non_dependent_expr (tree expr) /* Don't do this during nsdmi parsing as it can lead to unexpected recursive instantiations. */ && !parsing_nsdmi () - /* Don't do this during concept expansion either and for + /* Don't do this during concept processing either and for the same reason. */ - && !expanding_concept ()) - fold_non_dependent_expr (expr, tf_none); + && !processing_constraint_expression_p ()) + fold_non_dependent_expr (expr); STRIP_ANY_LOCATION_WRAPPER (expr); @@ -26755,8 +27158,7 @@ static tree make_auto_1 (tree name, bool set_canonical) { tree au = cxx_make_type (TEMPLATE_TYPE_PARM); - TYPE_NAME (au) = build_decl (input_location, - TYPE_DECL, name, au); + TYPE_NAME (au) = build_decl (input_location, TYPE_DECL, name, au); TYPE_STUB_DECL (au) = TYPE_NAME (au); TEMPLATE_TYPE_PARM_INDEX (au) = build_template_parm_index (0, processing_template_decl + 1, processing_template_decl + 1, @@ -26801,31 +27203,87 @@ template_placeholder_p (tree t) return is_auto (t) && CLASS_PLACEHOLDER_TEMPLATE (t); } -/* Make a "constrained auto" type-specifier. This is an - auto type with constraints that must be associated after - deduction. The constraint is formed from the given - CONC and its optional sequence of arguments, which are - non-null if written as partial-concept-id. */ +/* Make a "constrained auto" type-specifier. This is an auto or + decltype(auto) type with constraints that must be associated after + deduction. The constraint is formed from the given concept CON + and its optional sequence of template arguments ARGS. -tree -make_constrained_auto (tree con, tree args) -{ - tree type = make_auto_1 (auto_identifier, false); + TYPE must be the result of make_auto_type or make_decltype_auto_type. */ +static tree +make_constrained_placeholder_type (tree type, tree con, tree args) +{ /* Build the constraint. */ tree tmpl = DECL_TI_TEMPLATE (con); - tree expr = VAR_P (con) ? tmpl : ovl_make (tmpl); - expr = build_concept_check (expr, type, args); + tree expr = tmpl; + if (TREE_CODE (con) == FUNCTION_DECL) + expr = ovl_make (tmpl); + expr = build_concept_check (expr, type, args, tf_warning_or_error); - tree constr = normalize_expression (expr); - PLACEHOLDER_TYPE_CONSTRAINTS (type) = constr; + PLACEHOLDER_TYPE_CONSTRAINTS (type) = expr; /* Our canonical type depends on the constraint. */ TYPE_CANONICAL (type) = canonical_type_parameter (type); /* Attach the constraint to the type declaration. */ - tree decl = TYPE_NAME (type); - return decl; + return TYPE_NAME (type); +} + +/* Make a "constrained auto" type-specifier. */ + +tree +make_constrained_auto (tree con, tree args) +{ + tree type = make_auto_1 (auto_identifier, false); + return make_constrained_placeholder_type (type, con, args); +} + +/* Make a "constrained decltype(auto)" type-specifier. */ + +tree +make_constrained_decltype_auto (tree con, tree args) +{ + tree type = make_auto_1 (decltype_auto_identifier, false); + /* FIXME: I don't know why this isn't done in make_auto_1. */ + AUTO_IS_DECLTYPE (type) = true; + return make_constrained_placeholder_type (type, con, args); +} + +/* Build and return a concept definition. Like other templates, the + CONCEPT_DECL node is wrapped by a TEMPLATE_DECL. This returns the + the TEMPLATE_DECL. */ + +tree +finish_concept_definition (cp_expr id, tree init) +{ + gcc_assert (identifier_p (id)); + gcc_assert (processing_template_decl); + + location_t loc = id.get_location(); + + /* A concept-definition shall not have associated constraints. */ + if (TEMPLATE_PARMS_CONSTRAINTS (current_template_parms)) + { + error_at (loc, "a concept cannot be constrained"); + TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = NULL_TREE; + } + + /* A concept-definition shall appear in namespace scope. Templates + aren't allowed in block scope, so we only need to check for class + scope. */ + if (TYPE_P (current_scope()) || !DECL_NAMESPACE_SCOPE_P (current_scope ())) + { + error_at (loc, "concept %qE not in namespace scope", *id); + return error_mark_node; + } + + /* Initially build the concept declaration; it's type is bool. */ + tree decl = build_lang_decl_loc (loc, CONCEPT_DECL, *id, boolean_type_node); + DECL_CONTEXT (decl) = current_scope (); + DECL_INITIAL (decl) = init; + + /* Push the enclosing template. */ + return push_template_decl (decl); } /* Given type ARG, return std::initializer_list. */ @@ -27656,13 +28114,17 @@ do_auto_deduction (tree type, tree init, tree auto_node, /* Check any placeholder constraints against the deduced type. */ if (flag_concepts && !processing_template_decl) - if (tree constr = NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node))) + if (tree check = NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node))) { /* Use the deduced type to check the associated constraints. If we have a partial-concept-id, rebuild the argument list so that we check using the extra arguments. */ - gcc_assert (TREE_CODE (constr) == CHECK_CONSTR); - tree cargs = CHECK_CONSTR_ARGS (constr); + check = unpack_concept_check (check); + gcc_assert (TREE_CODE (check) == TEMPLATE_ID_EXPR); + tree cdecl = TREE_OPERAND (check, 0); + if (OVL_P (cdecl)) + cdecl = OVL_FIRST (cdecl); + tree cargs = TREE_OPERAND (check, 1); if (TREE_VEC_LENGTH (cargs) > 1) { cargs = copy_node (cargs); @@ -27670,7 +28132,11 @@ do_auto_deduction (tree type, tree init, tree auto_node, } else cargs = targs; - if (!constraints_satisfied_p (constr, cargs)) + + /* Rebuild the check using the deduced arguments. */ + check = build_concept_check (cdecl, cargs, tf_none); + + if (!constraints_satisfied_p (check, cargs)) { if (complain & tf_warning_or_error) { @@ -27695,7 +28161,7 @@ do_auto_deduction (tree type, tree init, tree auto_node, "placeholder constraints"); break; } - diagnose_constraints (input_location, constr, targs); + diagnose_constraints (input_location, check, targs); } return error_mark_node; } @@ -27936,6 +28402,23 @@ append_type_to_template_for_access_check (tree templ, scope, location); } +/* Recursively walk over && expressions searching for EXPR. Return a reference + to that expression. */ + +static tree *find_template_requirement (tree *t, tree key) +{ + if (*t == key) + return t; + if (TREE_CODE (*t) == TRUTH_ANDIF_EXPR) + { + if (tree *p = find_template_requirement (&TREE_OPERAND (*t, 0), key)) + return p; + if (tree *p = find_template_requirement (&TREE_OPERAND (*t, 1), key)) + return p; + } + return 0; +} + /* Convert the generic type parameters in PARM that match the types given in the range [START_IDX, END_IDX) from the current_template_parms into generic type packs. */ @@ -27957,9 +28440,8 @@ convert_generic_types_to_packs (tree parm, int start_idx, int end_idx) /* Create a distinct parameter pack type from the current parm and add it to the replacement args to tsubst below into the generic function parameter. */ - - tree o = TREE_TYPE (TREE_VALUE - (TREE_VEC_ELT (current, i))); + tree node = TREE_VEC_ELT (current, i); + tree o = TREE_TYPE (TREE_VALUE (node)); tree t = copy_type (o); TEMPLATE_TYPE_PARM_INDEX (t) = reduce_template_parm_level (TEMPLATE_TYPE_PARM_INDEX (o), @@ -27970,7 +28452,26 @@ convert_generic_types_to_packs (tree parm, int start_idx, int end_idx) TEMPLATE_TYPE_PARAMETER_PACK (t) = true; TYPE_CANONICAL (t) = canonical_type_parameter (t); TREE_VEC_ELT (replacement, i) = t; - TREE_VALUE (TREE_VEC_ELT (current, i)) = TREE_CHAIN (t); + + /* Replace the current template parameter with new pack. */ + TREE_VALUE (node) = TREE_CHAIN (t); + + /* Surgically adjust the associated constraint of adjusted parameter + and it's corresponding contribution to the current template + requirements. */ + if (tree constr = TEMPLATE_PARM_CONSTRAINTS (node)) + { + tree id = unpack_concept_check (constr); + TREE_VEC_ELT (TREE_OPERAND (id, 1), 0) = template_parm_to_arg (t); + tree fold = finish_left_unary_fold_expr (constr, TRUTH_ANDIF_EXPR); + TEMPLATE_PARM_CONSTRAINTS (node) = fold; + + /* If there was a constraint, we also need to replace that in + the template requirements, which we've already built. */ + tree *reqs = &TEMPLATE_PARMS_CONSTRAINTS (current_template_parms); + reqs = find_template_requirement (reqs, constr); + *reqs = fold; + } } for (int i = end_idx, e = TREE_VEC_LENGTH (current); i < e; ++i) @@ -27987,27 +28488,6 @@ 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 for constraint entries. */ -struct constr_hasher : ggc_ptr_hash -{ - 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. @@ -28015,7 +28495,7 @@ struct constr_hasher : ggc_ptr_hash FIXME: This is defined in pt.c because garbage collection code is not being generated for constraint.cc. */ -static GTY (()) hash_table *decl_constraints; +static GTY ((cache)) tree_cache_map *decl_constraints; /* Returns the template constraints of declaration T. If T is not constrained, return NULL_TREE. Note that T must be non-null. */ @@ -28025,14 +28505,15 @@ get_constraints (tree t) { if (!flag_concepts) return NULL_TREE; + if (!decl_constraints) + return NULL_TREE; 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); + tree* found = decl_constraints->get (t); if (found) - return found->ci; + return *found; else return NULL_TREE; } @@ -28050,12 +28531,8 @@ set_constraints (tree t, tree ci) gcc_assert (t && flag_concepts); 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 (); - *entry = elt; - *slot = entry; + bool found = hash_map_safe_put (decl_constraints, t, ci); + gcc_assert (!found); } /* Remove the associated constraints of the declaration T. */ @@ -28067,149 +28544,8 @@ remove_constraints (tree 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); -} - -/* Memoized satisfaction results for declarations. This - maps the pair (constraint_info, arguments) to the result computed - by constraints_satisfied_p. */ - -struct GTY((for_user)) constraint_sat_entry -{ - tree ci; - tree args; - tree result; -}; - -/* Hashing function and equality for constraint entries. */ - -struct constraint_sat_hasher : ggc_ptr_hash -{ - static hashval_t hash (constraint_sat_entry *e) - { - hashval_t val = iterative_hash_object(e->ci, 0); - return iterative_hash_template_arg (e->args, val); - } - - static bool equal (constraint_sat_entry *e1, constraint_sat_entry *e2) - { - return e1->ci == e2->ci && comp_template_args (e1->args, e2->args); - } -}; - -/* Memoized satisfaction results for concept checks. */ - -struct GTY((for_user)) concept_spec_entry -{ - tree tmpl; - tree args; - tree result; -}; - -/* Hashing function and equality for constraint entries. */ - -struct concept_spec_hasher : ggc_ptr_hash -{ - static hashval_t hash (concept_spec_entry *e) - { - return hash_tmpl_and_args (e->tmpl, e->args); - } - - static bool equal (concept_spec_entry *e1, concept_spec_entry *e2) - { - ++comparing_specializations; - bool eq = e1->tmpl == e2->tmpl && comp_template_args (e1->args, e2->args); - --comparing_specializations; - return eq; - } -}; - -static GTY (()) hash_table *constraint_memos; -static GTY (()) hash_table *concept_memos; - -/* Search for a memoized satisfaction result. Returns one of the - truth value nodes if previously memoized, or NULL_TREE otherwise. */ - -tree -lookup_constraint_satisfaction (tree ci, tree args) -{ - constraint_sat_entry elt = { ci, args, NULL_TREE }; - constraint_sat_entry* found = constraint_memos->find (&elt); - if (found) - return found->result; - else - return NULL_TREE; -} - -/* Memoize the result of a satisfication test. Returns the saved result. */ - -tree -memoize_constraint_satisfaction (tree ci, tree args, tree result) -{ - constraint_sat_entry elt = {ci, args, result}; - constraint_sat_entry** slot = constraint_memos->find_slot (&elt, INSERT); - constraint_sat_entry* entry = ggc_alloc (); - *entry = elt; - *slot = entry; - return result; -} - -/* Search for a memoized satisfaction result for a concept. */ - -tree -lookup_concept_satisfaction (tree tmpl, tree args) -{ - concept_spec_entry elt = { tmpl, args, NULL_TREE }; - concept_spec_entry* found = concept_memos->find (&elt); - if (found) - return found->result; - else - return NULL_TREE; -} - -/* Memoize the result of a concept check. Returns the saved result. */ - -tree -memoize_concept_satisfaction (tree tmpl, tree args, tree result) -{ - concept_spec_entry elt = {tmpl, args, result}; - concept_spec_entry** slot = concept_memos->find_slot (&elt, INSERT); - concept_spec_entry* entry = ggc_alloc (); - *entry = elt; - *slot = entry; - return result; -} - -static GTY (()) hash_table *concept_expansions; - -/* Returns a prior concept specialization. This returns the substituted - and normalized constraints defined by the concept. */ - -tree -get_concept_expansion (tree tmpl, tree args) -{ - concept_spec_entry elt = { tmpl, args, NULL_TREE }; - concept_spec_entry* found = concept_expansions->find (&elt); - if (found) - return found->result; - else - return NULL_TREE; -} - -/* Save a concept expansion for later. */ - -tree -save_concept_expansion (tree tmpl, tree args, tree def) -{ - concept_spec_entry elt = {tmpl, args, def}; - concept_spec_entry** slot = concept_expansions->find_slot (&elt, INSERT); - concept_spec_entry* entry = ggc_alloc (); - *entry = elt; - *slot = entry; - return def; + if (decl_constraints) + decl_constraints->remove (t); } static hashval_t @@ -28306,13 +28642,11 @@ init_constraint_processing (void) if (!flag_concepts) return; - decl_constraints = hash_table::create_ggc(37); - constraint_memos = hash_table::create_ggc(37); - concept_memos = hash_table::create_ggc(37); - concept_expansions = hash_table::create_ggc(37); subsumption_table = hash_table::create_ggc(37); } +GTY(()) tree current_failed_constraint; + /* __integer_pack(N) in a pack expansion expands to a sequence of numbers from 0..N-1. */ diff --git a/gcc/cp/search.c b/gcc/cp/search.c index b441af8ff09..5bc8679efd5 100644 --- a/gcc/cp/search.c +++ b/gcc/cp/search.c @@ -835,7 +835,10 @@ accessible_p (tree type, tree decl, bool consider_local_p) in default arguments for template parameters), and access checking should be performed in the outermost parameter list. */ if (processing_template_decl - && !expanding_concept () + /* FIXME CWG has been talking about doing access checking in the context + of the constraint-expression, rather than the constrained declaration, + in which case we would want to remove this test. */ + && !processing_constraint_expression_p () && (!processing_template_parmlist || processing_template_decl > 1)) return 1; diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 5a8c4d237e0..1839013fbbc 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -1788,10 +1788,10 @@ finish_mem_initializers (tree mem_inits) /* Obfuscate EXPR if it looks like an id-expression or member access so that the call to finish_decltype in do_auto_deduction will give the - right result. */ + right result. If EVEN_UNEVAL, do this even in unevaluated context. */ tree -force_paren_expr (tree expr) +force_paren_expr (tree expr, bool even_uneval) { /* This is only needed for decltype(auto) in C++14. */ if (cxx_dialect < cxx14) @@ -1799,7 +1799,7 @@ force_paren_expr (tree expr) /* If we're in unevaluated context, we can't be deducing a return/initializer type, so we don't need to mess with this. */ - if (cp_unevaluated_operand) + if (cp_unevaluated_operand && !even_uneval) return expr; if (!DECL_P (tree_strip_any_location_wrapper (expr)) @@ -2589,6 +2589,27 @@ finish_call_expr (tree fn, vec **args, bool disallow_virtual, /*fn_p=*/NULL, complain); } + else if (concept_check_p (fn)) + { + /* FN is actually a template-id referring to a concept definition. */ + tree id = unpack_concept_check (fn); + tree tmpl = TREE_OPERAND (id, 0); + tree args = TREE_OPERAND (id, 1); + + if (!function_concept_p (tmpl)) + { + error_at (EXPR_LOC_OR_LOC (fn, input_location), + "cannot call a concept as a function"); + return error_mark_node; + } + + /* Ensure the result is wrapped as a call expression. */ + result = build_concept_check (tmpl, args, tf_warning_or_error); + + /* Evaluate the check if it is non-dependent. */ + if (!uses_template_parms (args)) + result = evaluate_concept_check (result, complain); + } else if (is_overloaded_fn (fn)) { /* If the function is an overloaded builtin, resolve it. */ @@ -3833,9 +3854,10 @@ finish_id_expression_1 (tree id_expression, if (! error_operand_p (decl) && !dependent_p && integral_constant_expression_p - && ! decl_constant_var_p (decl) + && !decl_constant_var_p (decl) && TREE_CODE (decl) != CONST_DECL - && ! builtin_valid_in_constant_expr_p (decl)) + && !builtin_valid_in_constant_expr_p (decl) + && !concept_check_p (decl)) { if (!allow_non_integral_constant_expression_p) { @@ -3851,12 +3873,23 @@ finish_id_expression_1 (tree id_expression, decl = wrap; else if (TREE_CODE (decl) == TEMPLATE_ID_EXPR && !dependent_p - && variable_template_p (TREE_OPERAND (decl, 0))) + && variable_template_p (TREE_OPERAND (decl, 0)) + && !concept_check_p (decl)) { decl = finish_template_variable (decl); mark_used (decl); decl = convert_from_reference (decl); } + else if (concept_check_p (decl)) + { + /* If this is a standard or variable concept check, potentially + evaluate it. Function concepts need to be called as functions, + so don't try evaluating them here. */ + tree tmpl = TREE_OPERAND (decl, 0); + tree args = TREE_OPERAND (decl, 1); + if (!function_concept_p (tmpl) && !uses_template_parms (args)) + decl = evaluate_concept_check (decl, tf_warning_or_error); + } else if (scope) { if (TREE_CODE (decl) == SCOPE_REF) @@ -3929,6 +3962,16 @@ finish_id_expression_1 (tree id_expression, decl = baselink_for_fns (decl); } + else if (concept_check_p (decl)) + { + /* If this is a standard or variable concept check, potentially + evaluate it. Function concepts need to be called as functions, + so don't try evaluating them here. */ + tree tmpl = TREE_OPERAND (decl, 0); + tree args = TREE_OPERAND (decl, 1); + if (!function_concept_p (tmpl) && !uses_template_parms (args)) + decl = evaluate_concept_check (decl, tf_warning_or_error); + } else { if (DECL_P (decl) && DECL_NONLOCAL (decl) diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 1d9e9b8d05d..ba334e7d5cb 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -9644,7 +9644,8 @@ check_return_expr (tree retval, bool *no_warning) if (!retval) retval = void_node; auto_node = type_uses_auto (pattern); - type = do_auto_deduction (pattern, retval, auto_node); + type = do_auto_deduction (pattern, retval, auto_node, + tf_warning_or_error, adc_return_type); } if (type == error_mark_node) diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index 54d6b848157..2402c38fdf3 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -923,7 +923,7 @@ store_init_value (tree decl, tree init, vec** cleanups, int flags) return split_nonconstant_init (decl, value); /* DECL may change value; purge caches. */ - clear_cv_and_fold_caches (); + clear_cv_and_fold_caches (TREE_STATIC (decl)); /* If the value is a constant, just put it in DECL_INITIAL. If DECL is an automatic variable, the middle end will turn this into a @@ -2242,8 +2242,7 @@ build_functional_cast (tree exp, tree parms, tsubst_flags_t complain) if (!CLASS_PLACEHOLDER_TEMPLATE (anode)) { if (complain & tf_error) - error_at (DECL_SOURCE_LOCATION (TEMPLATE_TYPE_DECL (anode)), - "invalid use of %qT", anode); + error ("invalid use of %qT", anode); return error_mark_node; } else if (!parms) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 20e10c01804..dc9a3e5e1a3 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -2512,14 +2512,17 @@ exhaustion is signalled by throwing @code{std::bad_alloc}. See also @samp{new (nothrow)}. @item -fconcepts +@itemx -fconcepts-ts @opindex fconcepts -Enable support for the C++ Extensions for Concepts Technical -Specification, ISO 19217 (2015), which allows code like - -@smallexample -template concept bool Addable = requires (T t) @{ t + t; @}; -template T add (T a, T b) @{ return a + b; @} -@end smallexample +@opindex fconcepts-ts +Below @option{-std=c++2a}, @option{-fconcepts} enables support for the +C++ Extensions for Concepts Technical Specification, ISO 19217 (2015). + +With @option{-std=c++2a} and above, Concepts are part of the language +standard, so @option{-fconcepts} defaults to on. But the standard +specification of Concepts differs significantly from the TS, so some +constructs that were allowed in the TS but didn't make it into the +standard can still be enabled by @option{-fconcepts-ts}. @item -fconstexpr-depth=@var{n} @opindex fconstexpr-depth diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 26a3fcce243..93186555294 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2019-10-08 Andrew Sutton + + * lib/target-supports.exp (check_effective_target_concepts): Check + for std=c++2a. + 2019-10-09 Paolo Carlini * c-c++-common/Waddress-1.c: Test locations too. diff --git a/gcc/testsuite/g++.dg/concepts/alias1.C b/gcc/testsuite/g++.dg/concepts/alias1.C deleted file mode 100644 index 279a4787576..00000000000 --- a/gcc/testsuite/g++.dg/concepts/alias1.C +++ /dev/null @@ -1,16 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template - concept bool C() { return __is_class(T); } - -template - requires C() - using X = T*; - -struct S { }; - -int main() -{ - X x1; -} diff --git a/gcc/testsuite/g++.dg/concepts/alias2.C b/gcc/testsuite/g++.dg/concepts/alias2.C deleted file mode 100644 index 06ffb1af529..00000000000 --- a/gcc/testsuite/g++.dg/concepts/alias2.C +++ /dev/null @@ -1,14 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template - concept bool C() { return __is_class(T); } - -template using X = T*; - -struct S { }; - -int main() -{ - X x1; -} diff --git a/gcc/testsuite/g++.dg/concepts/alias3.C b/gcc/testsuite/g++.dg/concepts/alias3.C deleted file mode 100644 index 2901c041881..00000000000 --- a/gcc/testsuite/g++.dg/concepts/alias3.C +++ /dev/null @@ -1,14 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template - concept bool C() { return __is_class(T); } - -template - requires C() - using X = T*; - -int main() -{ - X x1; // { dg-error "constraint|invalid" } -} diff --git a/gcc/testsuite/g++.dg/concepts/alias4.C b/gcc/testsuite/g++.dg/concepts/alias4.C deleted file mode 100644 index 2c9f5defeb0..00000000000 --- a/gcc/testsuite/g++.dg/concepts/alias4.C +++ /dev/null @@ -1,20 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template - concept bool C() { return __is_class(T); } - -template - requires C() - using X = T*; - -// BUG: Alias templates are expanded at the point of use, regardless -// of whether or not they are dependent. This causes T* to be substituted -// without acutally checking the constraints. -template - using Y = X; - -int main() -{ - Y y1; // { dg-error "" "" { xfail *-*-* } } -} diff --git a/gcc/testsuite/g++.dg/concepts/auto1.C b/gcc/testsuite/g++.dg/concepts/auto1.C index 2682940cedd..e05330610fc 100644 --- a/gcc/testsuite/g++.dg/concepts/auto1.C +++ b/gcc/testsuite/g++.dg/concepts/auto1.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template class A { }; diff --git a/gcc/testsuite/g++.dg/concepts/auto3.C b/gcc/testsuite/g++.dg/concepts/auto3.C index abfb2019125..27a6afa4ed9 100644 --- a/gcc/testsuite/g++.dg/concepts/auto3.C +++ b/gcc/testsuite/g++.dg/concepts/auto3.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template class tuple {}; diff --git a/gcc/testsuite/g++.dg/concepts/auto4.C b/gcc/testsuite/g++.dg/concepts/auto4.C index 4eb2ae8f7d5..8bf3fa9b1ce 100644 --- a/gcc/testsuite/g++.dg/concepts/auto4.C +++ b/gcc/testsuite/g++.dg/concepts/auto4.C @@ -1,6 +1,6 @@ // PR c++/85006 -// { dg-do compile { target c++17 } } -// { dg-additional-options "-fconcepts" } +// { dg-do compile { target c++17_only } } +// { dg-options "-fconcepts" } template struct A {}; diff --git a/gcc/testsuite/g++.dg/concepts/class-deduction1.C b/gcc/testsuite/g++.dg/concepts/class-deduction1.C index 936dd6826f0..33597007752 100644 --- a/gcc/testsuite/g++.dg/concepts/class-deduction1.C +++ b/gcc/testsuite/g++.dg/concepts/class-deduction1.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template diff --git a/gcc/testsuite/g++.dg/concepts/class.C b/gcc/testsuite/g++.dg/concepts/class.C deleted file mode 100644 index dc5523e2407..00000000000 --- a/gcc/testsuite/g++.dg/concepts/class.C +++ /dev/null @@ -1,53 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template - concept bool Class() { return __is_class(T); } - -template - concept bool Union() { return __is_union(T); } - - -// Check ordering of specializations -template - concept bool One() { return sizeof(T) >= 4; } - -template - concept bool Two() { return One() && sizeof(T) >= 8; } - -// Check non-overlapping specializations -template - struct S1 { static const int value = 0; }; - -template - struct S1 { static const int value = 1; }; - -template - struct S1 { static const int value = 2; }; - -struct S { }; -union U { }; - -static_assert(S1::value == 0, ""); -static_assert(S1::value == 1, ""); -static_assert(S1::value == 2, ""); - - -// Check ordering of partial specializaitons -template - struct S2 { static const int value = 0; }; - -template - struct S2 { static const int value = 1; }; - -template - struct S2 { static const int value = 2; }; - -struct one_type { char x[4]; }; -struct two_type { char x[8]; }; - -static_assert(S2::value == 0, ""); -static_assert(S2::value == 1, ""); -static_assert(S2::value == 2, ""); - -int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/class1.C b/gcc/testsuite/g++.dg/concepts/class1.C deleted file mode 100644 index a738e6e82cd..00000000000 --- a/gcc/testsuite/g++.dg/concepts/class1.C +++ /dev/null @@ -1,15 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template - concept bool C() { return __is_class(T); } - -template - requires C() - struct S { }; - -struct X { }; - -S sx; - -int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/class2.C b/gcc/testsuite/g++.dg/concepts/class2.C deleted file mode 100644 index ec8718114a7..00000000000 --- a/gcc/testsuite/g++.dg/concepts/class2.C +++ /dev/null @@ -1,15 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template - concept bool C() { return __is_class(T); } - -template - requires C() - struct S { }; - -struct X { }; - -S sx; // { dg-error "constraint|invalid" } - -int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/class3.C b/gcc/testsuite/g++.dg/concepts/class3.C deleted file mode 100644 index 256370df46d..00000000000 --- a/gcc/testsuite/g++.dg/concepts/class3.C +++ /dev/null @@ -1,15 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template - concept bool C() { return __is_class(T); } - -// Check class redeclaration with alternative spellings. -template requires C() struct S; -template struct S { }; - -struct X { }; - -// S sx; - -int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/class4.C b/gcc/testsuite/g++.dg/concepts/class4.C deleted file mode 100644 index b583e55411d..00000000000 --- a/gcc/testsuite/g++.dg/concepts/class4.C +++ /dev/null @@ -1,22 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template - concept bool Class() { return __is_class(T); } - -template - concept bool Union() { return __is_union(T); } - -// Check non-overlapping specializations -template struct S1 { static const int value = 0; }; -template struct S1 { static const int value = 1; }; -template struct S1 { static const int value = 2; }; - -struct S { }; -union U { }; - -static_assert(S1::value == 0, ""); -static_assert(S1::value == 1, ""); -static_assert(S1::value == 2, ""); - -int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/class5.C b/gcc/testsuite/g++.dg/concepts/class5.C index 7bf620edc5c..ac9d7e83e9d 100644 --- a/gcc/testsuite/g++.dg/concepts/class5.C +++ b/gcc/testsuite/g++.dg/concepts/class5.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template diff --git a/gcc/testsuite/g++.dg/concepts/class6.C b/gcc/testsuite/g++.dg/concepts/class6.C index bdd60918c8e..f2345b19b04 100644 --- a/gcc/testsuite/g++.dg/concepts/class6.C +++ b/gcc/testsuite/g++.dg/concepts/class6.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template @@ -15,5 +15,3 @@ struct one_type { char x[4]; }; // Constraints are checked even when decls are not instantiatied. S4* x4b; // { dg-error "constraint|invalid" } - -int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/debug1.C b/gcc/testsuite/g++.dg/concepts/debug1.C index 87f2ac90b20..b9a544486ac 100644 --- a/gcc/testsuite/g++.dg/concepts/debug1.C +++ b/gcc/testsuite/g++.dg/concepts/debug1.C @@ -1,11 +1,11 @@ // PR c++/84551 -// { dg-do compile { target c++17 } } -// { dg-options "-g -O -fconcepts" } +// { dg-do compile { target c++17_only } } +// { dg-options "-fconcepts" } template concept bool C() { return true; } -template requires C() class> struct A {}; +template requires C() class TT> struct A {}; -template requires true struct B {}; +template requires C() struct B {}; A a; diff --git a/gcc/testsuite/g++.dg/concepts/decl-diagnose.C b/gcc/testsuite/g++.dg/concepts/decl-diagnose.C index 019a8ce1130..6a461a50366 100644 --- a/gcc/testsuite/g++.dg/concepts/decl-diagnose.C +++ b/gcc/testsuite/g++.dg/concepts/decl-diagnose.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } typedef concept int CINT; // { dg-error "'concept' cannot appear in a typedef declaration" } @@ -12,10 +12,10 @@ concept bool f3(); // { dg-error "14:concept .f3. has no definition" } struct X { template - concept int f4() { return 0; } // { dg-error "return type|member function" } - concept bool f5() { return true; } // { dg-error "member function" } + concept int f4() { return 0; } // { dg-error "cannot be a member" } + concept f5 = true; // { dg-error "declared 'concept'" } template - static concept bool f6() { return true; } // { dg-error "a concept cannot be a member function" } + static concept f6 = true; // { dg-error "declared 'concept'" } static concept bool x; // { dg-error "declared 'concept'" } // { dg-error "uninitialized 'const" "" { target *-*-* } .-1 } concept int x2; // { dg-error "declared 'concept'" } diff --git a/gcc/testsuite/g++.dg/concepts/deduction-constraint1.C b/gcc/testsuite/g++.dg/concepts/deduction-constraint1.C index bebbda1a1fa..eba57713089 100644 --- a/gcc/testsuite/g++.dg/concepts/deduction-constraint1.C +++ b/gcc/testsuite/g++.dg/concepts/deduction-constraint1.C @@ -1,5 +1,5 @@ // PR c++/67007 -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic1.C b/gcc/testsuite/g++.dg/concepts/diagnostic1.C index 9bb15060573..ced56d400ba 100644 --- a/gcc/testsuite/g++.dg/concepts/diagnostic1.C +++ b/gcc/testsuite/g++.dg/concepts/diagnostic1.C @@ -1,19 +1,19 @@ // PR c++/67159 -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template concept bool SameAs = __is_same_as(T, U); template -concept bool R1 = requires (T& t) { - { t.begin() } -> T - { t.end() } -> SameAs; +concept bool R1 = requires (T& t) { // { dg-message "in requirements" } + { t.begin() } -> T; // { dg-error "no match" } + { t.end() } -> SameAs; // { dg-error "does not satisfy" } }; template -concept bool R2 = requires (T& t) { - { t.end() } -> SameAs; +concept bool R2 = requires (T& t) { // { dg-message "in requirements" } + { t.end() } -> SameAs; // { dg-error "does not satisfy" } }; struct foo { diff --git a/gcc/testsuite/g++.dg/concepts/disjunction1.C b/gcc/testsuite/g++.dg/concepts/disjunction1.C deleted file mode 100644 index 930adf405c9..00000000000 --- a/gcc/testsuite/g++.dg/concepts/disjunction1.C +++ /dev/null @@ -1,60 +0,0 @@ -// PR c++/66962 -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template struct remove_cv; -template struct is_reference; -template void declval(); -template struct is_constructible; -template struct is_nothrow_constructible; -template using remove_cv_t = typename remove_cv<_Tp>::type; -template struct Trans_NS_extension_apply_list; -template using _t = typename T::type; -template void ImplicitlyConvertibleTo(); -template void Assignable(); -template int ConstructibleObject = requires { T{}; }; -template -concept bool BindableReference = - is_reference::value &&is_constructible::value; -template concept bool Constructible() { - return ConstructibleObject || BindableReference; -} -template concept bool DefaultConstructible() { - return Constructible() && requires { new T[0]; }; -} -template concept bool MoveConstructible() { - return Constructible() && ImplicitlyConvertibleTo; -} -template concept bool Movable() { - return MoveConstructible() && Assignable; -} -template int Swappable_ = requires { 0; }; -template int Swappable(); -template concept bool Dereferencable = requires{{0}}; -template using RvalueReferenceType = decltype(0); -template int IsValueType; -template struct value_type; -template -requires IsValueType< - _t>>> using ValueType = - _t>>; -template concept bool Readable() { - return Movable() && DefaultConstructible() && - Dereferencable && requires{{0}}; -} -template concept bool MoveWritable() { - return Movable() && DefaultConstructible() && - Dereferencable; -} -template concept bool IndirectlyMovable() { - return Readable() && Movable>() && - Constructible>() && - MoveWritable>() && - MoveWritable>(); -} -IndirectlyMovable { In, Out } -int is_nothrow_indirectly_movable_v = - is_nothrow_constructible>::value; -template - requires IndirectlyMovable() && - IndirectlyMovable() void iter_swap2(); diff --git a/gcc/testsuite/g++.dg/concepts/dr1430.C b/gcc/testsuite/g++.dg/concepts/dr1430.C index f865d5ec2c7..6f5bab1e106 100644 --- a/gcc/testsuite/g++.dg/concepts/dr1430.C +++ b/gcc/testsuite/g++.dg/concepts/dr1430.C @@ -1,5 +1,5 @@ // PR c++/66092 -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } #include @@ -29,11 +29,14 @@ template concept bool Similar = true; template -requires Same() // { dg-error "invalid reference" } +requires Same() // { dg-error "" "" { xfail *-*-* } } void foo( Args... args ) {} +// FIXME: The new method of building concept checks is suppressing the +// diagnostic for the invalid substitution. This produces an invalid +// requires-clause, which still prevents the function from being resolved. template -requires Similar // { dg-error "invalid reference" } +requires Similar // { dg-error "pack expansion" } void bar( Args... args ) {} int main() diff --git a/gcc/testsuite/g++.dg/concepts/equiv.C b/gcc/testsuite/g++.dg/concepts/equiv.C index faec3543461..640c2b5ec0d 100644 --- a/gcc/testsuite/g++.dg/concepts/equiv.C +++ b/gcc/testsuite/g++.dg/concepts/equiv.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do link { target c++17_only } } // { dg-options "-fconcepts" } // Check equivalence of short- and longhand declarations. diff --git a/gcc/testsuite/g++.dg/concepts/equiv2.C b/gcc/testsuite/g++.dg/concepts/equiv2.C index 2094ca9f388..dff719b86a5 100644 --- a/gcc/testsuite/g++.dg/concepts/equiv2.C +++ b/gcc/testsuite/g++.dg/concepts/equiv2.C @@ -1,4 +1,4 @@ -// { dg-do run { target c++17 } } +// { dg-do link { target c++17_only } } // { dg-options "-fconcepts" } @@ -21,9 +21,9 @@ int main() { void f1(C, C) { } -template -void f2(T, T) { } +template +void f2(T1, T2) { } -template - requires C -void f3(T, T) { } +template + requires C && C +void f3(T, U) { } diff --git a/gcc/testsuite/g++.dg/concepts/explicit-inst4.C b/gcc/testsuite/g++.dg/concepts/explicit-inst4.C deleted file mode 100644 index 20f43770539..00000000000 --- a/gcc/testsuite/g++.dg/concepts/explicit-inst4.C +++ /dev/null @@ -1,18 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template - concept bool C() { return __is_class(T); } - -template - concept bool D() { return C() && __is_empty(T); } - -template - struct S { - void g() requires C() { } // #1 - void g() requires D() { } // #2 - }; - -template void S::g(); // { dg-error "match" } - -int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec3.C b/gcc/testsuite/g++.dg/concepts/explicit-spec3.C deleted file mode 100644 index fd48da1c280..00000000000 --- a/gcc/testsuite/g++.dg/concepts/explicit-spec3.C +++ /dev/null @@ -1,14 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template - concept bool C() { return __is_class(T); } - -template struct S; - -struct X { }; - -// Not a valid explicit specialization, int does not satisfy C. -template<> struct S { }; // { dg-error "constraint" } - -int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/expression.C b/gcc/testsuite/g++.dg/concepts/expression.C index 33dad0a47a6..ba4c48d7dcc 100644 --- a/gcc/testsuite/g++.dg/concepts/expression.C +++ b/gcc/testsuite/g++.dg/concepts/expression.C @@ -1,6 +1,8 @@ -// { dg-do run { target c++17 } } +// { dg-do run { target c++17_only } } // { dg-options "-fconcepts" } +// TODO: ICE on gimplify 16? + #include #include @@ -8,13 +10,9 @@ template concept bool C1 = __is_class(T); template - concept bool C2() { return __is_class(T); } - -template - concept bool C3() { return requires (T a) { ++a; }; } + concept bool C3 = requires (T a) { ++a; }; int main() { if (C1) assert(false); - if (C2()) assert(false); - if (!C3()) assert(false); + if (!C3) assert(false); } diff --git a/gcc/testsuite/g++.dg/concepts/expression2.C b/gcc/testsuite/g++.dg/concepts/expression2.C index c5447df1d87..1cff60542f5 100644 --- a/gcc/testsuite/g++.dg/concepts/expression2.C +++ b/gcc/testsuite/g++.dg/concepts/expression2.C @@ -1,16 +1,16 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template concept bool C1() { - return requires (T t) { t.f(); }; + return requires (T t) { t.f(); }; // { dg-message "in requirements" } } template concept bool C2() { - return requires { typename T::type; }; + return requires { typename T::type; }; // { dg-message "in requirements" } } template @@ -22,7 +22,7 @@ template void f2(T x) { } // Note that these declarations are private and therefore -// cannot satisify the constraints. +// cannot satisfy the constraints. class S { using type = int; @@ -31,12 +31,12 @@ class S int main() { - f1(s); // { dg-error "cannot call" } + f1(s); // { dg-error "cannot call|private" } f2(s); // { dg-error "" } // When used in non-SFINAE contexts, make sure that we fail // the constraint check before emitting the access check - // failures. The context is being presented constistently + // failures. The context is being presented consistently // in both cases. static_assert(C1(), ""); // { dg-error "failed" } static_assert(C2(), ""); // { dg-error "" } diff --git a/gcc/testsuite/g++.dg/concepts/expression3.C b/gcc/testsuite/g++.dg/concepts/expression3.C index 26b829d338d..67646811284 100644 --- a/gcc/testsuite/g++.dg/concepts/expression3.C +++ b/gcc/testsuite/g++.dg/concepts/expression3.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template diff --git a/gcc/testsuite/g++.dg/concepts/feature-macro.C b/gcc/testsuite/g++.dg/concepts/feature-macro.C deleted file mode 100644 index d3d9b5420a8..00000000000 --- a/gcc/testsuite/g++.dg/concepts/feature-macro.C +++ /dev/null @@ -1,6 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -#ifndef __cpp_concepts -#error __cpp_concepts not defined -#endif diff --git a/gcc/testsuite/g++.dg/concepts/fn-concept1.C b/gcc/testsuite/g++.dg/concepts/fn-concept1.C index a4ade7c628d..d1b4c0c59f3 100644 --- a/gcc/testsuite/g++.dg/concepts/fn-concept1.C +++ b/gcc/testsuite/g++.dg/concepts/fn-concept1.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template diff --git a/gcc/testsuite/g++.dg/concepts/fn-concept2.C b/gcc/testsuite/g++.dg/concepts/fn-concept2.C index 0d70728abe8..899988c37a3 100644 --- a/gcc/testsuite/g++.dg/concepts/fn-concept2.C +++ b/gcc/testsuite/g++.dg/concepts/fn-concept2.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template diff --git a/gcc/testsuite/g++.dg/concepts/fn-generic-member-ool.C b/gcc/testsuite/g++.dg/concepts/fn-generic-member-ool.C index b664ccf3da1..245380388fa 100644 --- a/gcc/testsuite/g++.dg/concepts/fn-generic-member-ool.C +++ b/gcc/testsuite/g++.dg/concepts/fn-generic-member-ool.C @@ -1,4 +1,3 @@ -// Out-of-line generic member function definitions. // { dg-do compile { target c++14 } } // { dg-additional-options "-fconcepts" } diff --git a/gcc/testsuite/g++.dg/concepts/fn1.C b/gcc/testsuite/g++.dg/concepts/fn1.C index 17f14b9a46b..f23c057ab6b 100644 --- a/gcc/testsuite/g++.dg/concepts/fn1.C +++ b/gcc/testsuite/g++.dg/concepts/fn1.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template diff --git a/gcc/testsuite/g++.dg/concepts/fn10.C b/gcc/testsuite/g++.dg/concepts/fn10.C index 6993f34a89f..8d0a2e1d202 100644 --- a/gcc/testsuite/g++.dg/concepts/fn10.C +++ b/gcc/testsuite/g++.dg/concepts/fn10.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } // Test that constraint satisfaction checks work even when @@ -40,7 +40,7 @@ template template concept bool Concept() { - return requires( T t ) { + return requires( T t ) { // { dg-message "in requirements" } requires Float(); }; } diff --git a/gcc/testsuite/g++.dg/concepts/fn2.C b/gcc/testsuite/g++.dg/concepts/fn2.C index 250e0a8713a..debb3238a6e 100644 --- a/gcc/testsuite/g++.dg/concepts/fn2.C +++ b/gcc/testsuite/g++.dg/concepts/fn2.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template diff --git a/gcc/testsuite/g++.dg/concepts/fn3.C b/gcc/testsuite/g++.dg/concepts/fn3.C index bc0e126c96c..07b8e3a89ba 100644 --- a/gcc/testsuite/g++.dg/concepts/fn3.C +++ b/gcc/testsuite/g++.dg/concepts/fn3.C @@ -1,4 +1,4 @@ -// { dg-do run { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } #include diff --git a/gcc/testsuite/g++.dg/concepts/fn4.C b/gcc/testsuite/g++.dg/concepts/fn4.C index 830a1747865..bbaac46c9ff 100644 --- a/gcc/testsuite/g++.dg/concepts/fn4.C +++ b/gcc/testsuite/g++.dg/concepts/fn4.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template diff --git a/gcc/testsuite/g++.dg/concepts/fn5.C b/gcc/testsuite/g++.dg/concepts/fn5.C index 018b12f86e1..7714788c3c0 100644 --- a/gcc/testsuite/g++.dg/concepts/fn5.C +++ b/gcc/testsuite/g++.dg/concepts/fn5.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } // Check shorthand notation. diff --git a/gcc/testsuite/g++.dg/concepts/fn6.C b/gcc/testsuite/g++.dg/concepts/fn6.C index 97155f8eb86..031e87fdf23 100644 --- a/gcc/testsuite/g++.dg/concepts/fn6.C +++ b/gcc/testsuite/g++.dg/concepts/fn6.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } // Redefinition errors. diff --git a/gcc/testsuite/g++.dg/concepts/fn7.C b/gcc/testsuite/g++.dg/concepts/fn7.C index 0052f1aee73..869cb9c9391 100644 --- a/gcc/testsuite/g++.dg/concepts/fn7.C +++ b/gcc/testsuite/g++.dg/concepts/fn7.C @@ -1,8 +1,6 @@ -// { dg-do link { target c++17 } } +// { dg-do link { target c++14 } } // { dg-options "-fconcepts" } -// FIXME: What is this actually testing? - void f() requires true { } int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/fn8.C b/gcc/testsuite/g++.dg/concepts/fn8.C index a3daf4e1bad..ffcce4f0220 100644 --- a/gcc/testsuite/g++.dg/concepts/fn8.C +++ b/gcc/testsuite/g++.dg/concepts/fn8.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template diff --git a/gcc/testsuite/g++.dg/concepts/fn9.C b/gcc/testsuite/g++.dg/concepts/fn9.C index 84ed77ce1c1..2f5e88b945c 100644 --- a/gcc/testsuite/g++.dg/concepts/fn9.C +++ b/gcc/testsuite/g++.dg/concepts/fn9.C @@ -1,4 +1,4 @@ -// { dg-do run { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } #include diff --git a/gcc/testsuite/g++.dg/concepts/generic-fn-err.C b/gcc/testsuite/g++.dg/concepts/generic-fn-err.C index 5a9556531e7..816072d8d3a 100644 --- a/gcc/testsuite/g++.dg/concepts/generic-fn-err.C +++ b/gcc/testsuite/g++.dg/concepts/generic-fn-err.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template @@ -10,10 +10,10 @@ template template class X> concept bool Template() { return true; } -struct S { }; +void f1(Int) { } // { dg-error "does not constrain a type" } +void f2(Template) { } // { dg-error "does not constrain a type" } -void f1(Int) { } // { dg-error "" } -void f2(Template) { } // { dg-error "" } +struct S { }; struct S1 { void f1(auto x) { } diff --git a/gcc/testsuite/g++.dg/concepts/generic-fn.C b/gcc/testsuite/g++.dg/concepts/generic-fn.C index 3b10327a493..257608a57a7 100644 --- a/gcc/testsuite/g++.dg/concepts/generic-fn.C +++ b/gcc/testsuite/g++.dg/concepts/generic-fn.C @@ -1,4 +1,4 @@ -// { dg-do run { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } #include diff --git a/gcc/testsuite/g++.dg/concepts/iconv1.C b/gcc/testsuite/g++.dg/concepts/iconv1.C deleted file mode 100644 index e99254f3c27..00000000000 --- a/gcc/testsuite/g++.dg/concepts/iconv1.C +++ /dev/null @@ -1,21 +0,0 @@ -// PR c++/67240 -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -int foo(int x) -{ - return x; -} - -template -concept bool C1 = requires (T x) { - {foo(x)} -> int&; -}; - -template -concept bool C2 = requires (T x) { - {foo(x)} -> void; -}; - -static_assert( C1 ); // { dg-error "assert" } -static_assert( C2 ); // { dg-error "assert" } diff --git a/gcc/testsuite/g++.dg/concepts/inherit-ctor1.C b/gcc/testsuite/g++.dg/concepts/inherit-ctor1.C index 4b3f5619331..b137791bbb6 100644 --- a/gcc/testsuite/g++.dg/concepts/inherit-ctor1.C +++ b/gcc/testsuite/g++.dg/concepts/inherit-ctor1.C @@ -1,23 +1,33 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template - concept bool C() { return __is_class(T); } + concept bool C = __is_class(T); + +struct X { }; template - struct S1 { S1(double) requires C() { } }; + struct Base { + Base(double) requires C { } + }; -struct S2 : S1 { - using S1::S1; +struct Ok1 : Base { + using Base::Base; +}; + +struct Err1 : Base { + using Base::Base; }; template - struct S3 : S1 { - using S1::S1; + struct Generic : Base { + using Base::Base; }; -struct X { }; int main() { - S3 s(0.0); + Ok1 x1(0.0); + Err1 x2(0.0); // { dg-error "no matching" } + Generic x3(0.0); + Generic x4(0.0); // { dg-error "no matching" } } diff --git a/gcc/testsuite/g++.dg/concepts/inherit-ctor3.C b/gcc/testsuite/g++.dg/concepts/inherit-ctor3.C index 6f046323346..abfe96e8240 100644 --- a/gcc/testsuite/g++.dg/concepts/inherit-ctor3.C +++ b/gcc/testsuite/g++.dg/concepts/inherit-ctor3.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template @@ -11,12 +11,13 @@ template }; template - struct S2 : S1 { - using S1::S1; + struct S2 : S1 { // { dg-error "no matching function" } + using S1::S1; // { dg-error "no matching function" } }; struct X { } x; int main() { - S2 s = x; + S2 s1(0); // { dg-error "use of deleted function" } + S2 s2; // { dg-error "use of deleted function" } } diff --git a/gcc/testsuite/g++.dg/concepts/inherit-ctor4.C b/gcc/testsuite/g++.dg/concepts/inherit-ctor4.C deleted file mode 100644 index 43df6e67186..00000000000 --- a/gcc/testsuite/g++.dg/concepts/inherit-ctor4.C +++ /dev/null @@ -1,19 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template - concept bool C() { return __is_class(T); } - -template - struct S1 { - template S1(U x) { } - }; - -template - struct S2 : S1 { - using S1::S1; - }; - -int main() { - S2 s(0); // { dg-error "no matching function" } -} diff --git a/gcc/testsuite/g++.dg/concepts/intro1.C b/gcc/testsuite/g++.dg/concepts/intro1.C index 84fa6dbbed4..5f9bb7e08f0 100644 --- a/gcc/testsuite/g++.dg/concepts/intro1.C +++ b/gcc/testsuite/g++.dg/concepts/intro1.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template diff --git a/gcc/testsuite/g++.dg/concepts/intro2.C b/gcc/testsuite/g++.dg/concepts/intro2.C index 9c7c1733c6d..206777d1b94 100644 --- a/gcc/testsuite/g++.dg/concepts/intro2.C +++ b/gcc/testsuite/g++.dg/concepts/intro2.C @@ -1,4 +1,4 @@ -// { dg-do run { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } #include diff --git a/gcc/testsuite/g++.dg/concepts/intro3.C b/gcc/testsuite/g++.dg/concepts/intro3.C index 5e93f313270..f02f1bea247 100644 --- a/gcc/testsuite/g++.dg/concepts/intro3.C +++ b/gcc/testsuite/g++.dg/concepts/intro3.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template diff --git a/gcc/testsuite/g++.dg/concepts/intro4.C b/gcc/testsuite/g++.dg/concepts/intro4.C index a7e513535aa..0b275e14bf2 100644 --- a/gcc/testsuite/g++.dg/concepts/intro4.C +++ b/gcc/testsuite/g++.dg/concepts/intro4.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template @@ -18,11 +18,11 @@ template template concept bool C5() { return __is_class(U); } -C1{...A, B} void f1() {}; // { dg-error "no matching|wrong number" } -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" } +C1{...A, B} void f1() {}; // { dg-error "cannot deduce template parameters" } +C1{A} void f2() {} +C2{A, B} void f3() {}; +C3{...A} void f4() {}; // { dg-error "cannot be introduced" } +C4{A} void f5() {}; // { dg-error "cannot deduce template parameters" } C5{A, B} void f6() {}; int main() diff --git a/gcc/testsuite/g++.dg/concepts/intro5.C b/gcc/testsuite/g++.dg/concepts/intro5.C index e7cd7a48066..bbfef7bed9e 100644 --- a/gcc/testsuite/g++.dg/concepts/intro5.C +++ b/gcc/testsuite/g++.dg/concepts/intro5.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template @@ -7,10 +7,5 @@ template return sizeof(U) == sizeof(int); } -C{A} void f1() {} +C{A} void f1() {} // { dg-error "all template parameters" } -int main() -{ - f1(); - return 0; -} diff --git a/gcc/testsuite/g++.dg/concepts/intro6.C b/gcc/testsuite/g++.dg/concepts/intro6.C index 57b325a0e38..233c5bcec7b 100644 --- a/gcc/testsuite/g++.dg/concepts/intro6.C +++ b/gcc/testsuite/g++.dg/concepts/intro6.C @@ -1,5 +1,5 @@ // PR c++/67003 -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } namespace X { diff --git a/gcc/testsuite/g++.dg/concepts/intro7.C b/gcc/testsuite/g++.dg/concepts/intro7.C index d5bdc7e2789..343fe7a9824 100644 --- a/gcc/testsuite/g++.dg/concepts/intro7.C +++ b/gcc/testsuite/g++.dg/concepts/intro7.C @@ -1,14 +1,14 @@ // PR c++/66985 -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template