diff options
author | Jason Merrill <jason@gcc.gnu.org> | 2019-10-09 13:20:32 -0400 |
---|---|---|
committer | Jason Merrill <jason@gcc.gnu.org> | 2019-10-09 13:20:32 -0400 |
commit | cb57504a550158913258e5be8ddb991376475efb (patch) | |
tree | d1f7676666e845ba7e77b45fcc3d6e5e0a2acb82 | |
parent | 8cb6a77590957942e124b34e0bb0827d1404f341 (diff) | |
download | gcc-cb57504a550158913258e5be8ddb991376475efb.zip gcc-cb57504a550158913258e5be8ddb991376475efb.tar.gz gcc-cb57504a550158913258e5be8ddb991376475efb.tar.bz2 |
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
326 files changed, 9534 insertions, 4810 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 330e13b..a02d211 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,7 @@ +2019-10-08 Jason Merrill <jason@redhat.com> + + * doc/invoke.texi: Document -fconcepts-ts. + 2019-10-09 Richard Biener <rguenther@suse.de> * tree-vect-loop.c (vect_is_simple_reduction): Simplify and diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 93077ff..3012f8b 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,17 @@ +2019-10-08 Andrew Sutton <asutton@lock3software.com> + Jason Merrill <jason@redhat.com> + + 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 <joseph@codesourcery.com> * 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 9e0ce42..d9941e7 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 4ad24bd..0fffe60 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 88bbe2e..8f6867b 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 d75ea5d..e633823 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,461 @@ +2019-10-08 Andrew Sutton <asutton@lock3software.com> + Jason Merrill <jason@redhat.com> + + 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 <paolo.carlini@oracle.com> * decl.c (grok_ctor_properties): Use DECL_SOURCE_LOCATION. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 4ed424f..d36564d 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<tree, va_gc> **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 4abcfaf..b6afdc4 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 6e5f046..adfe1b2 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 f50cc04..ea50234 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 c7a172c..db4a818 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 %<bool%>"); + 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. */ - conjoin_constraints(a, NULL_TREE) == a +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; +} - and +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); +} + +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<tree, va_gc> *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<typename T> concept bool C = ...; - template<typenaem T> concept bool D = C<T> && true; - - From this, we can learn that D<T> implies C<T>. We cannot learn, - without further testing, that C<T> does not imply D<T>. If, for - example, C<T> were defined as true, then these constraints would - be logically equivalent. - - In rare cases, we may start with a logical equivalence. For example: - - template<typename T> concept bool C = ...; - template<typename T> concept bool D = C<T>; - - Here, we learn that C<T> implies D<T> 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); +/* Build a constrained placeholder type where SPEC is a type-constraint. + SPEC can be anything were concept_definition_p is true. -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); - - case DISJ_CONSTR: - if (cxt == conjunction_cxt) - return; - return learn_logical_operation (parent, constr, cxt); + gcc_assert (concept_definition_p (spec)); - 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,323 +468,230 @@ 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 (VAR_P (decl)) - return get_variable_initializer (decl); - else if (TREE_CODE (decl) == FUNCTION_DECL) - return get_returned_expression (decl); - gcc_unreachable (); -} - -int expansion_level = 0; - -class expanding_concept_sentinel -{ -public: - expanding_concept_sentinel () - { - ++expansion_level; - } - - ~expanding_concept_sentinel() - { - --expansion_level; - } -}; - - -} /* namespace */ - -/* Returns true when a concept is being expanded. */ - -bool -expanding_concept() -{ - return expansion_level > 0; -} - -/* Expand a concept declaration (not a template) and its arguments to - a constraint defined by the concept's initializer or definition. */ - -tree -expand_concept (tree decl, tree args) -{ - expanding_concept_sentinel sentinel; + if (TREE_CODE (decl) == OVERLOAD) + decl = OVL_FIRST (decl); if (TREE_CODE (decl) == TEMPLATE_DECL) decl = DECL_TEMPLATE_RESULT (decl); - tree tmpl = DECL_TI_TEMPLATE (decl); - - /* Check for a previous specialization. */ - if (tree spec = get_concept_expansion (tmpl, args)) - return spec; - /* Substitute the arguments to form a new definition expression. */ - tree def = get_concept_definition (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; - - /* 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); + if (TREE_CODE (decl) == CONCEPT_DECL) + return DECL_INITIAL (decl); + if (VAR_P (decl)) + return get_variable_initializer (decl); + if (TREE_CODE (decl) == FUNCTION_DECL) + return get_returned_expression (decl); + gcc_unreachable (); } - /*--------------------------------------------------------------------------- - Stepwise normalization of expressions + Normalization of expressions This set of functions will transform an expression into a constraint -in a sequence of steps. Normalization does not not look into concept -definitions. +in a sequence of steps. ---------------------------------------------------------------------------*/ -/* Transform a logical-or or logical-and expression into either - a conjunction or disjunction. */ - -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); -} - -/* A simple requirement T introduces an expression constraint - for its expression. */ - -inline tree -normalize_simple_requirement (tree t) -{ - return build_nt (EXPR_CONSTR, TREE_OPERAND (t, 0)); -} - -/* A type requirement T introduce a type constraint for its type. */ - -inline tree -normalize_type_requirement (tree t) -{ - return build_nt (TYPE_CONSTR, TREE_OPERAND (t, 0)); -} - -/* 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) +void +debug_parameter_mapping (tree map) { - 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)) + for (tree p = map; p; p = TREE_CHAIN (p)) { - 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); + 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 - type_constr = build_nt (ICONV_CONSTR, expr, type); - constr = conjoin_constraints (constr, type_constr); + verbatim ("MAP %qD TO %qE", TEMPLATE_PARM_DECL (parm), arg); + // debug_tree (parm); + // debug_tree (arg); } +} - /* If noexcept is present, append an exception constraint. */ - if (COMPOUND_REQ_NOEXCEPT_P (t)) +void +debug_argument_list (tree args) +{ + for (int i = 0; i < TREE_VEC_LENGTH (args); ++i) { - tree except = build_nt (EXCEPT_CONSTR, expr); - constr = conjoin_constraints (constr, except); + tree arg = TREE_VEC_ELT (args, i); + if (TYPE_P (arg)) + verbatim ("ARG %qT", arg); + else + verbatim ("ARG %qE", arg); } - - return constr; } -/* A nested requirement T introduces a conjunction of constraints - corresponding to its constraint-expression. +/* Associate each parameter in PARMS with its corresponding template + argument in ARGS. */ - 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. */ - -inline tree -normalize_nested_requirement (tree t) +static tree +map_arguments (tree parms, tree args) { - return normalize_expression (TREE_OPERAND (t, 0)); + 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; } -/* Transform a requirement T into one or more constraints. */ +/* Build the parameter mapping for EXPR using ARGS. */ -tree -normalize_requirement (tree t) +static tree +build_parameter_mapping (tree expr, tree args, tree decl) { - switch (TREE_CODE (t)) + int depth = 0; + if (decl) { - case SIMPLE_REQ: - return normalize_simple_requirement (t); - - case TYPE_REQ: - return normalize_type_requirement (t); - - case COMPOUND_REQ: - return normalize_compound_requirement (t); - - case NESTED_REQ: - return normalize_nested_requirement (t); - - default: - gcc_unreachable (); + gcc_assert (TREE_CODE (decl) == TEMPLATE_DECL); + tree parms = DECL_TEMPLATE_PARMS (decl); + depth = TREE_INT_CST_LOW (TREE_PURPOSE (parms)); } - return error_mark_node; + tree parms = find_template_parameters (expr, depth); + tree map = map_arguments (parms, args); + return map; } -/* Transform a sequence of requirements into a conjunction of - constraints. */ +/* True if the parameter mappings of two atomic constraints are equivalent. */ -tree -normalize_requirements (tree t) +static bool +parameter_mapping_equivalent_p (tree t1, tree t2) { - tree result = NULL_TREE; - for (; t; t = TREE_CHAIN (t)) + tree map1 = ATOMIC_CONSTR_MAP (t1); + tree map2 = ATOMIC_CONSTR_MAP (t2); + while (map1 && map2) { - tree constr = normalize_requirement (TREE_VALUE (t)); - result = conjoin_constraints (result, constr); + 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 result; + return true; } -/* 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. */ +/* Provides additional context for normalization. */ -tree -normalize_requires_expression (tree t) +struct norm_info : subst_info { - 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; -} + norm_info(tsubst_flags_t complain) + : subst_info (tf_warning_or_error | complain, NULL_TREE), + context() + {} -/* For a template-id referring to a variable concept, returns - a check constraint. Otherwise, returns a predicate constraint. */ - -tree -normalize_template_id_expression (tree t) -{ - 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; - } + /* Construct a top-level context for DECL. */ - tree decl = TREE_VALUE (info); - tree args = TREE_PURPOSE (info); - return build_nt (CHECK_CONSTR, decl, args); - } + norm_info (tree in_decl, tsubst_flags_t complain) + : subst_info (tf_warning_or_error | complain, in_decl), + context (make_context (in_decl)) + {} - /* 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; - } + bool generate_diagnostics() const + { + return complain & tf_norm; + } - return build_nt (PRED_CONSTR, t); -} + tree make_context(tree in_decl) + { + if (generate_diagnostics ()) + return build_tree_list (NULL_TREE, in_decl); + return NULL_TREE; + } -/* For a call expression to a function concept, returns a check - constraint. Otherwise, returns a predicate constraint. */ + 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); + } -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; - } + /* 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. */ - tree fn = TREE_VALUE (check); - tree args = TREE_PURPOSE (check); - return build_nt (CHECK_CONSTR, fn, args); -} + tree context; +}; -/* If T is a call to an overloaded && or || operator, diagnose that - as a non-SFINAEable error. Returns true if an error is emitted. +static tree normalize_expression (tree, tree, norm_info); - 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. */ +/* Transform a logical-or or logical-and expression into either + a conjunction or disjunction. */ -bool -check_for_logical_overloads (tree t) +static tree +normalize_logical_operation (tree t, tree args, tree_code c, norm_info info) { - if (TREE_CODE (t) != CALL_EXPR) - return false; + tree t0 = normalize_expression (TREE_OPERAND (t, 0), args, info); + tree t1 = normalize_expression (TREE_OPERAND (t, 1), args, info); - tree fn = CALL_EXPR_FN (t); + /* Build a new info object for the constraint. */ + tree ci = info.generate_diagnostics() + ? build_tree_list (t, info.context) + : NULL_TREE; - /* For member calls, try extracting the function from the - component ref. */ - if (TREE_CODE (fn) == COMPONENT_REF) - { - fn = TREE_OPERAND (fn, 1); - if (TREE_CODE (fn) == BASELINK) - fn = BASELINK_FUNCTIONS (fn); - } + return build2 (c, ci, t0, t1); +} - if (TREE_CODE (fn) != FUNCTION_DECL) - return false; +static tree +normalize_concept_check (tree check, tree args, norm_info info) +{ + tree id = unpack_concept_check (check); + tree tmpl = TREE_OPERAND (id, 0); + tree targs = TREE_OPERAND (id, 1); - if (DECL_OVERLOADED_OPERATOR_P (fn)) + /* A function concept is wrapped in an overload. */ + if (TREE_CODE (tmpl) == OVERLOAD) { - location_t loc = cp_expr_loc_or_input_loc (t); - error_at (loc, "constraint %qE, uses overloaded operator", t); - return true; + /* 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 false; + /* 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; + + /* 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; + + /* 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; + + info.update_context (check, args); + return normalize_expression (def, subst, info); } /* The normal form of an atom depends on the expression. The normal @@ -817,269 +700,236 @@ check_for_logical_overloads (tree t) concept is a check constraint for that concept. Otherwise, the constraint is a predicate constraint. */ -tree -normalize_atom (tree t) +static tree +normalize_atom (tree t, tree args, norm_info info) { - /* We can get constraints pushed down through pack expansions, so - just return them. */ - if (constraint_p (t)) - return t; + /* Concept checks are not atomic. */ + if (concept_check_p (t)) + return normalize_concept_check (t, args, info); - 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; + /* Build the parameter mapping for the atom. */ + tree map = build_parameter_mapping (t, args, info.in_decl); - type = cv_unqualified (type); - if (!same_type_p (type, boolean_type_node)) - { - error ("predicate constraint %q+E does not have type %<bool%>", t); - return error_mark_node; - } - } + /* Build a new info object for the atom. */ + tree ci = build_tree_list (t, info.context); - 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); + return build1 (ATOMIC_CONSTR, ci, map); } -/* Push down the pack expansion EXP into the leaves of the constraint PAT. */ +/* Returns the normal form of an expression. */ -tree -push_down_pack_expansion (tree exp, tree pat) +static tree +normalize_expression (tree t, tree args, norm_info info) { - switch (TREE_CODE (pat)) + if (!t) + return NULL_TREE; + + if (t == error_mark_node) + return error_mark_node; + + switch (TREE_CODE (t)) { - 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; - } + 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: - { - exp = copy_node (exp); - SET_PACK_EXPANSION_PATTERN (exp, pat); - return exp; - } + return normalize_atom (t, args, info); } } -/* 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. */ +/* Cache of the normalized form of constraints. Marked as deletable because it + can all be recalculated. */ +static GTY((deletable)) hash_map<tree,tree> *normalized_map; -tree -normalize_pack_expansion (tree t) +static tree +get_normalized_constraints (tree t, tree args, norm_info info) { - tree pat = normalize_expression (PACK_EXPANSION_PATTERN (t)); - return push_down_pack_expansion (t, pat); + auto_timevar time (TV_CONSTRAINT_NORM); + return normalize_expression (t, args, info); } -/* Transform an expression into a 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_any_expression (tree t) +static tree +get_normalized_constraints_from_info (tree ci, tree args, tree in_decl, + bool diag = false) { - switch (TREE_CODE (t)) - { - case TRUTH_ANDIF_EXPR: - return normalize_logical_operation (t, CONJ_CONSTR); + if (ci == NULL_TREE) + return NULL_TREE; - case TRUTH_ORIF_EXPR: - return normalize_logical_operation (t, DISJ_CONSTR); + /* 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; - case REQUIRES_EXPR: - return normalize_requires_expression (t); + return t; +} - case BIND_EXPR: - return normalize_expression (BIND_EXPR_BODY (t)); +/* Returns the normalized constraints for the declaration D. */ - case EXPR_PACK_EXPANSION: - return normalize_pack_expansion (t); +static tree +get_normalized_constraints_from_decl (tree d, bool diag = false) +{ + tree tmpl; + tree decl; - default: - /* All other constraints are atomic. */ - return normalize_atom (t); - } -} + /* For inherited constructors, consider the original declaration; + it has the correct template information attached. */ + d = strip_inheriting_ctors (d); -/* Transform a statement into an expression. */ -tree -normalize_any_statement (tree t) -{ - switch (TREE_CODE (t)) + if (TREE_CODE (d) == TEMPLATE_DECL) { - case RETURN_EXPR: - return normalize_expression (TREE_OPERAND (t, 0)); - default: - gcc_unreachable (); + tmpl = d; + decl = DECL_TEMPLATE_RESULT (tmpl); } - return error_mark_node; -} - -/* Reduction rules for the declaration T. */ - -tree -normalize_any_declaration (tree t) -{ - switch (TREE_CODE (t)) + else { - case VAR_DECL: - return normalize_atom (t); - default: - gcc_unreachable (); + if (tree ti = DECL_TEMPLATE_INFO (d)) + tmpl = TI_TEMPLATE (ti); + else + tmpl = NULL_TREE; + decl = d; } - return error_mark_node; -} -/* Returns the normal form of a constraint expression. */ + /* 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: -tree -normalize_expression (tree t) -{ - if (!t) - return NULL_TREE; + template<typename T> + struct S { + template<typename U> requires C<T, U> void f(U); + }; - if (t == error_mark_node) - return error_mark_node; + S<int>::f(0); - switch (TREE_CODE_CLASS (TREE_CODE (t))) - { - case tcc_unary: - case tcc_binary: - case tcc_expression: - case tcc_vl_exp: - return normalize_any_expression (t); + When we normalize the requirements for S<int>::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); + } - case tcc_statement: - return normalize_any_statement (t); + /* 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; - case tcc_declaration: - return normalize_any_declaration (t); + tree args = generic_targs_for (tmpl); + tree ci = get_constraints (decl); + tree norm = get_normalized_constraints_from_info (ci, args, tmpl, diag); - case tcc_exceptional: - case tcc_constant: - case tcc_reference: - case tcc_comparison: - /* These are all atomic predicate constraints. */ - return normalize_atom (t); + if (!diag) + hash_map_safe_put<hm_ggc> (normalized_map, tmpl, norm); - default: - /* Unhandled node kind. */ - gcc_unreachable (); - } - return error_mark_node; + return norm; } +/* Returns the normal form of TMPL's definition. */ -/*--------------------------------------------------------------------------- - Constraint normalization ----------------------------------------------------------------------------*/ - -tree normalize_constraint (tree); +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<hm_ggc> (normalized_map, tmpl, norm); + return norm; +} -/* The normal form of the disjunction T0 /\ T1 is the conjunction - of the normal form of T0 and the normal form of T1. */ +/* Returns the normal form of TMPL's requirements. */ -inline tree -normalize_conjunction (tree t) +static tree +normalize_template_requirements (tree tmpl, bool diag = false) { - tree t0 = normalize_constraint (TREE_OPERAND (t, 0)); - tree t1 = normalize_constraint (TREE_OPERAND (t, 1)); - return build_nt (CONJ_CONSTR, t0, t1); + return get_normalized_constraints_from_decl (tmpl, diag); } -/* The normal form of the disjunction T0 \/ T1 is the disjunction - of the normal form of T0 and the normal form of T1. */ +/* Returns the normal form of TMPL's requirements. */ -inline tree -normalize_disjunction (tree t) +static tree +normalize_nontemplate_requirements (tree decl, bool diag = false) { - tree t0 = normalize_constraint (TREE_OPERAND (t, 0)); - tree t1 = normalize_constraint (TREE_OPERAND (t, 1)); - return build_nt (DISJ_CONSTR, t0, t1); + return get_normalized_constraints_from_decl (decl, diag); } -/* 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. */ +/* Normalize an EXPR as a constraint. */ -tree -normalize_predicate_constraint (tree t) +static tree +normalize_constraint_expression (tree expr, bool diag = false) { + if (!expr || expr == error_mark_node) + return expr; + + /* 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)) + { + tree id = unpack_concept_check (expr); + args = TREE_OPERAND (id, 1); + } + else + args = NULL_TREE; + ++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<Args...>()` 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<tree, va_gc> *fargs = make_tree_vector (); + tree call = build_min_nt_call_vec (id, fargs); + release_tree_vector (fargs); + --processing_template_decl; -/* Construct an expression that checks the concept given by - TARGET. The TARGET must be: + return call; +} - - 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. +/* Builds an id-expression of the form `C<Args...>` 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<Args...>` 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. */ - 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 target, tree args, tsubst_flags_t complain) { + return build_concept_check (target, NULL_TREE, args, complain); +} + +/* Construct an expression that checks the concept given by DECL. If + concept_definition_p (DECL) is false, this returns null. */ + +tree +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<C... Ts> struct s; + + If C is variadic (and because Ts is a pack), we associate the + constraint C<Ts...>. In all other cases, we associate + the constraint (C<Ts> && ...). + + 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); - - /* Make the check a pack expansion if needed. + tree check = tmpl; + if (TREE_CODE (con) == FUNCTION_DECL) + check = ovl_make (tmpl); + check = build_concept_check (check, arg, args, tf_warning_or_error); - FIXME: We should be making a fold expression. */ - if (apply_to_all_p) - { - check = make_pack_expansion (check); - TREE_TYPE (check) = boolean_type_node; - } + /* 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); - 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 +get_deduced_wildcard (tree wildcard) +{ + if (ARGUMENT_PACK_P (wildcard)) + wildcard = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (wildcard), 0); + gcc_assert (TREE_CODE (wildcard) == WILDCARD_DECL); + return wildcard; +} + +/* Returns the prototype parameter for the nth deduced wildcard. */ + static tree -process_introduction_parm (tree parameter_list, tree src_parm) +get_introduction_prototype (tree wildcards, int index) { - // 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); + return TREE_TYPE (get_deduced_wildcard (TREE_VEC_ELT (wildcards, index))); +} - // 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); +/* Introduce a type template parameter. */ - tree ident = DECL_NAME (src_parm); - location_t parm_loc = DECL_SOURCE_LOCATION (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)); +} - // 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 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; +} - src_parm = TREE_TYPE (src_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. */ + +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 process_template_parm (parameter_list, parm_loc, parm, - is_non_type, is_parameter_pack); + 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<INTRO_LIST> 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<INTRO_LIST> filling in any extra arguments needed by the defaults + deduced. + + Returns NULL_TREE if no concept could be matched and error_mark_node if + an error occurred when matching. */ - 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<C1 T> struct S { - void f(T) requires C2<T>; - void g(T) requires T::value; - }; - S<int> s; // error instantiating S<int>::g(T) +/* 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<T> yields C2<int>, 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); - return build_nt (CHECK_CONSTR, decl, args); + PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) = saved_constr; + TYPE_CANONICAL (placeholder) = saved_type; + + if (deduced_type == error_mark_node) + return false; + + 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. */ - -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. */ - -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); -} - -/* 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. */ +/* 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. -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. */ - -tree -tsubst_compound_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); - 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); -} - -/* Substitute ARGS into the nested requirement T. */ - -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. */ + However, there are cases where substitution must produce a + new requires-expression, that is not a template constraint. + For example: -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; -} + template<typename T> + class X { + template<typename U> + static constexpr bool var = requires (U u) { T::fn(u); }; + }; -/* Substitute ARGS into the list of requirements T. Note that - substitution failures here result in ill-formed programs. */ + In the instantiation of X<Y> (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<int>. */ tree -tsubst_requirement_body (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) { - 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); -} + local_specialization_stack stack (lss_copy); -} /* namespace */ + subst_info info (complain, in_decl); -/* 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; - return finish_requires_expr (parms, reqs); + /* 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 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; -/* Substitute ARGS into the constraint T. */ + tsubst_flags_t complain = info.complain; + tree in_decl = info.in_decl; + + 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<auto T> + requires C<decltype(T)> + 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<sat_entry> +{ + 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_hasher> *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<tree,bool> *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<sat_hasher>::create_ggc (31); + sat_entry elt = {constr, args, result}; + sat_entry** slot = sat_cache->find_slot (&elt, INSERT); + sat_entry* entry = ggc_alloc<sat_entry> (); + *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<CARGS> 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; - - /* 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; +/* Compute the satisfaction of a conjunction. */ - /* Attempt the conversion as a direct initialization - of the form TYPE <unspecified> = 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<tree> > 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<tree> &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<tree> &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) +/* Compute the satisfaction of an atomic constraint. */ + +static tree +satisfy_atom (tree t, tree args, subst_info info) { - gcc_assert (!processing_template_decl); + satisfaction_cache cache (t, args, info.complain); + if (tree r = cache.get ()) + return r; - if (!t) - return boolean_false_node; + /* Perform substitution quietly. */ + subst_info quiet (tf_none, NULL_TREE); - if (t == error_mark_node) - return boolean_false_node; - - 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 %<bool%>"); + 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 the given constraint, returning boolean_true_node - if the constraint is satisfied and boolean_false_node - otherwise. */ +/* Evaluate EXPR as a constraint expression using ARGS, returning a + satisfaction value. */ -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 ()); } - tree eval = satisfy_associated_constraints (ci, args); - return eval == boolean_true_node; + 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); + } + + if (info.quiet ()) + hash_map_safe_put<hm_ggc> (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); -/* 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. */ + /* Update the declaration for diagnostics. */ + info.in_decl = t; -inline bool -constraint_expression_satisfied_p (tree expr, tree args) + 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; + } + + return true; +} + +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<ARGS>, 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 a, tree b) +strictly_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) && !subsumes (n2, n1); +} + +/* REturns true when the constraints in CI (with arguments ARGS) subsume + the associated constraints of TMPL. */ + +bool +weakly_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); } /* 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<CARGS>. - 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 %<noexcept%>", 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); -} + location_t loc = get_constraint_error_location (t); + iloc_sentinel loc_s (loc); -/* 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. */ - -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 %<false%>", 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 0a72231..f38b4e8 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 4db1d31..845a7e2 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 b82b580..9ff617b 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<tree>(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<int>::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<tree> 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<tree> (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 038f300..2a129a3 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, "<lambda>"); 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, "<unsatisfied-constrained-placeholder>"); + pp_cxx_ws_string(pp, "<unsatisfied-type-constraint>"); 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, "<placeholder>"); + 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); } @@ -2858,12 +2880,6 @@ pp_cxx_nested_requirement (cxx_pretty_printer *pp, tree t) } 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) { tree decl = CHECK_CONSTR_CONCEPT (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, "<valid-expression "); - pp_cxx_left_paren (pp); - 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, "<valid-type "); - 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, "<implicitly-conversion "); - pp_cxx_left_paren (pp); - 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, "<argument-deduction "); - pp_cxx_left_paren (pp); - 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, "<requires "); - if (tree parms = PARM_CONSTR_PARMS (t)) + /* Emit the expression. */ + 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 72c02af..6f4c589 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<typename T> requires C void f(...); + template<typename T> 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 %<bool%>"); + 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, - "%<auto%> parameter not permitted in this context"); + { + if (decl_context == PARM && AUTO_IS_DECLTYPE (auto_node)) + error_at (typespec_loc, + "cannot declare a parameter with %<decltype(auto)%>"); + else + error_at (typespec_loc, + "%<auto%> parameter not permitted in this context"); + } else error_at (typespec_loc, "parameter declared %<auto%>"); 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 %<auto%>", decl); return false; } diff --git a/gcc/cp/error.c b/gcc/cp/error.c index 5620134..1fd87d2 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, "<wildcard>"); + 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 c4fed16..b503e97 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 13cc321..2d4abaf 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<typename I> -inline I -next (I iter) +struct constraint_hash : default_hash_traits<tree> { - return ++iter; -} + static hashval_t hash (tree t) + { + return hash_atomic_constraint (t); + } -template<typename I, typename P> -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<tree>::iterator iterator; + typedef std::list<tree>::const_iterator const_iterator; -/* Hashing function and equality for constraint entries. */ + /* Initialize a clause with an initial term. */ -struct term_hasher : ggc_ptr_hash<term_entry> -{ - 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<tree>::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<tree> seq; - hash_table<term_hasher> tab; -}; + std::pair<iterator, bool> 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<iterator, bool> 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<iterator, bool> 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<iterator, bool> 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<term_entry> (); - *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<tree> m_terms; /* The list of terms. */ + hash_set<tree, false, constraint_hash> 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<proof_goal> +struct formula { -public: - proof_state (); + typedef std::list<clause>::iterator iterator; + typedef std::list<clause>::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<proof_goal> (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<clause> 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<int, bool> +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<int, bool> p1 = dnf_size_r (lhs); + std::pair<int, bool> 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<int, bool> +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<int, bool> p1 = cnf_size_r (lhs); + std::pair<int, bool> 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<int, bool> 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<int, bool> 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 ff6d5ee..57ab129 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 c61e0b2..b6e738c 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 %<bool%> keyword is not " + "allowed in a C++20 concept definition"); + else + pedwarn (token->location, 0, "C++20 concept definition syntax " + "is %<concept <name> = <expr>%> "); + } + /* 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 %<auto%> 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 %<auto%> or %<decltype(auto)%> 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 %<decltype(auto)%>"); + 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<?, ARGS> 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))) { @@ -26886,30 +26978,279 @@ cp_parser_label_declaration (cp_parser* parser) } // -------------------------------------------------------------------------- // +// 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 %<requires%> 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 %<requires%> 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<typename T> + struct S { + S() requires C<T> = 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, - "%<requires%> only available with %<-fconcepts%>"); + "%<requires%> 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<T>*). */ 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<T>*'. */ + 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 %<throw%>"); break; + case RT_AUTO: + gmsgid = G_("expected %<auto%>"); + 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 6310e7b..8446443 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<typename T> concept C = true; + + template<typename... Args> + requires C<Args...> + void f(); + + When parsing C<Args...>, we try to form a concept check of + C<?, Args...>. 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<tree> visited; + hash_set<tree> 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<typename T> + concept C = requires { <expr> } -> same_as<int>; + + the return specifier same_as<int> 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<T, const T> 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<tree>::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<ARG>. */ @@ -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<constr_entry> -{ - static hashval_t hash (constr_entry *e) - { - return (hashval_t)DECL_UID (e->decl); - } - - static bool equal (constr_entry *e1, constr_entry *e2) - { - return e1->decl == e2->decl; - } -}; - /* A mapping from declarations to constraint information. Note that both templates and their underlying declarations are mapped to the same constraint information. @@ -28015,7 +28495,7 @@ struct constr_hasher : ggc_ptr_hash<constr_entry> FIXME: This is defined in pt.c because garbage collection code is not being generated for constraint.cc. */ -static GTY (()) hash_table<constr_hasher> *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<constr_entry> (); - *entry = elt; - *slot = entry; + bool found = hash_map_safe_put<hm_ggc> (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<constraint_sat_entry> -{ - 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<concept_spec_entry> -{ - 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_sat_hasher> *constraint_memos; -static GTY (()) hash_table<concept_spec_hasher> *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<constraint_sat_entry> (); - *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<concept_spec_entry> (); - *entry = elt; - *slot = entry; - return result; -} - -static GTY (()) hash_table<concept_spec_hasher> *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<concept_spec_entry> (); - *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<constr_hasher>::create_ggc(37); - constraint_memos = hash_table<constraint_sat_hasher>::create_ggc(37); - concept_memos = hash_table<concept_spec_hasher>::create_ggc(37); - concept_expansions = hash_table<concept_spec_hasher>::create_ggc(37); subsumption_table = hash_table<subsumption_hasher>::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 b441af8..5bc8679 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 5a8c4d2..1839013 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<tree, va_gc> **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 1d9e9b8..ba334e7 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 54d6b84..2402c38 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -923,7 +923,7 @@ store_init_value (tree decl, tree init, vec<tree, va_gc>** 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 20e10c0..dc9a3e5 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 <class T> concept bool Addable = requires (T t) @{ t + t; @}; -template <Addable T> 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 26a3fcc..9318655 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2019-10-08 Andrew Sutton <asutton@lock3software.com> + + * lib/target-supports.exp (check_effective_target_concepts): Check + for std=c++2a. + 2019-10-09 Paolo Carlini <paolo.carlini@oracle.com> * 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 279a478..0000000 --- a/gcc/testsuite/g++.dg/concepts/alias1.C +++ /dev/null @@ -1,16 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template<typename T> - concept bool C() { return __is_class(T); } - -template<typename T> - requires C<T>() - using X = T*; - -struct S { }; - -int main() -{ - X<S> x1; -} diff --git a/gcc/testsuite/g++.dg/concepts/alias2.C b/gcc/testsuite/g++.dg/concepts/alias2.C deleted file mode 100644 index 06ffb1a..0000000 --- a/gcc/testsuite/g++.dg/concepts/alias2.C +++ /dev/null @@ -1,14 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template<typename T> - concept bool C() { return __is_class(T); } - -template<C T> using X = T*; - -struct S { }; - -int main() -{ - X<S> x1; -} diff --git a/gcc/testsuite/g++.dg/concepts/alias3.C b/gcc/testsuite/g++.dg/concepts/alias3.C deleted file mode 100644 index 2901c04..0000000 --- a/gcc/testsuite/g++.dg/concepts/alias3.C +++ /dev/null @@ -1,14 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template<typename T> - concept bool C() { return __is_class(T); } - -template<typename T> - requires C<T>() - using X = T*; - -int main() -{ - X<int> 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 2c9f5de..0000000 --- a/gcc/testsuite/g++.dg/concepts/alias4.C +++ /dev/null @@ -1,20 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template<typename T> - concept bool C() { return __is_class(T); } - -template<typename T> - requires C<T>() - 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<typename T> - using Y = X<T>; - -int main() -{ - Y<int> y1; // { dg-error "" "" { xfail *-*-* } } -} diff --git a/gcc/testsuite/g++.dg/concepts/auto1.C b/gcc/testsuite/g++.dg/concepts/auto1.C index 2682940..e053306 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 T1, class T2> class A { }; diff --git a/gcc/testsuite/g++.dg/concepts/auto3.C b/gcc/testsuite/g++.dg/concepts/auto3.C index abfb201..27a6afa 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...> class tuple {}; diff --git a/gcc/testsuite/g++.dg/concepts/auto4.C b/gcc/testsuite/g++.dg/concepts/auto4.C index 4eb2ae8..8bf3fa9 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<typename... Ts> struct A {}; diff --git a/gcc/testsuite/g++.dg/concepts/class-deduction1.C b/gcc/testsuite/g++.dg/concepts/class-deduction1.C index 936dd68..3359700 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 <class T> diff --git a/gcc/testsuite/g++.dg/concepts/class.C b/gcc/testsuite/g++.dg/concepts/class.C deleted file mode 100644 index dc5523e..0000000 --- a/gcc/testsuite/g++.dg/concepts/class.C +++ /dev/null @@ -1,53 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template<typename T> - concept bool Class() { return __is_class(T); } - -template<typename T> - concept bool Union() { return __is_union(T); } - - -// Check ordering of specializations -template<typename T> - concept bool One() { return sizeof(T) >= 4; } - -template<typename T> - concept bool Two() { return One<T>() && sizeof(T) >= 8; } - -// Check non-overlapping specializations -template<typename T> - struct S1 { static const int value = 0; }; - -template<Class T> - struct S1<T> { static const int value = 1; }; - -template<Union T> - struct S1<T> { static const int value = 2; }; - -struct S { }; -union U { }; - -static_assert(S1<int>::value == 0, ""); -static_assert(S1<S>::value == 1, ""); -static_assert(S1<U>::value == 2, ""); - - -// Check ordering of partial specializaitons -template<typename T> - struct S2 { static const int value = 0; }; - -template<One T> - struct S2<T> { static const int value = 1; }; - -template<Two T> - struct S2<T> { static const int value = 2; }; - -struct one_type { char x[4]; }; -struct two_type { char x[8]; }; - -static_assert(S2<char>::value == 0, ""); -static_assert(S2<one_type>::value == 1, ""); -static_assert(S2<two_type>::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 a738e6e..0000000 --- a/gcc/testsuite/g++.dg/concepts/class1.C +++ /dev/null @@ -1,15 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template<typename T> - concept bool C() { return __is_class(T); } - -template<typename T> - requires C<T>() - struct S { }; - -struct X { }; - -S<X> 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 ec87181..0000000 --- a/gcc/testsuite/g++.dg/concepts/class2.C +++ /dev/null @@ -1,15 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template<typename T> - concept bool C() { return __is_class(T); } - -template<typename T> - requires C<T>() - struct S { }; - -struct X { }; - -S<int> 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 256370d..0000000 --- a/gcc/testsuite/g++.dg/concepts/class3.C +++ /dev/null @@ -1,15 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template<typename T> - concept bool C() { return __is_class(T); } - -// Check class redeclaration with alternative spellings. -template<typename T> requires C<T>() struct S; -template<C T> struct S { }; - -struct X { }; - -// S<X> 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 b583e55..0000000 --- a/gcc/testsuite/g++.dg/concepts/class4.C +++ /dev/null @@ -1,22 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template<typename T> - concept bool Class() { return __is_class(T); } - -template<typename T> - concept bool Union() { return __is_union(T); } - -// Check non-overlapping specializations -template<typename T> struct S1 { static const int value = 0; }; -template<Class T> struct S1<T> { static const int value = 1; }; -template<Union T> struct S1<T> { static const int value = 2; }; - -struct S { }; -union U { }; - -static_assert(S1<int>::value == 0, ""); -static_assert(S1<S>::value == 1, ""); -static_assert(S1<U>::value == 2, ""); - -int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/class5.C b/gcc/testsuite/g++.dg/concepts/class5.C index 7bf620e..ac9d7e8 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<typename T> diff --git a/gcc/testsuite/g++.dg/concepts/class6.C b/gcc/testsuite/g++.dg/concepts/class6.C index bdd60918..f2345b1 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<typename T> @@ -15,5 +15,3 @@ struct one_type { char x[4]; }; // Constraints are checked even when decls are not instantiatied. S4<one_type>* 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 87f2ac9..b9a5444 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<typename> concept bool C() { return true; } -template<template<typename T> requires C<T>() class> struct A {}; +template<template<typename T> requires C<T>() class TT> struct A {}; -template<typename> requires true struct B {}; +template<typename U> requires C<U>() struct B {}; A<B> a; diff --git a/gcc/testsuite/g++.dg/concepts/decl-diagnose.C b/gcc/testsuite/g++.dg/concepts/decl-diagnose.C index 019a8ce..6a461a5 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<typename T> - 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<typename T> - 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 bebbda1..eba5771 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 <class U> diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic1.C b/gcc/testsuite/g++.dg/concepts/diagnostic1.C index 9bb1506..ced56d4 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 <class T, class U> concept bool SameAs = __is_same_as(T, U); template <class T> -concept bool R1 = requires (T& t) { - { t.begin() } -> T - { t.end() } -> SameAs<T*>; +concept bool R1 = requires (T& t) { // { dg-message "in requirements" } + { t.begin() } -> T; // { dg-error "no match" } + { t.end() } -> SameAs<T*>; // { dg-error "does not satisfy" } }; template <class T> -concept bool R2 = requires (T& t) { - { t.end() } -> SameAs<T*>; +concept bool R2 = requires (T& t) { // { dg-message "in requirements" } + { t.end() } -> SameAs<T*>; // { 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 930adf4..0000000 --- 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 <typename> struct remove_cv; -template <typename> struct is_reference; -template <typename> void declval(); -template <typename> struct is_constructible; -template <typename> struct is_nothrow_constructible; -template <typename _Tp> using remove_cv_t = typename remove_cv<_Tp>::type; -template <typename> struct Trans_NS_extension_apply_list; -template <typename T> using _t = typename T::type; -template <class> void ImplicitlyConvertibleTo(); -template <class> void Assignable(); -template <class T, class... Args> int ConstructibleObject = requires { T{}; }; -template <class T, class... Args> -concept bool BindableReference = - is_reference<T>::value &&is_constructible<T>::value; -template <class T, class... Args> concept bool Constructible() { - return ConstructibleObject<T> || BindableReference<T, Args...>; -} -template <class T> concept bool DefaultConstructible() { - return Constructible<T>() && requires { new T[0]; }; -} -template <class T> concept bool MoveConstructible() { - return Constructible<T>() && ImplicitlyConvertibleTo<T>; -} -template <class T> concept bool Movable() { - return MoveConstructible<T>() && Assignable<T &&>; -} -template <class, class> int Swappable_ = requires { 0; }; -template <class T, class U> int Swappable(); -template <class T> concept bool Dereferencable = requires{{0}}; -template <Dereferencable R> using RvalueReferenceType = decltype(0); -template <class T> int IsValueType; -template <class> struct value_type; -template <class T> -requires IsValueType< - _t<value_type<remove_cv_t<T>>>> using ValueType = - _t<value_type<remove_cv_t<T>>>; -template <class I> concept bool Readable() { - return Movable<I>() && DefaultConstructible<I>() && - Dereferencable<const I> && requires{{0}}; -} -template <class Out, class T> concept bool MoveWritable() { - return Movable<Out>() && DefaultConstructible<Out>() && - Dereferencable<Out>; -} -template <class In, class Out> concept bool IndirectlyMovable() { - return Readable<In>() && Movable<ValueType<In>>() && - Constructible<ValueType<In>>() && - MoveWritable<Out, RvalueReferenceType<In>>() && - MoveWritable<Out, ValueType<In>>(); -} -IndirectlyMovable { In, Out } -int is_nothrow_indirectly_movable_v = - is_nothrow_constructible<ValueType<In>>::value; -template <Readable R1, Readable R2> - requires IndirectlyMovable<R1, R2>() && - IndirectlyMovable<R2, R1>() void iter_swap2(); diff --git a/gcc/testsuite/g++.dg/concepts/dr1430.C b/gcc/testsuite/g++.dg/concepts/dr1430.C index f865d5e..6f5bab1 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 <type_traits> @@ -29,11 +29,14 @@ template <typename T, typename U, typename... Args> concept bool Similar = true; template <typename... Args> -requires Same<Args...>() // { dg-error "invalid reference" } +requires Same<Args...>() // { 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 <typename... Args> -requires Similar<Args...> // { dg-error "invalid reference" } +requires Similar<Args...> // { 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 faec354..640c2b5 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 2094ca9..dff719b 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<C T> -void f2(T, T) { } +template<C T1, C T2> +void f2(T1, T2) { } -template<typename T> - requires C<T> -void f3(T, T) { } +template<typename T, typename U> + requires C<T> && C<U> +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 20f4377..0000000 --- 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<typename T> - concept bool C() { return __is_class(T); } - -template<typename T> - concept bool D() { return C<T>() && __is_empty(T); } - -template<typename T> - struct S { - void g() requires C<T>() { } // #1 - void g() requires D<T>() { } // #2 - }; - -template void S<int>::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 fd48da1..0000000 --- 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<typename T> - concept bool C() { return __is_class(T); } - -template<C T> struct S; - -struct X { }; - -// Not a valid explicit specialization, int does not satisfy C. -template<> struct S<int> { }; // { dg-error "constraint" } - -int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/expression.C b/gcc/testsuite/g++.dg/concepts/expression.C index 33dad0a..ba4c48d 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 <cassert> #include <iostream> @@ -8,13 +10,9 @@ template<typename T> concept bool C1 = __is_class(T); template<typename T> - concept bool C2() { return __is_class(T); } - -template<typename T> - concept bool C3() { return requires (T a) { ++a; }; } + concept bool C3 = requires (T a) { ++a; }; int main() { if (C1<int>) assert(false); - if (C2<int>()) assert(false); - if (!C3<int>()) assert(false); + if (!C3<int>) assert(false); } diff --git a/gcc/testsuite/g++.dg/concepts/expression2.C b/gcc/testsuite/g++.dg/concepts/expression2.C index c5447df..1cff605 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<typename T> concept bool C1() { - return requires (T t) { t.f(); }; + return requires (T t) { t.f(); }; // { dg-message "in requirements" } } template<typename T> concept bool C2() { - return requires { typename T::type; }; + return requires { typename T::type; }; // { dg-message "in requirements" } } template<typename T> @@ -22,7 +22,7 @@ template<typename T> 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<S>(), ""); // { dg-error "failed" } static_assert(C2<S>(), ""); // { dg-error "" } diff --git a/gcc/testsuite/g++.dg/concepts/expression3.C b/gcc/testsuite/g++.dg/concepts/expression3.C index 26b829d..67646811 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<typename T> 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 d3d9b54..0000000 --- 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 a4ade7c..d1b4c0c 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<typename T> diff --git a/gcc/testsuite/g++.dg/concepts/fn-concept2.C b/gcc/testsuite/g++.dg/concepts/fn-concept2.C index 0d70728..899988c 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<typename T> 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 b664ccf..2453803 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 17f14b9..f23c057 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<typename T> diff --git a/gcc/testsuite/g++.dg/concepts/fn10.C b/gcc/testsuite/g++.dg/concepts/fn10.C index 6993f34..8d0a2e1 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 <typename T> template <typename T> concept bool Concept() { - return requires( T t ) { + return requires( T t ) { // { dg-message "in requirements" } requires Float<decltype( project(t) )>(); }; } diff --git a/gcc/testsuite/g++.dg/concepts/fn2.C b/gcc/testsuite/g++.dg/concepts/fn2.C index 250e0a8..debb323 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<typename T> diff --git a/gcc/testsuite/g++.dg/concepts/fn3.C b/gcc/testsuite/g++.dg/concepts/fn3.C index bc0e126..07b8e3a 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 <cassert> diff --git a/gcc/testsuite/g++.dg/concepts/fn4.C b/gcc/testsuite/g++.dg/concepts/fn4.C index 830a174..bbaac46 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<typename T> diff --git a/gcc/testsuite/g++.dg/concepts/fn5.C b/gcc/testsuite/g++.dg/concepts/fn5.C index 018b12f..7714788 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 97155f8..031e87f 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 0052f1a..869cb9c 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 a3daf4e..ffcce4f 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<typename T> diff --git a/gcc/testsuite/g++.dg/concepts/fn9.C b/gcc/testsuite/g++.dg/concepts/fn9.C index 84ed77c..2f5e88b 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 <cassert> diff --git a/gcc/testsuite/g++.dg/concepts/generic-fn-err.C b/gcc/testsuite/g++.dg/concepts/generic-fn-err.C index 5a95565..816072d 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<typename T> @@ -10,10 +10,10 @@ template<int N> template<template<typename> 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 3b10327..257608a 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 <cassert> diff --git a/gcc/testsuite/g++.dg/concepts/iconv1.C b/gcc/testsuite/g++.dg/concepts/iconv1.C deleted file mode 100644 index e99254f..0000000 --- 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 <typename T> -concept bool C1 = requires (T x) { - {foo(x)} -> int&; -}; - -template <typename T> -concept bool C2 = requires (T x) { - {foo(x)} -> void; -}; - -static_assert( C1<int> ); // { dg-error "assert" } -static_assert( C2<int> ); // { dg-error "assert" } diff --git a/gcc/testsuite/g++.dg/concepts/inherit-ctor1.C b/gcc/testsuite/g++.dg/concepts/inherit-ctor1.C index 4b3f561..b137791 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<typename T> - concept bool C() { return __is_class(T); } + concept bool C = __is_class(T); + +struct X { }; template<typename T> - struct S1 { S1(double) requires C<T>() { } }; + struct Base { + Base(double) requires C<T> { } + }; -struct S2 : S1<int> { - using S1<int>::S1; +struct Ok1 : Base<X> { + using Base<X>::Base; +}; + +struct Err1 : Base<int> { + using Base<int>::Base; }; template<typename T> - struct S3 : S1<T> { - using S1<T>::S1; + struct Generic : Base<T> { + using Base<T>::Base; }; -struct X { }; int main() { - S3<X> s(0.0); + Ok1 x1(0.0); + Err1 x2(0.0); // { dg-error "no matching" } + Generic<X> x3(0.0); + Generic<int> 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 6f04632..abfe96e 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<typename T> @@ -11,12 +11,13 @@ template<typename T> }; template<typename T> - struct S2 : S1<T> { - using S1<T>::S1; + struct S2 : S1<T> { // { dg-error "no matching function" } + using S1<T>::S1; // { dg-error "no matching function" } }; struct X { } x; int main() { - S2<X> s = x; + S2<X> s1(0); // { dg-error "use of deleted function" } + S2<X> 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 43df6e6..0000000 --- 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<typename T> - concept bool C() { return __is_class(T); } - -template<typename T> - struct S1 { - template<C U> S1(U x) { } - }; - -template<typename T> - struct S2 : S1<T> { - using S1<T>::S1; - }; - -int main() { - S2<int> 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 84fa6db..5f9bb7e 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<typename T> diff --git a/gcc/testsuite/g++.dg/concepts/intro2.C b/gcc/testsuite/g++.dg/concepts/intro2.C index 9c7c173..206777d 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 <cassert> diff --git a/gcc/testsuite/g++.dg/concepts/intro3.C b/gcc/testsuite/g++.dg/concepts/intro3.C index 5e93f31..f02f1be 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<typename ... T> diff --git a/gcc/testsuite/g++.dg/concepts/intro4.C b/gcc/testsuite/g++.dg/concepts/intro4.C index a7e5135..0b275e1 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<typename ... T> @@ -18,11 +18,11 @@ template<int N> template<typename T, typename U = int> concept bool C5() { return __is_class(U); } -C1{...A, B} void f1() {}; // { dg-error "no matching|wrong number" } -C1{A} void f2() {} // { dg-error "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 e7cd7a4..bbfef7b 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<typename T, typename U = int> @@ -7,10 +7,5 @@ template<typename T, typename U = int> return sizeof(U) == sizeof(int); } -C{A} void f1() {} +C{A} void f1() {} // { dg-error "all template parameters" } -int main() -{ - f1<char>(); - return 0; -} diff --git a/gcc/testsuite/g++.dg/concepts/intro6.C b/gcc/testsuite/g++.dg/concepts/intro6.C index 57b325a..233c5bc 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 d5bdc7e..343fe7a 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 <template <class> class T> -concept bool _Valid = requires { typename T<int>; }; +concept bool Valid = requires { typename T<int>; }; template <template <class> class T> struct __defer { }; -_Valid{T} +Valid{T} struct __defer<T> { using type = T<int>; }; diff --git a/gcc/testsuite/g++.dg/concepts/locations1.C b/gcc/testsuite/g++.dg/concepts/locations1.C index 33c3b62..fbad42f 100644 --- a/gcc/testsuite/g++.dg/concepts/locations1.C +++ b/gcc/testsuite/g++.dg/concepts/locations1.C @@ -1,5 +1,5 @@ -// { dg-do compile { target c++17 } } -// { dg-additional-options "-fconcepts" } +// { dg-do compile { target c++17_only } } +// { dg-options "-fconcepts" } struct S { diff --git a/gcc/testsuite/g++.dg/concepts/memtmpl1.C b/gcc/testsuite/g++.dg/concepts/memtmpl1.C deleted file mode 100644 index dc00a07..0000000 --- a/gcc/testsuite/g++.dg/concepts/memtmpl1.C +++ /dev/null @@ -1,16 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template <class T> -struct A { - template <class U> - requires sizeof(T) == 1 - static void f(U); - template <class U> - requires sizeof(T) == 2 - static void f(U); - void g() - { - f(42); - } -}; diff --git a/gcc/testsuite/g++.dg/concepts/partial-concept-id1.C b/gcc/testsuite/g++.dg/concepts/partial-concept-id1.C index 5f0f346..6b66b78 100644 --- a/gcc/testsuite/g++.dg/concepts/partial-concept-id1.C +++ b/gcc/testsuite/g++.dg/concepts/partial-concept-id1.C @@ -1,27 +1,37 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template<typename T> - concept bool Type() { return true; } + concept bool Type = true; template<typename T, typename U> - concept bool Same() { return __is_same_as(T, U); } + concept bool Same = __is_same_as(T, U); template<typename T, typename U> - concept bool C1() { return true; } + concept bool C1 = true; template<typename T, typename... Args> - concept bool C2() { return true; } + concept bool C2 = true; + +template<typename T, typename U> + concept bool C3 = __is_same_as(T, int) && __is_same_as(U, double); template<Same<int> T> struct S1 { }; template<typename T, Same<T> U> struct S2 { }; -void f(Same<int> q) { } -void g(Type a, Same<decltype(a)> b) { } +template<Same<int> Q> +void f(Q q) { } +template<Type A, Same<decltype(A{})> B> +void g(A a, B b) { } -void h0(Same<int>* a) { } -void h1(C1<int>* a) { } -void h2(C2<char, short, int, long>* a) { } +template<Same<int> A> +void h0(A* a) { } +template<C1<int> A> +void h1(A* a) { } +template<C2<char, short, int, long> A> +void h2(A* a) { } +template<C3<double> A> +void h3(A* a) { } int main() { S1<int> s1; @@ -30,5 +40,6 @@ int main() { g(0, 1); h0((int*)0); h1((int*)0); - h2((int*)0); + // h2((int*)0); + h3((int*)0); } diff --git a/gcc/testsuite/g++.dg/concepts/partial-concept-id2.C b/gcc/testsuite/g++.dg/concepts/partial-concept-id2.C index e51894b..2c14576 100644 --- a/gcc/testsuite/g++.dg/concepts/partial-concept-id2.C +++ b/gcc/testsuite/g++.dg/concepts/partial-concept-id2.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } // Make sure that we check partial concept ids diff --git a/gcc/testsuite/g++.dg/concepts/partial-spec5.C b/gcc/testsuite/g++.dg/concepts/partial-spec5.C index a5b853c..bec6715 100644 --- a/gcc/testsuite/g++.dg/concepts/partial-spec5.C +++ b/gcc/testsuite/g++.dg/concepts/partial-spec5.C @@ -1,5 +1,5 @@ // PR c++/67138 -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template <class T> diff --git a/gcc/testsuite/g++.dg/concepts/placeholder1.C b/gcc/testsuite/g++.dg/concepts/placeholder1.C deleted file mode 100644 index edd0003..0000000 --- a/gcc/testsuite/g++.dg/concepts/placeholder1.C +++ /dev/null @@ -1,35 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template<typename T, typename U> -struct is_same -{ - static constexpr bool value = false; -}; - -template<typename T> -struct is_same<T, T> -{ - static constexpr bool value = true; -}; - -template<class T, class U> -concept bool Same = is_same<T, U>::value; - -template<typename T> -concept bool C1 = true; - -template<typename T, typename U> -concept bool C2 = true; - -template<typename T> -concept bool C3() { return true; } - -template<typename T, typename U> -concept bool C4() { return true; } - -C1 c1 = 0; -C2<int> c2 = 0; -C3 c3 = 0; -C4<int> c4 = 0; -Same<int> s1 = 'a'; // { dg-error "does not satisfy|is_same" } diff --git a/gcc/testsuite/g++.dg/concepts/placeholder2.C b/gcc/testsuite/g++.dg/concepts/placeholder2.C index 3d6fc81..0c6f91a 100644 --- a/gcc/testsuite/g++.dg/concepts/placeholder2.C +++ b/gcc/testsuite/g++.dg/concepts/placeholder2.C @@ -1,9 +1,6 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } -// Check argument deduction constraints. -// TODO: We shoul have more of these... - template<typename T> concept bool C1 = sizeof(T) == 0; @@ -12,22 +9,16 @@ concept bool C2 = __is_same_as(T, U); template<typename T> -concept bool D1() -{ - return requires (T t) { { t } -> C1; }; -} +concept bool D1 = requires (T t) { { t } -> C1; }; template<typename T> -concept bool D2() -{ - return requires (T t) { { t } -> C2<void>; }; -} +concept bool D2 = requires (T t) { { t } -> C2<void>; }; -void f1(D1) { } -void f2(D2) { } +void f1(auto D1) { } // OK: D1 is declared as a parameter +void f2(auto D2) { } // OK: D2 is declared as a parameter int main() { - f1(0); // { dg-error "cannot call" } - f2(0); // { dg-error "cannot call" } + f1(0); + f2(0); } diff --git a/gcc/testsuite/g++.dg/concepts/placeholder3.C b/gcc/testsuite/g++.dg/concepts/placeholder3.C index 93f0c0d..4f8600b 100644 --- a/gcc/testsuite/g++.dg/concepts/placeholder3.C +++ b/gcc/testsuite/g++.dg/concepts/placeholder3.C @@ -1,5 +1,5 @@ // PR c++/66218 -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template <class T, class U> @@ -7,8 +7,8 @@ concept bool Same = __is_same_as(T, U); template <class T> concept bool C = - requires { - { 0 } -> Same<T>; + requires { // { dg-message "in requirements" } + { 0 } -> Same<T>; // { dg-error "does not satisfy" } }; template <C c> diff --git a/gcc/testsuite/g++.dg/concepts/placeholder4.C b/gcc/testsuite/g++.dg/concepts/placeholder4.C index d130813..2b5afbb 100644 --- a/gcc/testsuite/g++.dg/concepts/placeholder4.C +++ b/gcc/testsuite/g++.dg/concepts/placeholder4.C @@ -1,5 +1,5 @@ // PR c++/66218 -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template <class T, class U> @@ -7,8 +7,8 @@ concept bool Same = __is_same_as(T, U); template <class T> concept bool C = - requires { - { 0 } -> Same<T>; + requires { // { dg-message "in requirements" } + { 0 } -> Same<T>; // { dg-error "does not satisfy" } }; template <class T> diff --git a/gcc/testsuite/g++.dg/concepts/placeholder5.C b/gcc/testsuite/g++.dg/concepts/placeholder5.C index 245e27a..7881a40 100644 --- a/gcc/testsuite/g++.dg/concepts/placeholder5.C +++ b/gcc/testsuite/g++.dg/concepts/placeholder5.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template <class T, class U> @@ -8,11 +8,11 @@ const int i = 0; template <class T> concept bool C = requires { - { &i } -> const Same<T>*; + { &i } -> const Same<T>*; // { dg-error "not a plain type-constraint" } }; template <C c> constexpr bool f() { return true; } -static_assert(f<double>(), ""); // { dg-error "" } -static_assert(f<int>(), ""); +static_assert(f<double>(), ""); // { dg-error "cannot call|as type" } +static_assert(f<int>(), ""); // { dg-error "cannot call|as type" } diff --git a/gcc/testsuite/g++.dg/concepts/placeholder6.C b/gcc/testsuite/g++.dg/concepts/placeholder6.C index 51282d9..20b9c93 100644 --- a/gcc/testsuite/g++.dg/concepts/placeholder6.C +++ b/gcc/testsuite/g++.dg/concepts/placeholder6.C @@ -1,11 +1,8 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template <int I> struct B { static const int i = I; }; template <int I> concept bool Few = I < 10; -constexpr int g(B<Few> b) { return b.i; } +constexpr int g(B<Few> b); // { dg-error "does not constrain a type|invalid" } -#define SA(X) static_assert((X),#X) -SA(g(B<2>{}) == 2); -SA(g(B<10>{}) == 10); // { dg-error "" } diff --git a/gcc/testsuite/g++.dg/concepts/pr65634.C b/gcc/testsuite/g++.dg/concepts/pr65634.C index e383653..5fcb38a 100644 --- a/gcc/testsuite/g++.dg/concepts/pr65634.C +++ b/gcc/testsuite/g++.dg/concepts/pr65634.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template<typename T> diff --git a/gcc/testsuite/g++.dg/concepts/pr65636.C b/gcc/testsuite/g++.dg/concepts/pr65636.C index 2aec0e5..f927c9a 100644 --- a/gcc/testsuite/g++.dg/concepts/pr65636.C +++ b/gcc/testsuite/g++.dg/concepts/pr65636.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } using TD = int; diff --git a/gcc/testsuite/g++.dg/concepts/pr65681.C b/gcc/testsuite/g++.dg/concepts/pr65681.C index 0d8a69d..67153d6 100644 --- a/gcc/testsuite/g++.dg/concepts/pr65681.C +++ b/gcc/testsuite/g++.dg/concepts/pr65681.C @@ -1,66 +1,45 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template<typename T> -concept bool C() -{ - return requires (T t) { t.mf(); }; -} +concept bool C = requires (T t) { t.mf(); }; template<typename T> -concept bool CA1() -{ - return C<typename T::ca1_type>(); -} +concept bool CA1 = C<typename T::ca1_type>; template<typename T> -concept bool CA2() -{ - return CA1<T>() && requires () { typename T::ca2_type; }; -} +concept bool CA2 = CA1<T> && requires () { typename T::ca2_type; }; template<typename T> -concept bool CA3() -{ - return CA2<T>() && requires () { typename T::ca3_type; }; -} +concept bool CA3 = CA2<T> && requires () { typename T::ca3_type; }; template<typename T> -concept bool CB1() -{ - return requires () { typename T::cb1_type; }; -} +concept bool CB1 = requires () { typename T::cb1_type; }; template<typename T> -concept bool CB2() -{ - return CB1<T>() && requires () { typename T::cb2_type; }; -} +concept bool CB2 = CB1<T> && requires () { typename T::cb2_type; }; template<typename T> -concept bool CB3() -{ - return CB2<T>() && requires () { typename T::cb3_type; }; -} +concept bool CB3 = CB2<T> && requires () { typename T::cb3_type; }; struct MC { void mf(); }; -static_assert(C<MC>(), ""); +static_assert(C<MC>, ""); struct MA1 { using ca1_type = MC; }; struct MA2 : MA1 { using ca2_type = int; }; struct MA3 : MA2 { using ca3_type = int; }; -static_assert(CA1<MA1>(), ""); -static_assert(CA2<MA2>(), ""); -static_assert(CA3<MA3>(), ""); +static_assert(CA1<MA1>, ""); +static_assert(CA2<MA2>, ""); +static_assert(CA3<MA3>, ""); struct MB1 { using cb1_type = int; }; struct MB2 : MB1 { using cb2_type = int; }; struct MB3 : MB2 { using cb3_type = int; }; -static_assert(CB1<MB1>(), ""); -static_assert(CB2<MB2>(), ""); -static_assert(CB3<MB3>(), ""); +static_assert(CB1<MB1>, ""); +static_assert(CB2<MB2>, ""); +static_assert(CB3<MB3>, ""); template<typename T1, typename T2> @@ -73,29 +52,19 @@ struct S<T1, T2> // Specialization #1 }; template<CA1 T1, CB2 T2> - requires !CA2<T1>() + requires (!CA2<T1>) struct S<T1, T2> // Specialization #2 { static constexpr int value = 2; }; template<CA2 T1, CB3 T2> - requires !CA3<T1>() + requires (!CA3<T1>) struct S<T1, T2> // Specialization #3 { static constexpr int value = 3; }; -S<MA1,MB1> s11; -S<MA1,MB2> s12; -S<MA1,MB3> s13; -S<MA2,MB1> s21; -S<MA2,MB2> s22; -S<MA2,MB3> s23; -S<MA3,MB1> s31; -S<MA3,MB2> s32; -S<MA3,MB3> s33; - static_assert(S<MA1,MB1>::value == 1, ""); static_assert(S<MA1,MB2>::value == 2, ""); static_assert(S<MA1,MB3>::value == 2, ""); diff --git a/gcc/testsuite/g++.dg/concepts/pr65848.C b/gcc/testsuite/g++.dg/concepts/pr65848.C index 0678018..ea3077d 100644 --- a/gcc/testsuite/g++.dg/concepts/pr65848.C +++ b/gcc/testsuite/g++.dg/concepts/pr65848.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } // Performance test... This should be fast. diff --git a/gcc/testsuite/g++.dg/concepts/pr67249.C b/gcc/testsuite/g++.dg/concepts/pr67249.C index a0eca9b..382eba1 100644 --- a/gcc/testsuite/g++.dg/concepts/pr67249.C +++ b/gcc/testsuite/g++.dg/concepts/pr67249.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template<class T> concept bool C1 = true; diff --git a/gcc/testsuite/g++.dg/concepts/pr67544.C b/gcc/testsuite/g++.dg/concepts/pr67544.C index af4f677..6001760 100644 --- a/gcc/testsuite/g++.dg/concepts/pr67544.C +++ b/gcc/testsuite/g++.dg/concepts/pr67544.C @@ -1,4 +1,5 @@ -// { dg-additional-options "-fconcepts" } +// { dg-do compile { target c++11 } } +// { dg-options "-fconcepts" } template <typename T> struct A { diff --git a/gcc/testsuite/g++.dg/concepts/pr67595.C b/gcc/testsuite/g++.dg/concepts/pr67595.C index 7b5d712..7199e05 100644 --- a/gcc/testsuite/g++.dg/concepts/pr67595.C +++ b/gcc/testsuite/g++.dg/concepts/pr67595.C @@ -1,10 +1,10 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } -template <class X> concept bool allocatable = requires{{new X}->X * }; +template <class X> concept bool allocatable = requires{{new X}->X *; }; template <class X> concept bool semiregular = allocatable<X>; -template <class X> concept bool readable = requires{requires semiregular<X>}; -template <class> int weak_input_iterator = requires{{0}->readable}; +template <class X> concept bool readable = requires{requires semiregular<X>;}; +template <class> int weak_input_iterator = requires{{0}->readable;}; template <class X> bool input_iterator{weak_input_iterator<X>}; // { dg-warning "narrowing conversion" } template <class X> bool forward_iterator{input_iterator<X>}; template <class X> bool bidirectional_iterator{forward_iterator<X>}; diff --git a/gcc/testsuite/g++.dg/concepts/pr67655.C b/gcc/testsuite/g++.dg/concepts/pr67655.C index b155421..ddd069c 100644 --- a/gcc/testsuite/g++.dg/concepts/pr67655.C +++ b/gcc/testsuite/g++.dg/concepts/pr67655.C @@ -1,7 +1,7 @@ // { dg-do compile { target c++11 } } // { dg-additional-options "-fconcepts" } -template<class... Xs> +template<class... Xs> void consume(Xs&&...) {} template<class... Xs> diff --git a/gcc/testsuite/g++.dg/concepts/pr68434.C b/gcc/testsuite/g++.dg/concepts/pr68434.C index fc45898..16868ba 100644 --- a/gcc/testsuite/g++.dg/concepts/pr68434.C +++ b/gcc/testsuite/g++.dg/concepts/pr68434.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template <class> diff --git a/gcc/testsuite/g++.dg/concepts/pr71127.C b/gcc/testsuite/g++.dg/concepts/pr71127.C index 3ec6514..224eaa3 100644 --- a/gcc/testsuite/g++.dg/concepts/pr71127.C +++ b/gcc/testsuite/g++.dg/concepts/pr71127.C @@ -1,6 +1,7 @@ -// { dg-do compile { target c++14 } } -// { dg-additional-options "-fconcepts" } +// { dg-do compile { target c++17_only } } +// { dg-options "-fconcepts" } template<template<typename> class T> concept bool C = T<int>::value; -C c = 1; // { dg-error "invalid reference to concept" } + +C c = 1; // { dg-error "does not constrain a type" } diff --git a/gcc/testsuite/g++.dg/concepts/pr71128.C b/gcc/testsuite/g++.dg/concepts/pr71128.C index 8b4eb41..a150e37 100644 --- a/gcc/testsuite/g++.dg/concepts/pr71128.C +++ b/gcc/testsuite/g++.dg/concepts/pr71128.C @@ -1,5 +1,5 @@ -// { dg-do compile { target c++14 } } -// { dg-additional-options "-fconcepts" } +// { dg-do compile { target c++17_only } } +// { dg-options "-fconcepts" } template<typename T> concept bool C() { return true; } diff --git a/gcc/testsuite/g++.dg/concepts/pr71131.C b/gcc/testsuite/g++.dg/concepts/pr71131.C index 02f1999..675d66d 100644 --- a/gcc/testsuite/g++.dg/concepts/pr71131.C +++ b/gcc/testsuite/g++.dg/concepts/pr71131.C @@ -1,6 +1,7 @@ -// { dg-do compile { target c++14 } } -// { dg-additional-options "-fconcepts" } +// { dg-do compile { target c++17_only } } +// { dg-options "-fconcepts" } template<template<typename> class T> concept bool C = true; -C c = 1; // { dg-error "invalid reference to concept" } + +C c = 1; // { dg-error "does not constrain a type" } diff --git a/gcc/testsuite/g++.dg/concepts/pr71385.C b/gcc/testsuite/g++.dg/concepts/pr71385.C index 42d21f5..fb75425 100644 --- a/gcc/testsuite/g++.dg/concepts/pr71385.C +++ b/gcc/testsuite/g++.dg/concepts/pr71385.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template<class T> diff --git a/gcc/testsuite/g++.dg/concepts/pr84330.C b/gcc/testsuite/g++.dg/concepts/pr84330.C index 975ee89..0c2f456 100644 --- a/gcc/testsuite/g++.dg/concepts/pr84330.C +++ b/gcc/testsuite/g++.dg/concepts/pr84330.C @@ -1,12 +1,15 @@ // PR c++/84330 +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } struct A { - template<typename T> requires sizeof(T) >> 0 void foo(T); // { dg-error "predicate constraint" } + template<typename T> + requires (sizeof(T) >> 0) + void foo(T); void bar() { - foo(0); // { dg-error "no matching" } + foo(0); // { dg-error "no matching function" } } }; diff --git a/gcc/testsuite/g++.dg/concepts/pr85065.C b/gcc/testsuite/g++.dg/concepts/pr85065.C index 861a838..52a4264 100644 --- a/gcc/testsuite/g++.dg/concepts/pr85065.C +++ b/gcc/testsuite/g++.dg/concepts/pr85065.C @@ -1,6 +1,6 @@ -// { dg-do compile { target c++14 } } -// { dg-additional-options "-fconcepts" } +// { dg-do compile { target c++17_only } } +// { dg-options "-fconcepts" } template<int> concept bool C = true; -C c = 0; // { dg-error "invalid reference to concept" } +C c = 0; // { dg-error "does not constrain a type" } diff --git a/gcc/testsuite/g++.dg/concepts/req-neg1.C b/gcc/testsuite/g++.dg/concepts/req-neg1.C deleted file mode 100644 index 637f993..0000000 --- a/gcc/testsuite/g++.dg/concepts/req-neg1.C +++ /dev/null @@ -1,11 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -void f1(int a) requires true; // OK -auto f2(int a) -> bool requires true; // OK -auto f3(int a) requires true -> bool; // { dg-error "" } requires-clause precedes trailing-return-type -typedef void fn_t() requires true; // { dg-error "typedef" } -void (*pf)() requires true; // { dg-error "non-function" } -void (*fn(int))() requires false; // { dg-error "return type" } -void g(int (*)() requires true); // { dg-error "parameter|non-function" } -auto* p = new (void(*)(char) requires true); // { dg-error "type-id" } diff --git a/gcc/testsuite/g++.dg/concepts/req1.C b/gcc/testsuite/g++.dg/concepts/req1.C deleted file mode 100644 index fedea73..0000000 --- a/gcc/testsuite/g++.dg/concepts/req1.C +++ /dev/null @@ -1,29 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template<typename T> - concept bool Class () { return __is_class(T); } - -// Allow a requires-expression with no parms. -template<typename T> - concept bool C = requires { typename T::type; }; - -void f1(auto a) requires Class<decltype(a)>() { } -void f2(auto a) requires requires (decltype(a) x) { -x; } { } - -struct S { } s; - -// Allow non-type template parms as constraints. -template<bool B> requires B struct S0; // OK - -template<int N> requires N struct S1 { }; // { dg-error "does not have type" } -template<int N> requires N == 0 struct S2 { }; // OK - -template<typename T, T X> requires X struct S3 { }; // OK -S3<int, 0> s3a; // { dg-error "constraint failure|does not have type" } -S3<bool, false> s3b; // { dg-error "constraint failure" } - -int main() { - f1(s); - f2(0); -} diff --git a/gcc/testsuite/g++.dg/concepts/req10.C b/gcc/testsuite/g++.dg/concepts/req10.C deleted file mode 100644 index 949859c..0000000 --- a/gcc/testsuite/g++.dg/concepts/req10.C +++ /dev/null @@ -1,19 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -// Test that standard conversions are checked with -// implicit conversion constraints. - -template<typename T, typename U> -concept bool C() -{ - return requires(T& t) { {t} -> U&; }; -} - -struct B { }; -class D : B { }; - -int main() -{ - static_assert(C<D, B>(), ""); // { dg-error "failed" } -} diff --git a/gcc/testsuite/g++.dg/concepts/req11.C b/gcc/testsuite/g++.dg/concepts/req11.C deleted file mode 100644 index 8891cce..0000000 --- a/gcc/testsuite/g++.dg/concepts/req11.C +++ /dev/null @@ -1,29 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -// Check that we can evaluate constant requires-expressions -// as constant expressions, for the curious case when they -// appear within predicate constraints. - -template<typename... Ts> struct variant { }; - -template<typename T> -concept bool Streamable() -{ - return requires (T t) { t; }; -} - -template<typename T> -concept bool Range() -{ - return requires (T t) { t; }; -} - -template<class T> - requires Streamable<T>() and not Range<T>() -void print(const T& x) { } - -int main() -{ - print("hello"); // { dg-error "cannot call" } -} diff --git a/gcc/testsuite/g++.dg/concepts/req12.C b/gcc/testsuite/g++.dg/concepts/req12.C deleted file mode 100644 index c6b345a..0000000 --- a/gcc/testsuite/g++.dg/concepts/req12.C +++ /dev/null @@ -1,26 +0,0 @@ -// PR c++/66218 -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -#include <type_traits> - -template <class T, class U> -concept bool Same = - std::is_same<T, U>::value; - -template <class T> -concept bool C = - requires(T t) { - { t } -> Same<T>; - }; - -template <class> -constexpr bool f() { return false; } -template <C> -constexpr bool f() { return true; } - -static_assert(f<char>(), ""); -static_assert(f<int>(), ""); -static_assert(f<double>(), ""); - -int main() {} diff --git a/gcc/testsuite/g++.dg/concepts/req13.C b/gcc/testsuite/g++.dg/concepts/req13.C deleted file mode 100644 index 4fd2312..0000000 --- a/gcc/testsuite/g++.dg/concepts/req13.C +++ /dev/null @@ -1,14 +0,0 @@ -// PR c++/66758 -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template <class T, class...Args> -concept bool Constructible = - requires(Args&&...args) { - T{ ((Args&&)(args))... }; - new T{((Args&&)(args))...}; - }; - -template <Constructible T> struct A { }; -A<int> a; - diff --git a/gcc/testsuite/g++.dg/concepts/req16.C b/gcc/testsuite/g++.dg/concepts/req16.C deleted file mode 100644 index ca04d60..0000000 --- a/gcc/testsuite/g++.dg/concepts/req16.C +++ /dev/null @@ -1,20 +0,0 @@ -// PR c++/66988 -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -#include <type_traits> - -template <template <class> class T, class U> -concept bool _Valid = requires { typename T<U>; }; - -template <class T> -using __t = typename T::type; - -template <class T> -struct __has_type : std::false_type { }; - -template <class T> - requires _Valid<__t, T> -struct __has_type<T> : std::true_type { }; - -static_assert(!__has_type<int>(), ""); diff --git a/gcc/testsuite/g++.dg/concepts/req18.C b/gcc/testsuite/g++.dg/concepts/req18.C deleted file mode 100644 index cccfaed..0000000 --- a/gcc/testsuite/g++.dg/concepts/req18.C +++ /dev/null @@ -1,18 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template <class> struct all_same { - static constexpr bool value = 1; -}; -template <class T> concept bool Assignable -= requires(T t) -{ - requires all_same<decltype(t = 0)>::value; -}; - -template <class I> requires !Assignable<I> -int dispatch(); -template <Assignable> -void dispatch(); - -int main() { dispatch<int *>(); } diff --git a/gcc/testsuite/g++.dg/concepts/req19.C b/gcc/testsuite/g++.dg/concepts/req19.C deleted file mode 100644 index 97cd9e5..0000000 --- a/gcc/testsuite/g++.dg/concepts/req19.C +++ /dev/null @@ -1,14 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -struct B -{ - template <class T> void f(T t) - requires requires (T tt) { tt; } - { } -}; - -int main() -{ - B().f(42); -} diff --git a/gcc/testsuite/g++.dg/concepts/req2.C b/gcc/testsuite/g++.dg/concepts/req2.C deleted file mode 100644 index b32845a..0000000 --- a/gcc/testsuite/g++.dg/concepts/req2.C +++ /dev/null @@ -1,20 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template<typename T> - concept bool Class () { return __is_class(T); } - -void f1(auto a) requires Class<decltype(a)>() { } - -// FIXME: This is generating excess errors related to pretty -// printing the trailing requires expression. -void f2(auto a) - requires requires (decltype(a) x) { -x; } -{ } - -struct S { } s; - -int main() { - f1(0); // { dg-error "cannot call" } - f2((void*)0); // { dg-error "cannot call" } -} diff --git a/gcc/testsuite/g++.dg/concepts/req20.C b/gcc/testsuite/g++.dg/concepts/req20.C deleted file mode 100644 index bd6b0f9..0000000 --- a/gcc/testsuite/g++.dg/concepts/req20.C +++ /dev/null @@ -1,21 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template <class T> concept bool C = true; - -template <class T> -requires C<typename T::foo> -void f(T t) { } - -void f(...); - -template <class T> -requires C<T> -void g(T t) { } - -int main() -{ - f(42); - g(42); -} - diff --git a/gcc/testsuite/g++.dg/concepts/req3.C b/gcc/testsuite/g++.dg/concepts/req3.C deleted file mode 100644 index 8ce58e5..0000000 --- a/gcc/testsuite/g++.dg/concepts/req3.C +++ /dev/null @@ -1,17 +0,0 @@ -// { dg-do run { target c++17 } } -// { dg-options "-fconcepts" } - -template<typename T> - concept bool Class () { return __is_class(T); } - -struct Test { - void f(auto a) requires Class<decltype(a)>(); -} test; - -struct S { }s; - -int main() { - test.f(s); -} - -void Test::f(auto a) requires Class<decltype(a)>() { } diff --git a/gcc/testsuite/g++.dg/concepts/req4.C b/gcc/testsuite/g++.dg/concepts/req4.C deleted file mode 100644 index fcc13c6..0000000 --- a/gcc/testsuite/g++.dg/concepts/req4.C +++ /dev/null @@ -1,19 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -struct fool { - constexpr fool operator&&(fool) const { return {}; } - constexpr fool operator||(fool) const { return {}; } -}; - -template<typename T> constexpr fool p1() { return {}; } -template<typename T> constexpr fool p2() { return {}; } - -template<typename T> - concept bool C() { return p1<T>() && p2<T>(); } - -template<C T> void f(T x) { } - -int main() { - f(0); // { dg-error "cannot call|uses overloaded operator" } -} diff --git a/gcc/testsuite/g++.dg/concepts/req5.C b/gcc/testsuite/g++.dg/concepts/req5.C deleted file mode 100644 index 7ad1cab..0000000 --- a/gcc/testsuite/g++.dg/concepts/req5.C +++ /dev/null @@ -1,19 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -struct fool { }; - -constexpr fool operator&&(fool, fool) { return {}; } -constexpr fool operator||(fool, fool) { return {}; } - -template<typename T> constexpr fool p1() { return {}; } -template<typename T> constexpr fool p2() { return {}; } - -template<typename T> - concept bool C() { return p1<T>() && p2<T>(); } - -template<C T> void f(T x) { } - -int main() { - f(0); // { dg-error "cannot call|uses overloaded operator" } -} diff --git a/gcc/testsuite/g++.dg/concepts/req6.C b/gcc/testsuite/g++.dg/concepts/req6.C deleted file mode 100644 index dd7dbdd..0000000 --- a/gcc/testsuite/g++.dg/concepts/req6.C +++ /dev/null @@ -1,19 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -struct X { }; -int operator==(X, X) { return 0; } - -template<typename T> - concept bool C1() { return X(); } // { dg-error "bool" } - -template<C1 T> - void h(T) { } // OK until used. - -void f() -{ - h(0); // { dg-error "does not have|cannot call" } -} - -template<typename T> - concept bool C2() { return X() == X(); } // OK diff --git a/gcc/testsuite/g++.dg/concepts/req7.C b/gcc/testsuite/g++.dg/concepts/req7.C deleted file mode 100644 index a6cfb4b..0000000 --- a/gcc/testsuite/g++.dg/concepts/req7.C +++ /dev/null @@ -1,25 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -#include <vector> - -using namespace std; - -template<typename T> - struct Sequence : std::false_type { }; - -template<typename T> - struct Predicate : std::false_type { }; - -template<typename Seq, typename Fn> - requires Sequence<Seq>{} and Predicate<Fn>{} - bool all(const Seq& seq, Fn fn) { - for(const auto& x : seq) - if (not fn(x)) - return false; - return true; - } - -int main() { - all(vector<int>{0, 2}, true); // { dg-error "not|bool" } -} diff --git a/gcc/testsuite/g++.dg/concepts/req8.C b/gcc/testsuite/g++.dg/concepts/req8.C deleted file mode 100644 index 201be37..0000000 --- a/gcc/testsuite/g++.dg/concepts/req8.C +++ /dev/null @@ -1,17 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -// Check that type requirements are normalized correctly. - -template<typename T> - concept bool Has_member_type() { - return requires() { typename T::type; }; - } - -template<typename T> - concept bool Concept() { - return true && Has_member_type<T>(); - } - -template<Concept T> - void foo( T t ) { } diff --git a/gcc/testsuite/g++.dg/concepts/req9.C b/gcc/testsuite/g++.dg/concepts/req9.C deleted file mode 100644 index 497154c..0000000 --- a/gcc/testsuite/g++.dg/concepts/req9.C +++ /dev/null @@ -1,25 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template<typename T> -struct S1 {}; - -template<typename T> -concept bool C() { return requires(T x) { { x.fn() } -> S1<T>; }; } - -template<C U> -void fn(U x) -{ - x.fn(); -} - -struct S2 -{ - auto fn() const { return S1<S2>(); } -}; - -int main() -{ - fn(S2{}); - return 0; -} diff --git a/gcc/testsuite/g++.dg/concepts/template-parm1.C b/gcc/testsuite/g++.dg/concepts/template-parm1.C deleted file mode 100644 index 192226f..0000000 --- a/gcc/testsuite/g++.dg/concepts/template-parm1.C +++ /dev/null @@ -1,35 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template<typename T> - concept bool C1 = __is_same_as(T, int); - -template<int N> - concept bool C2 = N == 0; - -template<template<typename> class X> - concept bool C3 = true; - -template<typename> struct Foo; - -// Type template parameters -template<C1 T = int> struct S1 { }; -template<C1 = int> struct S2; -template<C1 T> struct S2 { }; - -// Non-type template parameters -template<C2 N = 0> struct S3 { }; -template<C2 = 0> struct S4; -template<C2 N> struct S4 { }; - -// Template template parameters -template<C3 X = Foo> struct S5 { }; -template<C3 = Foo> struct S6; -template<C3 X> struct S6 { }; - -S1<> s1; -S2<> s2; -S3<> s3; -S4<> s4; -S5<> s5; -S6<> s6; diff --git a/gcc/testsuite/g++.dg/concepts/template-parm10.C b/gcc/testsuite/g++.dg/concepts/template-parm10.C deleted file mode 100644 index 33bf372..0000000 --- a/gcc/testsuite/g++.dg/concepts/template-parm10.C +++ /dev/null @@ -1,18 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template<int N, class T> - concept bool P() { return true; } - -template<template<typename> class X, class T> - concept bool Q() { return true; } - -template<P<int> N> void f() { } -template<Q<int> X> void g() { } - -template<typename> struct S { }; - -int main() { - f<0>(); - g<S>(); -} diff --git a/gcc/testsuite/g++.dg/concepts/template-parm11.C b/gcc/testsuite/g++.dg/concepts/template-parm11.C index 04e11e2..07ad6e2 100644 --- a/gcc/testsuite/g++.dg/concepts/template-parm11.C +++ b/gcc/testsuite/g++.dg/concepts/template-parm11.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template<typename T> diff --git a/gcc/testsuite/g++.dg/concepts/template-parm12.C b/gcc/testsuite/g++.dg/concepts/template-parm12.C index 8745bb1..cb3e2c6 100644 --- a/gcc/testsuite/g++.dg/concepts/template-parm12.C +++ b/gcc/testsuite/g++.dg/concepts/template-parm12.C @@ -1,6 +1,6 @@ -// Conceptized version of template/ttp23.C -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } +// Conceptized version of template/ttp23.C template <class T> concept bool Foo = true; diff --git a/gcc/testsuite/g++.dg/concepts/template-parm2.C b/gcc/testsuite/g++.dg/concepts/template-parm2.C index adecc12..d708fd0 100644 --- a/gcc/testsuite/g++.dg/concepts/template-parm2.C +++ b/gcc/testsuite/g++.dg/concepts/template-parm2.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template<typename T> diff --git a/gcc/testsuite/g++.dg/concepts/template-parm3.C b/gcc/testsuite/g++.dg/concepts/template-parm3.C index 3d37e9c..028149c 100644 --- a/gcc/testsuite/g++.dg/concepts/template-parm3.C +++ b/gcc/testsuite/g++.dg/concepts/template-parm3.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template<typename T> diff --git a/gcc/testsuite/g++.dg/concepts/template-parm4.C b/gcc/testsuite/g++.dg/concepts/template-parm4.C index f546a75..d93dbc7 100644 --- a/gcc/testsuite/g++.dg/concepts/template-parm4.C +++ b/gcc/testsuite/g++.dg/concepts/template-parm4.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template<typename T> diff --git a/gcc/testsuite/g++.dg/concepts/template-parm5.C b/gcc/testsuite/g++.dg/concepts/template-parm5.C deleted file mode 100644 index cd93c607..0000000 --- a/gcc/testsuite/g++.dg/concepts/template-parm5.C +++ /dev/null @@ -1,20 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template<typename T> - concept bool C1 = __is_same_as(T, int); - -template<int N> - concept bool C2 = N == 0; - -template<template<typename> class X> - concept bool C3 = true; - -template<typename> struct Foo; - -template<C1... Ts = int> struct S1; // { dg-error "default argument" } -template<C1... = int> struct S2; // { dg-error "default argument" } -template<C2... Ns = 0> struct S3; // { dg-error "default argument" } -template<C2... = 0> struct S4; // { dg-error "default argument" } -template<C3... Ts = Foo> struct S5; // { dg-error "default argument" } -template<C3... = Foo> struct S6; // { dg-error "default argument" } diff --git a/gcc/testsuite/g++.dg/concepts/template-parm6.C b/gcc/testsuite/g++.dg/concepts/template-parm6.C deleted file mode 100644 index 9efe409..0000000 --- a/gcc/testsuite/g++.dg/concepts/template-parm6.C +++ /dev/null @@ -1,42 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template<typename... Ts> struct are_same; - -template<> - struct are_same<> { - static constexpr bool value = true; - }; - -template<typename T> - struct are_same<T> { - static constexpr bool value = true; - }; - -template<typename T, typename U, typename... Ts> - struct are_same<T, U, Ts...> { - static constexpr bool value = - __is_same_as(T, U) && are_same<U, Ts...>::value; - }; - -constexpr bool all_of() { return true; } -constexpr bool all_of(bool b) { return b; } - -template<typename... Ts> - constexpr bool all_of(bool a, bool b, Ts... args) { - return (a && b) && all_of(b, args...); - } - -template<typename... Ts> - concept bool C1 = are_same<Ts...>::value; - -template<bool... Bs> - concept bool C2 = all_of(Bs...); - -template<C1... Ts> struct S1 { }; -template<C1...> struct S2 { }; -template<C2... Bs> struct S4 { }; -template<C2...> struct S5 { }; - -S1<int, int, int> s1; -S4<true, true, true> s4; diff --git a/gcc/testsuite/g++.dg/concepts/template-parm7.C b/gcc/testsuite/g++.dg/concepts/template-parm7.C deleted file mode 100644 index 1dfa0d1..0000000 --- a/gcc/testsuite/g++.dg/concepts/template-parm7.C +++ /dev/null @@ -1,45 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template<typename... Ts> struct are_same; - -template<> - struct are_same<> { - static constexpr bool value = true; - }; - -template<typename T> - struct are_same<T> { - static constexpr bool value = true; - }; - -template<typename T, typename U, typename... Ts> - struct are_same<T, U, Ts...> { - static constexpr bool value = - __is_same_as(T, U) && are_same<U, Ts...>::value; - }; - -constexpr bool all_of() { return true; } -constexpr bool all_of(bool b) { return b; } - -template<typename... Ts> - constexpr bool all_of(bool a, bool b, Ts... args) { - return (a && b) && all_of(b, args...); - } - - -template<typename... Ts> - concept bool C1 = are_same<Ts...>::value; - -template<bool... Bs> - concept bool C2 = all_of(Bs...); - -template<C1... Ts> struct S1 { }; // OK -S1<int, int, char> s1; // { dg-error "constraint failure|invalid type" } -template<C1 Ts> struct S2 { }; // { dg-error "variadic constraint" } - -template<C2... Bs> struct S3 { }; // OK -S3<true, true, false> s3; // { dg-error "constraint failure|invalid type" } -template<C2 Bs> struct S4 { }; // { dg-error "variadic constraint" } - -int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/template-parm9.C b/gcc/testsuite/g++.dg/concepts/template-parm9.C deleted file mode 100644 index 64308cd..0000000 --- a/gcc/testsuite/g++.dg/concepts/template-parm9.C +++ /dev/null @@ -1,19 +0,0 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - -template<typename T> - concept bool C() { return __is_class(T); } - -template<typename T> - concept bool D() { return C<T>() and __is_empty(T); } - -template<template<typename Q> requires C<Q>() class X> - struct S { }; - -template<typename A> requires true struct T0 { }; -template<typename A> requires D<A>() struct T1 { }; - -S<T0> x3; // { dg-error "constraint mismatch|invalid type" } -S<T1> x4; // { dg-error "constraint mismatch|invalid type" } - -int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/template-template-parm1.C b/gcc/testsuite/g++.dg/concepts/template-template-parm1.C index 6c4dc2c..d701859 100644 --- a/gcc/testsuite/g++.dg/concepts/template-template-parm1.C +++ b/gcc/testsuite/g++.dg/concepts/template-template-parm1.C @@ -1,5 +1,5 @@ // PR c++/66937 -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } #include <tuple> diff --git a/gcc/testsuite/g++.dg/concepts/var-concept1.C b/gcc/testsuite/g++.dg/concepts/var-concept1.C index 90a88d8..21a4915 100644 --- a/gcc/testsuite/g++.dg/concepts/var-concept1.C +++ b/gcc/testsuite/g++.dg/concepts/var-concept1.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template<typename T> diff --git a/gcc/testsuite/g++.dg/concepts/var-concept2.C b/gcc/testsuite/g++.dg/concepts/var-concept2.C index c16d3e4..4ff00a0 100644 --- a/gcc/testsuite/g++.dg/concepts/var-concept2.C +++ b/gcc/testsuite/g++.dg/concepts/var-concept2.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template<typename T> diff --git a/gcc/testsuite/g++.dg/concepts/var-concept3.C b/gcc/testsuite/g++.dg/concepts/var-concept3.C index f3d642b..144c0ea 100644 --- a/gcc/testsuite/g++.dg/concepts/var-concept3.C +++ b/gcc/testsuite/g++.dg/concepts/var-concept3.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template<typename T> @@ -12,12 +12,19 @@ template<typename T> template<typename U> - requires C1<U>() // { dg-error "" } + requires C1<U>() // { dg-error "cannot be used as a function" } void f1(U) { } template<typename U> - requires C2<U> // { dg-error "invalid reference" } + requires C2<U> // { dg-error "must be called" } void f2(U) { } template<C3 T> // { dg-error "not a type" } - void f(T) { } // { dg-error "" } + void f(T) { } // { dg-error "declared void|not declared" } + +void foo() +{ + struct S { } s; + f2(s); + // f2(0); +} diff --git a/gcc/testsuite/g++.dg/concepts/var-concept4.C b/gcc/testsuite/g++.dg/concepts/var-concept4.C index 3864c9d..a7839ee 100644 --- a/gcc/testsuite/g++.dg/concepts/var-concept4.C +++ b/gcc/testsuite/g++.dg/concepts/var-concept4.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template<typename T, typename U> diff --git a/gcc/testsuite/g++.dg/concepts/var-concept5.C b/gcc/testsuite/g++.dg/concepts/var-concept5.C index b1e9cb5..d8fa298 100644 --- a/gcc/testsuite/g++.dg/concepts/var-concept5.C +++ b/gcc/testsuite/g++.dg/concepts/var-concept5.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template<typename T1, typename T2> @@ -8,8 +8,8 @@ template<typename T1, typename T2, typename T3> concept bool C2 = true; -template<C1 T> // { dg-error "not a type" } +template<C1 T> // { dg-error "wrong number of template arguments" } constexpr bool f1( ) { return true; } -template<C2<int> T> // { dg-error "expected|not a type" } +template<C2<int> T> // { dg-error "wrong number of template arguments" } constexpr bool f2( ) { return true; } diff --git a/gcc/testsuite/g++.dg/concepts/var-concept6.C b/gcc/testsuite/g++.dg/concepts/var-concept6.C index 8f5ac62..80984a7 100644 --- a/gcc/testsuite/g++.dg/concepts/var-concept6.C +++ b/gcc/testsuite/g++.dg/concepts/var-concept6.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template <class T> diff --git a/gcc/testsuite/g++.dg/concepts/var-concept7.C b/gcc/testsuite/g++.dg/concepts/var-concept7.C index 8371b37..2cfe266 100644 --- a/gcc/testsuite/g++.dg/concepts/var-concept7.C +++ b/gcc/testsuite/g++.dg/concepts/var-concept7.C @@ -1,6 +1,6 @@ // PR c++/85133 -// { dg-do compile { target c++17 } } -// { dg-additional-options "-fconcepts" } +// { dg-do compile { target c++17_only } } +// { dg-options "-fconcepts" } template<typename> concept bool C; // { dg-error "no initializer" } diff --git a/gcc/testsuite/g++.dg/concepts/var-templ1.C b/gcc/testsuite/g++.dg/concepts/var-templ1.C index b69d7d8d..4ac578f 100644 --- a/gcc/testsuite/g++.dg/concepts/var-templ1.C +++ b/gcc/testsuite/g++.dg/concepts/var-templ1.C @@ -1,5 +1,5 @@ // PR c++/67117 -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template <class T> diff --git a/gcc/testsuite/g++.dg/concepts/var-templ2.C b/gcc/testsuite/g++.dg/concepts/var-templ2.C index ffe5f1f..1b8890a 100644 --- a/gcc/testsuite/g++.dg/concepts/var-templ2.C +++ b/gcc/testsuite/g++.dg/concepts/var-templ2.C @@ -1,5 +1,5 @@ // PR c++/67139 -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template <class T> diff --git a/gcc/testsuite/g++.dg/concepts/var-templ3.C b/gcc/testsuite/g++.dg/concepts/var-templ3.C index 22f07ee..cc5ee5f 100644 --- a/gcc/testsuite/g++.dg/concepts/var-templ3.C +++ b/gcc/testsuite/g++.dg/concepts/var-templ3.C @@ -1,5 +1,5 @@ // PR c++/68666 -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } struct A { diff --git a/gcc/testsuite/g++.dg/concepts/variadic1.C b/gcc/testsuite/g++.dg/concepts/variadic1.C index 4c0f437..c3bc7f6 100644 --- a/gcc/testsuite/g++.dg/concepts/variadic1.C +++ b/gcc/testsuite/g++.dg/concepts/variadic1.C @@ -1,5 +1,5 @@ // PR c++/66712 -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template <class T, class...Args> diff --git a/gcc/testsuite/g++.dg/concepts/variadic2.C b/gcc/testsuite/g++.dg/concepts/variadic2.C index 4675d97..7b22009 100644 --- a/gcc/testsuite/g++.dg/concepts/variadic2.C +++ b/gcc/testsuite/g++.dg/concepts/variadic2.C @@ -1,17 +1,18 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template <class T> concept bool Copyable = requires (T t) { T(t); }; template <class T> concept bool Constructable = requires { T(); }; template <class T> concept bool Both = Copyable<T> && Constructable<T>; -template <Copyable... Ts> +template <Copyable... Ts> // requires (Copyable<Ts> && ...) constexpr int f(Ts...) { return 0; } // #1 -template <Both... Ts> +template <Both... Ts> // requires (Both<Ts> && ...) constexpr int f(Ts...) { return 1; } // #2 int main() { - static_assert(f(42) == 1); + static_assert(f(42) == 1); // { dg-error "ambiguous" } + // The associated constraints of the two functions are incomparable. } diff --git a/gcc/testsuite/g++.dg/concepts/variadic3.C b/gcc/testsuite/g++.dg/concepts/variadic3.C index f980e99..bd2f381 100644 --- a/gcc/testsuite/g++.dg/concepts/variadic3.C +++ b/gcc/testsuite/g++.dg/concepts/variadic3.C @@ -1,5 +1,5 @@ // PR c++/70036 -// { dg-do compile { target c++14 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template <class T> concept bool C = true; diff --git a/gcc/testsuite/g++.dg/concepts/variadic4.C b/gcc/testsuite/g++.dg/concepts/variadic4.C index e0f7903..d6eea49 100644 --- a/gcc/testsuite/g++.dg/concepts/variadic4.C +++ b/gcc/testsuite/g++.dg/concepts/variadic4.C @@ -1,5 +1,5 @@ // PR c++/73456 -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } template<typename...> struct list {}; @@ -7,13 +7,14 @@ template<typename...> struct list {}; template<typename Seq> concept bool Sequence = true; -template<Sequence... Seqs> +template<Sequence... Seqs> // requires (Sequence<Seqs> && ...) struct zip; template<Sequence... Seqs> - requires requires { typename list<Seqs...>; } -// main.cpp:12:8: internal compiler error: in non_atomic_constraint_p, at cp/logic.cc:315 -struct zip<Seqs...> {}; + requires requires { typename list<Seqs...>; } // && (Sequence<Seqs> && ...) +struct zip<Seqs...> {}; // { dg-error "does not specialize" } +// The constraints of the specialization and the sequence are not +// comparable; the specializations are unordered. int main() { diff --git a/gcc/testsuite/g++.dg/cpp0x/auto52.C b/gcc/testsuite/g++.dg/cpp0x/auto52.C index d33f927..c277d33 100644 --- a/gcc/testsuite/g++.dg/cpp0x/auto52.C +++ b/gcc/testsuite/g++.dg/cpp0x/auto52.C @@ -3,4 +3,4 @@ using T = auto() -> int; using U = void() -> int; // { dg-error "11:.type name. function with trailing return type not declared with .auto." } -using W = auto(); // { dg-error "11:invalid use of .auto." } +using W = auto(); // { dg-error "11:.*auto." } diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-err2.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-err2.C index aaa80f4..3399ce7 100644 --- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-err2.C +++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-err2.C @@ -6,7 +6,7 @@ int main() auto a = []() { return true; }; auto b = []() { return a(); }; // { dg-error "'a' is not captured" } int c, d; - while (b() && c < d) // { dg-error "could not convert" } + while (b() && c < d) { } } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-access1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-access1.C new file mode 100644 index 0000000..bbb1be0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-access1.C @@ -0,0 +1,15 @@ +// { dg-do compile { target concepts } } + +class A +{ + static void f(int); +public: + template <class T> void g(T t) + requires requires { f(t); } + {} +}; + +int main() +{ + A().g(42); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-alias.C b/gcc/testsuite/g++.dg/cpp2a/concepts-alias.C new file mode 100644 index 0000000..6b2ab0d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-alias.C @@ -0,0 +1,25 @@ +// { dg-do compile { target c++2a } } + +template<typename T> +concept Class = __is_class(T); + +template<typename T> + requires Class<T> +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. See the declaration of y1 +// below. +template<typename T> +using Y = X<T>; + +template<Class T> using Z = T*; + +struct S { }; + +X<S> x1; // OK +X<int> x2; // { dg-error "template constraint failure" } +Y<int> y1; // { dg-error "" "" { xfail *-*-* } } +Z<S> z1; // ok + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-alias2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-alias2.C new file mode 100644 index 0000000..b784030 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-alias2.C @@ -0,0 +1,12 @@ +// { dg-do compile { target concepts } } + +using Bool = bool; +template <class T> +const Bool b = true; + +template <class T> +concept BoolC = b<T>; + +template <BoolC T> struct A { }; + +A<int> a; diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-class.C b/gcc/testsuite/g++.dg/cpp2a/concepts-class.C new file mode 100644 index 0000000..aca5c44 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-class.C @@ -0,0 +1,115 @@ +// { dg-do compile { target c++2a } } + +template<typename T> +concept Class = __is_class(T); + +template<typename T> +concept Union = __is_union(T); + +template<typename T> +concept One = sizeof(T) >= 4; + +template<typename T> +concept Two = One<T> && sizeof(T) >= 8; + +// Basic checks +template<typename T> requires true struct ok { }; +template<typename T> requires false struct err { }; + +ok<int> ok1; +err<int> err1; // { dg-error "template constraint failure" } +err<int>* err2; // { dg-error "template constraint failure" } + +// Redeclarations +template<typename T> + requires Class<T> +struct S1; + +template<Class T> // { dg-error "template parameter | different constraints" } +struct S1 { }; + +template<typename T> + requires Class<T> +struct S2; + +template<typename T> + requires Union<T> +struct S2; // { dg-error "redeclaration | different constraints" } + + +// Check non-overlapping specializations +template<typename T> +struct S3 { static const int value = 0; }; + +template<typename T> + requires Class<T> +struct S3<T> { static const int value = 1; }; + +template<typename T> + requires Union<T> +struct S3<T> { static const int value = 2; }; + +struct S { }; +union U { }; + +static_assert(S3<int>::value == 0, ""); +static_assert(S3<S>::value == 1, ""); +static_assert(S3<U>::value == 2, ""); + +// Check ordering of partial specializations +template<typename T> +struct S4 { static const int value = 0; }; + +template<typename T> + requires One<T> +struct S4<T> { static const int value = 1; }; + +template<typename T> + requires Two<T> +struct S4<T> { static const int value = 2; }; + +struct one_type { char x[4]; }; +struct two_type { char x[8]; }; + +static_assert(S4<char>::value == 0, ""); +static_assert(S4<one_type>::value == 1, ""); +static_assert(S4<two_type>::value == 2, ""); + +// Specializations are more specialized. +template<typename T> requires Two<T> struct S5 { }; +template<typename T> requires One<T> struct S5<T> { }; // { dg-error "does not specialize" } + +// Constraints are checked even when decls are not instantiatied. +S5<one_type>* x4b; // { dg-error "constraint|invalid" } + +// Deduction guides +template <class T> +concept IsInt = __is_same_as(T,int); + +template<typename T> +struct A +{ + int i; + A(...); +}; + +template<typename I> + requires IsInt<I> +A(I) -> A<I>; + +A a(1); +A a2(1.0); // { dg-error "class template argument deduction | no matching function for call" } + + +template<typename T> +struct S6 +{ + template<typename U> + requires true + struct Inner; +}; + +template<typename T> +template<typename U> +struct S6<T>::Inner { }; // { dg-error "does not match" } + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-cmath.C b/gcc/testsuite/g++.dg/cpp2a/concepts-cmath.C new file mode 100644 index 0000000..b090c3a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-cmath.C @@ -0,0 +1,4 @@ +// { dg-do compile { target c++2a } } + +#include <cmath> + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-complete1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-complete1.C new file mode 100644 index 0000000..25c4ca0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-complete1.C @@ -0,0 +1,11 @@ +// { dg-do compile { target c++2a } } + +template <class T> concept has_mem_type = requires { typename T::type; }; + +template <has_mem_type T> int f () { return 0; } +template <class T> char f() { return 0; } + +struct A; +static_assert (sizeof (f<A>()) == 1); +struct A { typedef int type; }; +static_assert (sizeof (f<A>()) > 1); diff --git a/gcc/testsuite/g++.dg/concepts/constrained-parm.C b/gcc/testsuite/g++.dg/cpp2a/concepts-constrained-parm.C index c2b6614a..bb7e31d 100644 --- a/gcc/testsuite/g++.dg/concepts/constrained-parm.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-constrained-parm.C @@ -1,8 +1,7 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } template<typename T> - concept bool C() { return __is_class(T); } + concept C = __is_class(T); template<const C T> struct S1 { }; // { dg-error "cv-qualified" } template<volatile C T> struct S2 { }; // { dg-error "cv-qualified" } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-decltype.C b/gcc/testsuite/g++.dg/cpp2a/concepts-decltype.C new file mode 100644 index 0000000..db3cfdf --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-decltype.C @@ -0,0 +1,67 @@ +// { dg-do compile { target c++2a } } + +// Tests constrained decltype(auto). + +template<typename T> +concept Type = true; + +template<typename T> +concept Int = __is_same_as(T, int); + +template<typename T, typename U> +concept SameAs = __is_same_as(T, U); + +template<typename T, typename U> + requires SameAs<T, U> +constexpr bool check = true; + +int z = 0; +const int cz = 0; + +Type decltype(auto) x1 = 0; +static_assert(check<decltype(x1), int>); +Type decltype(auto) x2 = z; +static_assert(check<decltype(x2), int>); +Type decltype(auto) x3 = (z); +static_assert(check<decltype(x3), int&>); +Type decltype(auto) x4 = cz; +static_assert(check<decltype(x4), const int>); +Type decltype(auto) x5 = (cz); +static_assert(check<decltype(x5), const int&>); + +Type decltype(auto) f1() { return 0; } +static_assert(check<decltype(f1()), int>); +Type decltype(auto) f2() { return z; } +static_assert(check<decltype(f2()), int>); +Type decltype(auto) f3() { return (z); } +static_assert(check<decltype(f3()), int&>); +Type decltype(auto) f4() { return cz; } +static_assert(check<decltype(f4()), int>); // Top-level const is removed? +Type decltype(auto) f5() { return (cz); } +static_assert(check<decltype(f5()), const int&>); + +bool b = true; +const bool cb = true; + +Int decltype(auto) b1 = true; // { dg-error "deduced initializer" } +Int decltype(auto) b2 = (b); // { dg-error "deduced initializer" } +Int decltype(auto) b3 = (cb); // { dg-error "deduced initializer" } + +Int decltype(auto) g1() { } // { dg-error "deduced return type" } +Int decltype(auto) g2() { return; } // { dg-error "deduced return type" } +Int decltype(auto) g3() { return true; } // { dg-error "deduced return type" } +int g4(Type decltype(auto) x) { return 0; } // { dg-error "cannot declare" } +int g5(decltype(auto) x) { return 0; } // { dg-error "cannot declare" } + +template<Type decltype(auto) X, typename T> + requires SameAs<decltype(X), T> +constexpr bool deduced_as = true; + +constexpr int Z = 10; + +static_assert(deduced_as<0, int>); +static_assert(deduced_as<0, int&>); // { dg-error "invalid variable template" } +static_assert(deduced_as<Z, const int>); +static_assert(deduced_as<(Z), const int>); // { dg-error "invalid variable template" } +static_assert(deduced_as<(Z), const int&>); + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-defarg1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-defarg1.C new file mode 100644 index 0000000..c3ed309 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-defarg1.C @@ -0,0 +1,7 @@ +// { dg-do compile { target concepts } } + +template<typename T, typename U = T> concept C3 = true; +template<class T> struct s1 +{ + template <C3<T> U> void f() { } +}; diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-dep1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-dep1.C new file mode 100644 index 0000000..b4f6113 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-dep1.C @@ -0,0 +1,5 @@ +// { dg-do compile { target concepts } } + +template <class T> concept True = true; +template <class T, int I = static_cast<int>(True<T>)> struct A { }; +template <class T> struct B: A<T> { }; diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-dr1430.C b/gcc/testsuite/g++.dg/cpp2a/concepts-dr1430.C new file mode 100644 index 0000000..f39921f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-dr1430.C @@ -0,0 +1,14 @@ +// PR c++/66092 +// { dg-do compile { target c++2a } } + +template <typename T, typename U, typename... Args> + concept Similar = true; + +template <typename... Args> + requires Similar<Args...> // { dg-error "pack expansion" } +void foo( Args... args ) {} + +int main() +{ + foo(1, 2, 3); // { dg-error "cannot call" } +} diff --git a/gcc/testsuite/g++.dg/concepts/explicit-inst1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-inst1.C index 99bd72e..14d994c 100644 --- a/gcc/testsuite/g++.dg/concepts/explicit-inst1.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-inst1.C @@ -1,11 +1,13 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } +// { dg-final { scan-assembler "_Z1gI1XEvT_" } } +// { dg-final { scan-assembler "_Z1gI1YEvT_" } } +// { dg-final { scan-assembler "_Z1gIiEvT_" } } template<typename T> - concept bool C() { return __is_class(T); } + concept C = __is_class(T); template<typename T> - concept bool D() { return C<T>() && __is_empty(T); } + concept D = C<T> && __is_empty(T); struct X { }; struct Y { int n; }; @@ -14,7 +16,6 @@ template<typename T> void g(T) { } // #1 template<C T> void g(T) { } // #2 template<D T> void g(T) { } // #3 -// FIXME: How do I test that these generate the right symbols? template void g(int); // Instantiate #1 template void g(X); // Instantitae #3 template void g(Y); // Instantiate #2 diff --git a/gcc/testsuite/g++.dg/concepts/explicit-inst2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-inst2.C index ea31387..6074bc7 100644 --- a/gcc/testsuite/g++.dg/concepts/explicit-inst2.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-inst2.C @@ -1,11 +1,10 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } template<typename T> - concept bool C() { return __is_class(T); } + concept C = __is_class(T); template<typename T> - concept bool D() { return C<T>() && __is_empty(T); } + concept D = C<T> && __is_empty(T); struct X { }; struct Y { int n; }; diff --git a/gcc/testsuite/g++.dg/concepts/explicit-inst3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-inst3.C index 18d3c49..03ec9e9 100644 --- a/gcc/testsuite/g++.dg/concepts/explicit-inst3.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-inst3.C @@ -1,11 +1,10 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } template<typename T> - concept bool C() { return __is_class(T); } + concept C = __is_class(T); template<typename T> - concept bool D() { return C<T>() && __is_empty(T); } + concept D = C<T> && __is_empty(T); struct X { }; struct Y { int n; }; @@ -13,10 +12,10 @@ struct Y { int n; }; template<typename T> struct S { void f() { } // #1 - void f() requires C<T>() { } // #2 + void f() requires C<T> { } // #2 - void g() requires C<T>() { } // #1 - void g() requires D<T>() { } // #2 + void g() requires C<T> { } // #1 + void g() requires D<T> { } // #2 }; template void S<int>::f(); // #1 diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-inst4.C b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-inst4.C new file mode 100644 index 0000000..81bc081 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-inst4.C @@ -0,0 +1,17 @@ +// { dg-do compile { target c++2a } } + +template<typename T> + concept C = __is_class(T); + +template<typename T> + concept D = C<T> && __is_empty(T); + +template<typename T> + struct S { + void g() requires C<T> { } // #1 + void g() requires D<T> { } // #2 + }; + +template void S<int>::g(); // { dg-error "match" } + +int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec1.C index bff06f2..d54bcdb 100644 --- a/gcc/testsuite/g++.dg/concepts/explicit-spec1.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec1.C @@ -1,13 +1,12 @@ -// { dg-do run { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do run { target c++2a } } #include <cassert> template<typename T> - concept bool C() { return __is_class(T); } + concept C = __is_class(T); template<typename T> - concept bool D() { return C<T>() && __is_empty(T); } + concept D = C<T> && __is_empty(T); struct X { } x; struct Y { int n; } y; diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec2.C index ca8b8a0..4103714 100644 --- a/gcc/testsuite/g++.dg/concepts/explicit-spec2.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec2.C @@ -1,8 +1,7 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } template<typename T> - concept bool C() { return __is_class(T); } + concept C = __is_class(T); struct X { }; diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec3.C new file mode 100644 index 0000000..76c6fb9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec3.C @@ -0,0 +1,13 @@ +// { dg-do compile { target c++2a } } + +template<typename T> + concept C = __is_class(T); + +template<C T> struct S; + +struct X { }; + +// Not a valid explicit specialization, int does not satisfy C. +template<> struct S<int> { }; // { dg-error "constraint failure" } + +int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec4.C b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec4.C index 75a2dec..0634eaf 100644 --- a/gcc/testsuite/g++.dg/concepts/explicit-spec4.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec4.C @@ -1,13 +1,12 @@ -// { dg-do run { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do run { target c++2a } } #include <cassert> template<typename T> - concept bool C() { return __is_class(T); } + concept C = __is_class(T); template<typename T> - concept bool D() { return C<T>() && __is_empty(T); } + concept D = C<T> && __is_empty(T); struct X { } x; struct Y { int n; } y; @@ -17,10 +16,10 @@ int called = 0; template<typename T> struct S { void f() { called = 0; } // #1 - void f() requires C<T>() { called = 0; } // #2 + void f() requires C<T> { called = 0; } // #2 - void g() requires C<T>() { } // #1 - void g() requires D<T>() { } // #2 + void g() requires C<T> { } // #1 + void g() requires D<T> { } // #2 }; template<> void S<int>::f() { called = 1; } // Spec of #1 diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec5.C b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec5.C index d83eec1..b682b0d 100644 --- a/gcc/testsuite/g++.dg/concepts/explicit-spec5.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec5.C @@ -1,13 +1,12 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } #include <cassert> template<typename T> - concept bool C() { return __is_class(T); } + concept C = __is_class(T); template<typename T> - concept bool D() { return C<T>() && __is_empty(T); } + concept D = C<T> && __is_empty(T); struct X { } x; struct Y { int n; } y; @@ -16,7 +15,7 @@ int called = 0; template<typename T> struct S { - void f() requires C<T>(); + void f() requires C<T>; }; template<> void S<int>::f() { called = 1; } // { dg-error "match" } diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec6.C b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec6.C index b548707..13f04d7 100644 --- a/gcc/testsuite/g++.dg/concepts/explicit-spec6.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec6.C @@ -1,5 +1,4 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } template<typename T> struct A { diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-feature-macro.C b/gcc/testsuite/g++.dg/cpp2a/concepts-feature-macro.C new file mode 100644 index 0000000..56fbb68 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-feature-macro.C @@ -0,0 +1,5 @@ +// { dg-do compile { target c++2a } } + +#ifndef __cpp_concepts +#error __cpp_concepts not defined +#endif diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-fn1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-fn1.C new file mode 100644 index 0000000..33f3a74 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-fn1.C @@ -0,0 +1,248 @@ +// { dg-do compile { target c++2a } } + +template<typename T> +concept Type = true; + +template<typename T> +concept False = false; + +template<typename T> +concept Class = __is_class(T); + +template<typename T> +concept EmptyClass = Class<T> && __is_empty(T); + +template<typename T, typename U> +concept Classes = __is_class(T) && __is_class (U); + +struct empty { }; + +struct nonempty { int n; }; + +static_assert(Type<int>); +static_assert(False<int>); // { dg-error "static assertion failed" } + +// Basic checks + +template<typename T> + requires Type<T> +int fn1(T t) { return 0; } + +template<typename T> + requires False<T> +int fn2(T t) { return 0; } + +void driver() +{ + fn1(0); // OK + fn2(0); // { dg-error "cannot call function" } +} + +// Ordering + +template<typename T> +concept Cf = requires (T t) { t.f(); }; + +template<typename T> +concept Cfg = Cf<T> && requires (T t) { t.g(); }; + +template<typename T> +concept Cf2 = requires (T t) { t.f(); }; + +template<typename T> +constexpr int algo(T t) { return 0; } + +template<typename T> requires Cf<T> +constexpr int algo(T t) { return 1; } + +template<typename T> requires Cfg<T> +constexpr int algo(T t) { return 2; } + +template<typename T> requires Cf<T> +constexpr int ambig(T t) { return 1; } + +template<typename T> requires Cf2<T> +constexpr int ambig(T t) { return 1; } + +struct T1 { + void f() { } +}; + +struct T2 : T1 { + void g() { } +}; + +void driver_0() +{ + T1 x; + T2 y; + static_assert(algo(0) == 0); + static_assert(algo(x) == 1); + static_assert(algo(y) == 2); + ambig(x); // { dg-error "call of overload | is ambiguous" } +} + +template<typename T> +struct S +{ + void f() requires Class<T> { } + + template<typename U> + struct Inner + { + void g() requires Classes<T, U> { } + }; + + template<typename U> requires Classes<T, U> void h(U) { } +}; + +struct X { }; + +void driver_1() +{ + S<X> s1; + s1.f(); // OK + s1.h(s1); // OK + s1.h(0); // { dg-error "no matching function" } + + S<int> s2; + s2.f(); // { dg-error "no matching function" } + + S<X>::Inner<X> si1; + si1.g(); + + S<X>::Inner<int> si2; + si2.g(); // { dg-error "no matching function" } +} + +// Check constraints on non-dependent arguments, even when in a +// dependent context. + +template<typename T> requires Class<T> void f1(T x) { } + +// fn1-2.C -- Dependent checks +template<typename T> +void caller_1(T x) +{ + f1(x); // Unchecked dependent arg. + f1(empty{}); // Checked non-dependent arg, but OK + f1(0); // { dg-error "cannot call function" } +} + +// fn3.c -- Ordering +template<typename T> + requires Class<T> +constexpr int f2(T x) { return 1; } + +template<typename T> + requires EmptyClass<T> +constexpr int f2(T x) { return 2; } + +template<typename T> +constexpr int f3(T x) requires Class<T> { return 1; } + +template<typename T> +constexpr int f3(T x) requires EmptyClass<T> { return 2; } + +void driver_2() +{ + f2(0); // { dg-error "no matching function" } + static_assert (f2(empty{}) == 2); + static_assert (f2(nonempty{}) == 1); + f3(0); // { dg-error "no matching function" } + static_assert (f3(empty{}) == 2); + static_assert (f3(nonempty{}) == 1); +} + +// fn8.C -- Address of overloaded functions +template<typename T> requires Type<T> void ok(T) { } +template<typename T> requires Class<T> void err(T) { } + +auto p1 = &ok<int>; +auto p2 = &err<int>; // { dg-error "no matches" } +void (*p3)(int) = &ok<int>; +void (*p4)(int) = &err<int>; // { dg-error "no matches" } +void (*p5)(int) = &ok; +void (*p6)(int) = &err; // { dg-error "no matches" } + +template<typename T> void g(T x) { } + +void driver_3 () +{ + g(&ok<int>); + g(&err<int>); // { dg-error "no matches" } +} + + +struct S2 { + template<typename T> requires Type<T> int ok(T) { return 0; } + template<typename T> requires Class<T> int err(T) { return 0; } +}; + +auto p7 = &S2::ok<int>; +auto p8 = &S2::err<int>; // { dg-error "no matches" } +int (S2::*p9)(int) = &S2::ok<int>; +int (S2::*p10)(int) = &S2::err<int>; // { dg-error "no matches" } +int (S2::*p11)(int) = &S2::ok; +int (S2::*p12)(int) = &S2::err; // { dg-error "no matches" } + +// fn9.C -- Ordering with function address +template<typename T> + requires Class<T> +constexpr int fn(T) { return 1; } + +template<typename T> + requires EmptyClass<T> +constexpr int fn(T) { return 2; } + +struct S3 +{ + template<typename T> + requires Class<T> + constexpr int fn(T) const { return 1; } + + template<typename T> + requires EmptyClass<T> + constexpr int fn(T) const { return 2; } +}; + +void driver_5 () { + struct X { }; + struct Y { X x; }; + + constexpr X x; + constexpr Y y; + constexpr S3 s; + + constexpr auto p1 = &fn<X>; // Empty f + static_assert (p1(x) == 2); + + constexpr auto p2 = &fn<Y>; // Class f + static_assert(p2(y) == 1); + + constexpr auto p3 = &S3::fn<X>; // Empty f + static_assert((s.*p3)(x) == 2); + + constexpr auto p4 = &S3::fn<Y>; // Empty f + static_assert((s.*p4)(y) == 1); +} + + +// Redeclarations + +// FIXME: This should be a diagnosable error. The programmer has moved +// the requirements from the template-head to the declarator. +template<typename T> requires Type<T> void f3(); +template<typename T> void f3() requires Type<T>; + +void driver_4() +{ + f3<int>(); // { dg-error "call of overload | ambiguous" } +} + +template<template<typename T> requires true class X> void f4(); +template<template<typename T> class X> void f4(); // OK: different declarations + +template<typename T> requires Type<T> void def() { } +template<typename T> requires Type<T> void def() { } // { dg-error "redefinition" } + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-fn2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-fn2.C new file mode 100644 index 0000000..ddf99aa --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-fn2.C @@ -0,0 +1,111 @@ +// { dg-do run { target c++2a } } + +#define assert(E) if (!(E)) __builtin_abort(); + +template<typename T> + concept C = __is_class(T); + +template<typename T> + concept D = __is_empty(T); + +struct X { } x; +struct Y { int n; } y; + +int called = 0; + +// Test constrained member definitions +template<typename T> + struct S1 { + void f1() requires C<T> { } + + void f2() requires C<T> { called = 1; } + void f2() requires (!C<T>) { called = 2; } + + void f3() { called = 1; } + void f3() requires C<T> { called = 2; } + void f3() requires C<T> && D<T> { called = 3; } + + void g1() requires C<T> && true; + + void g2() requires C<T>; + void g2() requires (!C<T>); + + void g3(); + void g3() requires C<T>; + void g3() requires C<T> and D<T>; + + template<C U> void h1(U u) { called = 1; } + template<C U> void h2(U u); + template<C U> void h3(U u) requires D<U>; + }; + +template<C T> + struct S2 { + void f(T) requires D<T>; + }; + + +int main() { + S1<X> sx; + S1<Y> sy; + S1<int> si; + + // Defined in-class + sx.f1(); + sx.f2(); assert(called == 1); + sx.f3(); assert(called == 3); + + sy.f1(); + sy.f2(); assert(called == 1); + sy.f3(); assert(called == 2); + + si.f2(); assert(called == 2); + si.f3(); assert(called == 1); + + // Member function template tests + S1<int> s1i; + s1i.h1(x); assert(called == 1); + s1i.h2(x); assert(called == 2); + s1i.h3(x); assert(called == 3); + + // Defined out of class. + sx.g1(); + sx.g2(); assert(called == 1); + sx.g3(); assert(called == 3); + + sy.g1(); + sy.g2(); assert(called == 1); + sy.g3(); assert(called == 2); + + si.g2(); assert(called == 2); + si.g3(); assert(called == 1); +} + +template<typename T> + void S1<T>::g1() requires C<T> and true { } + +template<typename T> + void S1<T>::g2() requires C<T> { called = 1; } + +template<typename T> + void S1<T>::g2() requires (!C<T>) { called = 2; } + +template<typename T> + void S1<T>::g3() { called = 1; } + +template<typename T> + void S1<T>::g3() requires C<T> { called = 2; } + +template<typename T> + void S1<T>::g3() requires C<T> and D<T> { called = 3; } + +template<typename T> + template<C U> + void S1<T>::h2(U u) { called = 2; } + +template<typename T> + template<C U> + void S1<T>::h3(U u) requires D<U> { called = 3; } + +template<C T> + void S2<T>::f(T t) requires D<T> { called = 4; } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-fn3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-fn3.C new file mode 100644 index 0000000..e73ae23 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-fn3.C @@ -0,0 +1,49 @@ +// { dg-do compile { target c++2a } } + +template<typename T> +concept type = true; + +template<typename T, typename U> +concept same_as = __is_same_as(T, U); + +template<typename T> +concept integral = __is_same_as(T, int); + +template<typename... Ts> +concept all_integral = (integral<Ts> && ...); + +void f1(integral auto... args) { } +void f2(all_integral auto... args) { } + +template<type T> requires true +void f3(T, integral auto... args) { } + +template<type T> +struct S +{ + void f1(integral auto... args) { } + void f2(all_integral auto... args) { } + + template<type U> requires true + void f3(U, integral auto... args) { } +}; + +int main() +{ + f1(1, 2, 3); + f1(1, 2, 3u); // { dg-error "cannot call" } + f2(1, 2, 3); + f2(1, 2, 3u); // { dg-error "cannot call" } + f3(1, 2, 3); + f3(1, 2, 3u); // { dg-error "cannot call" } + f3(1u, 2, 3); + + S<void> s; + s.f1(1, 2, 3); + s.f1(1, 2, 3u); // { dg-error "no matching function" } + s.f2(1, 2, 3); + s.f2(1, 2, 3u); // { dg-error "no matching function" } + s.f3(1, 2, 3); + s.f3(1, 2, 3u); // { dg-error "no matching function" } + s.f3(1u, 2, 3); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-fnparm1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-fnparm1.C new file mode 100644 index 0000000..586b510 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-fnparm1.C @@ -0,0 +1,10 @@ +// { dg-do compile { target concepts } } + +template <class T> void f(T t) + requires requires { static_cast<T&&>(t); } +{} + +int main() +{ + f(42); +} diff --git a/gcc/testsuite/g++.dg/concepts/friend1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend1.C index 9050b55..da97575 100644 --- a/gcc/testsuite/g++.dg/concepts/friend1.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend1.C @@ -1,8 +1,7 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } template<typename T> - concept bool Eq() { return requires(T t) { t == t; }; } + concept Eq = requires(T t) { t == t; }; // { dg-message "in requirements" } struct Nt { template<Eq T> friend void f(T) { } @@ -15,7 +14,7 @@ template<Eq T> template<typename T> struct S { - friend bool operator==(S, S) requires Eq<T>() { return true; } + friend bool operator==(S, S) requires Eq<T> { return true; } friend void proc<>(S*); // { dg-error "does not match any template declaration" } }; diff --git a/gcc/testsuite/g++.dg/concepts/friend2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend2.C index 8ef6002..6aa9d961 100644 --- a/gcc/testsuite/g++.dg/concepts/friend2.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend2.C @@ -1,8 +1,7 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } template<typename T> - concept bool Eq() { return requires(T t) { t == t; }; } + concept Eq = requires(T t) { t == t; }; // { dg-message "in requirements" } template<Eq T> struct Foo { }; diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend3.C new file mode 100644 index 0000000..4f49358 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend3.C @@ -0,0 +1,20 @@ +// { dg-do compile { target concepts } } + +template <class T> concept True = true; + +template <True U> struct B { int i = ++U::x; }; +template <True U> void f() { ++U::x; } + +template <class V> class C +{ + static int x; + + template <True U> friend struct B; + template <True U> friend void f(); +}; + +int main() +{ + f<C<int>>(); + B<C<int>>(); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-iconv1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-iconv1.C new file mode 100644 index 0000000..cc2ce7e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-iconv1.C @@ -0,0 +1,22 @@ +// PR c++/67240 +// { dg-do compile { target c++2a } } + +template <class T, class U> concept Same = __is_same_as(T,U); + +int foo(int x) +{ + return x; +} + +template <typename T> +concept C1 = requires (T x) { + {foo(x)} -> Same<int&>; +}; + +template <typename T> +concept C2 = requires (T x) { + {foo(x)} -> Same<void>; +}; + +static_assert( C1<int> ); // { dg-error "assert" } +static_assert( C2<int> ); // { dg-error "assert" } diff --git a/gcc/testsuite/g++.dg/concepts/inherit-ctor2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor2.C index cb81d13..aa244bc 100644 --- a/gcc/testsuite/g++.dg/concepts/inherit-ctor2.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor2.C @@ -1,12 +1,11 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } template<typename T> - concept bool C() { return __is_class(T); } + concept C = __is_class(T); template<typename T> struct S1 { - S1(double) requires C<T>() { } + S1(double) requires C<T> { } }; template<typename T> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor4.C b/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor4.C new file mode 100644 index 0000000..75190eb3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor4.C @@ -0,0 +1,18 @@ +// { dg-do compile { target c++2a } } + +template<typename T> + concept C = __is_class(T); + +template<typename T> + struct S1 { + template<C U> S1(U x) { } + }; + +template<typename T> + struct S2 : S1<T> { + using S1<T>::S1; // { dg-error "no matching function" } + }; + +int main() { + S2<int> s(0); // { dg-error "use of deleted function" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor5.C b/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor5.C new file mode 100644 index 0000000..2044ab5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor5.C @@ -0,0 +1,18 @@ +// { dg-do compile { target concepts } } + +template <class T> struct A +{ + constexpr A(T) requires (sizeof(T) > 1) {} + + A(T); +}; + +template <class T> struct B: A<T> +{ + using A<T>::A; +}; + +int main() +{ + constexpr B<int> b = 42; +} diff --git a/gcc/testsuite/g++.dg/concepts/lambda1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda1.C index a77e654..ef19688 100644 --- a/gcc/testsuite/g++.dg/concepts/lambda1.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda1.C @@ -1,6 +1,5 @@ // PR c++/82565 -// { dg-do compile { target c++14 } } -// { dg-additional-options -fconcepts } +// { dg-do compile { target c++2a } } struct string { @@ -9,18 +8,20 @@ struct string bool empty() const; }; +template<class From, class To> +concept convertible_to = requires(From (&f)(), void (&g)(To)) { g(f()); }; + template<typename T, typename ReturnType> -concept bool Concept() { - return requires(T t, const string& s) { - { t(s) } -> ReturnType; +concept Concept = + requires(T t, const string& s) { + { t(s) } -> convertible_to<ReturnType>; }; -} struct test { string _str; template<typename Visitor> - requires Concept<Visitor, bool>() + requires Concept<Visitor, bool> decltype(auto) visit(Visitor&& visitor) const { return visitor(_str); } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-locations1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-locations1.C new file mode 100644 index 0000000..6c81c14 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-locations1.C @@ -0,0 +1,22 @@ +// { dg-do compile { target c++2a } } + +struct S +{ + concept S(); // { dg-error "3:a constructor cannot be .concept." } + // { dg-error "concept definition syntax" "" { target *-*-* } .-1 } + concept int s = 1; // { dg-error "3:non-static data member .s. declared .concept." } + // { dg-error "concept definition syntax" "" { target *-*-* } .-1 } + concept void foo(); // { dg-error "3:a concept cannot be a member function" } + // { dg-error "concept definition syntax" "" { target *-*-* } .-1 } + concept ~S(); // { dg-error "3:a destructor cannot be .concept." } + // { dg-error "concept definition syntax" "" { target *-*-* } .-1 } +}; + +typedef concept int my_int; // { dg-error "9:.concept. cannot appear in a typedef declaration" } +// { dg-error "concept definition syntax" "" { target *-*-* } .-1 } + +void bar(concept int); // { dg-error "10:a parameter cannot be declared .concept." } +// { dg-error "concept definition syntax" "" { target *-*-* } .-1 } + +concept int i = 0; // { dg-error "1:a non-template variable cannot be .concept." } +// { dg-error "concept definition syntax" "" { target *-*-* } .-1 } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-member-concept.C b/gcc/testsuite/g++.dg/cpp2a/concepts-member-concept.C new file mode 100644 index 0000000..f3a2d06 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-member-concept.C @@ -0,0 +1,21 @@ +// { dg-do compile { target c++2a } } + +// FIXME: Diagnostics should be better. + +struct Base { + template<typename T> + static concept D = __is_same_as(T, int); // { dg-error "static data member" } + + template<typename T, typename U> + static concept E = __is_same_as(T, U); // { dg-error "static data member" } + + template<typename T> + concept C1 = __is_same_as(T, int); // { dg-error "not in namespace scope" } +}; + +union U { + template<typename T> + concept C = true; // // { dg-error "not in namespace scope" } +}; + +// { dg-excess-errors "deprecated" } diff --git a/gcc/testsuite/g++.dg/concepts/memfun-err.C b/gcc/testsuite/g++.dg/cpp2a/concepts-memfun-err.C index 6e1ad00..acfa188 100644 --- a/gcc/testsuite/g++.dg/concepts/memfun-err.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-memfun-err.C @@ -1,12 +1,10 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } - +// { dg-do compile { target c++2a } } template<typename T> - concept bool C() { return __is_class(T); } + concept C = __is_class(T); template<typename T> - concept bool D() { return __is_empty(T); } + concept D = __is_empty(T); struct X { } x; struct Y { int n; } y; @@ -16,15 +14,15 @@ int called = 0; // Test constrained member definitions template<typename T> struct S1 { // { dg-message "defined here" } - void f1() requires C<T>() { } - void g1() requires C<T>() and true; + void f1() requires C<T> { } + void g1() requires C<T> and true; template<C U> void h1(U u) { called = 1; } - void g2() requires C<T>(); // { dg-message "candidate" } + void g2() requires C<T>; // { dg-message "candidate" } }; template<typename T> - void S1<T>::g2() requires D<T>() { } // { dg-error "no declaration matches" } + void S1<T>::g2() requires D<T> { } // { dg-error "no declaration matches" } int main() { S1<X> sx; diff --git a/gcc/testsuite/g++.dg/concepts/memfun.C b/gcc/testsuite/g++.dg/cpp2a/concepts-memfun.C index 818c7e6..f6ad519 100644 --- a/gcc/testsuite/g++.dg/concepts/memfun.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-memfun.C @@ -1,13 +1,12 @@ -// { dg-do run { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do run { target c++2a } } #include <cassert> template<typename T> - concept bool C() { return __is_class(T); } + concept C = __is_class(T); template<typename T> - concept bool D() { return __is_empty(T); } + concept D = __is_empty(T); struct X { } x; struct Y { int n; } y; @@ -17,32 +16,32 @@ int called = 0; // Test constrained member definitions template<typename T> struct S1 { - void f1() requires C<T>() { } + void f1() requires C<T> { } - void f2() requires C<T>() { called = 1; } - void f2() requires not C<T>() { called = 2; } + void f2() requires C<T> { called = 1; } + void f2() requires (not C<T>) { called = 2; } void f3() { called = 1; } - void f3() requires C<T>() { called = 2; } - void f3() requires C<T>() and D<T>() { called = 3; } + void f3() requires C<T> { called = 2; } + void f3() requires C<T> and D<T> { called = 3; } - void g1() requires C<T>() and true; + void g1() requires C<T> and true; - void g2() requires C<T>(); - void g2() requires not C<T>(); + void g2() requires C<T>; + void g2() requires (not C<T>); void g3(); - void g3() requires C<T>(); - void g3() requires C<T>() and D<T>(); + void g3() requires C<T>; + void g3() requires C<T> and D<T>; template<C U> void h1(U u) { called = 1; } template<C U> void h2(U u); - template<C U> void h3(U u) requires D<U>(); + template<C U> void h3(U u) requires D<U>; }; template<C T> struct S2 { - void f(T) requires D<T>(); + void f(T) requires D<T>; }; @@ -83,22 +82,22 @@ int main() { } template<typename T> - void S1<T>::g1() requires C<T>() and true { } + void S1<T>::g1() requires C<T> and true { } template<typename T> - void S1<T>::g2() requires C<T>() { called = 1; } + void S1<T>::g2() requires C<T> { called = 1; } template<typename T> - void S1<T>::g2() requires not C<T>() { called = 2; } + void S1<T>::g2() requires (not C<T>) { called = 2; } template<typename T> void S1<T>::g3() { called = 1; } template<typename T> - void S1<T>::g3() requires C<T>() { called = 2; } + void S1<T>::g3() requires C<T> { called = 2; } template<typename T> - void S1<T>::g3() requires C<T>() and D<T>() { called = 3; } + void S1<T>::g3() requires C<T> and D<T> { called = 3; } template<typename T> template<C U> @@ -106,7 +105,7 @@ template<typename T> template<typename T> template<C U> - void S1<T>::h3(U u) requires D<U>() { called = 3; } + void S1<T>::h3(U u) requires D<U> { called = 3; } template<C T> - void S2<T>::f(T t) requires D<T>() { called = 4; } + void S2<T>::f(T t) requires D<T> { called = 4; } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-memtmpl1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-memtmpl1.C new file mode 100644 index 0000000..ee28d5b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-memtmpl1.C @@ -0,0 +1,17 @@ +// { dg-do compile { target c++2a } } + +template <class T> +struct A { + template <class U> + requires (sizeof(T) == 1) + static void f(U); + + template <class U> + requires (sizeof(T) == 2) + static void f(U); + + void g() + { + f(42); + } +}; diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-memtmpl2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-memtmpl2.C new file mode 100644 index 0000000..f60b220 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-memtmpl2.C @@ -0,0 +1,15 @@ +// { dg-do compile { target concepts } } + +template <class T, class U> concept NotSame = !__is_same_as (T, U); + +template <class T, class X> +struct A +{ + template <NotSame<A> U> void f(U) { } + template <class U> void f(U); +}; + +int main() +{ + A<int,int>().f<char>(0); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-nested1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-nested1.C new file mode 100644 index 0000000..8fc965f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-nested1.C @@ -0,0 +1,13 @@ +// { dg-do compile { target concepts } } + +namespace N { template <class T> concept True = true; } +template <class T> struct A { }; + +template <class T> +requires N::True<T> && requires { typename A<T>; } +void f(); + +int main() +{ + f<int>(); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-noexcept1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-noexcept1.C new file mode 100644 index 0000000..e5a9b72 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-noexcept1.C @@ -0,0 +1,25 @@ +// { dg-do compile { target c++2a } } + +void f1(int); +void f2(int) noexcept; + +template<typename T> +concept C1 = requires (T t) { // { dg-message "in requirements" } + { f1(t) } noexcept; +}; + +template<typename T> +concept C2 = requires (T t) { + { f2(t) } noexcept; +}; + +template<C1 T> +void g1(T t); + +template<C2 T> +void g2(T t); + +void test() { + g1(0); // { dg-error "cannot call" } + g2(0); +}
\ No newline at end of file diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-p1141.C b/gcc/testsuite/g++.dg/cpp2a/concepts-p1141.C new file mode 100644 index 0000000..deb409a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-p1141.C @@ -0,0 +1,98 @@ +// { dg-do compile } +// { dg-options "-std=c++2a" } + +template<typename T> +concept Type = true; + +template<typename T> +concept Bottom = false; + +template<typename T> +concept Class = __is_class(T); + +template<auto N> +concept Number = true; + +template<template<typename> class T> +concept Template = true; + +struct empty { }; + +Type x1 = 0; // { dg-error "expected 'auto'" } +Type auto x2 = 0; + +Number x3 = 0; // { dg-error "does not constrain a type" } +Template x4 = 0; // { dg-error "does not constrain a type" } + +Type const& x5 = 0; // { dg-error "expected 'auto'" } +const Type& x6 = 0; // { dg-error "expected 'auto'" } +Type auto const& x7 = 0; +const Type auto& x8 = 0; +Type const auto& x9 = 0; // { dg-error "expected 'auto'|two or more data types" } + +template<Type T> // OK: T is a type parameter. +void f1(T); + +template<Number N> // { dg-error "does not constrain a type" } +void f2(); + +template<Template X> // { dg-error "does not constrain a type" } +void f3(); + +template<Type auto N> // OK: N is a non-type parameter. +void f4() { } + +template<Bottom auto N> // OK: but never usable. +void f5(); + +void driver() { + f4<0>(); + f5<0>(); // { dg-error "no matching function for call | constraints not satisfied" } +} + +Type f6() { return 0; } // { dg-error "expected 'auto'" } +static_assert(__is_same_as(decltype(f6), int())); + +Type auto f7() { return 0; } +static_assert(__is_same_as(decltype(f7), int())); + +Type f8() { return 0; } // { dg-error "expected 'auto'" } +auto f9() -> Type { return 0; } // { dg-error "expected 'auto'" } + +Type f10() { } // { dg-error "expected 'auto'" } +auto f11() -> Type { } // { dg-error "expected" } + +Number f12(); // { dg-error "does not constrain a type" } +auto f13() -> Number; // { dg-error "does not constrain a type" } + +Template f14(); // { dg-error "does not constrain a type" } +auto f15() -> Template; // { dg-error "does not constrain a type" } + +Type f16() { return 0; } // { dg-error "expected 'auto'" } +auto f17() -> Type { return 0; } // { dg-error "expected 'auto'" } + +// Abbreviated function templates + +void f18(Class x) { } // { dg-error "expected 'auto'" } +void f19(Class auto x) { } +void f20(Class auto x, Class auto y) { } + +void driver_1() +{ + f19(0); // { dg-error "cannot call function" } + f19(empty{}); + f20(0, empty{}); // { dg-error "cannot call function" } + f20(empty{}, empty{}); +} + +// Abbreviated function redeclarations + +// Functionally equivalent but not equivalent. +void f21(Class auto x); +template<Class T> void f21(T x); + +void driver_2() +{ + f21(empty{}); // { dg-error "call of overload | ambiguous" } +} + diff --git a/gcc/testsuite/g++.dg/concepts/partial-spec.C b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec.C index 0ff8ec2..25b4e2a 100644 --- a/gcc/testsuite/g++.dg/concepts/partial-spec.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec.C @@ -1,4 +1,4 @@ -// { dg-do compile { target c++17 } } +// { dg-do compile { target c++17_only } } // { dg-options "-fconcepts" } // Check that constraints don't break unconstrained partial diff --git a/gcc/testsuite/g++.dg/concepts/partial-spec2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec2.C index 2449c37..566d8dd 100644 --- a/gcc/testsuite/g++.dg/concepts/partial-spec2.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec2.C @@ -1,6 +1,11 @@ // PR c++/67084 // { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-additional-options "-fconcepts" } + +template <class T> +concept True = true; +template <class T> +concept False = false; template <class T> constexpr bool p = false; @@ -9,11 +14,11 @@ template <class T> constexpr bool p<T*> = false; template <class T> - requires true + requires True<T> constexpr bool p<T*> = false; template <class T> - requires true && T() == 0 + requires True<T> && (T() == 0) constexpr bool p<T*> = true; template <class T> @@ -23,11 +28,11 @@ template <class T> constexpr bool q<T*> = true; template <class T> - requires false + requires False<T> constexpr bool q<T*> = false; template <class T> - requires false && T() != 0 + requires False<T> && (T() != 0) constexpr bool q<T*> = false; static_assert (p<int*>,""); diff --git a/gcc/testsuite/g++.dg/concepts/partial-spec3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec3.C index d407181..2970af1 100644 --- a/gcc/testsuite/g++.dg/concepts/partial-spec3.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec3.C @@ -1,5 +1,5 @@ // { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-additional-options "-fconcepts" } template <class T> struct A { }; template <class T> requires false struct A<T*> { }; diff --git a/gcc/testsuite/g++.dg/concepts/partial-spec4.C b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec4.C index 70461b4..5ba3ab1 100644 --- a/gcc/testsuite/g++.dg/concepts/partial-spec4.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec4.C @@ -1,7 +1,6 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } -template <class T> concept bool is_int = __is_same_as(T,int); +template <class T> concept is_int = __is_same_as(T,int); template <class T> struct A { }; template <is_int T> struct A<T*> { diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec5.C b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec5.C new file mode 100644 index 0000000..f33f7496 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec5.C @@ -0,0 +1,12 @@ +// PR c++/67138 +// { dg-do compile { target c++2a } } + +template <class T> +concept Auto = true; + +template <Auto T> +struct test {}; + +template <Auto T> + requires requires (T t) { t + t; } +struct test<T> {}; diff --git a/gcc/testsuite/g++.dg/concepts/partial-spec6.C b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec6.C index 1d80ee3..2bb4bac 100644 --- a/gcc/testsuite/g++.dg/concepts/partial-spec6.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec6.C @@ -1,9 +1,8 @@ // PR c++/67152 -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } template <class T> -concept bool HasType = requires { typename T::type; }; +concept HasType = requires { typename T::type; }; template<class T> struct trait { @@ -19,7 +18,7 @@ trait<has_type>::type foo() {} // constraint so this partial specialization would not have been // selected. template<class T> - requires !HasType<T> + requires (!HasType<T>) struct trait<T> { using type = void; }; diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder1.C new file mode 100644 index 0000000..cbea81d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder1.C @@ -0,0 +1,14 @@ +// { dg-do compile { target c++2a } } + +template<class T, class U> +concept Same = __is_same_as(T, U); + +template<typename T> +concept C1 = true; + +template<typename T, typename U> +concept C2 = true; + +C1 auto c1 = 0; +C2<int> auto c2 = 0; +Same<int> auto s1 = 'a'; // { dg-error "does not satisfy|is_same" } diff --git a/gcc/testsuite/g++.dg/concepts/pr58500.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr58500.C index cb74072..cb74072 100644 --- a/gcc/testsuite/g++.dg/concepts/pr58500.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr58500.C diff --git a/gcc/testsuite/g++.dg/concepts/pr58534.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr58534.C index b2c3caa..b2c3caa 100644 --- a/gcc/testsuite/g++.dg/concepts/pr58534.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr58534.C diff --git a/gcc/testsuite/g++.dg/concepts/pr58535.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr58535.C index 3e212a0..3e212a0 100644 --- a/gcc/testsuite/g++.dg/concepts/pr58535.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr58535.C diff --git a/gcc/testsuite/g++.dg/concepts/pr58536.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr58536.C index 99c7ebe..99c7ebe 100644 --- a/gcc/testsuite/g++.dg/concepts/pr58536.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr58536.C diff --git a/gcc/testsuite/g++.dg/concepts/pr58548.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr58548.C index cd3e6fd..cd3e6fd 100644 --- a/gcc/testsuite/g++.dg/concepts/pr58548.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr58548.C diff --git a/gcc/testsuite/g++.dg/concepts/pr58549.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr58549.C index 6b66afc..6b66afc 100644 --- a/gcc/testsuite/g++.dg/concepts/pr58549.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr58549.C diff --git a/gcc/testsuite/g++.dg/concepts/regress/alias-decl-42.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr59200.C index 6d5a4d5..995672c 100644 --- a/gcc/testsuite/g++.dg/concepts/regress/alias-decl-42.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr59200.C @@ -1,5 +1,6 @@ // PR c++/59200 -// { dg-options "-std=c++17 -fconcepts" } +// { dg-do compile { target c++17 } } +// { dg-additional-options "-fconcepts" } struct A { diff --git a/gcc/testsuite/g++.dg/concepts/pr60052.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr60052.C index c5bc28e..c5bc28e 100644 --- a/gcc/testsuite/g++.dg/concepts/pr60052.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr60052.C diff --git a/gcc/testsuite/g++.dg/concepts/pr60053.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr60053.C index 20cf121..20cf121 100644 --- a/gcc/testsuite/g++.dg/concepts/pr60053.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr60053.C diff --git a/gcc/testsuite/g++.dg/concepts/pr60064.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr60064.C index b82b560..b82b560 100644 --- a/gcc/testsuite/g++.dg/concepts/pr60064.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr60064.C diff --git a/gcc/testsuite/g++.dg/concepts/pr60065.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr60065.C index b2b1aea..b2b1aea 100644 --- a/gcc/testsuite/g++.dg/concepts/pr60065.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr60065.C diff --git a/gcc/testsuite/g++.dg/concepts/pr60377.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr60377.C index 5b18717..5b18717 100644 --- a/gcc/testsuite/g++.dg/concepts/pr60377.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr60377.C diff --git a/gcc/testsuite/g++.dg/concepts/pr60390.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr60390.C index a453d1d..a453d1d 100644 --- a/gcc/testsuite/g++.dg/concepts/pr60390.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr60390.C diff --git a/gcc/testsuite/g++.dg/concepts/pr60391.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr60391.C index 5c5ca2c..5c5ca2c 100644 --- a/gcc/testsuite/g++.dg/concepts/pr60391.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr60391.C diff --git a/gcc/testsuite/g++.dg/concepts/pr60573.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr60573.C index 42cda21..42cda21 100644 --- a/gcc/testsuite/g++.dg/concepts/pr60573.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr60573.C diff --git a/gcc/testsuite/g++.dg/concepts/pr65552.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr65552.C index 318fdcc..5af2a40 100644 --- a/gcc/testsuite/g++.dg/concepts/pr65552.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr65552.C @@ -1,13 +1,11 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } template<typename T> -concept bool Concept() { - return requires () { +concept Concept = + requires () { typename T::member_type1; typename T::member_type2; }; -} struct model { using member_type1 = int; diff --git a/gcc/testsuite/g++.dg/concepts/pr65575.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr65575.C index efaf958..3bfde5e 100644 --- a/gcc/testsuite/g++.dg/concepts/pr65575.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr65575.C @@ -1,5 +1,6 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// PR c++/65575 +// { dg-do compile { target c++17_only } } +// { dg-additional-options "-fconcepts" } template<typename T> concept bool C = false; @@ -11,12 +12,10 @@ auto f4() -> int& requires false; auto f5() -> int* requires false; auto f6() -> int requires false; -int (*p)() requires true; // { dg-error "" } -int (&p)() requires true; // { dg-error "" } +int (*p1)() requires true; // { dg-error "" } +int (&p2)() requires true; // { dg-error "" } int g(int (*)() requires true); // { dg-error "" } -int f() { return 0; } - int main() { diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr65634.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr65634.C new file mode 100644 index 0000000..52f24ec --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr65634.C @@ -0,0 +1,18 @@ +// { dg-do compile { target c++2a } } + +template<typename T> +concept C1 = + requires () { + { T::smf() } noexcept; + }; + +struct M1 { + static void smf() noexcept; +}; +template<typename T> +concept C2 = C1<typename T::type>; + +struct M2 { + using type = M1; +}; +static_assert(C2<M2>, ""); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr65636.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr65636.C new file mode 100644 index 0000000..b99a343 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr65636.C @@ -0,0 +1,8 @@ +// { dg-do compile { target c++2a } } + +using TD = int; + +template<typename T> +concept C = requires () { typename TD; }; + +static_assert(C<int>, ""); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr65848.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr65848.C new file mode 100644 index 0000000..b246d25 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr65848.C @@ -0,0 +1,59 @@ +// { dg-do compile { target c++2a } } + +// Performance test... This should be fast. + +#include <type_traits> + +template<typename T> +concept Destructible = std::is_destructible<T>::value; +template<typename T, typename... Args> +concept Constructible = Destructible<T> && std::is_constructible<T, Args...>::value; +template<typename T> +concept Move_constructible = Constructible<T, T&&>; +template<typename T> +concept Copy_constructible = Move_constructible<T> && Constructible<T, const T&>; +template<typename T, typename U> +concept Assignable = std::is_assignable<T, U>::value; +template<typename T> +concept Move_assignable = Assignable<T&, T&&>; +template<typename T> +concept Copy_assignable = Move_assignable<T> && Assignable<T&, const T&>; +template<typename T> +concept Copyable = Copy_constructible<T> && Copy_assignable<T>; + +template<typename T> +concept C1 = Copyable<T>; +template<typename T> +concept C2 = C1<T>; +template<typename T> +concept C3 = C2<T>; +template<typename T> +concept C4 = C3<T>; +template<typename T> +concept C5 = C4<T>; +template<typename T> +concept C6 = C5<T>; +template<typename T> +concept C7 = C6<T>; +template<typename T> +concept C8 = C7<T>; +template<typename T> +concept C9 = C8<T>; +template<typename T> +concept C10 = C9<T>; +template<typename T> +concept C11 = C10<T>; + +struct S1 {}; +struct S2 {}; +struct S3 {}; +struct S4 {}; +struct S5 {}; +struct S6 {}; + +static_assert(C11<S1>, ""); +static_assert(C11<S2>, ""); +static_assert(C11<S3>, ""); +static_assert(C11<S4>, ""); +static_assert(C11<S5>, ""); +static_assert(C11<S6>, ""); diff --git a/gcc/testsuite/g++.dg/concepts/pr65854.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr65854.C index 28eac88..a21073c 100644 --- a/gcc/testsuite/g++.dg/concepts/pr65854.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr65854.C @@ -1,5 +1,5 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// PR c++/65854 +// { dg-do compile { target c++2a } } // Handle alias templates in type requirements. @@ -13,13 +13,10 @@ template<typename T1, typename T2> using Alias1 = typename BTT<T1, T2>::type; template<typename T1, typename T2> -concept bool C() -{ - return requires() { typename Alias1<T1, T2>; }; -} +concept C = requires() { typename Alias1<T1, T2>; }; // { dg-message "in requirements" } template<typename T1, typename T2> - requires C<T1, T2>() + requires C<T1, T2> int f(); auto i = f<char, int>(); // { dg-error "cannot call function" } diff --git a/gcc/testsuite/g++.dg/concepts/pr66091.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr66091.C index a71cd7b..ea51e31 100644 --- a/gcc/testsuite/g++.dg/concepts/pr66091.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr66091.C @@ -1,5 +1,6 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// PR c++/66091 +// { dg-do compile { target c++17_only } } +// { dg-additional-options "-fconcepts" } template<typename T> concept bool C1() diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr66844.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr66844.C new file mode 100644 index 0000000..64baab1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr66844.C @@ -0,0 +1,16 @@ +// PR c++/66844 +// { dg-do compile { target c++2a } } + +template <class T, class U> +concept Same = __is_same_as(T, U); + +template <class T> +concept C = requires (T t) { // { dg-error "invalid parameter|in requirements" } + requires Same<decltype(t),void>; + }; + +template <typename T> + requires C<T> +constexpr bool is_c() { return true; } + +static_assert(is_c<void>(), ""); // { dg-error "cannot call" } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr66962.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr66962.C new file mode 100644 index 0000000..ca57a09 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr66962.C @@ -0,0 +1,80 @@ +// PR c++/66962 +// { dg-do compile { target c++2a } } + +template <typename> struct remove_cv; +template <typename> struct is_reference; +template <typename> void declval(); +template <typename> struct is_constructible; +template <typename> struct is_nothrow_constructible; +template <typename _Tp> using remove_cv_t = typename remove_cv<_Tp>::type; +template <typename> struct Trans_NS_extension_apply_list; +template <typename T> using _t = typename T::type; +template <class> void ImplicitlyConvertibleTo(); +template <class> void Assignable(); +template <class T, class... Args> int ConstructibleObject = requires { T{}; }; + +template <class T, class... Args> +concept BindableReference = + is_reference<T>::value && is_constructible<T>::value; + +template <class T, class... Args> +concept Constructible = + ConstructibleObject<T> || BindableReference<T, Args...>; + +template <class T> +concept DefaultConstructible = + Constructible<T> && requires { new T[0]; }; + +template <class T> +concept MoveConstructible = + Constructible<T> && ImplicitlyConvertibleTo<T>; + +template <class T> +concept Movable = + MoveConstructible<T> && Assignable<T &&>; + +template <class, class> +int Swappable_ = requires { 0; }; + +template <class T, class U> +int Swappable(); + +template <class T> +concept Dereferencable = requires{{0};}; + +template <Dereferencable R> +using RvalueReferenceType = decltype(0); + +template <class T> +int IsValueType; + +template <class> +struct value_type; + +template <class T> + requires IsValueType<_t<value_type<remove_cv_t<T>>>> +using ValueType = _t<value_type<remove_cv_t<T>>>; + +template <class I> +concept Readable = + Movable<I> && DefaultConstructible<I> && Dereferencable<const I> && requires{{0};}; + +template <class Out, class T> +concept MoveWritable = + Movable<Out> && DefaultConstructible<Out> && Dereferencable<Out>; + +template <class In, class Out> +concept IndirectlyMovable = + Readable<In> && + Movable<ValueType<In>> && + Constructible<ValueType<In>> && + MoveWritable<Out, RvalueReferenceType<In>> && + MoveWritable<Out, ValueType<In>>; + +template<typename In, typename Out> + requires IndirectlyMovable<In, Out> +int is_nothrow_indirectly_movable_v = is_nothrow_constructible<ValueType<In>>::value; + +template <Readable R1, Readable R2> + requires IndirectlyMovable<R1, R2> && IndirectlyMovable<R2, R1> +void iter_swap2(); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67070.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67070.C new file mode 100644 index 0000000..548cb40 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67070.C @@ -0,0 +1,51 @@ +// { dg-do compile { target c++2a } } + +template <class T> +concept C1 = + requires { typename T::type; } && T::type::value; + +template <class T> +concept C2 = + requires { + typename T::Type; + requires T::Type::value; + }; + +template <class T> + requires (!C1<T>) +void f1() { } + +template <class T> + requires (!C2<T>) +void f2() { } + +struct S { }; + +void test() +{ + f1<S>(); + f2<S>(); +} + +// ------------------ + + +template<typename T> +concept C = requires (T t) { t.f(); }; + +template<typename A, typename B> + requires (!(C<A> && C<B>)) +void g1() { } + +template<typename A, typename B> + requires (!C<A> || !C<B>) +void g2() { } + +struct X { + void f(); +}; + +void test2() { + g1<X, X>(); // { dg-error "cannot call" } + g2<X, X>(); // { dg-error "cannot call" } +}
\ No newline at end of file diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67147.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67147.C new file mode 100644 index 0000000..db8c37e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67147.C @@ -0,0 +1,25 @@ +// PR c++/67147 +// { dg-do compile { target c++2a } } + +template <class F, class I1, class I2 = I1> +concept IndirectCallableRelation = true; + +template <class, class, class = void> +constexpr bool indirect_relation() { return false; } + +// FIXME: The original bug was found using the introducer syntax. + +template<typename F, typename I1> + requires IndirectCallableRelation<F, I1> +constexpr bool indirect_relation() { return true; } + +template<typename F, typename I1, typename I2> + requires IndirectCallableRelation<F, I1, I2> +constexpr bool indirect_relation() { return true; } + +// This was added to the discussion thread as a minimum repro. + +template<typename T, int = sizeof(T)> +concept C1 = true; + +template <C1 T> int test(); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67148.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67148.C new file mode 100644 index 0000000..c593996 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67148.C @@ -0,0 +1,121 @@ +// PR c++/67148 +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +namespace std +{ + template<typename T> + T declval(); + + typedef unsigned int size_t; + typedef int ptrdiff_t; + typedef decltype(nullptr) nullptr_t; + template<typename _Tp, _Tp... _Idx> + struct integer_sequence + { + typedef _Tp value_type; + static constexpr size_t size() { return sizeof...(_Idx); } + }; + + template <class T, T Value> + struct integral_constant { + using type = integral_constant; + using value_type = T; + constexpr operator T() const { return Value; } + constexpr T operator()() const { return Value; } + static constexpr T value {Value}; + }; + template <class T, T Value> + constexpr T integral_constant<T, Value>::value; + using true_type = integral_constant<bool, true>; + using false_type = integral_constant<bool, false>; + + template <class T, class U> + struct is_same : false_type {}; + template <class T> + struct is_same<T,T> : true_type {}; +} + +namespace meta +{ + inline namespace v1 + { + template <typename T> + using _t = typename T::type; + template <bool... Bools> + using and_c = std::is_same<std::integer_sequence<bool, Bools...>, + std::integer_sequence<bool, (Bools || true)...>>; + } +} + +namespace stl2 { inline namespace v1 { +using std::declval; +namespace detail { +template <class...> +struct all_same : std::true_type {}; +template <class T, class...Rest> +struct all_same<T, Rest...> : + meta::and_c<__is_same_as(T, Rest)...> {}; +} +template <class...Ts> +concept bool Same() { + return detail::all_same<Ts...>::value; +} +template <class F, class...Args> +using ResultType = decltype(declval<F>()(declval<Args>()...)); +template <class> +struct value_type {}; +template <class T> +struct value_type<T*> { + using type = T; +}; +template <class T> +using ValueType = + typename value_type<T>::type; + +template <class F, class...Args> +concept bool Function() { + return requires (F& f, Args&&...args) { + f((Args&&)args...); + requires Same<decltype(f((Args&&)args...)), ResultType<F, Args...> >(); + }; +} + +template <class, class...> struct __function : std::false_type {}; +Function{F, ...Args} struct __function<F, Args...> : std::true_type {}; + +template <class F, class I1, class I2> +concept bool IndirectCallable() { + return Function<F, ValueType<I1>, ValueType<I2>>(); +} + +template <class F, class I1, class I2> +concept bool IndirectCallable2() { + return __function<F, ValueType<I1>, ValueType<I2>>::value; +} + +namespace ext { namespace models { +template <class, class, class> +constexpr bool indirect_callable() { return false; } +IndirectCallable{F, I1, I2} +constexpr bool indirect_callable() { return true; } + +template <class, class, class> +constexpr bool indirect_callable2() { return false; } +IndirectCallable2{F, I1, I2} +constexpr bool indirect_callable2() { return true; } +}} +}} + +namespace models = stl2::ext::models; + +template <class T = void> +struct plus { + T operator()(T, T) const; +}; + +static_assert((models::indirect_callable<::plus<int>, int*, int*>())); +static_assert((models::indirect_callable2<::plus<int>, int*, int*>())); + +static_assert((models::indirect_callable<::plus<int>, int**, int*>())); // { dg-error "static assertion failed" } +static_assert((models::indirect_callable2<::plus<int>, int**, int*>())); // { dg-error "static assertion failed" } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67178.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67178.C new file mode 100644 index 0000000..f76f2e3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67178.C @@ -0,0 +1,26 @@ +// PR c++/67178 +// { dg-do compile { target c++2a } } + +template<typename T> +concept c = true; + +template<typename T> +concept C0 = requires (auto x) { // { dg-error "placeholder type" } + x; +}; + +template<typename T> +concept C1 = requires (C1 auto x) { // { dg-error "not been declared|placeholder|two or more|in requirements" } + x; // { dg-error "not declared" } + { x } -> c; // { dg-error "not declared|does not satisfy" } +}; + +template<typename T> + requires C1<T> +void f(T) {} + +int main() { + f(1); // { dg-error "cannot call" } +} + + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67210.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67210.C new file mode 100644 index 0000000..044e677 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67210.C @@ -0,0 +1,10 @@ +// PR c++/67210 +// { dg-do compile { target c++2a } } + +template <class T, class U> +concept C = true; + +template <class T> +struct A {}; + +void f(A<C<int> auto >) {} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67217.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67217.C new file mode 100644 index 0000000..35618ae --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67217.C @@ -0,0 +1,8 @@ +// PR c++/67217 +// { dg-do compile { target c++2a } } + +template <class T> + requires __is_same_as(T, double) +union A {}; + +int main() { A<int>{}; } // { dg-error "template constraint failure" } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-1.C new file mode 100644 index 0000000..1c5d73a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-1.C @@ -0,0 +1,32 @@ +// PR c++/67225 +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template <class T, class U> +concept bool Same() +{ + return true; +} + +template <class T> struct WrapT {T t;}; + +template <class T> +concept bool Destructible() +{ + return requires(T t, const T ct, WrapT<T>& wt) // { dg-message "in requirements" } + { + {wt.~WrapT()} noexcept; + // {&t} -> Same<T*>; // #1 + //{&t} -> T*; // #2 + }; +} + +template <Destructible T> +void f() {} + +struct Y {private: ~Y();}; + +int main() +{ + f<Y>(); // { dg-error "cannot call" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-2.C new file mode 100644 index 0000000..6218176 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-2.C @@ -0,0 +1,36 @@ +// PR c++/67225 +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template<typename Target> +// template<typename Target, typename... Ts> +concept bool has_resize () +{ + return requires (Target tgt) + { + { tgt.resize () }; + }; +} + +template<typename Target> +void resize (Target tgt) +{ + if constexpr (has_resize<Target> ()) + { + tgt.resize (); + } +} + +class MyClass +{ + private: + int foo (int i) + { + return i * 2; + } +}; + +int main () +{ + return MyClass {}.foo (7); // { dg-error "private within this context" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-3.C new file mode 100644 index 0000000..d08efb6 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-3.C @@ -0,0 +1,21 @@ +// PR c++/67225 +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template <class> +concept bool Dummy = true; + +template <typename> +class example { + template <Dummy U> + friend auto func(); +}; + +class test { + test() = default; +}; + +int main() +{ + test t; // { dg-error "private within this context" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-4.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-4.C new file mode 100644 index 0000000..0ef5ed8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-4.C @@ -0,0 +1,14 @@ +// PR c++/67225 +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template <class, class> +concept bool C1 = true; + +template <class> +concept bool C2 = requires { { 42 } -> C1<int>; }; + +int main() { + class A { int x; } a; + a.x = 42; // { dg-error "private within this context" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-5.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-5.C new file mode 100644 index 0000000..9a25831f4 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67225-5.C @@ -0,0 +1,17 @@ +// PR c++/67225 +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template<typename A, typename T> +concept bool SomeConcept = true; + +template <typename T> +void breaker(SomeConcept<int>); + +class SomeClass { + int privateMember; +}; + +int main() { + return SomeClass().privateMember; // { dg-error "private within this context" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67319.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67319.C new file mode 100644 index 0000000..6eb1c44 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67319.C @@ -0,0 +1,24 @@ +// PR c++/67319 +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template <typename T> +concept bool Any() +{ + return requires (T t) { +t; }; +} + +struct my_struct +{ + template <Any... Args> + auto sample(Args... args) -> void; +}; + +int main() +{ + my_struct{}.sample(); + my_struct{}.sample(0); + my_struct{}.sample(0, 'a'); + my_struct{}.sample(nullptr); // { dg-error "" } +} + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67427.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67427.C new file mode 100644 index 0000000..fcad301 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67427.C @@ -0,0 +1,22 @@ +// PR c++/67427 +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template <class S, class I> +concept bool Sentinel = + requires (I i) { i; }; + +template <class I, class S> +concept bool SizedIteratorRange = + Sentinel<S, I> && true; + +Sentinel{S, I} +void distance(I first, S last) {} + +template <class I, class S> + requires SizedIteratorRange<I, S> +void distance(I first, S last) {} + +int main() { + distance(42, 43); // { dg-error "ambiguous" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67654.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67654.C new file mode 100644 index 0000000..27ee205 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67654.C @@ -0,0 +1,30 @@ +// PR c++/67427 +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template <bool... Values> struct and_c_impl { + static constexpr bool value = true; +}; + +template <bool ValueFirst, bool... ValuesRest> +struct and_c_impl<ValueFirst, ValuesRest...> { + static constexpr bool value = ValueFirst && and_c_impl<ValuesRest...>::value; +}; + +template <bool... Values> constexpr bool and_c() { + return and_c_impl<Values...>::value; +} + +template<class T> concept bool C() { + return true; +} + +template<class... Tx> +struct A { + A() requires and_c<C<Tx>()...>() = default; +}; + +int main() { + A<int, double> a; + return 0; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67658.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67658.C new file mode 100644 index 0000000..087f4fc --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67658.C @@ -0,0 +1,14 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template<class T> concept bool C1() { return false; } +template<C1 T> concept bool C2() { return true; } // { dg-error "cannot be constrained" } + +void f(C2 x) { +} + +struct A {} a; + +int main() { + f(a); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67684.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67684.C new file mode 100644 index 0000000..35e9295 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67684.C @@ -0,0 +1,63 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template<class T> +class A { + public: + template<int I, class S> + requires I > 0 + friend int f1(const A<S>&); + + template<int I, class S> + friend int f2(const A<S>&) requires I > 0; + + private: + int x = 2; +}; + +template<int I, class S> + requires I > 0 +int f1(const A<S>& a) { + return a.x; +} + +template<int I, class S> +int f2(const A<S>& a) requires I > 0 { + return a.x; +} + +class B { + public: + template<int I> + requires I > 0 + friend int f3(const B&); + + template<int I> + friend int f4(const B&) requires I > 0; + + private: + int x = 2; +}; + +template<int I> + requires I > 0 +int f3(const B& a) { + return a.x; +} + +template<int I> +int f4(const B& a) requires I > 0 { + return a.x; +} + +int main() { + A<double> a; + f1<2>(a); + f2<2>(a); + + B b; + f3<2>(b); + f4<2>(b); + + return 0; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67685.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67685.C new file mode 100644 index 0000000..82f068e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67685.C @@ -0,0 +1,7 @@ +// PR c++/67685 +// { dg-do compile { target c++14 } } +// { dg-additional-options "-fconcepts" } + +void f(auto i) {requires {i;};} + +int main() {f(0);} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67692.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67692.C new file mode 100644 index 0000000..139ecd6 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67692.C @@ -0,0 +1,13 @@ +// { dg-do compile { target c++2a } } + +template<class T> +bool f(T x) { + return requires(T x) { + ++x; + }; +} + +int main() { + f(3); + return 0; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67697.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67697.C new file mode 100644 index 0000000..44c077b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67697.C @@ -0,0 +1,15 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template<class X> +concept bool C() { + return requires(X x, bool b) { + requires b; // { dg-error "not a constant expression" } + x++; + }; +} + +int main() { + C<int>(); + return 0; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67719.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67719.C new file mode 100644 index 0000000..d28b445 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67719.C @@ -0,0 +1,15 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template<class X> concept bool C() { + return __is_same_as(X, int) || __is_same_as(X, long); +} + +template<C... Tx> +struct Ax {}; + +int main() { + Ax<int, long> a; + Ax<int, long, void> b; // { dg-error "template constraint failure" } + return 0; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67774.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67774.C new file mode 100644 index 0000000..d363c59 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67774.C @@ -0,0 +1,26 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +#include <type_traits> +#include <utility> +#include <iostream> + +template <class X> concept bool cpt_RealScalar() { + return std::is_floating_point<X>::value; +} + +namespace detail { +template <class, class> constexpr bool k_evaluator_impl = false; + +template <std::size_t... Indexes, class E> +constexpr bool k_evaluator_impl<std::index_sequence<Indexes...>, E> = true; +} + +template <class X, std::size_t K> concept bool cpt_KEvaluator = + detail::k_evaluator_impl<std::make_index_sequence<K>, X>; + +int main() { + auto f = [](int, int, int) -> double { return 3; }; + std::cout << cpt_KEvaluator<decltype(f)> << '\n'; // { dg-error "wrong number of template arguments" } + return 0; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67825.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67825.C new file mode 100644 index 0000000..95698e9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67825.C @@ -0,0 +1,20 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +struct A { + template <class T> + double operator()(T x) const { + return 0; + } +}; + +template <class X> concept bool C() { + return requires { + &X::operator(); + }; +} + +int main() { + static_assert(C<A>()); + return 0; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67860.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67860.C new file mode 100644 index 0000000..8bad031 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67860.C @@ -0,0 +1,61 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +#include <type_traits> + +inline constexpr bool and_impl() { return true; } + +template <class OperandFirst, class... OperandsRest> +constexpr bool and_impl(OperandFirst operand_first, + OperandsRest... operands_rest) { + return operand_first && and_impl(operands_rest...); +} + +template <class... Operands> constexpr bool and_(Operands... operands) { + return and_impl(operands...); +} + +template <class X> concept bool C() { return true; } + +// v1 +template<int, class... Xs> + requires and_(C<Xs>()...) +constexpr int f(const Xs&... xs) { + return 0; +} + +// v2 +template<int, class... Xs> +constexpr int f(const Xs&... xs) { + return 1; +} + +int main() { + static_assert(f<10>(3.0, 2.0f) == 0); + return 0; +} + +// 2nd example + +template <typename T, typename... Us> +concept bool AreType() { + return (std::is_same<T,Us>::value && ...); + // return true; gives the same overloaded error +} + +// Function with constraint +template<typename T, AreType<T>... Us> +constexpr bool isValid(Us... us) { + return true; +} + +// Function with no constraint +template<typename T, typename... U> +constexpr bool isValid(U... u) { + return false; +} + +int main2() { + static_assert(isValid<int>(1)); // also isValid<int>(1, 2, 3); etc + return 0; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67862.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67862.C new file mode 100644 index 0000000..222e528 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67862.C @@ -0,0 +1,162 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +typedef int size_t; +template <typename _Tp> struct A { static constexpr _Tp value = 1; }; +template <typename _Tp> _Tp declval(); +template <typename _From, typename _To> struct __is_convertible_helper { + template <typename, typename> static A<bool> __test(int); + typedef decltype(__test<_From, _To>(0)) type; +}; +template <typename, typename> +struct is_convertible : __is_convertible_helper<int, int>::type {}; +template <typename> struct remove_reference; +template <typename _Tp> struct remove_reference<_Tp &> { typedef _Tp type; }; +struct base; +struct general; +template <typename _Tp, _Tp...> struct B; +template <typename _Tp, _Tp> using make_integer_sequence = B<int>; +template <size_t... _Idx> using index_sequence = B<size_t, _Idx...>; +template <size_t _Num> +using make_index_sequence = make_integer_sequence<size_t, _Num>; +template <bool...> struct and_c_impl { static constexpr bool value = true; }; +template <bool...> constexpr bool and_c() { return and_c_impl<>::value; } + +template <class X, class Y> concept bool cpt_Convertible() { + return is_convertible<X, Y>::value; +} + +template <class T> using uncvref_t = typename remove_reference<T>::type; +struct Plus; +using index_t = int; +template <class> bool cpt_Index; +template <class... Extents> +requires and_c<cpt_Index<Extents>()...>() class Dimensionality; +namespace detail_concept { +template <class> bool match_dimensionality; +template <class... Extents> +constexpr bool match_dimensionality<Dimensionality<Extents...>> = true; +} +template <class X> concept bool cpt_Dimensionality() { + return detail_concept::match_dimensionality<X>; +} + +template <class X> concept bool cpt_Shaped() { return requires(X x){{x};}; } + +template <class X> concept bool cpt_Dimensioned() { return cpt_Shaped<X>(); } + +template <class... Extents> +requires and_c<cpt_Index<Extents>()...>() class Dimensionality { +public: + static constexpr size_t num_dimensions = sizeof...(Extents); +}; +template <index_t...> using DimensionalityC = Dimensionality<>; +template <class> struct dimensionality_type_impl; +template <cpt_Dimensioned X> struct dimensionality_type_impl<X> { + using type = uncvref_t<decltype(declval<X>().dimensionality())>; +}; +template <cpt_Dimensioned X> +using dimensionality_type = typename dimensionality_type_impl<X>::type; +template <class Functor, class... Expressibles> +requires requires(Functor functor, Expressibles... expressibles) { + map_expressions_impl(functor, expressibles...); +} + +decltype(auto) map_impl(Functor, Expressibles...); +void cpt_ContinualScalar(); +template <class> concept bool cpt_Scalar() { return cpt_ContinualScalar; } + +template <class X> concept bool cpt_FlatEvaluator() { + return requires(X x){{x}->cpt_Scalar;}; +} + +template <class, class> bool k_evaluator_impl; +template <size_t... Indexes, class Evaluator> +constexpr bool k_evaluator_impl<index_sequence<Indexes...>, Evaluator> = true; +template <class X, size_t K> concept bool cpt_KEvaluator() { + return k_evaluator_impl<make_index_sequence<K>, X>; +} + +template <class X, size_t K> concept bool cpt_KCompatibleEvaluator() { + return cpt_KEvaluator<X, K>(); +} + +template <class X> concept bool cpt_Structure() { + return cpt_Convertible<X, base>(); +} + +template <cpt_Dimensionality Dimensionality, cpt_Structure, + cpt_KCompatibleEvaluator<Dimensionality::num_dimensions> Evaluator> +class NumericArrayExpression; +namespace detail_concept { + +template <class> bool match_numeric_array_expression; + +template <cpt_Dimensionality Dimensionality, + cpt_Structure Structure, + cpt_KCompatibleEvaluator<Dimensionality::num_dimensions> Evaluator> +constexpr bool match_numeric_array_expression< + NumericArrayExpression<Dimensionality, Structure, Evaluator>> = true; + +} +template <class X> concept bool cpt_NumericArrayExpression() { + return detail_concept::match_numeric_array_expression<X>; +} + +namespace expression_traits { +namespace detail_expression_traits { +template <class...> struct first_numeric_array_expression_impl; +template <cpt_NumericArrayExpression ExpressionFirst, class... ExpressionsRest> +struct first_numeric_array_expression_impl<ExpressionFirst, + ExpressionsRest...> { + using type = ExpressionFirst; +}; +} +template <class... Expressions> +using first_numeric_array_expression = + typename detail_expression_traits::first_numeric_array_expression_impl< + Expressions...>::type; +template <class... Expressions> +using first_expression_dimensionality = + dimensionality_type<first_numeric_array_expression<Expressions...>>; +} +template <cpt_Dimensionality Dimensionality, cpt_Structure, + cpt_KCompatibleEvaluator<Dimensionality::num_dimensions> Evaluator> +class NumericArrayExpression { +public: + NumericArrayExpression(Dimensionality, Evaluator) {} + Dimensionality &dimensionality(); +}; + +template <cpt_Structure Structure, cpt_Dimensionality Dimensionality, + cpt_KCompatibleEvaluator<Dimensionality::num_dimensions> Evaluator> +auto make_numeric_array_expression(Dimensionality dimensionality, + Evaluator evaluator) { + return NumericArrayExpression<Dimensionality, Structure, Evaluator>( + dimensionality, evaluator); +} + +template <size_t, class Functor, class... Evaluators> +auto make_map_evaluator_impl(Functor) requires + and_(cpt_FlatEvaluator<Evaluators>()...); +template <class Functor, class... Expressions> +requires +requires(Expressions... expressions, + expression_traits::first_expression_dimensionality<Expressions...> + dimensionality) { + make_map_evaluator_impl<decltype(dimensionality)::num_dimensions>( + expressions...); +} + +decltype(auto) map_expressions_impl(Functor, Expressions...); +template <class Functor, class... Expressibles> concept bool cpt_Mappable() { + return requires(Functor functor, Expressibles... expressibles) { + map_impl(functor, expressibles...); + }; +} + +void ____C_A_T_C_H____T_E_S_T____8() { + auto e1 = make_numeric_array_expression<general>(DimensionalityC<>(), [] {}); + using E1 = decltype(e1); + cpt_Mappable<Plus, E1>(); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr67969.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67969.C new file mode 100644 index 0000000..4f2ab51 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr67969.C @@ -0,0 +1,32 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } +template <class, class> +class NumericArray {}; + +template <class> +constexpr bool match_numeric_array = false; +template <class Scalar, class Shape> +constexpr bool + match_numeric_array<NumericArray<Scalar, Shape>> = + true; +template <class T> +concept bool cpt_NumericArrayContainer() { + return match_numeric_array<T>; +} + +template <class X> +concept bool cpt_NumericArray() { + return requires{requires cpt_NumericArrayContainer<X>();}; +} + + +template <class X> +requires !cpt_NumericArray<X>() auto func(int, X) {} + +template <class X> +requires cpt_NumericArray<X>() auto func(int, X) {} + +int main() { + NumericArray<double, int> v5; + func(0, v5); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr68093-1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr68093-1.C new file mode 100644 index 0000000..bc4ddee --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr68093-1.C @@ -0,0 +1,13 @@ +// { dg-do compile { target c++2a } } +template <typename t> +struct S +{ + template <typename t2> + requires false + friend void foobar(S, t2) {} +}; + +int main() +{ + foobar(S<double>{}, int{}); // { dg-error "cannot call" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr68093-2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr68093-2.C new file mode 100644 index 0000000..91e3c80 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr68093-2.C @@ -0,0 +1,14 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } +template <class T> +concept bool True = true; + +template <class T> +struct S { + friend bool operator==(S, int) requires True<T> { return true; } + friend bool operator==(S, int) requires !True<T> { return true; } +}; + +int main() { + S<int> s; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr68372.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr68372.C new file mode 100644 index 0000000..d1416eb --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr68372.C @@ -0,0 +1,48 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template<typename F> +concept bool FCallable() +{ + return requires(F) + { + F::f(); + }; +} + +class Test1 +{ +public: + template<FCallable P, FCallable... Pp> + static void g() + { + (Pp::f(), ...); + } +}; + +class A +{ +public: + static void f() {} +}; + +template<typename X> concept bool C = true; + +template<C... X> +void bar(X...) +{} + +struct foo +{ + template<C... X> + void bar(X...) + {} +}; + +int main() +{ + Test1::template g<A>(); + bar(); + foo {}.bar(); +} + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr68434.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr68434.C new file mode 100644 index 0000000..23ecf4d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr68434.C @@ -0,0 +1,18 @@ +// { dg-do compile { target c++2a } } + +template <class> +concept C1 = true; + +template <class> +concept C2 = true; + +template <class Expr> +concept C3 = + requires (Expr expr) { + {expr}->C1; + {expr}->C2; + }; + +template<C3 T> +auto f (T); + diff --git a/gcc/testsuite/g++.dg/concepts/pr68683.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr68683.C index ff7709e..f11d69f 100644 --- a/gcc/testsuite/g++.dg/concepts/pr68683.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr68683.C @@ -1,5 +1,5 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// PR c++/68683 +// { dg-do compile { target c++2a } } template <typename, typename> struct is_same { @@ -7,10 +7,10 @@ struct is_same { }; template <typename T, typename U> -concept bool Same = is_same<T, U>::value; +concept Same = is_same<T, U>::value; template <typename T> -concept bool Integral = requires { +concept Integral = requires { { T () } -> Same<typename T::value_type>; }; diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr68812.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr68812.C new file mode 100644 index 0000000..2d809e8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr68812.C @@ -0,0 +1,34 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +namespace zero +{ + template<int... s> + struct S + { + template<int... f> + requires(... and (s == f)) + static void F() + { + } + }; + + void foo(S<>) {} +} + +namespace one +{ + template<typename X, typename Y> concept bool Foo = true; + + template<typename... T> + struct foo + { + template<typename... U> + foo(U...) + requires (Foo<T, U> && ...) + {} + }; + + void bar(foo<int, long, double>) {} +} + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr69235.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr69235.C new file mode 100644 index 0000000..5e96d0e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr69235.C @@ -0,0 +1,48 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template<typename T> +concept bool Boolean() +{ + return requires(T t) + { + { t } -> bool; + }; +} + +template<typename T> +concept bool C() +{ + return requires (T t) + { + { t } -> Boolean; + }; +} + +template<typename T> +struct X; + +template<typename T> + requires ! C<typename T::type>() +struct X<T> +{ + using type = int; +}; + +template<typename T> + requires C<typename T::type>() +struct X<T> +{ + using type = int; +}; + +struct S +{ + using type = char; +}; + +void f() +{ + X<S>::type x; +} + diff --git a/gcc/testsuite/g++.dg/concepts/pr71368.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr71368.C index 5cd2b54..c67e632 100644 --- a/gcc/testsuite/g++.dg/concepts/pr71368.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr71368.C @@ -1,14 +1,16 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// PR c++/71368 +// { dg-do compile { target c++2a } } + +template <class T, class U> concept Same = __is_same_as(T,U); struct inner; -template<typename X> concept bool CompoundReq = requires { +template<typename X> concept CompoundReq = requires { // fine with concrete type in trailing type, i.e. inner& instead of X& - { X::inner_member() } -> X&; + { X::inner_member() } -> Same<X&>; }; -template<typename X> concept bool Concept = requires { +template<typename X> concept Concept = requires { { X::outer_member() } -> CompoundReq; }; diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr71385.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr71385.C new file mode 100644 index 0000000..f31997c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr71385.C @@ -0,0 +1,14 @@ +// { dg-do compile { target c++2a } } + +template<class From, class To> +concept convertible_to = requires(From (&f)(), void (&g)(To)) { g(f()); }; + +template<class T> +concept Addable = + requires(T x){ + {x + x} -> convertible_to<T>; + }; + +int main(){ + Addable auto t = 0; +} diff --git a/gcc/testsuite/g++.dg/concepts/pr71965.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr71965.C index 6bfaef1..4619cf9 100644 --- a/gcc/testsuite/g++.dg/concepts/pr71965.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr71965.C @@ -1,13 +1,10 @@ -// { dg-do compile { target c++14 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } template <class T> -concept bool Destructible() { - return false; -} +concept Destructible = false; template <class T, class...Args> -concept bool ConstructibleObject = +concept ConstructibleObject = // Concept evaluation should short-circuit even the template // substitution, so we shouldn't even substitute into the requires // constraint and the unimplemented multi-dimensional new T{...} @@ -15,7 +12,7 @@ concept bool ConstructibleObject = // sorry() message we used to for such constructs when asked not // to issue errors, this shouldn't be a problem for this and // similar cases. - Destructible<T>() && requires (Args&&...args) { + Destructible<T> && requires (Args&&...args) { new T{ (Args&&)args... }; }; diff --git a/gcc/testsuite/g++.dg/concepts/memfun2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr72415.C index 78a2cf7..04bdc51 100644 --- a/gcc/testsuite/g++.dg/concepts/memfun2.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr72415.C @@ -2,7 +2,7 @@ // { dg-do compile { target c++17 } } // { dg-options "-fconcepts" } -template<int... Indices> +template<int... Xs> struct indices {}; template<typename Dummy> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr78752.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr78752.C new file mode 100644 index 0000000..40eeaa7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr78752.C @@ -0,0 +1,18 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +#include <type_traits> + +template <class T, class U> +concept bool Same = std::is_same<T, U>::value; + +struct test { + template <Same<int>... Ints> + void func(Ints... ints) {} +}; + +int main() +{ + test t; + t.func(1, 2, 3); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr79759.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr79759.C new file mode 100644 index 0000000..a99103c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr79759.C @@ -0,0 +1,7 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template<typename T, T N> +concept bool C0() { return true; } + +void f(C0<0>); diff --git a/gcc/testsuite/g++.dg/concepts/pr80471.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr80471.C index d5fa5a5..d5fa5a5 100644 --- a/gcc/testsuite/g++.dg/concepts/pr80471.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr80471.C diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr80746.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr80746.C new file mode 100644 index 0000000..69e2fbe --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr80746.C @@ -0,0 +1,14 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template<typename T, typename T::type> +concept bool C = true; + +template<C<0> T> class ct {}; + +struct S +{ + using type = int; +}; + +template class ct<S>; diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr80773.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr80773.C new file mode 100644 index 0000000..34b96c5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr80773.C @@ -0,0 +1,33 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template<typename F> +concept bool FCallable() +{ + return requires(F) + { + F::f(); + }; +} + +class Test1 +{ +public: + template<FCallable P, FCallable... Pp> + static void g() + { + (Pp::f(), ...); + } +}; + +class A +{ +public: + static void f() {} +}; + +int main() +{ + Test1::template g<A>(); +} + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr82507.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr82507.C new file mode 100644 index 0000000..f743855 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr82507.C @@ -0,0 +1,17 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template<class I> +concept bool True = true; + +template<class T> +concept bool HasType = requires { typename T::type; }; + +template<class T> +struct S +{ + void foo() requires HasType<T> && True<typename T::type>; +}; + +S<int> s; + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr82740.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr82740.C new file mode 100644 index 0000000..6e0f062 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr82740.C @@ -0,0 +1,32 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template<class T> +concept bool C = requires(const T& t) { t.foo(); }; + +template<class T> +struct Base +{ + constexpr T const& derived() const + { + return static_cast<T const&>(*this); + } + constexpr bool bar() const + requires requires(const T& t) { t.foo(); } + { + derived().foo(); + return true; + } +}; + +template<class T> +struct Derived : Base<Derived<T>> +{ + constexpr void foo() const {} +}; + +int main() +{ + static_assert(Derived<int>{}.bar()); +} + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr84140.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84140.C new file mode 100644 index 0000000..d901ab2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84140.C @@ -0,0 +1,38 @@ +// { dg-do run { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template<class, class> constexpr bool is_same_v = false; +template<class T> constexpr bool is_same_v<T, T> = true; + +template<class T, class U> +concept bool Same = is_same_v<T, U>; + +template<class T, class U> +concept bool Diff = requires(T& t, U& u) { u - t; }; + +template<class I, class S> +int distance(I, S) { return 0; } + +template<class I, Diff<I> S> +int distance(I first, S last) +{ + return last - first; +} + +template<class T> +struct I +{ + template<class U> + requires Same<T, U> + friend int operator-(I const&, I<U> const&) + { + static_assert(Same<T, U>); + return 42; + } +}; + +int main() +{ + return distance(I<int>{}, I<void>{}); +} + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr84551.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84551.C new file mode 100644 index 0000000..e40796f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84551.C @@ -0,0 +1,11 @@ +// PR c++/84551 +// { dg-do compile { target c++2a } } +// { dg-additional-options "-g -O" } + +template<typename> concept C = true; + +template<template<typename T> requires C<T> class TT> struct A {}; + +template<typename U> requires true struct B {}; + +A<B> a; // { dg-error "" } diff --git a/gcc/testsuite/g++.dg/concepts/pr84979-2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84979-2.C index ce69a0f..ce69a0f 100644 --- a/gcc/testsuite/g++.dg/concepts/pr84979-2.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84979-2.C diff --git a/gcc/testsuite/g++.dg/concepts/pr84979-3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84979-3.C index 3809c2d..3809c2d 100644 --- a/gcc/testsuite/g++.dg/concepts/pr84979-3.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84979-3.C diff --git a/gcc/testsuite/g++.dg/concepts/pr84979.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84979.C index 9bd40df..9bd40df 100644 --- a/gcc/testsuite/g++.dg/concepts/pr84979.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84979.C diff --git a/gcc/testsuite/g++.dg/concepts/pr84980.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84980.C index 619c0bd..1703de0 100644 --- a/gcc/testsuite/g++.dg/concepts/pr84980.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84980.C @@ -1,6 +1,4 @@ -// { dg-do compile { target c++14 } } +// { dg-do compile { target c++17_only } } // { dg-additional-options "-fconcepts" } template<T> concept bool C = true; // { dg-error "has not been declared" } - -template<C...> struct A; diff --git a/gcc/testsuite/g++.dg/concepts/pr85265.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr85265.C index 86124ce..96aac69 100644 --- a/gcc/testsuite/g++.dg/concepts/pr85265.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr85265.C @@ -1,4 +1,5 @@ -// { dg-do compile { target c++14 } } +// PR c++/85265 +// { dg-do compile { target c++17_only } } // { dg-additional-options "-fconcepts" } template<typename> concept bool C = true; diff --git a/gcc/testsuite/g++.dg/concepts/class-deduction2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr85706.C index e0718d1..e0718d1 100644 --- a/gcc/testsuite/g++.dg/concepts/class-deduction2.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr85706.C diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr85808.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr85808.C new file mode 100644 index 0000000..bcba830 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr85808.C @@ -0,0 +1,19 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +namespace X +{ + template<class> constexpr bool x = true; +} + +template<int> using helper = void; + +template<typename T> +concept bool C = + requires + { + requires X::x<T>; + typename helper<T{}>; + }; + +static_assert(C<int>); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr86269.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr86269.C new file mode 100644 index 0000000..4428ee1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr86269.C @@ -0,0 +1,19 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } +#include <type_traits> + +template<typename t2, typename t = std::remove_reference_t<t2>> +concept bool IntegralOrIntegralRef = std::is_integral_v<t>; + +template<IntegralOrIntegralRef t> +auto foo(t && v) +{ + return v; +} + +int main() +{ + int i = 7; + foo(8); + return foo(i); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr87441.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr87441.C new file mode 100644 index 0000000..bcd4ba5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr87441.C @@ -0,0 +1,10 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template<typename X, typename Y = X> +concept bool HasBinaryAdd = requires(X x, Y y) +{ + {x + y} -> decltype(x + y); +}; + +void proc(HasBinaryAdd x, HasBinaryAdd y); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires1.C new file mode 100644 index 0000000..f1603af --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires1.C @@ -0,0 +1,71 @@ +// { dg-do compile { target c++2a } } + +template<typename T> +concept Class = __is_class(T); + +// Allow a requires-expression with no parms. +template<typename T> +concept C = requires { typename T::type; }; + +void f1(int a) requires true; // OK +auto f2(int a) -> bool requires true; // OK +auto f3(int a) requires true -> bool; // { dg-error "" } requires-clause precedes trailing-return-type +typedef void fn_t() requires true; // { dg-error "typedef" } +void (*pf)() requires true; // { dg-error "non-function" } +void (*fn(int))() requires false; // { dg-error "return type" } +void g(int (*)() requires true); // { dg-error "parameter|non-function" } +auto* p = new (void(*)(char) requires true); // { dg-error "type-id" } +void f4(auto a) requires Class<decltype(a)> { } +void f5(auto a) requires requires (decltype(a) x) { -x; } { } // { dg-message "in requirements" } + +struct Test { + void f(auto a) requires Class<decltype(a)>; +} test; + +void driver_1() { + struct S { } s; + f4(s); + f5(0); + f5((void*)0); // { dg-error "cannot call" } + test.f(s); +} + +void Test::f(auto a) requires Class<decltype(a)> { } + +template<bool B> requires B struct S0; // OK + +template<int N> requires N struct S1 { }; // { dg-error "does not have type" } +S1<1> x0; // { dg-error "template constraint failure|does not have type" } + +template<int N> requires N == 0 struct S2 { }; // { dg-error "does not have type|must be enclosed" } + +template<int N> requires (N == 0) struct S3 { }; // OK + +template<typename T, T X> requires X struct S4 { }; // OK +S4<int, 0> x1; // { dg-error "template constraint failure|does not have type" } +S4<bool, true> x2; // OK +S4<bool, false> x3; // { dg-error "template constraint failure" } + + +// req11.C +template<typename T> +concept Streamable = requires (T t) { t; }; + +template<typename T> +concept Range = requires (T t) { t; }; + +// FIXME: There are two syntax errors here when there should be +// just one.Note that !Range<T> is not a primary-expression and needs to +// be wrapped in parens to be syntactically valid. +template<class T> + requires Streamable<T> && !Range<T> // { dg-error "must be enclosed" } +void print1(const T& x) { } + +template<class T> + requires Streamable<T> && (!Range<T>) +void print2(const T& x) { } + +void driver_3() +{ + print2("hello"); // { dg-error "cannot call" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires10.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires10.C new file mode 100644 index 0000000..bb0e3b0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires10.C @@ -0,0 +1,32 @@ +// PR c++/66988 +// { dg-do compile { target c++2a } } + +template<bool B> +struct bool_constant { + static constexpr bool value = B; + constexpr operator bool() const { return value; } +}; + +using true_type = bool_constant<true>; +using false_type = bool_constant<false>; + +template <template <class> class T, class U> +concept _Valid = requires { typename T<U>; }; + +template <class T> +using nested_type = typename T::type; + +template <class T> +struct has_nested_type : false_type { }; + +template <class T> + requires _Valid<nested_type, T> +struct has_nested_type<T> : true_type { }; + +struct Nested +{ + using type = int; +}; + +static_assert(!has_nested_type<int>()); +static_assert(has_nested_type<Nested>()); diff --git a/gcc/testsuite/g++.dg/concepts/req17.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires11.C index 472cfef..be6409e 100644 --- a/gcc/testsuite/g++.dg/concepts/req17.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires11.C @@ -1,20 +1,20 @@ // PR c++/67018 -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } template <typename T> constexpr bool Val = true; template <class I> -concept bool InputIterator = requires (I i) { - requires Val <decltype(i++)>; +concept InputIterator = requires (I i) { + requires Val<decltype(i++)>; }; template <class I> -concept bool ForwardIterator = InputIterator<I> && true; +concept ForwardIterator = InputIterator<I> && true; template<InputIterator> constexpr bool f() { return false; } + template<ForwardIterator> constexpr bool f() { return true; } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires12.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires12.C new file mode 100644 index 0000000..c8e3cfd --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires12.C @@ -0,0 +1,21 @@ +// { dg-do compile { target c++2a } } + +template <class> struct all_same { + static constexpr bool value = true; +}; + +template <class T> +concept Assignable = requires(T t) +{ + requires all_same<decltype(t = 0)>::value; +}; + +template <class I> + requires (!Assignable<I>) +int dispatch(); + +template <class I> + requires Assignable<I> +void dispatch(); + +int main() { dispatch<int *>(); } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires13.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires13.C new file mode 100644 index 0000000..8ba3862 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires13.C @@ -0,0 +1,20 @@ +// { dg-do compile { target c++2a } } + +template <class T> concept C = true; + +template <class T> + requires C<typename T::foo> +void f(T t) { } + +void f(...); + +template <class T> + requires C<T> +void g(T t) { } + +int main() +{ + f(42); + g(42); +} + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires14.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires14.C new file mode 100644 index 0000000..e2893c0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires14.C @@ -0,0 +1,24 @@ +// { dg-do compile { target c++2a } } + +template<typename T> +concept A = sizeof(T) >= 4; + +template<typename T> +concept B = __is_class(T); + +template<A T> +void ok1(T a) { + return; +} + +template<typename T> + requires B<T> +void ok2(T a) { + return; +} + +template<A T> + requires B<T> +void fun(T a) { + return; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires15.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires15.C new file mode 100644 index 0000000..81d9196 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires15.C @@ -0,0 +1,15 @@ +// { dg-do compile { target c++2a } } + +struct string; + +template<typename T> +concept C = string; // { dg-error "expected primary-expression" } + +template<C T> +void fun(T s) { } + +int main(int, char **) { + fun((int *)0); // { dg-error "cannot call function" } + return 0; +} + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires16.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires16.C new file mode 100644 index 0000000..209c916 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires16.C @@ -0,0 +1,47 @@ +// { dg-do compile { target c++2a } } + +// A poor mans Integral concept. +template<typename T> +concept Integral = __is_same_as(T, int); + +template<int N> +concept Nonnegative = N >= 0; + +template<typename... Args> +concept UnaryPack = (sizeof...(Args) == 1); + +template<typename... Args> + requires Integral<Args...> // { dg-error "non-pack parameter" } +void f1(); + +template<typename... Args> + requires Integral<Args>... // { dg-error "parameter packs not expanded|expected unqualified-id" } +void f2(); + +template<typename... Args> + requires (Integral<Args> && ...) +void f3() { } + +template<Integral... Args> +void f4() { } + +// FIXME: This syntax is likely to be made invalid. +template<Nonnegative... Args> // { dg-error "does not constrain a type" } +void f5() { } + +template<UnaryPack Arg> // requires UnaryPack<Arg> +void f6() { } + +template<UnaryPack... Args> // requires (... && UnaryPack<Args>) +void f7() { } + +void driver() +{ + f1<int, int>(); // { dg-error "cannot call function" } + f3<int, int>(); + f3<int, void>(); // { dg-error "cannot call function" } + f4<int, int>(); + f4<int, void>(); // { dg-error "cannot call function" } + f7<int>(); + f7<int, int>(); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires17.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires17.C new file mode 100644 index 0000000..1ec1d59 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires17.C @@ -0,0 +1,11 @@ +// { dg-do compile { target c++2a } } + +template<typename T> +concept Type = true; + +template<typename T> +concept C = + requires (T a) { + { a.f() } -> Type; // OK + { a.g() } -> const Type*; // { dg-error "not a plain type-constraint" } + }; diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires2.C new file mode 100644 index 0000000..45bb423 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires2.C @@ -0,0 +1,74 @@ +// { dg-do compile { target c++2a } } + +// Test the types of atomic constraints + +// req5.C +struct fool { + constexpr fool operator&&(fool) const { return {}; } + constexpr fool operator||(fool) const { return {}; } +}; + +template<typename T> constexpr fool p1() { return {}; } +template<typename T> constexpr fool p2() { return {}; } + +template<typename T> +concept Bad = p1<T>() && p2<T>(); + +template<typename T> requires Bad<T> void bad(T x) { } + +void driver_2() +{ + bad(0); // { dg-error "cannot call" } +} + +// req6.C +struct X { }; +int operator==(X, X) { return 0; } + +template<typename T> +concept C1 = (X()); + +template<typename T> +concept C2 = (X() == X()); + +template<typename T> + requires C1<T> +void h1(T) { } + +template<typename T> + requires C2<T> +void h2(T); + +void driver_3() +{ + h1(0); // { dg-error "cannot call" } + h2(0); // { dg-error "cannot call" } +} + +// req7.C +template<bool B> +struct boolean_constant +{ + constexpr operator bool() const { return B; } +}; + +using true_type = boolean_constant<true>; +using false_type = boolean_constant<false>; + +template<typename T> +struct dependent_true : true_type { }; + +template<typename T> +struct dependent_false : false_type { }; + +template<typename T> + requires (dependent_true<T>{}) // { dg-message "bool" } +struct S5 { }; + +template<typename T> + requires (dependent_false<T>{}) // { dg-message "bool" } +struct S6 { }; + +S5<int> x5; // { dg-error "template constraint failure" } +S6<int> x6; // { dg-error "template constraint failure" } + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires3.C new file mode 100644 index 0000000..bbcba0d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires3.C @@ -0,0 +1,32 @@ +// { dg-do compile { target c++2a } } + +// Test basic expression requirements + +// req13.C + +template<class T, class...Args> +concept Constructible = + requires(Args&&...args) { + T {((Args&&)(args))...}; + new T{((Args&&)(args))...}; + }; + +template<typename T> + requires Constructible<T> +struct A { }; + +A<int> a; + +// req19.C + +struct B +{ + template <class T> + void f(T t) requires requires (T tt) { tt; } + { } +}; + +int main() +{ + B().f(42); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires4.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires4.C new file mode 100644 index 0000000..61aa72d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires4.C @@ -0,0 +1,15 @@ +// { dg-do compile { target c++2a } } + +// Test associated type requirements + +// req8.C + +template<typename T> +concept Has_member_type = requires { typename T::type; }; + +template<typename T> +concept Concept = true && Has_member_type<T>; + +template<typename T> + requires Concept<T> +void foo(T t) { } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires5.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires5.C new file mode 100644 index 0000000..fe37ed4 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires5.C @@ -0,0 +1,45 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options -fconcepts-ts } + +// Test conversion requirements (not in C++20) + +// req9.C + +template<typename T> +struct S1 { }; + +template<typename T> +concept C = requires(T x) { { x.fn() } -> S1<T>; }; + +template<typename U> + requires C<U> +void fn(U x) +{ + x.fn(); +} + +struct S2 +{ + auto fn() const { return S1<S2>(); } +}; + +int driver_1() +{ + fn(S2{}); + return 0; +} + +// req10.C +// Test implicit conversion requirements + +template<typename T, typename U> +concept ConvertibleTo = requires(T& t) { {t} -> U&; }; + +struct B { }; +class D : /*private*/ B { }; + +void driver_2() +{ + static_assert(ConvertibleTo<D, B>()); // { dg-error "cannot be used as a function" } + static_assert(ConvertibleTo<D, B>); // { dg-error "static assertion failed" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires6.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires6.C new file mode 100644 index 0000000..20df78b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires6.C @@ -0,0 +1,34 @@ +// { dg-do compile { target c++2a } } + +// Test deduction requirements. + +// req12.C + +template <typename T, typename U> +concept SameAs = __is_same_as(T, U); + +template <typename T> +concept C1 = requires(T t) { // { dg-message "in requirements" } + { t } -> SameAs<T>; // NOTE: t deduced as decltype((t)) + // { dg-error "does not satisfy placeholder constraints" "" { target *-*-* } .-1 } +}; + +template <typename T> + requires C1<T> +constexpr bool f1() { return true; } + +static_assert(f1<char>()); // { dg-error "cannot call" } +static_assert(f1<int>()); // { dg-error "cannot call" } +static_assert(f1<double>()); // { dg-error "cannot call" } + + +template <typename T> +concept C2 = requires(T t) { + { t } -> SameAs<T&>; // NOTE: t deduced as decltype((t)) +}; + +template <typename T> + requires C2<T> +constexpr bool f2() { return true; } + +static_assert(f2<int>()); // OK diff --git a/gcc/testsuite/g++.dg/concepts/req14.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires7.C index 6e60b6f..89057c4 100644 --- a/gcc/testsuite/g++.dg/concepts/req14.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires7.C @@ -1,14 +1,15 @@ // PR c++/66758 -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } template <class T, class U> -concept bool C = requires (T t, U u) { t + u; }; +concept C = requires (T t, U u) { t + u; }; template <class T, class U> -requires C<T,U> + requires C<T,U> void f(T t, U u) { t + u; } +struct non_addable { }; + int main() { using T = decltype(f(42, 24)); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires8.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires8.C new file mode 100644 index 0000000..f10e4bc --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires8.C @@ -0,0 +1,17 @@ +// { dg-do compile { target c++2a } } + +template <class T, class U> +concept C = requires (T t, U u) { t + u; }; // { dg-message "in requirements" } + +template <class T, class U> + requires C<T,U> +void f(T t, U u) { t + u; } + +struct non_addable { }; + +int main() +{ + // FIXME: This diagnostic is being emitted twice, when it should + // be emitted just once. + using U = decltype(f(42, non_addable{})); // { dg-error "cannot call function" } +} diff --git a/gcc/testsuite/g++.dg/concepts/req15.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires9.C index 385d51d..3594176 100644 --- a/gcc/testsuite/g++.dg/concepts/req15.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires9.C @@ -1,6 +1,5 @@ // PR c++/66832 -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } template <class T, class U, unsigned N> requires requires (T& t, U &u) { t.foo(); u.foo(); } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-sfinae1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-sfinae1.C new file mode 100644 index 0000000..87b5d75 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-sfinae1.C @@ -0,0 +1,10 @@ +// { dg-do compile { target concepts } } + +template <class T> struct A +{ + static const int x = 42; +}; + +template <class T> concept R42 = A<T&>::x == 42; + +static_assert (!R42<void>); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm1.C new file mode 100644 index 0000000..4ff401a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm1.C @@ -0,0 +1,19 @@ +// { dg-do compile { target c++2a } } + +template<typename T> +concept Int = __is_same_as(T, int); + +// Type template parameters +template<Int T = int> struct S1 { }; +template<Int = int> struct S2; +template<Int T> struct S2 { }; + +// Non-type template parameters +template<Int auto N = 0> struct S3 { }; +template<Int auto = 0> struct S4; +template<Int auto N> struct S4 { }; + +S1<> s1; +S2<> s2; +S3<> s3; +S4<> s4; diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm10.C b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm10.C new file mode 100644 index 0000000..239b485 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm10.C @@ -0,0 +1,10 @@ +// { dg-do compile { target c++2a } } + +template <class> +concept Dummy = true; + +template <typename> +class example { + template <Dummy<> U> + friend auto func(); +}; diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C new file mode 100644 index 0000000..7c1d69b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm2.C @@ -0,0 +1,15 @@ +// { dg-do compile { target c++2a } } + +template<typename T> + concept Int = __is_same_as(T, int); + +template<typename> struct Foo; + +// Instantiation of default arguments happens at the point of +// instantiation for the class. + +template<Int T = char> struct S1 { }; +template<Int auto X = false> struct S2 { }; + +S1<> s1; // { dg-error "constraint failure" } +S2<> s2; // { dg-error "placeholder constraints not satisfied" } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm5.C b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm5.C new file mode 100644 index 0000000..78b6f1c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm5.C @@ -0,0 +1,9 @@ +// { dg-do compile { target c++2a } } + +template<typename T> + concept Int = __is_same_as(T, int); + +template<Int... Ts = int> struct S1; // { dg-error "default argument" } +template<Int... = int> struct S2; // { dg-error "default argument" } +template<Int auto... Ns = 0> struct S3; // { dg-error "default argument" } +template<Int auto... = 0> struct S4; // { dg-error "default argument" } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C new file mode 100644 index 0000000..47c8ef7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm6.C @@ -0,0 +1,44 @@ +// { dg-do compile { target c++2a } } + +template<typename... Ts> struct are_same; + +template<> + struct are_same<> { + static constexpr bool value = true; + }; + +template<typename T> + struct are_same<T> { + static constexpr bool value = true; + }; + +template<typename T, typename U, typename... Ts> + struct are_same<T, U, Ts...> { + static constexpr bool value = + __is_same_as(T, U) && are_same<U, Ts...>::value; + }; + +template<typename... Ts> +concept Same = are_same<Ts...>::value; + +template<typename T> +concept Int = __is_same_as(T, int); + +// NOTE: The use of Same is misleading; it constraints each T, not +// the pack of Ts. +template<Same... Ts> struct S1 { }; // requires (C<Ts> && ...) +S1<int, int, int> s1; +S1<int, int, bool> s2; + +// // NOTE: The use of Same is misleading; it constraints each deduced +// // type, not the pack of Ts. +template<Same auto... Xs> struct S2 { }; // requires (C<X> && ...) with each X deduced +S2<true, true, true> s3; +S2<true, true, 'a'> s4; // OK + +template<Int... Ts> struct S3 { }; // requires (C<Ts> && ...) +S3<int, int, char> x0; // { dg-error "template constraint failure" } + +template<Int auto... Xs> struct S4 { }; // requires (C<X> && ...) with each X deduced +S4<0, 1, 2, 'a'> x1; // { dg-error "placeholder constraints not satisfied" } + diff --git a/gcc/testsuite/g++.dg/concepts/template-parm8.C b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm8.C index 63c4512..adcffed 100644 --- a/gcc/testsuite/g++.dg/concepts/template-parm8.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm8.C @@ -1,13 +1,12 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } template<typename T> - concept bool C() { return __is_class(T); } + concept C = __is_class(T); template<typename T> - concept bool D() { return C<T>() and __is_empty(T); } + concept D = C<T> and __is_empty(T); -template<template<typename Q> requires C<Q>() class X> +template<template<typename Q> requires C<Q> class X> struct S { }; // An unconstrained template can be used as an argument for any @@ -16,7 +15,7 @@ template<typename A> struct T0 { }; S<T0> x1; // Matching constraints are valid. -template<typename A> requires C<A>() struct T1 { }; +template<typename A> requires C<A> struct T1 { }; S<T1> x2; int main() { } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm9.C b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm9.C new file mode 100644 index 0000000..d578b35 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-template-parm9.C @@ -0,0 +1,18 @@ +// { dg-do compile { target c++2a } } + +template<typename T> + concept C = __is_class(T); + +template<typename T> + concept D = C<T> and __is_empty(T); + +template<template<typename Q> requires C<Q> class X> + struct S { }; + +template<typename A> requires true struct T0 { }; +template<typename A> requires D<A> struct T1 { }; + +S<T0> x3; // { dg-error "constraint mismatch|invalid type" } +S<T1> x4; // { dg-error "constraint mismatch|invalid type" } + +int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/traits1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-traits1.C index 27610e2..0ea529e 100644 --- a/gcc/testsuite/g++.dg/concepts/traits1.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-traits1.C @@ -1,62 +1,61 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-options "-std=c++2a" } template<typename T> - concept bool Nothrow_assignable() { return __has_nothrow_assign(T); } + concept Nothrow_assignable = __has_nothrow_assign(T); template<typename T> - concept bool Nothrow_constructible() { return __has_nothrow_constructor(T); } + concept Nothrow_constructible = __has_nothrow_constructor(T); template<typename T> - concept bool Nothrow_copyable() { return __has_nothrow_copy(T); } + concept Nothrow_copyable = __has_nothrow_copy(T); template<typename T> - concept bool Trivially_assignable() { return __has_trivial_assign(T); } + concept Trivially_assignable = __has_trivial_assign(T); template<typename T> - concept bool Trivially_constructible() { return __has_trivial_constructor(T); } + concept Trivially_constructible = __has_trivial_constructor(T); template<typename T> - concept bool Trivially_copyable() { return __has_trivial_copy(T); } + concept Trivially_copyable = __has_trivial_copy(T); template<typename T> - concept bool Trivially_destructible() { return __has_trivial_destructor(T); } + concept Trivially_destructible = __has_trivial_destructor(T); template<typename T> - concept bool Dynamically_destructible() { return __has_virtual_destructor(T); } + concept Dynamically_destructible = __has_virtual_destructor(T); template<typename T> - concept bool Abstract() { return __is_abstract(T); } + concept Abstract = __is_abstract(T); template<typename T> - concept bool Polymorphic() { return __is_polymorphic(T); } + concept Polymorphic = __is_polymorphic(T); template<typename T> - concept bool Class() { return __is_class(T); } + concept Class = __is_class(T); template<typename T> - concept bool Empty() { return __is_empty(T); } + concept Empty = __is_empty(T); template<typename T> - concept bool Enum() { return __is_enum(T); } + concept Enum = __is_enum(T); template<typename T> - concept bool Final() { return __is_final(T); } + concept Final = __is_final(T); template<typename T> - concept bool Literal_type() { return __is_literal_type(T); } + concept Literal_type = __is_literal_type(T); template<typename T> - concept bool Pod() { return __is_pod(T); } + concept Pod = __is_pod(T); template<typename T> - concept bool Standard_layout() { return __is_standard_layout(T); } + concept Standard_layout = __is_standard_layout(T); template<typename T> - concept bool Trivial() { return __is_trivial(T); } + concept Trivial = __is_trivial(T); template<typename T> - concept bool Union() { return __is_union(T); } + concept Union = __is_union(T); template<Nothrow_assignable T> void f1() { } template<Nothrow_copyable T> void f2() { } diff --git a/gcc/testsuite/g++.dg/concepts/traits2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-traits2.C index 71dcfd3..1dc5ffe 100644 --- a/gcc/testsuite/g++.dg/concepts/traits2.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-traits2.C @@ -1,62 +1,61 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } template<typename T> - concept bool Nothrow_assignable() { return __has_nothrow_assign(T); } + concept Nothrow_assignable = __has_nothrow_assign(T); template<typename T> - concept bool Nothrow_constructible() { return __has_nothrow_constructor(T); } + concept Nothrow_constructible = __has_nothrow_constructor(T); template<typename T> - concept bool Nothrow_copyable() { return __has_nothrow_copy(T); } + concept Nothrow_copyable = __has_nothrow_copy(T); template<typename T> - concept bool Trivially_assignable() { return __has_trivial_assign(T); } + concept Trivially_assignable = __has_trivial_assign(T); template<typename T> - concept bool Trivially_constructible() { return __has_trivial_constructor(T); } + concept Trivially_constructible = __has_trivial_constructor(T); template<typename T> - concept bool Trivially_copyable() { return __has_trivial_copy(T); } + concept Trivially_copyable = __has_trivial_copy(T); template<typename T> - concept bool Trivially_destructible() { return __has_trivial_destructor(T); } + concept Trivially_destructible = __has_trivial_destructor(T); template<typename T> - concept bool Dynamically_destructible() { return __has_virtual_destructor(T); } + concept Dynamically_destructible = __has_virtual_destructor(T); template<typename T> - concept bool Abstract() { return __is_abstract(T); } + concept Abstract = __is_abstract(T); template<typename T> - concept bool Polymorphic() { return __is_polymorphic(T); } + concept Polymorphic = __is_polymorphic(T); template<typename T> - concept bool Class() { return __is_class(T); } + concept Class = __is_class(T); template<typename T> - concept bool Empty() { return __is_empty(T); } + concept Empty = __is_empty(T); template<typename T> - concept bool Enum() { return __is_enum(T); } + concept Enum = __is_enum(T); template<typename T> - concept bool Final() { return __is_final(T); } + concept Final = __is_final(T); template<typename T> - concept bool Literal_type() { return __is_literal_type(T); } + concept Literal_type = __is_literal_type(T); template<typename T> - concept bool Pod() { return __is_pod(T); } + concept Pod = __is_pod(T); template<typename T> - concept bool Standard_layout() { return __is_standard_layout(T); } + concept Standard_layout = __is_standard_layout(T); template<typename T> - concept bool Trivial() { return __is_trivial(T); } + concept Trivial = __is_trivial(T); template<typename T> - concept bool Union() { return __is_union(T); } + concept Union = __is_union(T); template<Nothrow_assignable T> void f1(); template<Nothrow_copyable T> void f2(); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-ts1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-ts1.C new file mode 100644 index 0000000..b819ad4 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-ts1.C @@ -0,0 +1,49 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +// This tests the terse notation. + +template<typename T> +concept True = true; + +template<typename T> +concept False = false; + +template<typename T, typename U> +concept SameAs = __is_same_as(T, U); + +True x1 = 0; +False x2 = 0; // { dg-error "deduced initializer does not satisfy" } + +void f1(True t) { } +void f2(False t) { } +void f3(SameAs<int> q) { } +void f4(True a, SameAs<decltype(a)> b) { } + +True f5() { return 0; } +False f6() { return 0; } // { dg-error "deduced return type" } +SameAs<int> f7() { return 0; } +SameAs<int> f8() { return 'a'; } // { dg-error "deduced return type" } +auto f9() -> True { return 0; } +auto f10() -> False { return 0; } // { dg-error "deduced return type" } +auto f11() -> SameAs<int> { return 0; } +auto f12() -> SameAs<char> { return 0; } // { dg-error "deduced return type" } +auto f13(int n) -> SameAs<decltype(n)> { return n; } +auto f14(int n) -> SameAs<decltype(n)> { return 'a'; } // { dg-error "deduced return type" } +auto f15(auto x) -> SameAs<decltype(x)> { return 0; } // { dg-error "deduced return type" } + +void driver() +{ + f1(0); + f2(0); // { dg-error "cannot call" } + f3(0); + f3('a'); // { dg-error "cannot call" } + f4(0, 0); + f4(0, 'a'); // { dg-error "cannot call" } + f15(0); + f15('a'); // { dg-message "" } +} + +template<class T> concept bool C1() { return false; } +template<C1 T> concept bool C2() { return true; } // { dg-error "cannot be constrained" } +template<C1 T> concept bool C3 = true; // { dg-error "cannot be constrained" } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-ts2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-ts2.C new file mode 100644 index 0000000..9d2dbee --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-ts2.C @@ -0,0 +1,260 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +// Basic tests using function concepts. + +template<typename T> +concept bool Type() { return true; } + +template<typename T> +concept bool False() { return false; } + +template<typename T> +concept bool Class() { return __is_class(T); } + +template<typename T> +concept bool EmptyClass() { return Class<T>() && __is_empty(T); } + +template<typename T, typename U> +concept bool Classes() { return __is_class(T) && __is_class (U); } + +struct empty { }; + +struct nonempty { int n; }; + +static_assert(Type<int>()); +static_assert(False<int>()); // { dg-error "static assertion failed" } + +// Basic checks + +template<typename T> + requires (Type<T>()) +int fn1(T t) { return 0; } + +template<typename T> + requires (False<T>()) +int fn2(T t) { return 0; } + +void driver() +{ + fn1(0); // OK + fn2(0); // { dg-error "cannot call function" } +} + +// Ordering + +template<typename T> +concept bool Cf() { return requires (T t) { t.f(); }; } + +template<typename T> +concept bool Cfg() { return Cf<T>() && requires (T t) { t.g(); }; } + +template<typename T> +concept bool Cf2() { return requires (T t) { t.f(); }; } + +template<typename T> +constexpr int algo(T t) { return 0; } + +template<typename T> requires (Cf<T>()) +constexpr int algo(T t) { return 1; } + +template<typename T> requires (Cfg<T>()) +constexpr int algo(T t) { return 2; } + +template<typename T> requires (Cf<T>()) +constexpr int ambig(T t) { return 1; } + +template<typename T> requires (Cf2<T>()) +constexpr int ambig(T t) { return 1; } + +struct T1 { + void f() { } +}; + +struct T2 : T1 { + void g() { } +}; + +void driver_0() +{ + T1 x; + T2 y; + static_assert(algo(0) == 0); + static_assert(algo(x) == 1); + static_assert(algo(y) == 2); + ambig(x); // { dg-error "call of overload | is ambiguous" } +} + +template<typename T> +struct S +{ + void f() requires (Class<T>()) { } + + template<typename U> + struct Inner + { + void g() requires (Classes<T, U>()) { } + }; + + template<typename U> requires (Classes<T, U>()) void h(U) { } +}; + +struct X { }; + +void driver_1() +{ + S<X> s1; + s1.f(); // OK + s1.h(s1); // OK + s1.h(0); // { dg-error "no matching function" } + + S<int> s2; + s2.f(); // { dg-error "no matching function" } + + S<X>::Inner<X> si1; + si1.g(); + + S<X>::Inner<int> si2; + si2.g(); // { dg-error "no matching function" } +} + +// Check constraints on non-dependent arguments, even when in a +// dependent context. + +template<typename T> requires (Class<T>()) void f1(T x) { } + +// fn1-2.C -- Dependent checks +template<typename T> +void caller_1(T x) +{ + f1(x); // Unchecked dependent arg. + f1(empty{}); // Checked non-dependent arg, but OK + f1(0); // { dg-error "cannot call function" } +} + +// fn3.c -- Ordering +template<typename T> + requires (Class<T>()) +constexpr int f2(T x) { return 1; } + +template<typename T> + requires (EmptyClass<T>()) +constexpr int f2(T x) { return 2; } + +template<typename T> +constexpr int f3(T x) requires (Class<T>()) { return 1; } + +template<typename T> +constexpr int f3(T x) requires (EmptyClass<T>()) { return 2; } + +void driver_2() +{ + f2(0); // { dg-error "no matching function" } + static_assert (f2(empty{}) == 2); + static_assert (f2(nonempty{}) == 1); + f3(0); // { dg-error "no matching function" } + static_assert (f3(empty{}) == 2); + static_assert (f3(nonempty{}) == 1); +} + +// fn8.C -- Address of overloaded functions +template<typename T> requires (Type<T>()) void ok(T) { } +template<typename T> requires (Class<T>()) void err(T) { } + +auto p1 = &ok<int>; +auto p2 = &err<int>; // { dg-error "no matches" } +void (*p3)(int) = &ok<int>; +void (*p4)(int) = &err<int>; // { dg-error "no matches" } +void (*p5)(int) = &ok; +void (*p6)(int) = &err; // { dg-error "no matches" } + +template<typename T> void g(T x) { } + +void driver_3 () +{ + g(&ok<int>); + g(&err<int>); // { dg-error "no matches" } +} + + +struct S2 { + template<typename T> requires (Type<T>()) int ok(T) { return 0; } + template<typename T> requires (Class<T>()) int err(T) { return 0; } +}; + +auto p7 = &S2::ok<int>; +auto p8 = &S2::err<int>; // { dg-error "no matches" } +int (S2::*p9)(int) = &S2::ok<int>; +int (S2::*p10)(int) = &S2::err<int>; // { dg-error "no matches" } +int (S2::*p11)(int) = &S2::ok; +int (S2::*p12)(int) = &S2::err; // { dg-error "no matches" } + +// fn9.C -- Ordering with function address +template<typename T> + requires (Class<T>()) +constexpr int fn(T) { return 1; } + +template<typename T> + requires (EmptyClass<T>()) +constexpr int fn(T) { return 2; } + +struct S3 +{ + template<typename T> + requires (Class<T>()) + constexpr int fn(T) const { return 1; } + + template<typename T> + requires (EmptyClass<T>()) + constexpr int fn(T) const { return 2; } +}; + +void driver_5 () { + struct X { }; + struct Y { X x; }; + + constexpr X x; + constexpr Y y; + constexpr S3 s; + + constexpr auto p1 = &fn<X>; // Empty f + static_assert (p1(x) == 2); + + constexpr auto p2 = &fn<Y>; // Class f + static_assert(p2(y) == 1); + + constexpr auto p3 = &S3::fn<X>; // Empty f + static_assert((s.*p3)(x) == 2); + + constexpr auto p4 = &S3::fn<Y>; // Empty f + static_assert((s.*p4)(y) == 1); +} + + +// Redeclarations + +// FIXME: This should be a diagnosable error. The programmer has moved +// the requirements from the template-head to the declarator. +template<typename T> requires (Type<T>()) void f3(); +template<typename T> void f3() requires (Type<T>()); + +void driver_4() +{ + f3<int>(); // { dg-error "call of overload | ambiguous" } +} + +template<template<typename T> requires true class X> void f4(); +template<template<typename T> class X> void f4(); // OK: different declarations + +template<typename T> requires (Type<T>()) void def() { } +template<typename T> requires (Type<T>()) void def() { } // { dg-error "redefinition" } + +// fn-concept1.C + +template<typename T> +concept bool Tuple() { // { dg-error "multiple statements" } + static_assert(T::value, ""); + return true; +} + +void f(Tuple&); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-ts3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-ts3.C new file mode 100644 index 0000000..c1f4ba9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-ts3.C @@ -0,0 +1,251 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +// Basic tests using variable concepts. + +template<typename T> +concept bool Type = true; + +template<typename T> +concept bool False = false; + +template<typename T> +concept bool Class = __is_class(T); + +template<typename T> +concept bool EmptyClass = Class<T> && __is_empty(T); + +template<typename T, typename U> +concept bool Classes = __is_class(T) && __is_class (U); + +struct empty { }; + +struct nonempty { int n; }; + +static_assert(Type<int>); +static_assert(False<int>); // { dg-error "static assertion failed" } + +// Basic checks + +template<typename T> + requires Type<T> +int fn1(T t) { return 0; } + +template<typename T> + requires False<T> +int fn2(T t) { return 0; } + +void driver() +{ + fn1(0); // OK + fn2(0); // { dg-error "cannot call function" } +} + +// Ordering + +template<typename T> +concept bool Cf = requires (T t) { t.f(); }; + +template<typename T> +concept bool Cfg = Cf<T> && requires (T t) { t.g(); }; + +template<typename T> +concept bool Cf2 = requires (T t) { t.f(); }; + +template<typename T> +constexpr int algo(T t) { return 0; } + +template<typename T> requires Cf<T> +constexpr int algo(T t) { return 1; } + +template<typename T> requires Cfg<T> +constexpr int algo(T t) { return 2; } + +template<typename T> requires Cf<T> +constexpr int ambig(T t) { return 1; } + +template<typename T> requires Cf2<T> +constexpr int ambig(T t) { return 1; } + +struct T1 { + void f() { } +}; + +struct T2 : T1 { + void g() { } +}; + +void driver_0() +{ + T1 x; + T2 y; + static_assert(algo(0) == 0); + static_assert(algo(x) == 1); + static_assert(algo(y) == 2); + ambig(x); // { dg-error "call of overload | is ambiguous" } +} + +template<typename T> +struct S +{ + void f() requires Class<T> { } + + template<typename U> + struct Inner + { + void g() requires Classes<T, U> { } + }; + + template<typename U> requires Classes<T, U> void h(U) { } +}; + +struct X { }; + +void driver_1() +{ + S<X> s1; + s1.f(); // OK + s1.h(s1); // OK + s1.h(0); // { dg-error "no matching function" } + + S<int> s2; + s2.f(); // { dg-error "no matching function" } + + S<X>::Inner<X> si1; + si1.g(); + + S<X>::Inner<int> si2; + si2.g(); // { dg-error "no matching function" } +} + +// Check constraints on non-dependent arguments, even when in a +// dependent context. + +template<typename T> requires Class<T> void f1(T x) { } + +// fn1-2.C -- Dependent checks +template<typename T> +void caller_1(T x) +{ + f1(x); // Unchecked dependent arg. + f1(empty{}); // Checked non-dependent arg, but OK + f1(0); // { dg-error "cannot call function" } +} + +// fn3.c -- Ordering +template<typename T> + requires Class<T> +constexpr int f2(T x) { return 1; } + +template<typename T> + requires EmptyClass<T> +constexpr int f2(T x) { return 2; } + +template<typename T> +constexpr int f3(T x) requires Class<T> { return 1; } + +template<typename T> +constexpr int f3(T x) requires EmptyClass<T> { return 2; } + +void driver_2() +{ + f2(0); // { dg-error "no matching function" } + static_assert (f2(empty{}) == 2); + static_assert (f2(nonempty{}) == 1); + f3(0); // { dg-error "no matching function" } + static_assert (f3(empty{}) == 2); + static_assert (f3(nonempty{}) == 1); +} + +// fn8.C -- Address of overloaded functions +template<typename T> requires Type<T> void ok(T) { } +template<typename T> requires Class<T> void err(T) { } + +auto p1 = &ok<int>; +auto p2 = &err<int>; // { dg-error "no matches" } +void (*p3)(int) = &ok<int>; +void (*p4)(int) = &err<int>; // { dg-error "no matches" } +void (*p5)(int) = &ok; +void (*p6)(int) = &err; // { dg-error "no matches" } + +template<typename T> void g(T x) { } + +void driver_3 () +{ + g(&ok<int>); + g(&err<int>); // { dg-error "no matches" } +} + + +struct S2 { + template<typename T> requires Type<T> int ok(T) { return 0; } + template<typename T> requires Class<T> int err(T) { return 0; } +}; + +auto p7 = &S2::ok<int>; +auto p8 = &S2::err<int>; // { dg-error "no matches" } +int (S2::*p9)(int) = &S2::ok<int>; +int (S2::*p10)(int) = &S2::err<int>; // { dg-error "no matches" } +int (S2::*p11)(int) = &S2::ok; +int (S2::*p12)(int) = &S2::err; // { dg-error "no matches" } + +// fn9.C -- Ordering with function address +template<typename T> + requires Class<T> +constexpr int fn(T) { return 1; } + +template<typename T> + requires EmptyClass<T> +constexpr int fn(T) { return 2; } + +struct S3 +{ + template<typename T> + requires Class<T> + constexpr int fn(T) const { return 1; } + + template<typename T> + requires EmptyClass<T> + constexpr int fn(T) const { return 2; } +}; + +void driver_5 () { + struct X { }; + struct Y { X x; }; + + constexpr X x; + constexpr Y y; + constexpr S3 s; + + constexpr auto p1 = &fn<X>; // Empty f + static_assert (p1(x) == 2); + + constexpr auto p2 = &fn<Y>; // Class f + static_assert(p2(y) == 1); + + constexpr auto p3 = &S3::fn<X>; // Empty f + static_assert((s.*p3)(x) == 2); + + constexpr auto p4 = &S3::fn<Y>; // Empty f + static_assert((s.*p4)(y) == 1); +} + + +// Redeclarations + +// FIXME: This should be a diagnosable error. The programmer has moved +// the requirements from the template-head to the declarator. +template<typename T> requires Type<T> void f3(); +template<typename T> void f3() requires Type<T>; + +void driver_4() +{ + f3<int>(); // { dg-error "call of overload | ambiguous" } +} + +template<template<typename T> requires true class X> void f4(); +template<template<typename T> class X> void f4(); // OK: different declarations + +template<typename T> requires Type<T> void def() { } +template<typename T> requires Type<T> void def() { } // { dg-error "redefinition" } + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-ts4.C b/gcc/testsuite/g++.dg/cpp2a/concepts-ts4.C new file mode 100644 index 0000000..3d720e8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-ts4.C @@ -0,0 +1,34 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +// Basic tests for introduction syntax. + +template<typename T> +concept True = true; + +template<typename T> +concept False = false; + +template<typename T, typename U> +concept Same = __is_same_as(T, U); + +template<int N> +concept Pos = N >= 0; + +True{T} void f1(T) { } +False{T} void f2(T) { } +Same{X, Y} void same(); +Pos{N} void fn(); + +void driver() +{ + f1(0); + f2(0); // { dg-error "cannot call function" } + + same<int, int>(); + same<int, char>(); // { dg-error "cannot call function" } + + fn<0>(); // OK + fn<-1>(); // { dg-error "cannot call function" } + fn<int>(); // { dg-error "no matching function" } +} diff --git a/gcc/testsuite/g++.dg/concepts/member-concept.C b/gcc/testsuite/g++.dg/cpp2a/concepts-ts5.C index ef577a1..cdc40df 100644 --- a/gcc/testsuite/g++.dg/concepts/member-concept.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-ts5.C @@ -1,5 +1,5 @@ -// { dg-do compile { target c++17 } } -// { dg-options "-fconcepts" } +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } struct Base { template<typename T> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-ts6.C b/gcc/testsuite/g++.dg/cpp2a/concepts-ts6.C new file mode 100644 index 0000000..bf665aa --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-ts6.C @@ -0,0 +1,72 @@ +// { dg-do compile { target c++2a } } +// { dg-additional-options "-fconcepts-ts" } + +template<typename T, int N, typename... Xs> concept bool C1 = true; + +template<template<typename> class X> concept bool C2 = true; + +template<typename... Ts> concept bool C3 = true; + +C1{A, B, ...C} struct S1 { }; + +C2{T} void f(); + +C2{...Ts} void g(); // { dg-error "cannot be introduced" } + +C3{...Ts} struct S2 { }; +C3{T, U, V} struct S3 { }; +C3{...Ts, U} struct S4 { }; // { dg-error "cannot deduce template parameters" } + +template<typename> struct X { }; + +void driver1() { + S1<int, 0, char, bool, float> s1a; + S1<0, 0, char, bool, float> s1b; // { dg-error "type/value mismatch" } + + f<X>(); + f<int>(); // { dg-error "no matching function for call" } + + S2<int> s2a; + S2<char, signed char, unsigned char> s2b; + S2<0> s2c; // { dg-error "type/value mismatch" } + + S3<int, int, int> s3a; + S3<int, int> s3b; // { dg-error "wrong number of template arguments" } +} + +template<typename... Args> +struct all_same; + +template<typename T, typename U, typename... Args> +struct all_same<T, U, Args...> +{ + static constexpr bool value = __is_same_as(T, U) && all_same<U, Args...>::value; +}; + +template<typename T> +struct all_same<T> +{ + static constexpr bool value = true; +}; + +template<> +struct all_same<> +{ + static constexpr bool value = true; +}; + +template<typename... Ts> +concept AllSame = all_same<Ts...>::value; + +AllSame{...Ts} struct S5 { }; +AllSame{T, U} struct S6 { }; + +void driver2() +{ + S5<int, int> s5a; + S5<int, int, int, int> s5b; + S5<int, int, int, char> s5c; // { dg-error "template constraint failure" } + S6<void, void> s6a; + S6<void, int> s6c; // { dg-error "template constraint failure" } + S6<void, void, void> s6b; // { dg-error "wrong number of template arguments" } +}
\ No newline at end of file diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-using1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-using1.C new file mode 100644 index 0000000..733382d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-using1.C @@ -0,0 +1,17 @@ +// PR c++/79591 +// { dg-do compile { target c++2a } } + +template <class> concept True = true; + +// Fine. +namespace X { + void f(auto) {} + void f(True auto) {} +} + +void f(auto) {} +namespace Y { + void f(True auto) {} + using ::f; + // error: 'template<class auto:3> void f(auto:3)' conflicts with a previous declaration +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts.C b/gcc/testsuite/g++.dg/cpp2a/concepts.C new file mode 100644 index 0000000..5839207 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts.C @@ -0,0 +1,57 @@ +// { dg-do compile { target c++2a } } + +// Change in grammar for the expression trailing `requires`. +template<typename T> + requires true != false // { dg-error "must be enclosed" } +void f1(T) +{ } + +template<typename T> +void f3(T) requires true != false // { dg-error "must be enclosed" } +{ } + +template<typename T> + requires requires { + requires true == false; // OK: logical-or-expressions allowed here. + } +void f3(T) +{ } + +template<typename T> +concept bool C1 = true; // { dg-error "bool" } +template<typename T> +bool concept C2 = true; // { dg-error "concept definition syntax" } + +template<typename T> +concept C3 = true; // OK +template<typename T> +concept C3 = true; // { dg-error "redefinition" } +template<typename T, typename U> +concept C3 = true; // { dg-error "different template parameters" } +template<int> +concept C3 = true; // { dg-error "different template parameters" } +int C3 = 0; // { dg-error "different kind of entity" } + +int C4 = 0; +template<typename T> +concept C4 = true; // { dg-error "different kind of entity" } + +// Concepts as expressions + +template<typename T> +concept True = true; + +template<typename T> +concept False = false; + +static_assert(True<int>); +static_assert(False<int>); // { dg-error "static assertion failed" } + +void f4(True auto); + +template<True T> concept C5 = true; // { dg-error "cannot be constrained" } + + +template<typename T> +concept Recursive = Recursive<T>; // { dg-error "not declared|expected" } + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts1.C b/gcc/testsuite/g++.dg/cpp2a/concepts1.C new file mode 100644 index 0000000..f22464a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts1.C @@ -0,0 +1,27 @@ +// { dg-do compile { target c++2a } } + +template<typename T> +concept Class = __is_class(T); + +template<Class T> +void f1(T) { } + +struct empty { }; + +// Redeclarations involving brief template parameters. + +template<Class T> +void decl1(T); + +template<typename T> + requires Class<T> +void decl1(T); + +void driver_1() +{ + f1(0); // { dg-error "cannot call function" } + f1(empty{}); + + decl1(empty{}); // { dg-error "call of overload | ambiguous" } +} + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts2.C b/gcc/testsuite/g++.dg/cpp2a/concepts2.C new file mode 100644 index 0000000..d3b45f7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts2.C @@ -0,0 +1,69 @@ +// { dg-do compile { target c++2a } } + +template<typename T> +concept True = true; + +template<typename T> +concept False = false; + +template<typename T> +concept Int = __is_same_as(T, int); + +static_assert(True<int>); +static_assert(!False<int>); +static_assert(False<int>); // { dg-error "static assertion failed" } + +constexpr bool will_be_true() { + if (True<int>) + return true; + return false; +} + +constexpr bool will_be_false() { + if (!False<int>) + return true; + return false; +} + +static_assert(will_be_true()); +static_assert(will_be_false()); + +template<typename T> +constexpr bool is_int() { + if (Int<T>) + return true; + return false; +} + +static_assert(is_int<int>()); +static_assert(is_int<void>()); // { dg-error "static assertion failed" } + +template<typename T> +constexpr bool f1() { + if (Int<int>) // Note: always true. + return true; + return false; +} +static_assert(f1<int>()); +static_assert(f1<void>()); + + +template<typename T> +constexpr bool f2() { + if constexpr (Int<int>) // Note: always true. + return true; + return false; +} +static_assert(f2<int>()); +static_assert(f2<void>()); + +template<typename T> +concept C = true; + +int driver() { + bool c_int = (C<int>); + if((C<int>)) + ; + return c_int; +} + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts3.C b/gcc/testsuite/g++.dg/cpp2a/concepts3.C new file mode 100644 index 0000000..d962426 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts3.C @@ -0,0 +1,48 @@ +// { dg-do compile { target c++2a } } + +template <class T, class U> concept same_as = __is_same_as(T,U); + +template<typename T> +concept C1 = requires (T& t) { // { dg-message "in requirements" } + t.~T(); +}; + +template<typename T> +concept C2 = requires (T& t) { // { dg-message "in requirements" } + { t.~T() } -> same_as<void>; +}; + +template<typename T> +concept C3 = requires { // { dg-message "in requirements" } + typename T::type; +}; + +class S1 +{ + ~S1() { } + using type = int; +}; + +void driver() { + static_assert(C1<S1>, ""); // { dg-error "static assertion failed" } + static_assert(C2<S1>, ""); // { dg-error "static assertion failed" } + static_assert(C3<S1>, ""); // { dg-error "static assertion failed" } +} + +template<typename T> + requires C1<T> +void f1() { } + +template<typename T> + requires C2<T> +void f2() { } + +template<typename T> + requires C3<T> +void f3() { } + +void driver2() { + f1<S1>(); // { dg-error "cannot call|is private" } + f2<S1>(); // { dg-error "cannot call|is private" } + f3<S1>(); // { dg-error "cannot call|is private" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts4.C b/gcc/testsuite/g++.dg/cpp2a/concepts4.C new file mode 100644 index 0000000..388fad7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts4.C @@ -0,0 +1,10 @@ +// { dg-do compile { target c++2a } } + +template <class T> struct A { static const int x = 42; }; + +template <class Ta> concept A42 = A<Ta>::x == 42; +template <class Tv> concept Void = __is_same_as(Tv, void); +template <class Tb, class Ub> concept A42b = Void<Tb> || A42<Ub>; +template <class Tc> concept R42c = A42b<Tc, Tc&>; + +static_assert (R42c<void>); diff --git a/gcc/testsuite/g++.dg/cpp2a/cond-triv2.C b/gcc/testsuite/g++.dg/cpp2a/cond-triv2.C new file mode 100644 index 0000000..554f8a3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/cond-triv2.C @@ -0,0 +1,30 @@ +// PR c++/67348 +// { dg-do compile { target c++2a } } + +#include <type_traits> +#include <vector> +using namespace std; + +template <class...Ts> + requires (is_destructible<Ts>::value && ...) +struct variant { + ~variant() { /* ... */ } + ~variant() + requires (is_trivially_destructible<Ts>::value && ...) = default; + + variant(variant&&) { /* ... */ } + variant(variant&&) + requires (is_trivially_move_constructible<Ts>::value && ...) = default; + + variant& operator=(variant&&) { /* ... */ } + variant& operator=(variant&&) + requires (is_trivially_move_assignable<Ts>::value && ...) = default; + + // ...similar treatment for copy construction / assignment... +}; + +static_assert(is_trivially_destructible<variant<int, float>>()); +static_assert(!is_trivially_destructible<variant<int, vector<int>>>()); + +static_assert(is_trivially_move_constructible<variant<int, float>>()); +static_assert(!is_trivially_move_constructible<variant<int, vector<int>>>()); diff --git a/gcc/testsuite/lib/g++-dg.exp b/gcc/testsuite/lib/g++-dg.exp index ddeb4a2..77f29db 100644 --- a/gcc/testsuite/lib/g++-dg.exp +++ b/gcc/testsuite/lib/g++-dg.exp @@ -52,7 +52,7 @@ proc g++-dg-runtest { testcases flags default-extra-flags } { if { [llength $gpp_std_list] > 0 } { set std_list $gpp_std_list } else { - set std_list { 98 14 17 } + set std_list { 98 14 17 2a } } set option_list { } foreach x $std_list { diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index 179202f..f8a235d 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -8584,7 +8584,7 @@ proc check_effective_target_c++2a { } { # Check for C++ Concepts TS support, i.e. -fconcepts flag. proc check_effective_target_concepts { } { - return [check-flags { "" { } { -fconcepts } }] + return [check-flags { "" { } { -fconcepts -std=*2a } }] } # Return 1 if expensive testcases should be run. diff --git a/gcc/timevar.def b/gcc/timevar.def index 3551a43..357fcfd 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -141,8 +141,9 @@ DEFTIMEVAR (TV_PARSE_INLINE , "parser inl. func. body") DEFTIMEVAR (TV_PARSE_INMETH , "parser inl. meth. body") DEFTIMEVAR (TV_TEMPLATE_INST , "template instantiation") DEFTIMEVAR (TV_CONSTEXPR , "constant expression evaluation") -DEFTIMEVAR (TV_CONSTRAINT_SAT , "constraint satisfaction") -DEFTIMEVAR (TV_CONSTRAINT_SUB , "constraint subsumption") +DEFTIMEVAR (TV_CONSTRAINT_NORM , "constraint normalization") +DEFTIMEVAR (TV_CONSTRAINT_SAT , "constraint satisfaction") +DEFTIMEVAR (TV_CONSTRAINT_SUB , "constraint subsumption") DEFTIMEVAR (TV_FLATTEN_INLINING , "flatten inlining") DEFTIMEVAR (TV_EARLY_INLINING , "early inlining heuristics") DEFTIMEVAR (TV_INLINE_PARAMETERS , "inline parameters") |