Add C++ Concepts TS support.

gcc/c-family/
	* c-common.c (c_common_reswords): Add __is_same_as, concept, requires.
	* c-common.h (enum rid): Add RID_IS_SAME_AS, RID_CONCEPT, RID_REQUIRES.
	(D_CXX_CONCEPTS, D_CXX_CONCEPTS_FLAGS): New.
	* c-cppbuiltin.c (c_cpp_builtins): Define __cpp_concepts.
	* c-opts.c (set_std_cxx1z): Set flag_concepts.
	* c.opt (fconcepts): New.
gcc/cp/
	* constraint.cc, logic.cc: New files.
	* Make-lang.in (CXX_AND_OBJCXX_OBJS): Add constraint.o and logic.o.
	(c++.tags): Also process .cc files.
	* call.c (enum rejection_reason_code): Add rr_constraint_failure.
	(print_z_candidate): Handle it.
	(constraint_failure): New.
	(add_function_candidate): Check constraints.
	(build_new_function_call): Handle evaluating concepts.
	(joust): Check more_constrained.
	* class.c (add_method): Check equivalently_constrained.
	(build_clone): Copy constraints.
	(currently_open_class): Return tree.
	(resolve_address_of_overloaded_function): Check constraints.
	* constexpr.c (cxx_eval_constant_expression): Handle REQUIRES_EXPR.
	(potential_constant_expression_1): Likewise.
	* cp-objcp-common.c (cp_tree_size): Handle CONSTRAINT_INFO.
	(cp_common_init_ts): Handle WILDCARD_DECL and REQUIRES_EXPR.
	* cp-tree.def: Add CONSTRAINT_INFO, WILDCARD_DECL, REQUIRES_EXPR,
	SIMPLE_REQ, TYPE_REQ, COMPOUND_REQ, NESTED_REQ, PRED_CONSTR,
	EXPR_CONSTR, TYPE_CONSTR, ICONV_CONSTR, DEDUCT_CONSTR,
	EXCEPT_CONSTR, PARM_CONSTR, CONJ_CONSTR, DISJ_CONSTR.
	* cp-tree.h (struct tree_constraint_info, check_nonnull)
	(check_constraint_info, CI_TEMPLATE_REQS, CI_DECLARATOR_REQS)
	(CI_ASSOCIATED_CONSTRAINTS, CI_NORMALIZED_CONSTRAINTS)
	(CI_ASSUMPTIONS, TEMPLATE_PARMS_CONSTRAINTS)
	(TEMPLATE_PARM_CONSTRAINTS, COMPOUND_REQ_NOEXCEPT_P)
	(PLACEHOLDER_TYPE_CONSTRAINTS, PRED_CONSTR_EXPR, EXPR_CONSTR_EXPR)
	(TYPE_CONSTR_TYPE, ICONV_CONSTR_EXPR, ICONV_CONSTR_TYPE)
	(DEDUCT_CONSTR_EXPR, DEDUCT_CONSTR_PATTERN)
	(DEDUCT_CONSTR_PLACEHOLDER, EXCEPT_CONSTR_EXPR, PARM_CONSTR_PARMS)
	(PARM_CONSTR_OPERAND, CONSTRAINT_VAR_P, CONSTRAINED_PARM_CONCEPT)
	(CONSTRAINED_PARM_EXTRA_ARGS, CONSTRAINED_PARM_PROTOTYPE)
	(DECL_DECLARED_CONCEPT_P, WILDCARD_PACK_P, struct cp_unevaluated)
	(struct local_specialization_stack, enum auto_deduction_context)
	(variable_concept_p, concept_template_p)
	(struct deferring_access_check_sentinel): New.
	(enum cp_tree_node_structure_enum): Add TS_CP_CONSTRAINT_INFO.
	(union lang_tree_node): Add constraint_info field.
	(struct lang_decl_base): Add concept_p flag.
	(enum cp_decl_spec): Add ds_concept.
	(struct cp_declarator): Add requires_clause.
	* cxx-pretty-print.c (cxx_pretty_printer::primary_expression)
	(cxx_pretty_printer::expression): Handle REQUIRES_EXPR,
	TRAIT_EXPR, *_CONSTR.
	(pp_cxx_parameter_declaration_clause): Accept a chain of
	PARM_DECLs.
	(cxx_pretty_printer::declarator): Print requires-clause.
	(pp_cxx_template_declaration): Likewise.
	(pp_cxx_trait_expression): Handle CPTK_IS_SAME_AS.
	(pp_cxx_requires_clause, pp_cxx_requirement)
	(pp_cxx_requirement_list, pp_cxx_requirement_body)
	(pp_cxx_requires_expr, pp_cxx_simple_requirement)
	(pp_cxx_type_requirement, pp_cxx_compound_requirement)
	(pp_cxx_nested_requirement, pp_cxx_predicate_constraint)
	(pp_cxx_expression_constraint, pp_cxx_type_constraint)
	(pp_cxx_implicit_conversion_constraint)
	(pp_cxx_argument_deduction_constraint)
	(pp_cxx_exception_constraint, pp_cxx_parameterized_constraint)
	(pp_cxx_conjunction, pp_cxx_disjunction, pp_cxx_constraint): New.
	* cxx-pretty-print.h: Declare them.
	* decl.c (decls_match): Compare constraints.
	(duplicate_decls): Likewise.  Remove constraints before freeing.
	(cxx_init_decl_processing): Call init_constraint_processing.
	(cp_finish_decl): Diagnose concept without initializer.
	(grokfndecl, grokvardecl): Handle concepts and constraints.
	(grokdeclarator): Handle concept, requires-clause.
	(grokparms): No longer static.
	(xref_tag_1): Check constraints.
	(finish_function): Call check_function_concept.
	(cp_tree_node_structure): Handle CONSTRAINT_INFO.
	(check_concept_refinement, is_concept_var, check_concept_fn): New.
	* decl2.c (check_classfn): Compare constraints.
	(mark_used): Don't instantiate concepts.
	* error.c (dump_template_decl): Print constraints.
	(dump_function_decl): Likewise.
	(dump_expr): Handle REQUIRES_EXPR, *_REQ, *_CONSTR.
	* lex.c (init_reswords): Set D_CXX_CONCEPTS.
	* method.c (implicitly_declare_fn): Copy constraints from
	inherited ctor.
	* parser.h (struct cp_parser): Add in_result_type_constraint_p and
	prevent_constrained_type_specifiers fields.
	* parser.c (make_call_declarator): Add requires_clause parm.
	(cp_parser_new): Clear prevent_constrained_type_specifiers.
	(cp_parser_primary_expression): Handle RID_IS_SAME_AS, RID_REQUIRES.
	(cp_parser_postfix_expression): Set prevent_constrained_type_specifiers.
	(cp_parser_trait_expr): Handle RID_IS_SAME_AS.
	(cp_parser_declaration): Handle concept introduction.
	(cp_parser_member_declaration): Likewise.
	(cp_parser_template_parameter): Handle constrained parameter.
	(cp_parser_type_parameter): Handle constraints.
	(cp_parser_decl_specifier_seq): Handle RID_CONCEPT.
	(cp_parser_template_id): Handle partial concept id.
	(cp_parser_type_name): Add overload that takes typename_keyword_p.
	Handle constrained parameter.
	(cp_parser_nonclass_name): Handle concept names.
	(cp_parser_alias_declaration): Handle constraints.
	(cp_parser_late_return_type_opt): Also handle requires-clause.
	(cp_parser_type_id_1): Handle deduction constraint.
	(cp_parser_parameter_declaration): Handle constrained parameters.
	(cp_parser_class_specifier_1): Handle constraints.
	(cp_parser_template_declaration_after_parameters): Split out from
	cp_parser_template_declaration_after_export.
	(cp_parser_single_declaration): Handle constraints.
	(synthesize_implicit_template_parm): Handle constraints.
	(cp_parser_maybe_concept_name, cp_parser_maybe_partial_concept_id)
	(cp_parser_introduction_list, get_id_declarator)
	(get_unqualified_id, is_constrained_parameter)
	(cp_parser_check_constrained_type_parm)
	(cp_parser_constrained_type_template_parm)
	(cp_parser_constrained_template_template_parm)
	(constrained_non_type_template_parm, finish_constrained_parameter)
	(declares_constrained_type_template_parameter)
	(declares_constrained_template_template_parameter)
	(check_type_concept, cp_parser_maybe_constrained_type_specifier)
	(cp_parser_maybe_concept_name, cp_parser_maybe_partial_concept_id)
	(cp_parser_requires_clause, cp_parser_requires_clause_opt)
	(cp_parser_requires_expression)
	(cp_parser_requirement_parameter_list, cp_parser_requirement_body)
	(cp_parser_requirement_list, cp_parser_requirement)
	(cp_parser_simple_requirement, cp_parser_type_requirement)
	(cp_parser_compound_requirement, cp_parser_nested_requirement)
	(cp_parser_template_introduction)
	(cp_parser_explicit_template_declaration)
	(get_concept_from_constraint): New.
	* pt.c (local_specialization_stack): Implement.
	(maybe_new_partial_specialization): New.
	(maybe_process_partial_specialization): Use it.
	(retrieve_local_specialization, register_local_specialization)
	(template_parm_to_arg, build_template_decl, extract_fnparm_pack)
	(tsubst_expr): No longer static.
	(spec_hasher::equal): Compare constraints.
	(determine_specialization): Handle constraints.
	(check_explicit_specialization): Handle concepts.
	(process_template_parm): Handle constraints.
	(end_template_parm_list): Add overload taking no arguments.
	(process_partial_specialization): Handle concepts and constraints.
	Register partial specializations of variable templates.
	(redeclare_class_template): Handle constraints.
	(convert_template_argument): Handle WILDCARD_DECL.  Check
	is_compatible_template_arg.
	(coerce_template_parameter_pack): Handle wildcard packs.
	(coerce_template_parms): DR 1430 also applies to concepts.  Add
	overloads taking fewer parameters.
	(lookup_template_class_1): Handle constraints.
	(lookup_template_variable): Concepts are always bool.
	(finish_template_variable): Handle concepts and constraints.
	(tsubst_friend_class): Handle constraints.
	(gen_elem_of_pack_expansion_instantiation): Handle constraints.
	(tsubst_pack_expansion): Handle local parameters.
	(tsubst_decl) [FUNCTION_DECL]: Handle constraints.
	(tsubst) [TEMPLATE_TYPE_PARM]: Handle deduction constraints.
	(tsubst_copy_and_build): Handle REQUIRES_EXPR.
	(more_specialized_fn, more_specialized_partial_spec): Check constraints.
	(more_specialized_inst): Split out from most_specialized_instantiation.
	(most_specialized_partial_spec): Check constraints.
	(instantiate_decl): Never instantiate a concept.
	(value_dependent_expression_p): Handle REQUIRES_EXPR, TYPE_REQ,
	variable concepts.
	(type_dependent_expression_p): Handle WILDCARD_DECL, REQUIRES_EXPR.
	(instantiation_dependent_r): Handle REQUIRES_EXPR and concepts.
	(do_auto_deduction): Add overload taking tsubst flags and context enum.
	Handle constraints.
	(get_template_for_ordering, most_constrained_function)
	(is_compatible_template_arg, convert_wildcard_argument)
	(struct	constr_entry, struct constr_hasher, decl_constraints)
	(valid_constraints_p, get_constraints, set_constraints)
	(remove_constraints, init_constraint_processing): New.
	* ptree.c (cxx_print_xnode): Handle CONSTRAINT_INFO.
	* search.c (lookup_member): Do lookup in the open partial
	instantiation.
	* semantics.c (finish_template_template_parm): Handle constraints.
	(fixup_template_type): New.
	(finish_template_type): Call it.
	(trait_expr_value, finish_trait_expr): Handle CPTK_IS_SAME_AS.
	* tree.c (cp_tree_equal): Handle local parameters, CONSTRAINT_INFO.
	(cp_walk_subtrees): Handle REQUIRES_EXPR.
	* typeck.c (cp_build_function_call_vec): Check constraints.

Co-Authored-By: Braden Obrzut <admin@maniacsvault.net>
Co-Authored-By: Jason Merrill <jason@redhat.com>

From-SVN: r226713
This commit is contained in:
Andrew Sutton 2015-08-07 05:44:49 +00:00 committed by Jason Merrill
parent bf5372e7f0
commit 971e17ff87
158 changed files with 10006 additions and 243 deletions

View File

@ -1,3 +1,15 @@
2015-08-06 Andrew Sutton <andrew.n.sutton@gmail.com>
Braden Obrzut <admin@maniacsvault.net>
Jason Merrill <jason@redhat.com>
Add C++ Concepts TS support.
* c-common.c (c_common_reswords): Add __is_same_as, concept, requires.
* c-common.h (enum rid): Add RID_IS_SAME_AS, RID_CONCEPT, RID_REQUIRES.
(D_CXX_CONCEPTS, D_CXX_CONCEPTS_FLAGS): New.
* c-cppbuiltin.c (c_cpp_builtins): Define __cpp_concepts.
* c-opts.c (set_std_cxx1z): Set flag_concepts.
* c.opt (fconcepts): New.
2015-08-02 Patrick Palka <ppalka@gcc.gnu.org>
* c-indentation.c (should_warn_for_misleading_indentation):

View File

@ -491,6 +491,7 @@ const struct c_common_resword c_common_reswords[] =
{ "__is_literal_type", RID_IS_LITERAL_TYPE, D_CXXONLY },
{ "__is_pod", RID_IS_POD, D_CXXONLY },
{ "__is_polymorphic", RID_IS_POLYMORPHIC, D_CXXONLY },
{ "__is_same_as", RID_IS_SAME_AS, D_CXXONLY },
{ "__is_standard_layout", RID_IS_STD_LAYOUT, D_CXXONLY },
{ "__is_trivial", RID_IS_TRIVIAL, D_CXXONLY },
{ "__is_trivially_assignable", RID_IS_TRIVIALLY_ASSIGNABLE, D_CXXONLY },
@ -589,6 +590,11 @@ const struct c_common_resword c_common_reswords[] =
{ "volatile", RID_VOLATILE, 0 },
{ "wchar_t", RID_WCHAR, D_CXXONLY },
{ "while", RID_WHILE, 0 },
/* Concepts-related keywords */
{ "concept", RID_CONCEPT, D_CXX_CONCEPTS_FLAGS | D_CXXWARN },
{ "requires", RID_REQUIRES, D_CXX_CONCEPTS_FLAGS | D_CXXWARN },
/* These Objective-C keywords are recognized only immediately after
an '@'. */
{ "compatibility_alias", RID_AT_ALIAS, D_OBJC },

View File

@ -142,6 +142,7 @@ enum rid
RID_IS_EMPTY, RID_IS_ENUM,
RID_IS_FINAL, RID_IS_LITERAL_TYPE,
RID_IS_POD, RID_IS_POLYMORPHIC,
RID_IS_SAME_AS,
RID_IS_STD_LAYOUT, RID_IS_TRIVIAL,
RID_IS_TRIVIALLY_ASSIGNABLE, RID_IS_TRIVIALLY_CONSTRUCTIBLE,
RID_IS_TRIVIALLY_COPYABLE,
@ -150,6 +151,9 @@ enum rid
/* C++11 */
RID_CONSTEXPR, RID_DECLTYPE, RID_NOEXCEPT, RID_NULLPTR, RID_STATIC_ASSERT,
/* C++ concepts */
RID_CONCEPT, RID_REQUIRES,
/* Cilk Plus keywords. */
RID_CILK_SPAWN, RID_CILK_SYNC, RID_CILK_FOR,
@ -386,6 +390,9 @@ extern machine_mode c_default_pointer_mode;
#define D_OBJC 0x080 /* In Objective C and neither C nor C++. */
#define D_CXX_OBJC 0x100 /* In Objective C, and C++, but not C. */
#define D_CXXWARN 0x200 /* In C warn with -Wcxx-compat. */
#define D_CXX_CONCEPTS 0x400 /* In C++, only with concepts. */
#define D_CXX_CONCEPTS_FLAGS D_CXXONLY | D_CXX_CONCEPTS
/* The reserved keyword table. */
extern const struct c_common_resword c_common_reswords[];

View File

@ -865,6 +865,10 @@ c_cpp_builtins (cpp_reader *pfile)
cpp_define (pfile, "__cpp_variable_templates=201304");
cpp_define (pfile, "__cpp_digit_separators=201309");
}
if (flag_concepts)
/* Use a value smaller than the 201507 specified in
the TS, since we don't yet support extended auto. */
cpp_define (pfile, "__cpp_concepts=201500");
if (flag_sized_deallocation)
cpp_define (pfile, "__cpp_sized_deallocation=201309");
}

View File

@ -1558,6 +1558,8 @@ set_std_cxx1z (int iso)
/* C++11 includes the C99 standard library. */
flag_isoc94 = 1;
flag_isoc99 = 1;
/* Enable concepts by default. */
flag_concepts = 1;
flag_isoc11 = 1;
cxx_dialect = cxx1z;
lang_hooks.name = "GNU C++14"; /* Pretend C++14 till standarization. */

View File

@ -1086,6 +1086,10 @@ fcilkplus
C ObjC C++ ObjC++ LTO Report Var(flag_cilkplus) Init(0)
Enable Cilk Plus
fconcepts
C++ ObjC++ Var(flag_concepts)
Enable support for C++ concepts
fcond-mismatch
C ObjC C++ ObjC++
Allow the arguments of the '?' operator to have different types

View File

@ -1,3 +1,186 @@
2015-08-06 Andrew Sutton <andrew.n.sutton@gmail.com>
Braden Obrzut <admin@maniacsvault.net>
Jason Merrill <jason@redhat.com>
Add C++ Concepts TS support.
* constraint.cc, logic.cc: New files.
* Make-lang.in (CXX_AND_OBJCXX_OBJS): Add constraint.o and logic.o.
(c++.tags): Also process .cc files.
* call.c (enum rejection_reason_code): Add rr_constraint_failure.
(print_z_candidate): Handle it.
(constraint_failure): New.
(add_function_candidate): Check constraints.
(build_new_function_call): Handle evaluating concepts.
(joust): Check more_constrained.
* class.c (add_method): Check equivalently_constrained.
(build_clone): Copy constraints.
(currently_open_class): Return tree.
(resolve_address_of_overloaded_function): Check constraints.
* constexpr.c (cxx_eval_constant_expression): Handle REQUIRES_EXPR.
(potential_constant_expression_1): Likewise.
* cp-objcp-common.c (cp_tree_size): Handle CONSTRAINT_INFO.
(cp_common_init_ts): Handle WILDCARD_DECL and REQUIRES_EXPR.
* cp-tree.def: Add CONSTRAINT_INFO, WILDCARD_DECL, REQUIRES_EXPR,
SIMPLE_REQ, TYPE_REQ, COMPOUND_REQ, NESTED_REQ, PRED_CONSTR,
EXPR_CONSTR, TYPE_CONSTR, ICONV_CONSTR, DEDUCT_CONSTR,
EXCEPT_CONSTR, PARM_CONSTR, CONJ_CONSTR, DISJ_CONSTR.
* cp-tree.h (struct tree_constraint_info, check_nonnull)
(check_constraint_info, CI_TEMPLATE_REQS, CI_DECLARATOR_REQS)
(CI_ASSOCIATED_CONSTRAINTS, CI_NORMALIZED_CONSTRAINTS)
(CI_ASSUMPTIONS, TEMPLATE_PARMS_CONSTRAINTS)
(TEMPLATE_PARM_CONSTRAINTS, COMPOUND_REQ_NOEXCEPT_P)
(PLACEHOLDER_TYPE_CONSTRAINTS, PRED_CONSTR_EXPR, EXPR_CONSTR_EXPR)
(TYPE_CONSTR_TYPE, ICONV_CONSTR_EXPR, ICONV_CONSTR_TYPE)
(DEDUCT_CONSTR_EXPR, DEDUCT_CONSTR_PATTERN)
(DEDUCT_CONSTR_PLACEHOLDER, EXCEPT_CONSTR_EXPR, PARM_CONSTR_PARMS)
(PARM_CONSTR_OPERAND, CONSTRAINT_VAR_P, CONSTRAINED_PARM_CONCEPT)
(CONSTRAINED_PARM_EXTRA_ARGS, CONSTRAINED_PARM_PROTOTYPE)
(DECL_DECLARED_CONCEPT_P, WILDCARD_PACK_P, struct cp_unevaluated)
(struct local_specialization_stack, enum auto_deduction_context)
(variable_concept_p, concept_template_p)
(struct deferring_access_check_sentinel): New.
(enum cp_tree_node_structure_enum): Add TS_CP_CONSTRAINT_INFO.
(union lang_tree_node): Add constraint_info field.
(struct lang_decl_base): Add concept_p flag.
(enum cp_decl_spec): Add ds_concept.
(struct cp_declarator): Add requires_clause.
* cxx-pretty-print.c (cxx_pretty_printer::primary_expression)
(cxx_pretty_printer::expression): Handle REQUIRES_EXPR,
TRAIT_EXPR, *_CONSTR.
(pp_cxx_parameter_declaration_clause): Accept a chain of
PARM_DECLs.
(cxx_pretty_printer::declarator): Print requires-clause.
(pp_cxx_template_declaration): Likewise.
(pp_cxx_trait_expression): Handle CPTK_IS_SAME_AS.
(pp_cxx_requires_clause, pp_cxx_requirement)
(pp_cxx_requirement_list, pp_cxx_requirement_body)
(pp_cxx_requires_expr, pp_cxx_simple_requirement)
(pp_cxx_type_requirement, pp_cxx_compound_requirement)
(pp_cxx_nested_requirement, pp_cxx_predicate_constraint)
(pp_cxx_expression_constraint, pp_cxx_type_constraint)
(pp_cxx_implicit_conversion_constraint)
(pp_cxx_argument_deduction_constraint)
(pp_cxx_exception_constraint, pp_cxx_parameterized_constraint)
(pp_cxx_conjunction, pp_cxx_disjunction, pp_cxx_constraint): New.
* cxx-pretty-print.h: Declare them.
* decl.c (decls_match): Compare constraints.
(duplicate_decls): Likewise. Remove constraints before freeing.
(cxx_init_decl_processing): Call init_constraint_processing.
(cp_finish_decl): Diagnose concept without initializer.
(grokfndecl, grokvardecl): Handle concepts and constraints.
(grokdeclarator): Handle concept, requires-clause.
(grokparms): No longer static.
(xref_tag_1): Check constraints.
(finish_function): Call check_function_concept.
(cp_tree_node_structure): Handle CONSTRAINT_INFO.
(check_concept_refinement, is_concept_var, check_concept_fn): New.
* decl2.c (check_classfn): Compare constraints.
(mark_used): Don't instantiate concepts.
* error.c (dump_template_decl): Print constraints.
(dump_function_decl): Likewise.
(dump_expr): Handle REQUIRES_EXPR, *_REQ, *_CONSTR.
* lex.c (init_reswords): Set D_CXX_CONCEPTS.
* method.c (implicitly_declare_fn): Copy constraints from
inherited ctor.
* parser.h (struct cp_parser): Add in_result_type_constraint_p and
prevent_constrained_type_specifiers fields.
* parser.c (make_call_declarator): Add requires_clause parm.
(cp_parser_new): Clear prevent_constrained_type_specifiers.
(cp_parser_primary_expression): Handle RID_IS_SAME_AS, RID_REQUIRES.
(cp_parser_postfix_expression): Set prevent_constrained_type_specifiers.
(cp_parser_trait_expr): Handle RID_IS_SAME_AS.
(cp_parser_declaration): Handle concept introduction.
(cp_parser_member_declaration): Likewise.
(cp_parser_template_parameter): Handle constrained parameter.
(cp_parser_type_parameter): Handle constraints.
(cp_parser_decl_specifier_seq): Handle RID_CONCEPT.
(cp_parser_template_id): Handle partial concept id.
(cp_parser_type_name): Add overload that takes typename_keyword_p.
Handle constrained parameter.
(cp_parser_nonclass_name): Handle concept names.
(cp_parser_alias_declaration): Handle constraints.
(cp_parser_late_return_type_opt): Also handle requires-clause.
(cp_parser_type_id_1): Handle deduction constraint.
(cp_parser_parameter_declaration): Handle constrained parameters.
(cp_parser_class_specifier_1): Handle constraints.
(cp_parser_template_declaration_after_parameters): Split out from
cp_parser_template_declaration_after_export.
(cp_parser_single_declaration): Handle constraints.
(synthesize_implicit_template_parm): Handle constraints.
(cp_parser_maybe_concept_name, cp_parser_maybe_partial_concept_id)
(cp_parser_introduction_list, get_id_declarator)
(get_unqualified_id, is_constrained_parameter)
(cp_parser_check_constrained_type_parm)
(cp_parser_constrained_type_template_parm)
(cp_parser_constrained_template_template_parm)
(constrained_non_type_template_parm, finish_constrained_parameter)
(declares_constrained_type_template_parameter)
(declares_constrained_template_template_parameter)
(check_type_concept, cp_parser_maybe_constrained_type_specifier)
(cp_parser_maybe_concept_name, cp_parser_maybe_partial_concept_id)
(cp_parser_requires_clause, cp_parser_requires_clause_opt)
(cp_parser_requires_expression)
(cp_parser_requirement_parameter_list, cp_parser_requirement_body)
(cp_parser_requirement_list, cp_parser_requirement)
(cp_parser_simple_requirement, cp_parser_type_requirement)
(cp_parser_compound_requirement, cp_parser_nested_requirement)
(cp_parser_template_introduction)
(cp_parser_explicit_template_declaration)
(get_concept_from_constraint): New.
* pt.c (local_specialization_stack): Implement.
(maybe_new_partial_specialization): New.
(maybe_process_partial_specialization): Use it.
(retrieve_local_specialization, register_local_specialization)
(template_parm_to_arg, build_template_decl, extract_fnparm_pack)
(tsubst_expr): No longer static.
(spec_hasher::equal): Compare constraints.
(determine_specialization): Handle constraints.
(check_explicit_specialization): Handle concepts.
(process_template_parm): Handle constraints.
(end_template_parm_list): Add overload taking no arguments.
(process_partial_specialization): Handle concepts and constraints.
Register partial specializations of variable templates.
(redeclare_class_template): Handle constraints.
(convert_template_argument): Handle WILDCARD_DECL. Check
is_compatible_template_arg.
(coerce_template_parameter_pack): Handle wildcard packs.
(coerce_template_parms): DR 1430 also applies to concepts. Add
overloads taking fewer parameters.
(lookup_template_class_1): Handle constraints.
(lookup_template_variable): Concepts are always bool.
(finish_template_variable): Handle concepts and constraints.
(tsubst_friend_class): Handle constraints.
(gen_elem_of_pack_expansion_instantiation): Handle constraints.
(tsubst_pack_expansion): Handle local parameters.
(tsubst_decl) [FUNCTION_DECL]: Handle constraints.
(tsubst) [TEMPLATE_TYPE_PARM]: Handle deduction constraints.
(tsubst_copy_and_build): Handle REQUIRES_EXPR.
(more_specialized_fn, more_specialized_partial_spec): Check constraints.
(more_specialized_inst): Split out from most_specialized_instantiation.
(most_specialized_partial_spec): Check constraints.
(instantiate_decl): Never instantiate a concept.
(value_dependent_expression_p): Handle REQUIRES_EXPR, TYPE_REQ,
variable concepts.
(type_dependent_expression_p): Handle WILDCARD_DECL, REQUIRES_EXPR.
(instantiation_dependent_r): Handle REQUIRES_EXPR and concepts.
(do_auto_deduction): Add overload taking tsubst flags and context enum.
Handle constraints.
(get_template_for_ordering, most_constrained_function)
(is_compatible_template_arg, convert_wildcard_argument)
(struct constr_entry, struct constr_hasher, decl_constraints)
(valid_constraints_p, get_constraints, set_constraints)
(remove_constraints, init_constraint_processing): New.
* ptree.c (cxx_print_xnode): Handle CONSTRAINT_INFO.
* search.c (lookup_member): Do lookup in the open partial
instantiation.
* semantics.c (finish_template_template_parm): Handle constraints.
(fixup_template_type): New.
(finish_template_type): Call it.
(trait_expr_value, finish_trait_expr): Handle CPTK_IS_SAME_AS.
* tree.c (cp_tree_equal): Handle local parameters, CONSTRAINT_INFO.
(cp_walk_subtrees): Handle REQUIRES_EXPR.
* typeck.c (cp_build_function_call_vec): Check constraints.
2015-08-06 Jason Merrill <jason@redhat.com>
PR c++/66533

View File

@ -78,7 +78,8 @@ CXX_AND_OBJCXX_OBJS = cp/call.o cp/decl.o cp/expr.o cp/pt.o cp/typeck2.o \
cp/mangle.o cp/cp-objcp-common.o cp/name-lookup.o cp/cxx-pretty-print.o \
cp/cp-cilkplus.o \
cp/cp-gimplify.o cp/cp-array-notation.o cp/lambda.o \
cp/vtable-class-hierarchy.o cp/constexpr.o cp/cp-ubsan.o $(CXX_C_OBJS)
cp/vtable-class-hierarchy.o cp/constexpr.o cp/cp-ubsan.o \
cp/constraint.o cp/logic.o $(CXX_C_OBJS)
# Language-specific object files for C++.
CXX_OBJS = cp/cp-lang.o c-family/stub-objc.o $(CXX_AND_OBJCXX_OBJS)
@ -131,7 +132,7 @@ c++.srcinfo:
c++.srcextra:
c++.tags: force
cd $(srcdir)/cp; etags -o TAGS.sub *.c *.h --language=none \
cd $(srcdir)/cp; etags -o TAGS.sub *.c *.cc *.h --language=none \
--regex='/DEFTREECODE [(]\([A-Z_]+\)/\1/' cp-tree.def; \
etags --include TAGS.sub --include ../TAGS.sub

View File

@ -425,7 +425,8 @@ enum rejection_reason_code {
rr_arg_conversion,
rr_bad_arg_conversion,
rr_template_unification,
rr_invalid_copy
rr_invalid_copy,
rr_constraint_failure
};
struct conversion_info {
@ -688,6 +689,27 @@ invalid_copy_with_fn_template_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.
static struct rejection_reason *
constraint_failure (tree fn)
{
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;
}
/* Dynamically allocate a conversion. */
static conversion *
@ -1957,10 +1979,20 @@ add_function_candidate (struct z_candidate **candidates,
viable = 0;
reason = arity_rejection (first_arg, i + remaining, len);
}
/* Second, for a function to be viable, its constraints must be
satisfied. */
if (flag_concepts && viable
&& !constraints_satisfied_p (fn))
{
reason = constraint_failure (fn);
viable = false;
}
/* When looking for a function from a subobject from an implicit
copy/move constructor/operator=, don't consider anything that takes (a
reference to) an unrelated type. See c++/44909 and core 1092. */
else if (parmlist && (flags & LOOKUP_DEFAULTED))
if (viable && parmlist && (flags & LOOKUP_DEFAULTED))
{
if (DECL_CONSTRUCTOR_P (fn))
i = 1;
@ -1984,7 +2016,7 @@ add_function_candidate (struct z_candidate **candidates,
if (! viable)
goto out;
/* Second, for F to be a viable function, there shall exist for each
/* Third, for F to be a viable function, there shall exist for each
argument an implicit conversion sequence that converts that argument
to the corresponding parameter of F. */
@ -3387,6 +3419,13 @@ print_z_candidate (location_t loc, const char *msgstr,
" a constructor taking a single argument of its own "
"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);
}
break;
case rr_none:
default:
/* This candidate didn't have any issues or we failed to
@ -4044,9 +4083,13 @@ build_new_function_call (tree fn, vec<tree, va_gc> **args, bool koenig_p,
{
if (complain & tf_error)
{
// If there is a single (non-viable) function candidate,
// let the error be diagnosed by cp_build_function_call_vec.
if (!any_viable_p && candidates && ! candidates->next
&& (TREE_CODE (candidates->fn) == FUNCTION_DECL))
return cp_build_function_call_vec (candidates->fn, args, complain);
// Otherwise, emit notes for non-viable candidates.
if (TREE_CODE (fn) == TEMPLATE_ID_EXPR)
fn = TREE_OPERAND (fn, 0);
print_error_for_call_failure (fn, *args, candidates);
@ -4061,7 +4104,26 @@ build_new_function_call (tree fn, vec<tree, va_gc> **args, bool koenig_p,
through flags so that later we can use it to decide whether to warn
about peculiar null pointer conversion. */
if (TREE_CODE (fn) == TEMPLATE_ID_EXPR)
flags |= LOOKUP_EXPLICIT_TMPL_ARGS;
{
/* If overload resolution selects a specialization of a
function concept 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;
}
result = build_over_call (cand, flags, complain);
}
@ -9095,6 +9157,15 @@ 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.
if (flag_concepts)
{
winner = more_constrained (cand1->fn, cand2->fn);
if (winner)
return winner;
}
/* Check whether we can discard a builtin candidate, either because we
have two identical ones or matching builtin and non-builtin candidates.

View File

@ -1138,7 +1138,8 @@ add_method (tree type, tree method, tree using_decl)
if (compparms (parms1, parms2)
&& (!DECL_CONV_FN_P (fn)
|| same_type_p (TREE_TYPE (fn_type),
TREE_TYPE (method_type))))
TREE_TYPE (method_type)))
&& equivalently_constrained (fn, method))
{
/* For function versions, their parms and types match
but they are not duplicates. Record function versions
@ -4602,6 +4603,14 @@ build_clone (tree fn, tree name)
TREE_TYPE (clone) = TREE_TYPE (result);
return clone;
}
else
{
// Clone constraints.
if (flag_concepts)
if (tree ci = get_constraints (fn))
set_constraints (clone, copy_node (ci));
}
SET_DECL_ASSEMBLER_NAME (clone, NULL_TREE);
DECL_CLONED_FUNCTION (clone) = fn;
@ -7281,15 +7290,17 @@ pop_class_stack (void)
}
/* Returns 1 if the class type currently being defined is either T or
a nested type of T. */
a nested type of T. Returns the type from the current_class_stack,
which might be equivalent to but not equal to T in case of
constrained partial specializations. */
bool
tree
currently_open_class (tree t)
{
int i;
if (!CLASS_TYPE_P (t))
return false;
return NULL_TREE;
t = TYPE_MAIN_VARIANT (t);
@ -7309,9 +7320,9 @@ currently_open_class (tree t)
if (!c)
continue;
if (same_type_p (c, t))
return true;
return c;
}
return false;
return NULL_TREE;
}
/* If either current_class_type or one of its enclosing classes are derived
@ -7661,6 +7672,12 @@ resolve_address_of_overloaded_function (tree target_type,
/* Instantiation failed. */
continue;
/* Constraints must be satisfied. This is done before
return type deduction since that instantiates the
function. */
if (flag_concepts && !constraints_satisfied_p (instantiation))
continue;
/* And now force instantiation to do return type deduction. */
if (undeduced_auto_decl (instantiation))
{

View File

@ -3525,6 +3525,25 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
non_constant_p, overflow_p, jump_target);
break;
case REQUIRES_EXPR:
/* It's possible to get a requires-expression in a constant
expression. For example:
template<typename T> concept bool C() {
return requires (T t) { t; };
}
template<typename T> requires !C<T>() void f(T);
Normalization leaves f with the associated constraint
'!requires (T t) { ... }' which is not transformed into
a constraint. */
if (!processing_template_decl)
return evaluate_constraint_expression (t, NULL_TREE);
else
*non_constant_p = true;
return t;
default:
if (STATEMENT_CODE_P (TREE_CODE (t)))
{
@ -3897,6 +3916,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
case PLACEHOLDER_EXPR:
case BREAK_STMT:
case CONTINUE_STMT:
case REQUIRES_EXPR:
return true;
case AGGR_INIT_EXPR:

2617
gcc/cp/constraint.cc Normal file

File diff suppressed because it is too large Load Diff

View File

@ -100,6 +100,8 @@ cp_tree_size (enum tree_code code)
case TEMPLATE_INFO: return sizeof (struct tree_template_info);
case CONSTRAINT_INFO: return sizeof (struct tree_constraint_info);
case USERDEF_LITERAL: return sizeof (struct tree_userdef_literal);
case TEMPLATE_DECL: return sizeof (struct tree_template_decl);
@ -240,6 +242,7 @@ cp_common_init_ts (void)
{
MARK_TS_DECL_NON_COMMON (USING_DECL);
MARK_TS_DECL_COMMON (TEMPLATE_DECL);
MARK_TS_DECL_COMMON (WILDCARD_DECL);
MARK_TS_COMMON (TEMPLATE_TEMPLATE_PARM);
MARK_TS_COMMON (TEMPLATE_TYPE_PARM);
@ -311,6 +314,7 @@ cp_common_init_ts (void)
MARK_TS_TYPED (LAMBDA_EXPR);
MARK_TS_TYPED (CTOR_INITIALIZER);
MARK_TS_TYPED (ARRAY_NOTATION_REF);
MARK_TS_TYPED (REQUIRES_EXPR);
}
#include "gt-cp-cp-objcp-common.h"

View File

@ -475,6 +475,94 @@ DEFTREECODE (BASES, "bases", tcc_type, 0)
instantiation time. */
DEFTREECODE (TEMPLATE_INFO, "template_info", tcc_exceptional, 0)
/* Extensions for Concepts. */
/* Used to represent information associated with constrained declarations. */
DEFTREECODE (CONSTRAINT_INFO, "constraint_info", tcc_exceptional, 0)
/* A wildcard declaration is a placeholder for a template parameter
used to resolve constrained-type-names in concepts. During
resolution, the matching argument is saved as the TREE_TYPE
of the wildcard. */
DEFTREECODE (WILDCARD_DECL, "wildcard_decl", tcc_declaration, 0)
/* A requires-expr is a binary expression. The first operand is
its parameter list (possibly NULL). The second is a list of
requirements, which are denoted by the _REQ* tree codes
below. */
DEFTREECODE (REQUIRES_EXPR, "requires_expr", tcc_expression, 2)
/* A requirement for an expression. */
DEFTREECODE (SIMPLE_REQ, "simple_req", tcc_expression, 1)
/* A requirement for a type. */
DEFTREECODE (TYPE_REQ, "type_req", tcc_expression, 1)
/* A requirement for an expression and its properties. The
first operand is the expression, and the 2nd is its type.
The accessor COMPOUND_REQ_NOEXCEPT determines whether
the noexcept keyword was present. */
DEFTREECODE (COMPOUND_REQ, "compound_req", tcc_expression, 2)
/* A requires clause within a requires expression. */
DEFTREECODE (NESTED_REQ, "nested_req", tcc_expression, 1)
/* Constraints are modeled as kinds of expressions.
The operands of a constraint can be either types or expressions.
Unlike expressions, constraints do not have a type. */
/* A predicate constraint evaluates an expression E.
PRED_CONSTR_EXPR has the expression to be evaluated. */
DEFTREECODE (PRED_CONSTR, "pred_constr", tcc_expression, 1)
/* An expression constraint determines the validity of a expression E.
EXPR_CONST_EXPR has the expression being validated. */
DEFTREECODE (EXPR_CONSTR, "expr_constr", tcc_expression, 1)
/* A type constraint determines the validity of a type T. Note that
TYPE_CONST_TYPE has the type being validated */
DEFTREECODE (TYPE_CONSTR, "type_constr", tcc_expression, 1)
/* An implicit conversion constraint determines if an expression
E is implicitly convertible to a type T. Note that T may
be dependent but does not contain any placeholders.
ICONV_CONSTR_EXPR has the expression E.
ICONV_CONSTR_TYPE has the type T.
*/
DEFTREECODE (ICONV_CONSTR, "iconv_constr", tcc_expression, 2)
/* An argument deduction constraint determines if the type of an
expression E can be deduced from a type pattern T. Note that
T must contain at least one place holder.
DEDUCT_CONSTR_EXPR has the expression E
DEDUCT_CONSTR_PATTERN has the type patter T.
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

View File

@ -77,6 +77,8 @@ c-common.h, not after.
PACK_EXPANSION_LOCAL_P (in *_PACK_EXPANSION)
TINFO_HAS_ACCESS_ERRORS (in TEMPLATE_INFO)
SIZEOF_EXPR_TYPE_P (in SIZEOF_EXPR)
COMPOUND_REQ_NOEXCEPT_P (in COMPOUND_REQ)
WILDCARD_PACK_P (in WILDCARD_DECL)
BLOCK_OUTER_CURLY_BRACE_P (in BLOCK)
1: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE)
TI_PENDING_TEMPLATE_FLAG.
@ -154,6 +156,7 @@ c-common.h, not after.
LABEL_DECL_CONTINUE (in LABEL_DECL)
2: DECL_THIS_EXTERN (in VAR_DECL or FUNCTION_DECL).
DECL_IMPLICIT_TYPEDEF_P (in a TYPE_DECL)
DECL_CONSTRAINT_VAR_P (in a PARM_DECL)
TEMPLATE_DECL_COMPLEX_ALIAS_P (in TEMPLATE_DECL)
DECL_INSTANTIATING_NSDMI_P (in a FIELD_DECL)
3: DECL_IN_AGGR_P.
@ -660,6 +663,7 @@ typedef enum cp_trait_kind
CPTK_IS_LITERAL_TYPE,
CPTK_IS_POD,
CPTK_IS_POLYMORPHIC,
CPTK_IS_SAME_AS,
CPTK_IS_STD_LAYOUT,
CPTK_IS_TRIVIAL,
CPTK_IS_TRIVIALLY_ASSIGNABLE,
@ -812,6 +816,154 @@ struct GTY(()) tree_template_info {
vec<qualified_typedef_usage_t, va_gc> *typedefs_needing_access_checking;
};
// Constraint information for a C++ declaration. Constraint information is
// comprised of:
//
// - a constraint expression introduced by the template header
// - a constraint expression introduced by a function declarator
// - the associated constraints, which are the conjunction of those,
// and used for declaration matching
// - the cached normalized associated constraints which are used
// to support satisfaction and subsumption.
// - assumptions which is the result of decomposing the normalized
// constraints.
//
// The template and declarator requirements are kept to support pretty
// printing constrained declarations.
struct GTY(()) tree_constraint_info {
struct tree_base base;
tree template_reqs;
tree declarator_reqs;
tree associated_constr;
tree normalized_constr;
tree assumptions;
};
// Require that pointer P is non-null before returning.
template<typename T>
inline T*
check_nonnull (T* p)
{
gcc_assert (p);
return p;
}
// Returns true iff T is non-null and represents constraint info.
inline tree_constraint_info *
check_constraint_info (tree t)
{
if (t && TREE_CODE (t) == CONSTRAINT_INFO)
return (tree_constraint_info *)t;
return NULL;
}
// Access the expression describing the template constraints. This may be
// null if no constraints were introduced in the template parameter list,
// a requirements clause after the template parameter list, or constraints
// through a constrained-type-specifier.
#define CI_TEMPLATE_REQS(NODE) \
check_constraint_info (check_nonnull(NODE))->template_reqs
// Access the expression describing the trailing constraints. This is non-null
// for any implicit instantiation of a constrained declaration. For a
// templated declaration it is non-null only when a trailing requires-clause
// was specified.
#define CI_DECLARATOR_REQS(NODE) \
check_constraint_info (check_nonnull(NODE))->declarator_reqs
// The computed associated constraint expression for a declaration.
#define CI_ASSOCIATED_CONSTRAINTS(NODE) \
check_constraint_info (check_nonnull(NODE))->associated_constr
// The normalized associated constraints.
#define CI_NORMALIZED_CONSTRAINTS(NODE) \
check_constraint_info (check_nonnull(NODE))->normalized_constr
// Get the set of assumptions associated with the constraint info node.
#define CI_ASSUMPTIONS(NODE) \
check_constraint_info (check_nonnull(NODE))->assumptions
// Access the logical constraints on the template parameters introduced
// at a given template parameter list level indicated by NODE.
#define TEMPLATE_PARMS_CONSTRAINTS(NODE) \
TREE_TYPE (TREE_LIST_CHECK (NODE))
// Access the logical constraints on the template parameter declaration
// indicated by NODE.
#define TEMPLATE_PARM_CONSTRAINTS(NODE) \
TREE_TYPE (TREE_LIST_CHECK (NODE))
/* Non-zero if the noexcept is present in a compound requirement. */
#define COMPOUND_REQ_NOEXCEPT_P(NODE) \
TREE_LANG_FLAG_0 (TREE_CHECK (NODE, COMPOUND_REQ))
/* The constraints on an 'auto' placeholder type, used in an argument deduction
constraint. */
#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)
/* 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) \
DECL_LANG_FLAG_2 (TREE_CHECK (NODE, PARM_DECL))
/* The concept constraining this constrained template-parameter. */
#define CONSTRAINED_PARM_CONCEPT(NODE) \
DECL_SIZE_UNIT (TYPE_DECL_CHECK (NODE))
/* Any extra template arguments specified for a constrained
template-parameter. */
#define CONSTRAINED_PARM_EXTRA_ARGS(NODE) \
DECL_SIZE (TYPE_DECL_CHECK (NODE))
/* The first template parameter of CONSTRAINED_PARM_CONCEPT to be used as a
prototype for the constrained parameter in finish_shorthand_constraint,
attached for convenience. */
#define CONSTRAINED_PARM_PROTOTYPE(NODE) \
DECL_INITIAL (TYPE_DECL_CHECK (NODE))
enum cp_tree_node_structure_enum {
TS_CP_GENERIC,
TS_CP_IDENTIFIER,
@ -829,6 +981,7 @@ enum cp_tree_node_structure_enum {
TS_CP_TRAIT_EXPR,
TS_CP_LAMBDA_EXPR,
TS_CP_TEMPLATE_INFO,
TS_CP_CONSTRAINT_INFO,
TS_CP_USERDEF_LITERAL,
LAST_TS_CP_ENUM
};
@ -856,6 +1009,8 @@ union GTY((desc ("cp_tree_node_structure (&%h)"),
lambda_expression;
struct tree_template_info GTY ((tag ("TS_CP_TEMPLATE_INFO")))
template_info;
struct tree_constraint_info GTY ((tag ("TS_CP_CONSTRAINT_INFO")))
constraint_info;
struct tree_userdef_literal GTY ((tag ("TS_CP_USERDEF_LITERAL")))
userdef_literal;
};
@ -2021,7 +2176,8 @@ struct GTY(()) lang_decl_base {
unsigned template_conv_p : 1; /* var or template */
unsigned odr_used : 1; /* var or fn */
unsigned u2sel : 1;
/* 1 spare bit */
unsigned concept_p : 1; /* applies to vars and functions */
/* 0 spare bits */
};
/* True for DECL codes which have template info and access. */
@ -2569,6 +2725,12 @@ struct GTY(()) lang_decl {
#define DECL_DECLARED_CONSTEXPR_P(DECL) \
DECL_LANG_FLAG_8 (VAR_OR_FUNCTION_DECL_CHECK (STRIP_TEMPLATE (DECL)))
// True if NODE was declared as 'concept'. The flag implies that the
// declaration is constexpr, that the declaration cannot be specialized or
// refined, and that the result type must be convertible to bool.
#define DECL_DECLARED_CONCEPT_P(NODE) \
(DECL_LANG_SPECIFIC (NODE)->u.base.concept_p)
/* Nonzero if this DECL is the __PRETTY_FUNCTION__ variable in a
template function. */
#define DECL_PRETTY_FUNCTION_P(NODE) \
@ -3036,6 +3198,9 @@ extern void decl_shadowed_for_var_insert (tree, tree);
/* True iff this pack expansion is within a function context. */
#define PACK_EXPANSION_LOCAL_P(NODE) TREE_LANG_FLAG_0 (NODE)
/* True iff the wildcard can match a template parameter pack. */
#define WILDCARD_PACK_P(NODE) TREE_LANG_FLAG_0 (NODE)
/* Determine if this is an argument pack. */
#define ARGUMENT_PACK_P(NODE) \
(TREE_CODE (NODE) == TYPE_ARGUMENT_PACK \
@ -4504,6 +4669,15 @@ extern int comparing_specializations;
extern int cp_unevaluated_operand;
/* RAII class used to inhibit the evaluation of operands during parsing
and template instantiation. Evaluation warnings are also inhibited. */
struct cp_unevaluated
{
cp_unevaluated ();
~cp_unevaluated ();
};
/* in pt.c */
/* These values are used for the `STRICT' parameter to type_unification and
@ -4516,6 +4690,17 @@ typedef enum unification_kind_t {
DEDUCE_EXACT
} unification_kind_t;
// An RAII class used to create a new pointer map for local
// specializations. When the stack goes out of scope, the
// previous pointer map is restored.
struct local_specialization_stack
{
local_specialization_stack ();
~local_specialization_stack ();
hash_map<tree, tree> *saved;
};
/* in class.c */
extern int current_class_depth;
@ -4810,6 +4995,17 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
#define TEMPLATE_TYPE_PARAMETER_PACK(NODE) \
(TEMPLATE_PARM_PARAMETER_PACK (TEMPLATE_TYPE_PARM_INDEX (NODE)))
/* Contexts in which auto deduction occurs. These flags are
used to control diagnostics in do_auto_deduction. */
enum auto_deduction_context
{
adc_unspecified, /* Not given */
adc_variable_type, /* Variable initializer deduction */
adc_return_type, /* Return type deduction */
adc_requirement /* Argument dedution constraint */
};
/* True iff this TEMPLATE_TYPE_PARM represents decltype(auto). */
#define AUTO_IS_DECLTYPE(NODE) \
(TYPE_LANG_FLAG_5 (TEMPLATE_TYPE_PARM_CHECK (NODE)))
@ -4963,6 +5159,7 @@ typedef enum cp_decl_spec {
ds_std_attribute,
ds_storage_class,
ds_long_long,
ds_concept,
ds_last /* This enumerator must always be the last one. */
} cp_decl_spec;
@ -5092,6 +5289,8 @@ struct cp_declarator {
tree exception_specification;
/* The late-specified return type, if any. */
tree late_return_type;
/* The trailing requires-clause, if any. */
tree requires_clause;
} function;
/* For arrays. */
struct {
@ -5157,7 +5356,7 @@ class_of_this_parm (const_tree fntype)
return TREE_TYPE (type_of_this_parm (fntype));
}
/* True if T designates a variable template declaration. */
/* True iff T is a variable template declaration. */
inline bool
variable_template_p (tree t)
{
@ -5170,6 +5369,30 @@ variable_template_p (tree t)
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;
@ -5266,7 +5489,7 @@ extern tree get_vtable_decl (tree, int);
extern void resort_type_method_vec (void *, void *,
gt_pointer_operator, void *);
extern bool add_method (tree, tree, tree);
extern bool currently_open_class (tree);
extern tree currently_open_class (tree);
extern tree currently_open_derived_class (tree);
extern tree outermost_open_class (void);
extern tree current_nonlambda_class_type (void);
@ -5402,6 +5625,7 @@ extern tree build_ptrmemfunc_type (tree);
extern tree build_ptrmem_type (tree, tree);
/* the grokdeclarator prototype is in decl.h */
extern tree build_this_parm (tree, cp_cv_quals);
extern tree grokparms (tree, tree *);
extern int copy_fn_p (const_tree);
extern bool move_fn_p (const_tree);
extern bool move_signature_fn_p (const_tree);
@ -5679,7 +5903,10 @@ 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 do_auto_deduction (tree, tree, tree);
extern tree do_auto_deduction (tree, tree, tree);
extern tree do_auto_deduction (tree, tree, tree,
tsubst_flags_t,
auto_deduction_context);
extern tree type_uses_auto (tree);
extern tree type_uses_auto_or_concept (tree);
extern void append_type_to_template_for_access_check (tree, tree, tree,
@ -5691,13 +5918,14 @@ extern bool is_auto_or_concept (const_tree);
extern tree process_template_parm (tree, location_t, tree,
bool, bool);
extern tree end_template_parm_list (tree);
extern void end_template_parm_list (void);
extern void end_template_decl (void);
extern tree maybe_update_decl_type (tree, tree);
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 bool redeclare_class_template (tree, tree);
extern bool redeclare_class_template (tree, tree, tree);
extern tree lookup_template_class (tree, tree, tree, tree,
int, tsubst_flags_t);
extern tree lookup_template_function (tree, tree);
@ -5742,6 +5970,9 @@ extern tree tsubst_default_argument (tree, tree, tree,
extern tree tsubst (tree, tree, tsubst_flags_t, tree);
extern tree tsubst_copy_and_build (tree, tree, tsubst_flags_t,
tree, bool, bool);
extern tree tsubst_expr (tree, tree, tsubst_flags_t,
tree, bool);
extern tree tsubst_pack_expansion (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);
@ -5792,6 +6023,12 @@ extern tree get_template_argument_pack_elems (const_tree);
extern tree get_function_template_decl (const_tree);
extern tree resolve_nondeduced_context (tree);
extern hashval_t iterative_hash_template_arg (tree arg, hashval_t val);
extern tree coerce_template_parms (tree, tree, tree);
extern tree coerce_template_parms (tree, tree, tree, tsubst_flags_t);
extern void register_local_specialization (tree, tree);
extern tree retrieve_local_specialization (tree);
extern tree extract_fnparm_pack (tree, tree *);
extern tree template_parm_to_arg (tree);
/* in repo.c */
extern void init_repo (void);
@ -5882,6 +6119,22 @@ extern bool perform_access_checks (vec<deferred_access_check, va_gc> *,
extern bool perform_deferred_access_checks (tsubst_flags_t);
extern bool perform_or_defer_access_check (tree, tree, tree,
tsubst_flags_t);
/* RAII sentinel to ensures that deferred access checks are popped before
a function returns. */
struct deferring_access_check_sentinel
{
deferring_access_check_sentinel ()
{
push_deferring_access_checks (dk_deferred);
}
~deferring_access_check_sentinel ()
{
pop_deferring_access_checks ();
}
};
extern int stmts_are_full_exprs_p (void);
extern void init_cp_semantics (void);
extern tree do_poplevel (tree);
@ -6392,6 +6645,60 @@ extern bool cxx_omp_privatize_by_reference (const_tree);
extern void suggest_alternatives_for (location_t, tree);
extern tree strip_using_decl (tree);
/* in constraint.cc */
extern void init_constraint_processing ();
extern bool constraint_p (tree);
extern tree make_predicate_constraint (tree);
extern tree conjoin_constraints (tree, tree);
extern tree conjoin_constraints (tree);
extern bool valid_constraints_p (tree);
extern tree get_constraints (tree);
extern void set_constraints (tree, tree);
extern void remove_constraints (tree);
extern tree current_template_constraints (void);
extern tree associate_classtype_constraints (tree);
extern tree build_constraints (tree, tree);
extern tree get_shorthand_constraints (tree);
extern tree build_concept_check (tree, tree, tree = NULL_TREE);
extern tree build_constrained_parameter (tree, tree, tree = NULL_TREE);
extern tree make_constrained_auto (tree, 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 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 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 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 bool constraints_satisfied_p (tree);
extern bool constraints_satisfied_p (tree, tree);
extern bool equivalent_constraints (tree, tree);
extern bool equivalently_constrained (tree, tree);
extern bool subsumes_constraints (tree, tree);
extern int more_constrained (tree, tree);
extern void diagnose_constraints (location_t, tree, tree);
/* in logic.cc */
extern tree decompose_assumptions (tree);
extern tree decompose_conclusions (tree);
extern bool subsumes (tree, tree);
/* in vtable-class-hierarchy.c */
extern void vtv_compute_class_hierarchy_transitive_closure (void);
extern void vtv_generate_init_routine (void);

View File

@ -451,6 +451,10 @@ cxx_pretty_printer::primary_expression (tree t)
pp_cxx_offsetof_expression (this, t);
break;
case REQUIRES_EXPR:
pp_cxx_requires_expr (this, t);
break;
default:
c_pretty_printer::primary_expression (t);
break;
@ -1064,6 +1068,7 @@ cxx_pretty_printer::expression (tree t)
case TEMPLATE_PARM_INDEX:
case TEMPLATE_TEMPLATE_PARM:
case STMT_EXPR:
case REQUIRES_EXPR:
primary_expression (t);
break;
@ -1158,6 +1163,22 @@ cxx_pretty_printer::expression (tree t)
pp_cxx_ws_string (this, "<lambda>");
break;
case TRAIT_EXPR:
pp_cxx_trait_expression (this, t);
break;
case PRED_CONSTR:
case EXPR_CONSTR:
case TYPE_CONSTR:
case ICONV_CONSTR:
case DEDUCT_CONSTR:
case EXCEPT_CONSTR:
case PARM_CONSTR:
case CONJ_CONSTR:
case DISJ_CONSTR:
pp_cxx_constraint (this, t);
break;
case PAREN_EXPR:
pp_cxx_left_paren (this);
expression (TREE_OPERAND (t, 0));
@ -1423,10 +1444,26 @@ pp_cxx_parameter_declaration (cxx_pretty_printer *pp, tree t)
static void
pp_cxx_parameter_declaration_clause (cxx_pretty_printer *pp, tree t)
{
tree args = TYPE_P (t) ? NULL : FUNCTION_FIRST_USER_PARM (t);
tree types =
TYPE_P (t) ? TYPE_ARG_TYPES (t) : FUNCTION_FIRST_USER_PARMTYPE (t);
const bool abstract = args == NULL || pp->flags & pp_c_flag_abstract;
tree args;
tree types;
bool abstract;
// For a requires clause or the explicit printing of a parameter list
// we expect T to be a chain of PARM_DECLs. Otherwise, the list of
// args and types are taken from the function decl T.
if (TREE_CODE (t) == PARM_DECL)
{
args = t;
types = t;
abstract = false;
}
else
{
bool type_p = TYPE_P (t);
args = type_p ? NULL : FUNCTION_FIRST_USER_PARM (t);
types = type_p ? TYPE_ARG_TYPES (t) : FUNCTION_FIRST_USER_PARMTYPE (t);
abstract = args == NULL || pp->flags & pp_c_flag_abstract;
}
bool first = true;
/* Skip artificial parameter for nonstatic member functions. */
@ -1574,6 +1611,12 @@ void
cxx_pretty_printer::declarator (tree t)
{
direct_declarator (t);
// Print a requires clause.
if (flag_concepts)
if (tree ci = get_constraints (t))
if (tree reqs = CI_DECLARATOR_REQS (ci))
pp_cxx_requires_clause (this, reqs);
}
/* ctor-initializer:
@ -2154,7 +2197,13 @@ pp_cxx_canonical_template_parameter (cxx_pretty_printer *pp, tree parm)
/*
template-declaration:
export(opt) template < template-parameter-list > declaration */
export(opt) template < template-parameter-list > declaration
Concept extensions:
template-declaration:
export(opt) template < template-parameter-list >
requires-clause(opt) declaration */
static void
pp_cxx_template_declaration (cxx_pretty_printer *pp, tree t)
@ -2171,6 +2220,15 @@ pp_cxx_template_declaration (cxx_pretty_printer *pp, tree t)
pp_cxx_end_template_argument_list (pp);
pp_newline_and_indent (pp, 3);
}
if (flag_concepts)
if (tree ci = get_constraints (t))
if (tree reqs = CI_TEMPLATE_REQS (ci))
{
pp_cxx_requires_clause (pp, reqs);
pp_newline_and_indent (pp, 6);
}
if (TREE_CODE (t) == FUNCTION_DECL && DECL_SAVED_TREE (t))
pp_cxx_function_definition (pp, t);
else
@ -2387,6 +2445,9 @@ pp_cxx_trait_expression (cxx_pretty_printer *pp, tree t)
case CPTK_IS_POLYMORPHIC:
pp_cxx_ws_string (pp, "__is_polymorphic");
break;
case CPTK_IS_SAME_AS:
pp_cxx_ws_string (pp, "__is_same_as");
break;
case CPTK_IS_STD_LAYOUT:
pp_cxx_ws_string (pp, "__is_std_layout");
break;
@ -2416,7 +2477,7 @@ pp_cxx_trait_expression (cxx_pretty_printer *pp, tree t)
pp_cxx_left_paren (pp);
pp->type_id (TRAIT_EXPR_TYPE1 (t));
if (kind == CPTK_IS_BASE_OF)
if (kind == CPTK_IS_BASE_OF || kind == CPTK_IS_SAME_AS)
{
pp_cxx_separate_with (pp, ',');
pp->type_id (TRAIT_EXPR_TYPE2 (t));
@ -2424,6 +2485,272 @@ pp_cxx_trait_expression (cxx_pretty_printer *pp, tree t)
pp_cxx_right_paren (pp);
}
// requires-clause:
// 'requires' logical-or-expression
void
pp_cxx_requires_clause (cxx_pretty_printer *pp, tree t)
{
if (!t)
return;
pp->padding = pp_before;
pp_cxx_ws_string (pp, "requires");
pp_space (pp);
pp->expression (t);
}
/* requirement:
simple-requirement
compound-requirement
type-requirement
nested-requirement */
static void
pp_cxx_requirement (cxx_pretty_printer *pp, tree t)
{
switch (TREE_CODE (t))
{
case SIMPLE_REQ:
pp_cxx_simple_requirement (pp, t);
break;
case TYPE_REQ:
pp_cxx_type_requirement (pp, t);
break;
case COMPOUND_REQ:
pp_cxx_compound_requirement (pp, t);
break;
case NESTED_REQ:
pp_cxx_nested_requirement (pp, t);
break;
default:
gcc_unreachable ();
}
}
// requirement-list:
// requirement
// requirement-list ';' requirement[opt]
//
static void
pp_cxx_requirement_list (cxx_pretty_printer *pp, tree t)
{
for (; t; t = TREE_CHAIN (t))
pp_cxx_requirement (pp, TREE_VALUE (t));
}
// requirement-body:
// '{' requirement-list '}'
static void
pp_cxx_requirement_body (cxx_pretty_printer *pp, tree t)
{
pp_cxx_left_brace (pp);
pp_cxx_requirement_list (pp, t);
pp_cxx_right_brace (pp);
}
// requires-expression:
// 'requires' requirement-parameter-list requirement-body
void
pp_cxx_requires_expr (cxx_pretty_printer *pp, tree t)
{
pp_string (pp, "requires");
if (tree parms = TREE_OPERAND (t, 0))
{
pp_cxx_parameter_declaration_clause (pp, parms);
pp_cxx_whitespace (pp);
}
pp_cxx_requirement_body (pp, TREE_OPERAND (t, 1));
}
/* simple-requirement:
expression ';' */
void
pp_cxx_simple_requirement (cxx_pretty_printer *pp, tree t)
{
pp->expression (TREE_OPERAND (t, 0));
pp_cxx_semicolon (pp);
}
/* type-requirement:
typename type-name ';' */
void
pp_cxx_type_requirement (cxx_pretty_printer *pp, tree t)
{
pp->type_id (TREE_OPERAND (t, 0));
pp_cxx_semicolon (pp);
}
/* compound-requirement:
'{' expression '}' 'noexcept' [opt] trailing-return-type [opt] */
void
pp_cxx_compound_requirement (cxx_pretty_printer *pp, tree t)
{
pp_cxx_left_brace (pp);
pp->expression (TREE_OPERAND (t, 0));
pp_cxx_right_brace (pp);
if (COMPOUND_REQ_NOEXCEPT_P (t))
pp_cxx_ws_string (pp, "noexcept");
if (tree type = TREE_OPERAND (t, 1))
{
pp_cxx_ws_string (pp, "->");
pp->type_id (type);
}
}
/* nested requirement:
'requires' constraint-expression */
void
pp_cxx_nested_requirement (cxx_pretty_printer *pp, tree t)
{
pp_cxx_ws_string (pp, "requires");
pp->expression (TREE_OPERAND (t, 0));
pp_cxx_semicolon (pp);
}
void
pp_cxx_predicate_constraint (cxx_pretty_printer *pp, tree t)
{
pp_string (pp, "predicate");
pp_left_paren (pp);
pp->expression (TREE_OPERAND (t, 0));
pp_right_paren (pp);
}
void
pp_cxx_expression_constraint (cxx_pretty_printer *pp, tree t)
{
pp_string (pp, "valid_expr");
pp_left_paren (pp);
pp->expression (TREE_OPERAND (t, 0));
pp_right_paren (pp);
}
void
pp_cxx_type_constraint (cxx_pretty_printer *pp, tree t)
{
pp_string (pp, "valid_type");
pp_left_paren (pp);
pp->type_id (TREE_OPERAND (t, 0));
pp_right_paren (pp);
}
void
pp_cxx_implicit_conversion_constraint (cxx_pretty_printer *pp, tree t)
{
pp_string (pp, "convertible");
pp_left_paren (pp);
pp->expression (ICONV_CONSTR_EXPR (t));
pp_cxx_separate_with (pp, ',');
pp->expression (ICONV_CONSTR_TYPE (t));
pp_right_paren (pp);
}
void
pp_cxx_argument_deduction_constraint (cxx_pretty_printer *pp, tree t)
{
pp_string (pp, "deducible");
pp_left_paren (pp);
pp->expression (DEDUCT_CONSTR_EXPR (t));
pp_cxx_separate_with (pp, ',');
pp->expression (DEDUCT_CONSTR_PATTERN (t));
pp_right_paren (pp);
}
void
pp_cxx_exception_constraint (cxx_pretty_printer *pp, tree t)
{
pp_cxx_ws_string (pp, "noexcept");
pp_left_paren (pp);
pp->expression (TREE_OPERAND (t, 0));
pp_right_paren (pp);
}
void
pp_cxx_parameterized_constraint (cxx_pretty_printer *pp, tree t)
{
pp_left_paren (pp);
pp_string (pp, "forall");
if (tree parms = PARM_CONSTR_PARMS (t))
{
if (parms)
pp_cxx_parameter_declaration_clause (pp, parms);
pp_cxx_whitespace (pp);
}
pp_cxx_constraint (pp, PARM_CONSTR_OPERAND (t));
pp_right_paren (pp);
}
void
pp_cxx_conjunction (cxx_pretty_printer *pp, tree t)
{
pp_cxx_constraint (pp, TREE_OPERAND (t, 0));
pp_string (pp, " and ");
pp_cxx_constraint (pp, TREE_OPERAND (t, 1));
}
void
pp_cxx_disjunction (cxx_pretty_printer *pp, tree t)
{
pp_cxx_constraint (pp, TREE_OPERAND (t, 0));
pp_string (pp, " or ");
pp_cxx_constraint (pp, TREE_OPERAND (t, 1));
}
void
pp_cxx_constraint (cxx_pretty_printer *pp, tree t)
{
if (t == error_mark_node)
return pp->expression (t);
switch (TREE_CODE (t))
{
case PRED_CONSTR:
pp_cxx_predicate_constraint (pp, t);
break;
case EXPR_CONSTR:
pp_cxx_expression_constraint (pp, t);
break;
case TYPE_CONSTR:
pp_cxx_type_constraint (pp, t);
break;
case ICONV_CONSTR:
pp_cxx_implicit_conversion_constraint (pp, t);
break;
case DEDUCT_CONSTR:
pp_cxx_argument_deduction_constraint (pp, t);
break;
case EXCEPT_CONSTR:
pp_cxx_exception_constraint (pp, t);
break;
case PARM_CONSTR:
pp_cxx_parameterized_constraint (pp, t);
break;
case CONJ_CONSTR:
pp_cxx_conjunction (pp, t);
break;
case DISJ_CONSTR:
pp_cxx_disjunction (pp, t);
break;
default:
gcc_unreachable ();
}
}
typedef c_pretty_print_fn pp_fun;

View File

@ -91,6 +91,21 @@ void pp_cxx_trait_expression (cxx_pretty_printer *, tree);
void pp_cxx_va_arg_expression (cxx_pretty_printer *, tree);
void pp_cxx_offsetof_expression (cxx_pretty_printer *, tree);
void pp_cxx_userdef_literal (cxx_pretty_printer *, tree);
void pp_cxx_requires_clause (cxx_pretty_printer *, tree);
void pp_cxx_requires_expr (cxx_pretty_printer *, tree);
void pp_cxx_simple_requirement (cxx_pretty_printer *, tree);
void pp_cxx_type_requirement (cxx_pretty_printer *, tree);
void pp_cxx_compound_requirement (cxx_pretty_printer *, tree);
void pp_cxx_nested_requirement (cxx_pretty_printer *, tree);
void pp_cxx_predicate_constraint (cxx_pretty_printer *, tree);
void pp_cxx_expression_constraint (cxx_pretty_printer *, tree);
void pp_cxx_type_constraint (cxx_pretty_printer *, tree);
void pp_cxx_implicit_conversion_constraint (cxx_pretty_printer *, tree);
void pp_cxx_argument_deduction_constraint (cxx_pretty_printer *, tree);
void pp_cxx_exception_constraint (cxx_pretty_printer *, tree);
void pp_cxx_parameterized_constraint (cxx_pretty_printer *, tree);
void pp_cxx_conjunction (cxx_pretty_printer *, tree);
void pp_cxx_disjunction (cxx_pretty_printer *, tree);
void pp_cxx_constraint (cxx_pretty_printer *, tree);
#endif /* GCC_CXX_PRETTY_PRINT_H */

View File

@ -72,7 +72,6 @@ enum bad_spec_place {
BSP_FIELD /* field */
};
static tree grokparms (tree parmlist, tree *);
static const char *redeclaration_error_message (tree, tree);
static int decl_jump_unsafe (tree);
@ -1079,8 +1078,10 @@ decls_match (tree newdecl, tree olddecl)
}
else if (TREE_CODE (newdecl) == TEMPLATE_DECL)
{
if (TREE_CODE (DECL_TEMPLATE_RESULT (newdecl))
!= TREE_CODE (DECL_TEMPLATE_RESULT (olddecl)))
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),
@ -1088,11 +1089,12 @@ decls_match (tree newdecl, tree olddecl)
return 0;
if (TREE_CODE (DECL_TEMPLATE_RESULT (newdecl)) == TYPE_DECL)
types_match = same_type_p (TREE_TYPE (DECL_TEMPLATE_RESULT (olddecl)),
TREE_TYPE (DECL_TEMPLATE_RESULT (newdecl)));
types_match = (same_type_p (TREE_TYPE (oldres), TREE_TYPE (newres))
&& equivalently_constrained (olddecl, newdecl));
else
types_match = decls_match (DECL_TEMPLATE_RESULT (olddecl),
DECL_TEMPLATE_RESULT (newdecl));
// 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);
}
else
{
@ -1120,6 +1122,11 @@ decls_match (tree newdecl, tree olddecl)
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;
}
@ -1243,6 +1250,36 @@ validate_constexpr_redeclaration (tree old_decl, tree new_decl)
return true;
}
// If OLDDECL and NEWDECL are concept declarations with the same type
// (i.e., and template parameters), but different requirements,
// emit diagnostics and return true. Otherwise, return false.
static inline bool
check_concept_refinement (tree olddecl, tree newdecl)
{
if (!DECL_DECLARED_CONCEPT_P (olddecl) || !DECL_DECLARED_CONCEPT_P (newdecl))
return false;
tree d1 = DECL_TEMPLATE_RESULT (olddecl);
tree d2 = DECL_TEMPLATE_RESULT (newdecl);
if (TREE_CODE (d1) != TREE_CODE (d2))
return false;
tree t1 = TREE_TYPE (d1);
tree t2 = TREE_TYPE (d2);
if (TREE_CODE (d1) == FUNCTION_DECL)
{
if (compparms (TYPE_ARG_TYPES (t1), TYPE_ARG_TYPES (t2))
&& comp_template_parms (DECL_TEMPLATE_PARMS (olddecl),
DECL_TEMPLATE_PARMS (newdecl))
&& !equivalently_constrained (olddecl, newdecl))
{
error ("cannot specialize concept %q#D", olddecl);
return true;
}
}
return false;
}
/* DECL is a redeclaration of a function or function template. If
it does have default arguments issue a diagnostic. Note: this
function is used to enforce the requirements in C++11 8.3.6 about
@ -1571,12 +1608,17 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
/* Template functions can be disambiguated by
return type. */
&& same_type_p (TREE_TYPE (TREE_TYPE (newdecl)),
TREE_TYPE (TREE_TYPE (olddecl))))
TREE_TYPE (TREE_TYPE (olddecl)))
// Template functions can also be disambiguated by
// constraints.
&& equivalently_constrained (olddecl, newdecl))
{
error ("ambiguating new declaration %q+#D", newdecl);
inform (DECL_SOURCE_LOCATION (olddecl),
"old declaration %q#D", olddecl);
}
else if (check_concept_refinement (olddecl, newdecl))
return error_mark_node;
return NULL_TREE;
}
if (TREE_CODE (newdecl) == FUNCTION_DECL)
@ -1593,8 +1635,11 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
are not ambiguous. */
else if ((!DECL_FUNCTION_VERSIONED (newdecl)
&& !DECL_FUNCTION_VERSIONED (olddecl))
// The functions have the same parameter types.
&& compparms (TYPE_ARG_TYPES (TREE_TYPE (newdecl)),
TYPE_ARG_TYPES (TREE_TYPE (olddecl))))
TYPE_ARG_TYPES (TREE_TYPE (olddecl)))
// And the same constraints.
&& equivalently_constrained (newdecl, olddecl))
{
error ("ambiguating new declaration of %q+#D", newdecl);
inform (DECL_SOURCE_LOCATION (olddecl),
@ -2576,6 +2621,12 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
if (snode)
snode->remove ();
}
/* Remove the associated constraints for newdecl, if any, before
reclaiming memory. */
if (flag_concepts)
remove_constraints (newdecl);
ggc_free (newdecl);
return olddecl;
@ -3981,6 +4032,10 @@ cxx_init_decl_processing (void)
/* Ensure attribs.c is initialized. */
init_attributes ();
/* Ensure constraint.cc is initialized. */
init_constraint_processing ();
extvisattr = build_tree_list (get_identifier ("externally_visible"),
NULL_TREE);
newattrs = tree_cons (get_identifier ("alloc_size"),
@ -6368,6 +6423,16 @@ value_dependent_init_p (tree init)
return false;
}
// Returns true if a DECL is VAR_DECL with the concept specifier.
static inline bool
is_concept_var (tree decl)
{
return (VAR_P (decl)
// Not all variables have DECL_LANG_SPECIFIC.
&& DECL_LANG_SPECIFIC (decl)
&& DECL_DECLARED_CONCEPT_P (decl));
}
/* Finish processing of a declaration;
install its line number and initial value.
If the length of an array type is not known before,
@ -6446,7 +6511,9 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
tf_warning_or_error);
d_init = resolve_nondeduced_context (d_init);
type = TREE_TYPE (decl) = do_auto_deduction (type, d_init,
auto_node);
auto_node,
tf_warning_or_error,
adc_variable_type);
if (type == error_mark_node)
return;
cp_apply_type_quals_to_decl (cp_type_quals (type), decl);
@ -6544,6 +6611,8 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
init = NULL_TREE;
release_tree_vector (cleanups);
}
else if (!init && is_concept_var (decl))
error ("variable concept has no initializer");
else if (!DECL_PRETTY_FUNCTION_P (decl))
{
/* Deduce array size even if the initializer is dependent. */
@ -7606,6 +7675,23 @@ check_static_quals (tree decl, cp_cv_quals quals)
decl);
}
// Check that FN takes no arguments and returns bool.
static void
check_concept_fn (tree fn)
{
// A constraint is nullary.
if (DECL_ARGUMENTS (fn))
error ("concept %q#D declared with function parameters", fn);
// The declared return type of the concept shall be bool, and
// it shall not be deduced from it definition.
tree type = TREE_TYPE (TREE_TYPE (fn));
if (is_auto (type))
error ("concept %q#D declared with a deduced return type", fn);
else if (type != boolean_type_node)
error ("concept %q#D with non-%<bool%> return type %qT", fn, type);
}
/* Helper function. Replace the temporary this parameter injected
during cp_finish_omp_declare_simd with the real this parameter. */
@ -7646,6 +7732,7 @@ grokfndecl (tree ctype,
tree declarator,
tree parms,
tree orig_declarator,
tree decl_reqs,
int virtualp,
enum overload_flags flags,
cp_cv_quals quals,
@ -7667,6 +7754,16 @@ grokfndecl (tree ctype,
int staticp = ctype && TREE_CODE (type) == FUNCTION_TYPE;
tree t;
// Was the concept specifier present?
bool concept_p = inlinep & 4;
// Concept declarations must have a corresponding definition.
if (concept_p && !funcdef_flag)
{
error ("concept %qD has no definition", declarator);
return NULL_TREE;
}
if (rqual)
type = build_ref_qualified_type (type, rqual);
if (raises)
@ -7674,6 +7771,21 @@ grokfndecl (tree ctype,
decl = build_lang_decl (FUNCTION_DECL, declarator, type);
/* Set the constraints on the declaration. */
if (flag_concepts)
{
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 = make_predicate_constraint (decl_reqs);
tree ci = build_constraints (tmpl_reqs, decl_reqs);
set_constraints (decl, ci);
}
/* If we have an explicit location, use it, otherwise use whatever
build_lang_decl used (probably input_location). */
if (location != UNKNOWN_LOCATION)
@ -7848,6 +7960,14 @@ grokfndecl (tree ctype,
if (inlinep & 2)
DECL_DECLARED_CONSTEXPR_P (decl) = true;
// If the concept declaration specifier was found, check
// that the declaration satisfies the necessary requirements.
if (concept_p)
{
DECL_DECLARED_CONCEPT_P (decl) = true;
check_concept_fn (decl);
}
DECL_EXTERNAL (decl) = 1;
if (TREE_CODE (type) == FUNCTION_TYPE)
{
@ -7967,7 +8087,8 @@ grokfndecl (tree ctype,
decl = check_explicit_specialization (orig_declarator, decl,
template_count,
2 * funcdef_flag +
4 * (friendp != 0));
4 * (friendp != 0) +
8 * concept_p);
if (decl == error_mark_node)
return NULL_TREE;
@ -8125,7 +8246,7 @@ grokvardecl (tree type,
tree orig_declarator,
const cp_decl_specifier_seq *declspecs,
int initialized,
int constp,
int flags,
int template_count,
tree scope)
{
@ -8134,6 +8255,9 @@ grokvardecl (tree type,
gcc_assert (!name || identifier_p (name));
bool constp = flags&1;
bool conceptp = flags&2;
/* Compute the scope in which to place the variable, but remember
whether or not that scope was explicitly specified by the user. */
explicit_scope = scope;
@ -8231,10 +8355,33 @@ grokvardecl (tree type,
else
DECL_INTERFACE_KNOWN (decl) = 1;
/* Check that the variable can be safely declared as a concept.
Note that this also forbids explicit specializations. */
if (conceptp)
{
if (!processing_template_decl)
{
error ("a non-template variable cannot be %<concept%>");
return NULL_TREE;
}
else
DECL_DECLARED_CONCEPT_P (decl) = true;
if (!same_type_ignoring_top_level_qualifiers_p (type, boolean_type_node))
error_at (declspecs->locations[ds_type_spec],
"concept must have type %<bool%>");
}
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);
}
// Handle explicit specializations and instantiations of variable templates.
if (orig_declarator)
decl = check_explicit_specialization (orig_declarator, decl,
template_count, 0);
template_count, conceptp * 8);
return decl != error_mark_node ? decl : NULL_TREE;
}
@ -8969,6 +9116,7 @@ grokdeclarator (const cp_declarator *declarator,
bool array_parameter_p = false;
source_location saved_loc = input_location;
const char *errmsg;
tree reqs = NULL_TREE;
signed_p = decl_spec_seq_has_spec_p (declspecs, ds_signed);
unsigned_p = decl_spec_seq_has_spec_p (declspecs, ds_unsigned);
@ -8978,6 +9126,12 @@ grokdeclarator (const cp_declarator *declarator,
explicit_intN = declspecs->explicit_intN_p;
thread_p = decl_spec_seq_has_spec_p (declspecs, ds_thread);
// Was concept_p specified? Note that ds_concept
// implies ds_constexpr!
bool concept_p = decl_spec_seq_has_spec_p (declspecs, ds_concept);
if (concept_p)
constexpr_p = true;
if (decl_context == FUNCDEF)
funcdef_flag = true, decl_context = NORMAL;
else if (decl_context == MEMFUNCDEF)
@ -9220,6 +9374,12 @@ grokdeclarator (const cp_declarator *declarator,
if (name == NULL)
name = decl_context == PARM ? "parameter" : "type name";
if (concept_p && typedef_p)
{
error ("%<concept%> cannot appear in a typedef declaration");
return error_mark_node;
}
if (constexpr_p && typedef_p)
{
error ("%<constexpr%> cannot appear in a typedef declaration");
@ -9548,9 +9708,12 @@ grokdeclarator (const cp_declarator *declarator,
|| thread_p)
error ("storage class specifiers invalid in parameter declarations");
/* Function parameters cannot be concept. */
if (concept_p)
error ("a parameter cannot be declared %<concept%>");
/* Function parameters cannot be constexpr. If we saw one, moan
and pretend it wasn't there. */
if (constexpr_p)
else if (constexpr_p)
{
error ("a parameter cannot be declared %<constexpr%>");
constexpr_p = 0;
@ -9778,6 +9941,10 @@ grokdeclarator (const cp_declarator *declarator,
if (raises == error_mark_node)
raises = NULL_TREE;
if (reqs)
error_at (location_of (reqs), "requires-clause on return type");
reqs = declarator->u.function.requires_clause;
/* Say it's a definition only for the CALL_EXPR
closest to the identifier. */
funcdecl_p = inner_declarator && inner_declarator->kind == cdk_id;
@ -10373,6 +10540,9 @@ grokdeclarator (const cp_declarator *declarator,
type = error_mark_node;
}
if (reqs)
error_at (location_of (reqs), "requires-clause on typedef");
if (decl_context == FIELD)
decl = build_lang_decl (TYPE_DECL, unqualified_id, type);
else
@ -10566,6 +10736,9 @@ grokdeclarator (const cp_declarator *declarator,
error ("invalid qualifiers on non-member function type");
}
if (reqs)
error_at (location_of (reqs), "requires-clause on type-id");
return type;
}
else if (unqualified_id == NULL_TREE && decl_context != PARM
@ -10587,6 +10760,13 @@ grokdeclarator (const cp_declarator *declarator,
return error_mark_node;
}
if (reqs
&& TREE_CODE (type) != FUNCTION_TYPE
&& TREE_CODE (type) != METHOD_TYPE)
error_at (location_of (reqs),
"requires-clause on declaration of non-function type %qT",
type);
/* We don't check parameter types here because we can emit a better
error message later. */
if (decl_context != PARM)
@ -10744,6 +10924,11 @@ grokdeclarator (const cp_declarator *declarator,
uqname, ctype);
return error_mark_node;
}
if (concept_p)
{
error ("a destructor cannot be %<concept%>");
return error_mark_node;
}
if (constexpr_p)
{
error ("a destructor cannot be %<constexpr%>");
@ -10757,6 +10942,17 @@ grokdeclarator (const cp_declarator *declarator,
id_declarator->u.id.unqualified_name);
return error_mark_node;
}
if (sfk == sfk_constructor)
if (concept_p)
{
error ("a constructor cannot be %<concept%>");
return error_mark_node;
}
if (concept_p)
{
error ("a concept cannot be a member function");
concept_p = false;
}
if (TREE_CODE (unqualified_id) == TEMPLATE_ID_EXPR)
{
@ -10785,9 +10981,10 @@ grokdeclarator (const cp_declarator *declarator,
? unqualified_id : dname,
parms,
unqualified_id,
reqs,
virtualp, flags, memfn_quals, rqual, raises,
friendp ? -1 : 0, friendp, publicp,
inlinep | (2 * constexpr_p),
inlinep | (2 * constexpr_p) | (4 * concept_p),
initialized == SD_DELETED, sfk,
funcdef_flag, template_count, in_namespace,
attrlist, declarator->id_loc);
@ -10890,8 +11087,10 @@ grokdeclarator (const cp_declarator *declarator,
if (declspecs->gnu_thread_keyword_p)
SET_DECL_GNU_TLS_P (decl);
}
if (constexpr_p && !initialized)
if (concept_p)
error ("static data member %qE declared %<concept%>",
unqualified_id);
else if (constexpr_p && !initialized)
{
error ("constexpr static data member %qD must have an "
"initializer", decl);
@ -10900,7 +11099,10 @@ grokdeclarator (const cp_declarator *declarator,
}
else
{
if (constexpr_p)
if (concept_p)
error ("non-static data member %qE declared %<concept%>",
unqualified_id);
else if (constexpr_p)
{
error ("non-static data member %qE declared %<constexpr%>",
unqualified_id);
@ -11010,10 +11212,12 @@ grokdeclarator (const cp_declarator *declarator,
TYPE_HAS_LATE_RETURN_TYPE (type) = 1;
decl = grokfndecl (ctype, type, original_name, parms, unqualified_id,
virtualp, flags, memfn_quals, rqual, raises,
reqs, virtualp, flags, memfn_quals, rqual, raises,
1, friendp,
publicp, inlinep | (2 * constexpr_p),
initialized == SD_DELETED, sfk,
publicp,
inlinep | (2 * constexpr_p) | (4 * concept_p),
initialized == SD_DELETED,
sfk,
funcdef_flag,
template_count, in_namespace, attrlist,
declarator->id_loc);
@ -11054,7 +11258,7 @@ grokdeclarator (const cp_declarator *declarator,
decl = grokvardecl (type, dname, unqualified_id,
declspecs,
initialized,
(type_quals & TYPE_QUAL_CONST) != 0,
((type_quals & TYPE_QUAL_CONST) != 0) | (2 * concept_p),
template_count,
ctype ? ctype : in_namespace);
if (decl == NULL_TREE)
@ -11304,7 +11508,7 @@ type_is_deprecated (tree type)
*PARMS is set to the chain of PARM_DECLs created. */
static tree
tree
grokparms (tree parmlist, tree *parms)
{
tree result = NULL_TREE;
@ -12399,8 +12603,16 @@ xref_tag_1 (enum tag_types tag_code, tree name,
{
if (template_header_p && MAYBE_CLASS_TYPE_P (t))
{
if (!redeclare_class_template (t, current_template_parms))
return error_mark_node;
/* Check that we aren't trying to overload a class with different
constraints. */
tree constr = NULL_TREE;
if (current_template_parms)
{
tree reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms);
constr = build_constraints (reqs, NULL_TREE);
}
if (!redeclare_class_template (t, current_template_parms, constr))
return error_mark_node;
}
else if (!processing_template_decl
&& CLASS_TYPE_P (t)
@ -14252,6 +14464,10 @@ finish_function (int flags)
fntype = TREE_TYPE (fndecl);
}
// If this is a concept, check that the definition is reasonable.
if (DECL_DECLARED_CONCEPT_P (fndecl))
check_function_concept (fndecl);
/* Save constexpr function body before it gets munged by
the NRV transformation. */
maybe_save_function_definition (fndecl);
@ -14742,6 +14958,7 @@ cp_tree_node_structure (union lang_tree_node * t)
case TRAIT_EXPR: return TS_CP_TRAIT_EXPR;
case LAMBDA_EXPR: return TS_CP_LAMBDA_EXPR;
case TEMPLATE_INFO: return TS_CP_TEMPLATE_INFO;
case CONSTRAINT_INFO: return TS_CP_CONSTRAINT_INFO;
case USERDEF_LITERAL: return TS_CP_USERDEF_LITERAL;
default: return TS_CP_GENERIC;
}

View File

@ -714,6 +714,10 @@ check_classfn (tree ctype, tree function, tree template_parms)
!= type_memfn_rqual (TREE_TYPE (fndecl)))
continue;
// Include constraints in the match.
tree c1 = get_constraints (function);
tree c2 = get_constraints (fndecl);
/* While finding a match, same types and params are not enough
if the function is versioned. Also check version ("target")
attributes. */
@ -724,6 +728,7 @@ check_classfn (tree ctype, tree function, tree template_parms)
&& (!is_template
|| comp_template_parms (template_parms,
DECL_TEMPLATE_PARMS (fndecl)))
&& equivalent_constraints (c1, c2)
&& (DECL_TEMPLATE_SPECIALIZATION (function)
== DECL_TEMPLATE_SPECIALIZATION (fndecl))
&& (!DECL_TEMPLATE_SPECIALIZATION (function)
@ -5081,6 +5086,7 @@ mark_used (tree decl, tsubst_flags_t complain)
|| (TREE_CODE (decl) == FUNCTION_DECL
&& DECL_OMP_DECLARE_REDUCTION_P (decl))
|| undeduced_auto_decl (decl))
&& !DECL_DECLARED_CONCEPT_P (decl)
&& !uses_template_parms (DECL_TI_ARGS (decl)))
{
/* Instantiating a function will result in garbage collection. We
@ -5179,6 +5185,7 @@ mark_used (tree decl, tsubst_flags_t complain)
}
else if (VAR_OR_FUNCTION_DECL_P (decl)
&& DECL_TEMPLATE_INFO (decl)
&& !DECL_DECLARED_CONCEPT_P (decl)
&& (!DECL_EXPLICIT_INSTANTIATION (decl)
|| always_instantiate_p (decl)))
/* If this is a function or variable that is an instance of some

View File

@ -1333,6 +1333,15 @@ dump_template_decl (cxx_pretty_printer *pp, tree t, int flags)
}
}
if (flag_concepts)
if (tree ci = get_constraints (t))
if (check_constraint_info (ci))
if (tree reqs = CI_TEMPLATE_REQS (ci))
{
pp_cxx_requires_clause (pp, reqs);
pp_cxx_whitespace (pp);
}
if (DECL_CLASS_TEMPLATE_P (t))
dump_type (pp, TREE_TYPE (t),
((flags & ~TFF_CLASS_KEY_OR_ENUM) | TFF_TEMPLATE_NAME
@ -1564,6 +1573,11 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
if (show_return)
dump_type_suffix (pp, TREE_TYPE (fntype), flags);
if (flag_concepts)
if (tree ci = get_constraints (t))
if (tree reqs = CI_DECLARATOR_REQS (ci))
pp_cxx_requires_clause (pp, reqs);
dump_substitution (pp, t, template_parms, template_args, flags);
}
else if (template_args)
@ -2689,6 +2703,38 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
pp_cxx_right_paren (pp);
break;
case REQUIRES_EXPR:
pp_cxx_requires_expr (cxx_pp, t);
break;
case SIMPLE_REQ:
pp_cxx_simple_requirement (cxx_pp, t);
break;
case TYPE_REQ:
pp_cxx_type_requirement (cxx_pp, t);
break;
case COMPOUND_REQ:
pp_cxx_compound_requirement (cxx_pp, t);
break;
case NESTED_REQ:
pp_cxx_nested_requirement (cxx_pp, t);
break;
case PRED_CONSTR:
case EXPR_CONSTR:
case TYPE_CONSTR:
case ICONV_CONSTR:
case DEDUCT_CONSTR:
case EXCEPT_CONSTR:
case PARM_CONSTR:
case CONJ_CONSTR:
case DISJ_CONSTR:
pp_cxx_constraint (cxx_pp, t);
break;
case PLACEHOLDER_EXPR:
pp_string (pp, M_("*this"));
break;

View File

@ -174,6 +174,8 @@ init_reswords (void)
if (cxx_dialect < cxx11)
mask |= D_CXX11;
if (!flag_concepts)
mask |= D_CXX_CONCEPTS;
if (flag_no_asm)
mask |= D_ASM | D_EXT;
if (flag_no_gnu_keywords)

497
gcc/cp/logic.cc Normal file
View File

@ -0,0 +1,497 @@
/* Derivation and subsumption rules for constraints.
Copyright (C) 2013-2015 Free Software Foundation, Inc.
Contributed by Andrew Sutton (andrew.n.sutton@gmail.com)
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "hash-set.h"
#include "machmode.h"
#include "vec.h"
#include "double-int.h"
#include "input.h"
#include "alias.h"
#include "symtab.h"
#include "wide-int.h"
#include "inchash.h"
#include "tree.h"
#include "stringpool.h"
#include "attribs.h"
#include "intl.h"
#include "flags.h"
#include "cp-tree.h"
#include "c-family/c-common.h"
#include "c-family/c-objc.h"
#include "cp-objcp-common.h"
#include "tree-inline.h"
#include "decl.h"
#include "toplev.h"
#include "type-utils.h"
#include <list>
namespace {
// Helper algorithms
// Increment iter distance(first, last) times.
template<typename I1, typename I2, typename I3>
I1 next_by_distance (I1 iter, I2 first, I3 last)
{
for ( ; first != last; ++first, ++iter)
;
return iter;
}
/*---------------------------------------------------------------------------
Proof state
---------------------------------------------------------------------------*/
/* A term list is a list of atomic constraints. It is used
to maintain the lists of assumptions and conclusions in a
proof goal.
Each term list maintains an iterator that refers to the current
term. This can be used by various tactics to support iteration
and stateful manipulation of the list. */
struct term_list : std::list<tree>
{
term_list ();
term_list (const term_list &x);
term_list& operator= (const term_list &x);
tree current_term () { return *current; }
const_tree current_term () const { return *current; }
void insert (tree t);
tree erase ();
void start ();
void next ();
bool done() const;
iterator current;
};
inline
term_list::term_list ()
: std::list<tree> (), current (end ())
{ }
inline
term_list::term_list (const term_list &x)
: std::list<tree> (x)
, current (next_by_distance (begin (), x.begin (), x.current))
{ }
inline term_list&
term_list::operator= (const term_list &x)
{
std::list<tree>::operator=(x);
current = next_by_distance (begin (), x.begin (), x.current);
return *this;
}
/* Try saving the term T into the list of terms. If
T is already in the list of terms, then no action is
performed. Otherwise, insert T before the current
position, making this term current.
Note that not inserting terms is an optimization
that corresponds to the structural rule of
contraction.
NOTE: With the contraction rule, this data structure
would be more efficiently represented as an ordered set
or hash set. */
void
term_list::insert (tree t)
{
/* Search the current term list. If there is already
a matching term, do not add the new one. */
for (iterator i = begin(); i != end(); ++i)
if (cp_tree_equal (*i, t))
return;
current = std::list<tree>::insert (current, t);
}
/* Remove the current term from the list, repositioning to
the term following the removed term. Note that the new
position could be past the end of the list.
The removed term is returned. */
inline tree
term_list::erase ()
{
tree t = *current;
current = std::list<tree>::erase (current);
return t;
}
/* Initialize the current term to the first in the list. */
inline void
term_list::start ()
{
current = begin ();
}
/* Advance to the next term in the list. */
inline void
term_list::next ()
{
++current;
}
/* Returns true when the current position is past the end. */
inline bool
term_list::done () const
{
return current == 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).
*/
struct proof_goal
{
term_list assumptions;
term_list conclusions;
};
/* A proof state owns a list of goals and tracks the
current sub-goal. The class also provides facilities
for managing subgoals and constructing term lists. */
struct proof_state : std::list<proof_goal>
{
proof_state ();
iterator branch (iterator i);
};
/* An alias for proof state iterators. */
typedef proof_state::iterator goal_iterator;
/* Initialize the state with a single empty goal,
and set that goal as the current subgoal. */
inline
proof_state::proof_state ()
: std::list<proof_goal> (1)
{ }
/* Branch the current goal by creating a new subgoal,
returning a reference to // the new object. This does
not update the current goal. */
inline proof_state::iterator
proof_state::branch (iterator i)
{
gcc_assert (i != end());
proof_goal& g = *i;
return insert (++i, g);
}
/*---------------------------------------------------------------------------
Logical rules
---------------------------------------------------------------------------*/
/*These functions modify the current state and goal by decomposing
logical expressions using the logical rules of sequent calculus for
first order logic.
Note that in each decomposition rule, the term T has been erased
from term list before the specific rule is applied. */
/* The left logical rule for conjunction adds both operands
to the current set of constraints. */
void
left_conjunction (proof_state &, goal_iterator i, tree t)
{
gcc_assert (TREE_CODE (t) == CONJ_CONSTR);
/* Insert the operands into the current branch. Note that the
final order of insertion is left-to-right. */
term_list &l = i->assumptions;
l.insert (TREE_OPERAND (t, 1));
l.insert (TREE_OPERAND (t, 0));
}
/* The left logical rule for disjunction creates a new goal,
adding the first operand to the original set of
constraints and the second operand to the new set
of constraints. */
void
left_disjunction (proof_state &s, goal_iterator i, tree t)
{
gcc_assert (TREE_CODE (t) == DISJ_CONSTR);
/* Branch the current subgoal. */
goal_iterator j = s.branch (i);
term_list &l1 = i->assumptions;
term_list &l2 = j->assumptions;
/* Insert operands into the different branches. */
l1.insert (TREE_OPERAND (t, 0));
l2.insert (TREE_OPERAND (t, 1));
}
/* The left logical rules for parameterized constraints
adds its operand to the current goal. The list of
parameters are effectively discarded. */
void
left_parameterized_constraint (proof_state &, goal_iterator i, tree t)
{
gcc_assert (TREE_CODE (t) == PARM_CONSTR);
term_list &l = i->assumptions;
l.insert (PARM_CONSTR_OPERAND (t));
}
/*---------------------------------------------------------------------------
Decomposition
---------------------------------------------------------------------------*/
/* The following algorithms decompose expressions into sets of
atomic propositions. In terms of the sequent calculus, these
functions exercise the logical rules only.
This is equivalent, for the purpose of determining subsumption,
to rewriting a constraint in disjunctive normal form. It also
allows the resulting assumptions to be used as declarations
for the purpose of separate checking. */
/* Apply the left logical rules to the proof state. */
void
decompose_left_term (proof_state &s, goal_iterator i)
{
term_list &l = i->assumptions;
tree t = l.current_term ();
switch (TREE_CODE (t))
{
case CONJ_CONSTR:
left_conjunction (s, i, l.erase ());
break;
case DISJ_CONSTR:
left_disjunction (s, i, l.erase ());
break;
case PARM_CONSTR:
left_parameterized_constraint (s, i, l.erase ());
break;
default:
l.next ();
break;
}
}
/* Apply the left logical rules of the sequent calculus
until the current goal is fully decomposed into atomic
constraints. */
void
decompose_left_goal (proof_state &s, goal_iterator i)
{
term_list& l = i->assumptions;
l.start ();
while (!l.done ())
decompose_left_term (s, i);
}
/* Apply the left logical rules of the sequent calculus
until the antecedents are fully decomposed into atomic
constraints. */
void
decompose_left (proof_state& s)
{
goal_iterator iter = s.begin ();
goal_iterator end = s.end ();
for ( ; iter != end; ++iter)
decompose_left_goal (s, iter);
}
/* Returns a vector of terms from the term list L. */
tree
extract_terms (term_list& l)
{
tree result = make_tree_vec (l.size());
term_list::iterator iter = l.begin();
term_list::iterator end = l.end();
for (int n = 0; iter != end; ++iter, ++n)
TREE_VEC_ELT (result, n) = *iter;
return result;
}
/* Extract the assumptions from the proof state S
as a vector of vectors of atomic constraints. */
inline tree
extract_assumptions (proof_state& s)
{
tree result = make_tree_vec (s.size ());
goal_iterator iter = s.begin ();
goal_iterator end = s.end ();
for (int n = 0; iter != end; ++iter, ++n)
TREE_VEC_ELT (result, n) = extract_terms (iter->assumptions);
return result;
}
} // namespace
/* Decompose the required expression T into a constraint set: a
vector of vectors containing only atomic propositions. If T is
invalid, return an error. */
tree
decompose_assumptions (tree t)
{
if (!t || t == error_mark_node)
return t;
/* Create a proof state, and insert T as the sole assumption. */
proof_state s;
term_list &l = s.begin ()->assumptions;
l.insert (t);
/* Decompose the expression into a constraint set, and then
extract the terms for the AST. */
decompose_left (s);
return extract_assumptions (s);
}
/*---------------------------------------------------------------------------
Subsumption Rules
---------------------------------------------------------------------------*/
namespace {
bool subsumes_constraint (tree, tree);
bool subsumes_conjunction (tree, tree);
bool subsumes_disjunction (tree, tree);
bool subsumes_parameterized_constraint (tree, tree);
bool subsumes_atomic_constraint (tree, tree);
/* Returns true if the assumption A matches the conclusion C. This
is generally the case when A and C have the same syntax.
NOTE: There will be specialized matching rules to accommodate
type equivalence, conversion, inheritance, etc. But this is not
in the current concepts draft. */
inline bool
match_terms (tree a, tree c)
{
return cp_tree_equal (a, c);
}
/* Returns true if the list of assumptions AS subsumes the atomic
proposition C. This is the case when we can find a proposition
in AS that entails the conclusion C. */
bool
subsumes_atomic_constraint (tree as, tree c)
{
for (int i = 0; i < TREE_VEC_LENGTH (as); ++i)
if (match_terms (TREE_VEC_ELT (as, i), c))
return true;
return false;
}
/* Returns true when both operands of C are subsumed by the
assumptions AS. */
inline bool
subsumes_conjunction (tree as, tree c)
{
tree l = TREE_OPERAND (c, 0);
tree r = TREE_OPERAND (c, 1);
return subsumes_constraint (as, l) && subsumes_constraint (as, r);
}
/* Returns true when either operand of C is subsumed by the
assumptions AS. */
inline bool
subsumes_disjunction (tree as, tree c)
{
tree l = TREE_OPERAND (c, 0);
tree r = TREE_OPERAND (c, 1);
return subsumes_constraint (as, l) || subsumes_constraint (as, r);
}
/* Returns true when the operand of C is subsumed by the
assumptions in AS. The parameters are not considered in
the subsumption rules. */
bool
subsumes_parameterized_constraint (tree as, tree c)
{
tree t = PARM_CONSTR_OPERAND (c);
return subsumes_constraint (as, t);
}
/* Returns true when the list of assumptions AS subsumes the
concluded proposition C. This is a simple recursive descent
on C, matching against propositions in the assumption list AS. */
bool
subsumes_constraint (tree as, tree c)
{
switch (TREE_CODE (c))
{
case CONJ_CONSTR:
return subsumes_conjunction (as, c);
case DISJ_CONSTR:
return subsumes_disjunction (as, c);
case PARM_CONSTR:
return subsumes_parameterized_constraint (as, c);
default:
return subsumes_atomic_constraint (as, c);
}
}
/* Returns true if the LEFT constraints subsume the RIGHT constraints.
This is done by checking that the RIGHT requirements follow from
each of the LEFT subgoals. */
bool
subsumes_constraints_nonnull (tree left, tree right)
{
gcc_assert (check_constraint_info (left));
gcc_assert (check_constraint_info (right));
/* Check that the required expression in RIGHT is subsumed by each
subgoal in the assumptions of LEFT. */
tree as = CI_ASSUMPTIONS (left);
tree c = CI_NORMALIZED_CONSTRAINTS (right);
for (int i = 0; i < TREE_VEC_LENGTH (as); ++i)
if (!subsumes_constraint (TREE_VEC_ELT (as, i), c))
return false;
return true;
}
} /* namespace */
/* Returns true if the LEFT constraints subsume the RIGHT
constraints. */
bool
subsumes (tree left, tree right)
{
if (left == right)
return true;
if (!left)
return false;
if (!right)
return true;
return subsumes_constraints_nonnull (left, right);
}

View File

@ -1924,6 +1924,14 @@ implicitly_declare_fn (special_function_kind kind, tree type,
rest_of_decl_compilation (fn, toplevel_bindings_p (), at_eof);
gcc_assert (!TREE_USED (fn));
/* Propagate constraints from the inherited constructor. */
if (flag_concepts && inherited_ctor)
if (tree orig_ci = get_constraints (inherited_ctor))
{
tree new_ci = copy_node (orig_ci);
set_constraints (fn, new_ci);
}
/* Restore PROCESSING_TEMPLATE_DECL. */
processing_template_decl = saved_processing_template_decl;

File diff suppressed because it is too large Load Diff

View File

@ -397,6 +397,15 @@ typedef struct GTY(()) cp_parser {
member definition using a generic type, it is the sk_class scope. */
cp_binding_level* implicit_template_scope;
/* True if parsing a result type in a compound requirement. This permits
constrained-type-specifiers inside what would normally be a trailing
return type. */
bool in_result_type_constraint_p;
/* True if a constrained-type-specifier is not allowed in this
context e.g., because they could never be deduced. */
int prevent_constrained_type_specifiers;
} cp_parser;
/* In parser.c */

File diff suppressed because it is too large Load Diff

View File

@ -253,6 +253,19 @@ cxx_print_xnode (FILE *file, tree node, int indent)
fprintf (file, "pending_template");
}
break;
case CONSTRAINT_INFO:
{
tree_constraint_info *cinfo = (tree_constraint_info *)node;
if (cinfo->template_reqs)
print_node (file, "template_reqs", cinfo->template_reqs, indent+4);
if (cinfo->declarator_reqs)
print_node (file, "declarator_reqs", cinfo->declarator_reqs,
indent+4);
print_node (file, "associated_constr",
cinfo->associated_constr, indent+4);
print_node_brief (file, "assumptions", cinfo->assumptions, indent+4);
break;
}
case ARGUMENT_PACK_SELECT:
print_node (file, "pack", ARGUMENT_PACK_SELECT_FROM_PACK (node),
indent+4);

View File

@ -1203,6 +1203,12 @@ lookup_member (tree xbasetype, tree name, int protect, bool want_type,
}
type = complete_type (type);
/* Make sure we're looking for a member of the current instantiation in the
right partial specialization. */
if (flag_concepts && dependent_type_p (type))
type = currently_open_class (type);
if (!basetype_path)
basetype_path = TYPE_BINFO (type);

View File

@ -2710,10 +2710,18 @@ finish_template_template_parm (tree aggr, tree identifier)
{
tree decl = build_decl (input_location,
TYPE_DECL, identifier, NULL_TREE);
tree tmpl = build_lang_decl (TEMPLATE_DECL, identifier, NULL_TREE);
DECL_TEMPLATE_PARMS (tmpl) = current_template_parms;
DECL_TEMPLATE_RESULT (tmpl) = decl;
DECL_ARTIFICIAL (decl) = 1;
// Associate the constraints with the underlying declaration,
// not the template.
tree reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms);
tree constr = build_constraints (reqs, NULL_TREE);
set_constraints (decl, constr);
end_template_decl ();
gcc_assert (DECL_TEMPLATE_PARMS (tmpl));
@ -2977,6 +2985,72 @@ finish_template_decl (tree parms)
end_specialization ();
}
// Returns the template type of the class scope being entered. If we're
// entering a constrained class scope. TYPE is the class template
// scope being entered and we may need to match the intended type with
// a constrained specialization. For example:
//
// template<Object T>
// struct S { void f(); }; #1
//
// template<Object T>
// void S<T>::f() { } #2
//
// We check, in #2, that S<T> refers precisely to the type declared by
// #1 (i.e., that the constraints match). Note that the following should
// be an error since there is no specialization of S<T> that is
// unconstrained, but this is not diagnosed here.
//
// template<typename T>
// void S<T>::f() { }
//
// We cannot diagnose this problem here since this function also matches
// qualified template names that are not part of a definition. For example:
//
// template<Integral T, Floating_point U>
// typename pair<T, U>::first_type void f(T, U);
//
// Here, it is unlikely that there is a partial specialization of
// pair constrained for for Integral and Floating_point arguments.
//
// The general rule is: if a constrained specialization with matching
// constraints is found return that type. Also note that if TYPE is not a
// class-type (e.g. a typename type), then no fixup is needed.
static tree
fixup_template_type (tree type)
{
// Find the template parameter list at the a depth appropriate to
// the scope we're trying to enter.
tree parms = current_template_parms;
int depth = template_class_depth (type);
for (int n = processing_template_decl; n > depth && parms; --n)
parms = TREE_CHAIN (parms);
if (!parms)
return type;
tree cur_reqs = TEMPLATE_PARMS_CONSTRAINTS (parms);
tree cur_constr = build_constraints (cur_reqs, NULL_TREE);
// Search for a specialization whose type and constraints match.
tree tmpl = CLASSTYPE_TI_TEMPLATE (type);
tree specs = DECL_TEMPLATE_SPECIALIZATIONS (tmpl);
while (specs)
{
tree spec_constr = get_constraints (TREE_VALUE (specs));
// If the type and constraints match a specialization, then we
// are entering that type.
if (same_type_p (type, TREE_TYPE (specs))
&& equivalent_constraints (cur_constr, spec_constr))
return TREE_TYPE (specs);
specs = TREE_CHAIN (specs);
}
// If no specialization matches, then must return the type
// previously found.
return type;
}
/* Finish processing a template-id (which names a type) of the form
NAME < ARGS >. Return the TYPE_DECL for the type named by the
template-id. If ENTERING_SCOPE is nonzero we are about to enter
@ -2990,6 +3064,16 @@ finish_template_type (tree name, tree args, int entering_scope)
type = lookup_template_class (name, args,
NULL_TREE, NULL_TREE, entering_scope,
tf_warning_or_error | tf_user);
/* If we might be entering the scope of a partial specialization,
find the one with the right constraints. */
if (flag_concepts
&& entering_scope
&& CLASS_TYPE_P (type)
&& dependent_type_p (type)
&& PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (type)))
type = fixup_template_type (type);
if (type == error_mark_node)
return type;
else if (CLASS_TYPE_P (type) && !alias_type_or_template_p (type))
@ -7442,6 +7526,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
case CPTK_IS_POLYMORPHIC:
return (CLASS_TYPE_P (type1) && TYPE_POLYMORPHIC_P (type1));
case CPTK_IS_SAME_AS:
return same_type_p (type1, type2);
case CPTK_IS_STD_LAYOUT:
return (std_layout_type_p (type1));
@ -7549,8 +7636,9 @@ finish_trait_expr (cp_trait_kind kind, tree type1, tree type2)
case CPTK_IS_CLASS:
case CPTK_IS_ENUM:
case CPTK_IS_UNION:
case CPTK_IS_SAME_AS:
break;
default:
gcc_unreachable ();
}

View File

@ -2955,16 +2955,19 @@ cp_tree_equal (tree t1, tree t2)
up for expressions that involve 'this' in a member function
template. */
if (comparing_specializations)
if (comparing_specializations && !CONSTRAINT_VAR_P (t1))
/* When comparing hash table entries, only an exact match is
good enough; we don't want to replace 'this' with the
version from another function. */
version from another function. But be more flexible
with local parameters in a requires-expression. */
return false;
if (same_type_p (TREE_TYPE (t1), TREE_TYPE (t2)))
{
if (DECL_ARTIFICIAL (t1) ^ DECL_ARTIFICIAL (t2))
return false;
if (CONSTRAINT_VAR_P (t1) ^ CONSTRAINT_VAR_P (t2))
return false;
if (DECL_ARTIFICIAL (t1)
|| (DECL_PARM_LEVEL (t1) == DECL_PARM_LEVEL (t2)
&& DECL_PARM_INDEX (t1) == DECL_PARM_INDEX (t2)))
@ -3000,6 +3003,10 @@ cp_tree_equal (tree t1, tree t2)
return (cp_tree_equal (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0))
&& cp_tree_equal (TREE_OPERAND (t1, 1), TREE_OPERAND (t2, 1)));
case CONSTRAINT_INFO:
return cp_tree_equal (CI_ASSOCIATED_CONSTRAINTS (t1),
CI_ASSOCIATED_CONSTRAINTS (t2));
case TREE_VEC:
{
unsigned ix;
@ -3876,6 +3883,14 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func,
*walk_subtrees_p = 0;
break;
case REQUIRES_EXPR:
// Only recurse through the nested expression. Do not
// walk the parameter list. Doing so causes false
// positives in the pack expansion checker since the
// requires parameters are introduced as pack expansions.
WALK_SUBTREE (TREE_OPERAND (*tp, 1));
*walk_subtrees_p = 0;
break;
default:
return NULL_TREE;

View File

@ -3474,6 +3474,25 @@ cp_build_function_call_vec (tree function, vec<tree, va_gc> **params,
if (TREE_CODE (function) == FUNCTION_DECL)
{
/* If the function is a non-template member function
or a non-template friend, then we need to check the
constraints.
Note that if overload resolution failed with a single
candidate this function will be used to explicitly diagnose
the failure for the single call expression. The check is
technically redundant since we also would have failed in
add_function_candidate. */
if (flag_concepts
&& (complain & tf_error)
&& !constraints_satisfied_p (function))
{
error ("cannot call function %qD", function);
location_t loc = DECL_SOURCE_LOCATION (function);
diagnose_constraints (loc, function, NULL_TREE);
return error_mark_node;
}
if (!mark_used (function, complain) && !(complain & tf_error))
return error_mark_node;
fndecl = function;

View File

@ -19280,7 +19280,8 @@ Predefined Macros,cpp,The GNU C Preprocessor}).
* C++ Attributes:: Variable, function, and type attributes for C++ only.
* Function Multiversioning:: Declaring multiple function versions.
* Namespace Association:: Strong using-directives for namespace association.
* Type Traits:: Compiler support for type traits
* Type Traits:: Compiler support for type traits.
* C++ Concepts:: Improved support for generic programming.
* Java Exceptions:: Tweaking exception handling to work with Java.
* Deprecated Features:: Things will disappear from G++.
* Backwards Compatibility:: Compatibilities with earlier definitions of C++.
@ -20076,6 +20077,52 @@ an enumeration type ([dcl.enum]).
@end table
@node C++ Concepts
@section C++ Concepts
C++ concepts provide much-improved support for generic programming. In
particular, they allow the specification of constraints on template arguments.
The constraints are used to extend the usual overloading and partial
specialization capabilities of the language, allowing generic data structures
and algorithms to be ``refined'' based on their properties rather than their
type names.
The following keywords are reserved for concepts.
@table @code
@item assumes
States an expression as an assumption, and if possible, verifies that the
assumption is valid. For example, @code{assume(n > 0)}.
@item axiom
Introduces an axiom definition. Axioms introduce requirements on values.
@item forall
Introduces a universally quantified object in an axiom. For example,
@code{forall (int n) n + 0 == n}).
@item concept
Introduces a concept definition. Concepts are sets of syntactic and semantic
requirements on types and their values.
@item requires
Introduces constraints on template arguments or requirements for a member
function of a class template.
@end table
The front end also exposes a number of internal mechanism that can be used
to simplify the writing of type traits. Note that some of these traits are
likely to be removed in the future.
@table @code
@item __is_same (type1, type2)
A binary type trait: true whenever the type arguments are the same.
@end table
@node Java Exceptions
@section Java Exceptions

View File

@ -0,0 +1,15 @@
// { dg-options "-std=c++1z" }
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;
}

View File

@ -0,0 +1,13 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool C() { return __is_class(T); }
template<C T> using X = T*;
struct S { };
int main()
{
X<S> x1;
}

View File

@ -0,0 +1,13 @@
// { dg-options "-std=c++1z" }
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" }
}

View File

@ -0,0 +1,19 @@
// { dg-options "-std=c++1z" }
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 *-*-* } }
}

View File

@ -0,0 +1,52 @@
// { dg-options "-std=c++1z" }
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() { }

View File

@ -0,0 +1,14 @@
// { dg-options "-std=c++1z" }
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() { }

View File

@ -0,0 +1,14 @@
// { dg-options "-std=c++1z" }
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() { }

View File

@ -0,0 +1,14 @@
// { dg-options "-std=c++1z" }
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() { }

View File

@ -0,0 +1,21 @@
// { dg-options "-std=c++1z" }
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() { }

View File

@ -0,0 +1,26 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool One() { return sizeof(T) >= 4; }
template<typename T>
concept bool Two() { return One<T>() && sizeof(T) >= 8; }
// 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() { }

View File

@ -0,0 +1,18 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool One() { return sizeof(T) >= 4; }
template<typename T>
concept bool Two() { return One<T>() && sizeof(T) >= 8; }
// Check that there is no ecsacpe hatch
template<Two T> struct S4 { };
template<One T> struct S4<T> { }; // { dg-error "does not specialize" }
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() { }

View File

@ -0,0 +1,13 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool C() { return __is_class(T); }
template<const C T> struct S1 { }; // { dg-error "cv-qualified" }
template<volatile C T> struct S2 { }; // { dg-error "cv-qualified" }
template<C* T> struct S3 { }; // { dg-error "invalid" }
template<C const* T> struct S3a { }; // { dg-error "invalid" }
template<C* const T> struct S3b { }; // { dg-error "invalid" }
template<C& T> struct S4 { }; // { dg-error "invalid" }
template<C[3] T> struct S4 { }; // { dg-error "invalid|expected" }
template<C(*T)()> struct S5 { }; // { dg-error "invalid" }

View File

@ -0,0 +1,32 @@
// { dg-options "-std=c++1z" }
typedef concept int CINT; // { dg-error "'concept' cannot appear in a typedef declaration" }
void f(concept int); // { dg-error "a parameter cannot be declared 'concept'" }
template<typename T>
concept int f2() { return 0; } // { dg-error "return type" }
concept bool f3(); // { dg-error "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" }
template<typename T>
static concept bool f6() { return true; } // { dg-error "a concept cannot be a member function" }
static concept bool x; // { dg-error "declared 'concept'" }
concept int x2; // { dg-error "declared 'concept'" }
concept ~X(); // { dg-error "a destructor cannot be 'concept'" }
concept X(); // { dg-error "a constructor cannot be 'concept'" }
};
concept bool X2; // { dg-error "non-template variable" }
template<typename T>
concept bool X3; // { dg-error "has no initializer" }
struct S {
template<typename T>
static concept bool C1 = true; // { dg-error "static data member" }
};

View File

@ -0,0 +1,12 @@
// PR c++/67007
// { dg-options -std=c++1z }
template <class U>
concept bool A =
requires (U u) { u; };
template <class T>
concept bool B =
requires (T t) { { t } -> A; };
void foo(B);

View File

@ -0,0 +1,59 @@
// PR c++/66962
// { dg-options -std=c++1z }
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();

View File

@ -0,0 +1,34 @@
// PR c++/66092
// { dg-options "-std=c++1z" }
#include <type_traits>
template <typename T, typename U, typename... Args>
requires (sizeof...(Args) == 0)
constexpr decltype(auto) check()
{
return std::integral_constant<bool, __is_same_as(T, U)>();
}
template <typename T, typename U, typename... Args>
requires (sizeof...(Args) > 0)
constexpr decltype(auto) check()
{
return std::integral_constant<bool, __is_same_as(T, U)
&& decltype(check<U, Args...>())::value>();
}
template <typename T, typename U, typename... Args>
concept bool Same()
{
return decltype(check<T, U, Args...>())::value;
}
template <typename... Args>
requires Same<Args...>() // { dg-error "concept" }
void foo( Args... args ) {}
int main()
{
foo(1, 2, 3); // { dg-error "" }
}

View File

@ -0,0 +1,34 @@
// { dg-options "-std=c++1z" }
// Check equivalence of short- and longhand declarations.
template<typename T>
concept bool C() { return __is_class(T); }
template<typename T>
concept bool D() { return __is_empty(T); }
struct X { } x;
void f1(C x);
template<C T> void f2(T x);
void f3(C x);
template<C T> void f4(T x) requires D<T>();
template<C T> void f5(T x) requires D<T>();
template<C T> void f6(T x) requires D<T>();
int main() {
f1(x);
f2(x);
f3(x);
f4(x);
f5(x);
f6(x);
}
template<typename T> requires C<T>() void f1(T x) { }
template<typename T> requires C<T>() void f2(T x) { }
template<C T> void f3(T x) { }
template<typename T> requires C<T>() void f4(T x) requires D<T>() { }
template<typename T> requires C<T>() and D<T>() void f5(T x) { }
template<typename T> void f6(T x) requires C<T>() and D<T>() { }

View File

@ -0,0 +1,29 @@
// { dg-do run }
// { dg-options "-std=c++1z" }
// template<typename T>
// concept bool C() { return true; }
template<typename T>
concept bool C = true;
void f1(C, C);
void f2(C, C);
void f3(C, C);
int main() {
f1(0, 0);
f2(0, 0);
f3(0, 0);
}
void f1(C, C) { }
template<C T>
void f2(T, T) { }
template<typename T>
requires C<T>
void f3(T, T) { }

View File

@ -0,0 +1,21 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool C() { return __is_class(T); }
template<typename T>
concept bool D() { return C<T>() && __is_empty(T); }
struct X { };
struct Y { int n; };
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
int main() { }

View File

@ -0,0 +1,24 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool C() { return __is_class(T); }
template<typename T>
concept bool D() { return C<T>() && __is_empty(T); }
struct X { };
struct Y { int n; };
template<typename T> struct S { void f1() { } }; // #1
template<C T> struct S<T> { void f2() { } }; // #2
template<D T> struct S<T> { void f3() { } }; // #3
template struct S<int>; // Instantiate #1
template struct S<X>; // Instantiate #2
template struct S<Y>; // Instantiate #2
int main() {
S<int> i; i.f1();
S<X> x; x.f3();
S<Y> y; y.f2();
}

View File

@ -0,0 +1,27 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool C() { return __is_class(T); }
template<typename T>
concept bool D() { return C<T>() && __is_empty(T); }
struct X { };
struct Y { int n; };
template<typename T>
struct S {
void f() { } // #1
void f() requires C<T>() { } // #2
void g() requires C<T>() { } // #1
void g() requires D<T>() { } // #2
};
template void S<int>::f(); // #1
template void S<X>::f(); // #2
template void S<X>::g(); // #2
template void S<Y>::g(); // #1
int main() { }

View File

@ -0,0 +1,17 @@
// { dg-options "-std=c++1z" }
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() { }

View File

@ -0,0 +1,32 @@
// { dg-do run }
// { dg-options "-std=c++1z" }
#include <cassert>
template<typename T>
concept bool C() { return __is_class(T); }
template<typename T>
concept bool D() { return C<T>() && __is_empty(T); }
struct X { } x;
struct Y { int n; } y;
template<typename T> void g(T) { } // #1
template<C T> void g(T) { } // #2
template<D T> void g(T) { } // #3
int called;
template<> void g(int) { called = 1; } // Specialization of #1
template<> void g<X>(X) { called = 2; } // Specialization of #3
template<> void g(Y) { called = 3; } // Specialization of #2
int main() {
g(0);
assert(called == 1);
g(x);
assert(called == 2);
g(y);
assert(called == 3);
}

View File

@ -0,0 +1,13 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool C() { return __is_class(T); }
struct X { };
template<C T> struct S;
template<> struct S<X> { void f() { } };
int main() {
S<X> x; x.f();
}

View File

@ -0,0 +1,13 @@
// { dg-options "-std=c++1z" }
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() { }

View File

@ -0,0 +1,51 @@
// { dg-do run }
// { dg-options "-std=c++1z" }
#include <cassert>
template<typename T>
concept bool C() { return __is_class(T); }
template<typename T>
concept bool D() { return C<T>() && __is_empty(T); }
struct X { } x;
struct Y { int n; } y;
int called = 0;
template<typename T>
struct S {
void f() { called = 0; } // #1
void f() requires C<T>() { called = 0; } // #2
void g() requires C<T>() { } // #1
void g() requires D<T>() { } // #2
};
template<> void S<int>::f() { called = 1; } // Spec of #1
template<> void S<X>::f() { called = 2; } // Spec of #2
template<> void S<X>::g() { called = 3; } // Spec of #2
template<> void S<Y>::g() { called = 4; } // Spec of #1
int main() {
S<double> sd;
S<int> si;
S<X> sx;
S<Y> sy;
sd.f();
assert(called == 0);
si.f();
assert(called == 1);
sx.f();
assert(called == 2);
sy.f();
assert(called == 0);
sx.g();
assert(called == 3);
sy.g();
assert(called == 4);
}

View File

@ -0,0 +1,21 @@
// { dg-options "-std=c++1z" }
#include <cassert>
template<typename T>
concept bool C() { return __is_class(T); }
template<typename T>
concept bool D() { return C<T>() && __is_empty(T); }
struct X { } x;
struct Y { int n; } y;
int called = 0;
template<typename T>
struct S {
void f() requires C<T>();
};
template<> void S<int>::f() { called = 1; } // { dg-error "match" }

View File

@ -0,0 +1,18 @@
// { dg-options "-std=c++1z" }
template<typename T>
struct A {
template<class T2> void f1(T, T2); // member template
template<class T2> void f2(T, T2); // member template
};
template<>
template<class X1> void A<int>::f1(int, X1);
// Specialization with template-id
template<>
template<> void A<int>::f2<char>(int, char);
// Specialization with deduction
template<>
template<> void A<int>::f1(int, char);

View File

@ -0,0 +1,20 @@
// { dg-do run }
// { dg-options "-std=c++1z" }
#include <cassert>
#include <iostream>
template<typename T>
concept bool C1 = __is_class(T);
template<typename T>
concept bool C2() { return __is_class(T); }
template<typename T>
concept bool C3() { return requires (T a) { ++a; }; }
int main() {
if (C1<int>) assert(false);
if (C2<int>()) assert(false);
if (!C3<int>()) assert(false);
}

View File

@ -0,0 +1,42 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool C1()
{
return requires (T t) { t.f(); };
}
template<typename T>
concept bool C2()
{
return requires { typename T::type; };
}
template<typename T>
requires C1<T>()
void f1(T x) { }
template<typename T>
requires C2<T>()
void f2(T x) { }
// Note that these declarations are private and therefore
// cannot satisify the constraints.
class S
{
using type = int;
void f() { }
} s;
int main()
{
f1(s); // { dg-error "cannot call" }
f2(s); // { dg-error "cannot call" }
// 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
// in both cases.
static_assert(C1<S>(), ""); // { dg-error "failed" }
static_assert(C2<S>(), ""); // { dg-error "failed" }
}

View File

@ -0,0 +1,23 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool C()
{
return requires (T& t) { t.~T(); };
}
class S1
{
~S1() { }
};
class S2
{
~S2() = delete;
};
int main()
{
static_assert(C<S1>(), ""); // { dg-error "failed" }
static_assert(C<S2>(), ""); // { dg-error "failed" }
}

View File

@ -0,0 +1,5 @@
// { dg-options -std=c++1z }
#ifndef __cpp_concepts
#error __cpp_concepts not defined
#endif

View File

@ -0,0 +1,9 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool Tuple() { // { dg-error "multiple statements" }
static_assert(T::value, "");
return true;
}
void f(Tuple&);

View File

@ -0,0 +1,7 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept auto C1() { return 0; } // { dg-error "deduced return type" }
template<typename T>
concept int C2() { return 0; } // { dg-error "return type" }

View File

@ -0,0 +1,25 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool C() { return __is_class(T); }
struct S { } s;
template<typename T>
requires C<T>()
void f(T x) { }
// Calls are valid when arguments are dependent,
template<typename T>
void g(T x) { f(x); }
// Calls are checked when arguments are non-dependent.
template<typename T>
void h(T x) {
f(s);
}
int main() {
f(s);
g(s);
}

View File

@ -0,0 +1,92 @@
// { dg-do compile }
// { dg-options "-std=c++1z" }
// Test that constraint satisfaction checks work even when
// processing template declarations.
namespace std
{
struct ostream { };
ostream cout;
template<typename T>
auto begin(T& t) -> decltype(t.begin()) { return t.begin(); }
template<typename T>
auto begin(T const& t) -> decltype(t.begin()) { return t.begin(); }
template<typename T>
auto end(T& t) -> decltype(t.end()) { return t.end(); }
template<typename T>
auto end(T const& t) -> decltype(t.end()) { return t.end(); }
} // namespace std
template <typename T>
concept bool Float()
{
return __is_same_as( T, float );
}
template <typename T>
constexpr decltype(auto) project( T t )
{
return t;
}
template <typename T>
concept bool Concept()
{
return requires( T t ) {
requires Float<decltype( project(t) )>();
};
}
template <Concept E, Concept F>
constexpr decltype(auto) operator<<( E&& e, F&& f ) {}
template <Concept T>
void foo( T t )
{
// Try to resolve operator<< from within a template context but
// with non-dependent arguments. We need to ensure that template
// processing is turned off whenever checking for satisfaction.
std::cout << "OK"; // { dg-error "no match" }
}
template <typename R>
concept bool Range()
{
return requires( R r ) {
requires __is_same_as(
decltype(std::begin(r)), decltype(std::end(r)) );
};
}
struct A
{
A() = default;
A( const A& ) = default;
// Derivation from this class forces the instantiation of
// this constructor, which results in the __is_same_as type
// trait above to become error_mark_node in this declaration.
template <Range R>
explicit A( R&& r ) { }
};
struct C : A
{
C() = default;
C( const C& ) = default;
};
int main()
{
C c; // OK
return 0;
}

View File

@ -0,0 +1,18 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool C() { return __is_class(T); }
template<typename T>
requires C<T>()
void f(T x) { }
// Non-dependent args are checked even in dependent scope.
template<typename T>
void h(T x) {
f(0); // { dg-error "cannot call" }
}
int main() {
f(0); // { dg-error "cannot call" }
}

View File

@ -0,0 +1,25 @@
// { dg-do run }
// { dg-options "-std=c++1z" }
#include <cassert>
// Check partial ordering during overload resolution.
template<typename T>
concept bool C() { return __is_class(T); }
template<typename T>
concept bool D() { return C<T>() and __is_empty(T); }
struct S1 { } s1;
struct S2 { int n; } s2;
int called = 0;
template<C T> void f1(T x) { called = 1;}
template<D T> void f1(T x) { called = 2;}
int main() {
f1(s1); assert(called == 2);
f1(s2); assert(called == 1);
}

View File

@ -0,0 +1,17 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool C() { return __is_class(T); }
template<typename T>
concept bool D() { return C<T>() and __is_empty(T); }
struct S1 { } s1;
struct S2 { int n; } s2;
template<C T> void f1(T x) { }
template<D T> void f1(T x) { }
int main() {
f1(0); // { dg-error "matching" }
}

View File

@ -0,0 +1,23 @@
// { dg-options "-std=c++1z" }
// Check shorthand notation.
template<typename T>
concept bool Type() { return true; }
template<typename T, typename U>
concept bool Same() { return __is_same_as(T, U); }
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) { }
int main() {
S1<char> s1; // { dg-error "constraint|invalid" }
S2<int, char> s2; // { dg-error "constraint|invalid" }
f('a'); // { dg-error "cannot" }
g(0, 'a'); // { dg-error "cannot" }
}

View File

@ -0,0 +1,16 @@
// { dg-options "-std=c++1z" }
// Redefinition errors.
template<typename T>
concept bool C() { return __is_class(T); }
template<typename T>
concept bool D() { return C<T>() and __is_empty(T); }
template<C T> void f(T x) { }
template<typename T>
requires C<T>()
void f(T x) { } // { dg-error "redefinition" }
int main() { }

View File

@ -0,0 +1,8 @@
// { dg-do link }
// { dg-options "-std=c++1z" }
// FIXME: What is this actually testing?
void f() requires true { }
int main() { }

View File

@ -0,0 +1,27 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool Class() { return __is_class(T); }
template<Class T> void f(T) { }
template<typename T> void fn(T) { }
auto p1 = &f<int>; // { dg-error "no matches" }
void (*p2)(int) = &f<int>; // { dg-error "no matches" }
void (*p3)(int) = &f; // { dg-error "no matches" }
struct S {
template<Class T> int f(T) { }
};
auto p4 = &S::template f<int>; // { dg-error "no matches" }
int (S::*p6)(int) = &S::template f<int>; // { dg-error "no matches" }
int (S::*p7)(int) = &S::f; // { dg-error "no matches" }
template<typename T>
void g(T x) { }
int main () {
g(&f<int>); // { dg-error "no matches" }
}

View File

@ -0,0 +1,35 @@
// { dg-do run }
// { dg-options "-std=c++1z" }
#include <cassert>
template<typename T>
concept bool Class() { return __is_class(T); }
template<typename T>
concept bool Empty() { return Class<T>() and __is_empty(T); }
template<Class T> int f(T) { return 1; }
template<Empty T> int f(T) { return 2; }
struct S {
template<Class T> int f(T) { return 1; }
template<Empty T> int f(T) { return 2; }
} s;
struct X { } x;
struct Y { X x; } y;
int main () {
auto p1 = &f<X>; // Empty f
assert(p1(x) == 2);
auto p2 = &f<Y>; // Class f
assert(p2(y) == 1);
auto p3 = &S::template f<X>; // Empty f
assert((s.*p3)(x) == 2);
auto p4 = &S::template f<Y>; // Empty f
assert((s.*p4)(y) == 1);
}

View File

@ -0,0 +1,34 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool Eq() { return requires(T t) { t == t; }; }
struct Nt {
template<Eq T> friend void f(T) { }
} nt;
template<typename T> struct S;
template<Eq T>
void proc(S<T>*);
template<typename T>
struct S {
friend bool operator==(S, S) requires Eq<T>() { return true; }
friend void proc<>(S*); // { dg-error "does not match any template declaration" }
};
struct X { } x;
int main() {
// f(0); // OK
f(nt); // { dg-error "cannot call" }
f(x); // { dg-error "not declared" }
S<int> si;
si == si; // OK
S<X> sx;
sx == sx; // { dg-error "no match" }
}

View File

@ -0,0 +1,20 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool Eq() { return requires(T t) { t == t; }; }
template<Eq T> struct Foo { };
template<typename T>
struct S { // { dg-error "constraint failure" }
template<Eq U> friend class Bar;
friend class Foo<T>;
};
struct X { };
int main() {
S<int> si; // OK
S<X> sx;
}

View File

@ -0,0 +1,49 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool C() { return __is_class(T); }
template<int N>
concept bool Int() { return true; }
template<template<typename> class X>
concept bool Template() { return true; }
struct S { };
void f1(Int) { } // { dg-error "invalid" }
void f2(Template) { } // { dg-error "invalid" }
struct S1 {
void f1(auto x) { }
void f2(C x) { }
void f3(auto x) { }
void f3(C x) { }
};
template<C T>
struct S2 {
void f1(auto x) { }
void f2(C x) { }
void h1(auto x);
void h2(C x);
template<C U>
void g(T t, U u) { }
};
int main() {
S s;
S1 s1;
s1.f2(0); // { dg-error "matching" }
S2<S> s2;
s2.f2(0); // { dg-error "matching" }
s2.h2(0); // { dg-error "matching" }
s2.g(s, 0); // { dg-error "matching" }
s2.g(0, s); // { dg-error "matching" }
}

View File

@ -0,0 +1,157 @@
// { dg-do run }
// { dg-options "-std=c++1z" }
#include <cassert>
#include <type_traits>
template<typename T>
concept bool C() { return __is_class(T); }
template<typename T>
concept bool Type() { return true; }
struct S { };
int called;
// Basic terse notation
void f(auto x) { called = 1; }
void g(C x) { called = 2; }
// Overloading generic functions
void h(auto x) { called = 1; }
void h(C x) { called = 2; }
void p(auto x);
void p(C x);
struct S1 {
void f1(auto x) { called = 1; }
void f2(C x) { called = 2; }
void f3(auto x) { called = 1; }
void f3(C x) { called = 2; }
};
template<C T>
struct S2 {
void f1(auto x) { called = 1; }
void f2(C x) { called = 2; }
void f3(auto x) { called = 1; }
void f3(C x) { called = 2; }
void h1(auto x);
void h2(C x);
void h3(auto x);
void h3(C x);
template<C U>
void g1(T t, U u) { called = 1; }
template<C U>
void g2(T t, U u);
};
void ptr(C*) { called = 1; }
void ptr(const C*) { called = 2; }
void ref(C&) { called = 1; }
void ref(const C&) { called = 2; }
void
fwd_lvalue_ref(Type&& x) {
using T = decltype(x);
static_assert(std::is_lvalue_reference<T>::value, "not an lvlaue reference");
}
void
fwd_const_lvalue_ref(Type&& x) {
using T = decltype(x);
static_assert(std::is_lvalue_reference<T>::value, "not an lvalue reference");
using U = typename std::remove_reference<T>::type;
static_assert(std::is_const<U>::value, "not const-qualified");
}
void fwd_rvalue_ref(Type&& x) {
using T = decltype(x);
static_assert(std::is_rvalue_reference<T>::value, "not an rvalue reference");
}
// Make sure we can use nested names speicifers for concept names.
namespace N {
template<typename T>
concept bool C() { return true; }
} // namesspace N
void foo(N::C x) { }
int main() {
S s;
const S cs;
f(0); assert(called == 1);
g(s); assert(called == 2);
h(0); assert(called == 1);
h(s); assert(called == 2);
S1 s1;
s1.f1(0); assert(called == 1);
s1.f2(s); assert(called == 2);
s1.f3(0); assert(called == 1);
s1.f3(s); assert(called == 2);
S2<S> s2;
s2.f1(0); assert(called == 1);
s2.f2(s); assert(called == 2);
s2.f3(0); assert(called == 1);
s2.f3(s); assert(called == 2);
s2.h1(0); assert(called == 1);
s2.h2(s); assert(called == 2);
s2.h3(0); assert(called == 1);
s2.h3(s); assert(called == 2);
s2.g1(s, s); assert(called == 1);
s2.g2(s, s); assert(called == 2);
ptr(&s); assert(called == 1);
ptr(&cs); assert(called == 2);
ref(s); assert(called == 1);
ref(cs); assert(called == 2);
// Check forwarding problems
fwd_lvalue_ref(s);
fwd_const_lvalue_ref(cs);
fwd_rvalue_ref(S());
foo(0);
}
// Test that decl/def matching works.
void p(auto x) { called = 1; }
void p(C x) { called = 2; }
template<C T>
void S2<T>::h1(auto x) { called = 1; }
template<C T>
void S2<T>::h2(C x) { called = 2; }
template<C T>
void S2<T>::h3(auto x) { called = 1; }
template<C T>
void S2<T>::h3(C x) { called = 2; }
template<C T>
template<C U>
void S2<T>::g2(T t, U u) { called = 2; }

View File

@ -0,0 +1,22 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool C() { return __is_class(T); }
template<typename T>
struct S1 { S1(double) requires C<T>() { } };
struct S2 : S1<int> {
using S1<int>::S1;
};
template<typename T>
struct S3 : S1<T> {
using S1<T>::S1;
};
struct X { };
int main() {
S3<X> s(0.0);
}

View File

@ -0,0 +1,18 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool C() { return __is_class(T); }
template<typename T>
struct S1 {
S1(double) requires C<T>() { }
};
template<typename T>
struct S2 : S1<T> { // { dg-error "matching" }
using S1<T>::S1;
};
int main() {
S2<int> s; // { dg-error "deleted function" }
}

View File

@ -0,0 +1,21 @@
// { dg-options "-std=c++1z" }
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;
};
struct X { } x;
int main() {
S2<X> s = x;
}

View File

@ -0,0 +1,18 @@
// { dg-options "-std=c++1z" }
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" }
}

View File

@ -0,0 +1,38 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool C = __is_class(T);
C{T} void f1();
struct S1
{
C{T} void f2();
C{T} static void f3();
};
int main()
{
S1 s;
f1<S1>();
s.f2<S1>();
S1::f3<S1>();
return 0;
}
template<typename T>
void f1() requires C<T>
{
}
template<typename T>
void S1::f2() requires C<T>
{
}
template<typename T>
void S1::f3() requires C<T>
{
}

View File

@ -0,0 +1,27 @@
// { dg-do run }
// { dg-options "-std=c++1z" }
#include <cassert>
template<typename T>
concept bool C() { return __is_class(T); }
template<int N>
concept bool P() { return true; }
C{A} struct S1
{
P{B} int f1();
};
struct S2 {};
int main()
{
S1<S2> s;
assert(s.f1<10>() == sizeof(S2) + 10);
return 0;
}
C{A} P{B} int S1<A>::f1() { return B + sizeof(A); }

View File

@ -0,0 +1,17 @@
// { dg-options "-std=c++1z" }
template<typename ... T>
concept bool C1 = true;
template<int ... N>
concept bool C2 = true;
C1{...A} void f1() {};
C2{...A} void f2() {};
int main()
{
f1<int, short, char>();
f2<1, 2, 3>();
return 0;
}

View File

@ -0,0 +1,32 @@
// { dg-options "-std=c++1z" }
template<typename ... T>
concept bool C1 = true;
template<int ... N>
concept bool C2 = true;
template<typename T>
concept bool C3 = __is_class(T);
template<typename ... T>
concept bool C4() { return true; }
template<int N>
concept bool C4() { return true; }
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" }
C5{A, B} void f6() {};
int main()
{
// Defaults should not transfer
f6<int>(); // { dg-error "no matching" }
return 0;
}

View File

@ -0,0 +1,15 @@
// { dg-options "-std=c++1z" }
template<typename T, typename U = int>
concept bool C()
{
return sizeof(U) == sizeof(int);
}
C{A} void f1() {}
int main()
{
f1<char>();
return 0;
}

View File

@ -0,0 +1,12 @@
// PR c++/67003
// { dg-options "-std=c++1z" }
namespace X {
template<class>
concept bool C = true;
}
X::C{T}
void foo() {}
int main() { foo<int>(); }

View File

@ -0,0 +1,13 @@
// PR c++/66985
// { dg-options "-std=c++1z" }
template <template <class> class T>
concept bool _Valid = requires { typename T<int>; };
template <template <class> class T>
struct __defer { };
_Valid{T}
struct __defer<T> {
using type = T<int>;
};

View File

@ -0,0 +1,9 @@
// { dg-options "-std=c++1z" }
struct Base {
template<typename T>
static concept bool D() { return __is_same_as(T, int); } // { dg-error "a concept cannot be a member function" }
template<typename T, typename U>
static concept bool E() { return __is_same_as(T, U); } // { dg-error "a concept cannot be a member function" }
};

View File

@ -0,0 +1,37 @@
// { dg-do run}
// { dg-options "-std=c++1z" }
template<typename T>
concept bool C() { return __is_class(T); }
template<typename T>
concept bool D() { return __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 g1() requires C<T>() and true;
template<C U> void h1(U u) { called = 1; }
void g2() requires C<T>(); // { dg-error "candidate" }
};
template<typename T>
void S1<T>::g2() requires D<T>() { } // { dg-error "prototype" }
int main() {
S1<X> sx;
S1<Y> sy;
S1<int> si;
si.f1(); // { dg-error "matching" }
si.g1(); // { dg-error "matching" }
si.h1(0); // { dg-error "matching" }
}

View File

@ -0,0 +1,112 @@
// { dg-do run}
// { dg-options "-std=c++1z" }
#include <cassert>
template<typename T>
concept bool C() { return __is_class(T); }
template<typename T>
concept bool D() { return __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 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 g1() requires C<T>() and true;
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>();
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 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; }
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; }

View File

@ -0,0 +1,33 @@
// { dg-options "-std=c++1z" }
template<typename T>
concept bool Type() { return true; }
template<typename T, typename U>
concept bool Same() { return __is_same_as(T, U); }
template<typename T, typename U>
concept bool C1() { return true; }
template<typename T, typename... Args>
concept bool C2() { return true; }
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) { }
void h0(Same<int>* a) { }
void h1(C1<int>* a) { }
void h2(C2<char, short, int, long>* a) { }
int main() {
S1<int> s1;
S2<int, int> s2;
f(0);
g(0, 1);
h0((int*)0);
h1((int*)0);
h2((int*)0);
}

View File

@ -0,0 +1,15 @@
// { dg-options "-std=c++1z" }
// Make sure that we check partial concept ids
// with variable concepts.
template<class A, class B>
concept bool C = true;
template<C<int> D>
struct E
{
int f = 0;
};
E<double> e;

View File

@ -0,0 +1,15 @@
// { dg-options "-std=c++1z" }
// Check that constraints don't break unconstrained partial
// specializations.
template<typename T>
struct S { };
template<typename T>
struct S<T*> { };
template<>
struct S<int> { };
int main() { }

View File

@ -0,0 +1,32 @@
// PR c++/67084
// { dg-options -std=c++1z }
template <class T>
constexpr bool p = false;
template <class T>
constexpr bool p<T*> = false;
template <class T>
requires true
constexpr bool p<T*> = false;
template <class T>
requires true && T() == 0
constexpr bool p<T*> = true;
template <class T>
constexpr bool q = false;
template <class T>
constexpr bool q<T*> = true;
template <class T>
requires false
constexpr bool q<T*> = false;
template <class T>
requires false && T() != 0
constexpr bool q<T*> = false;
static_assert (p<int*>,"");

View File

@ -0,0 +1,7 @@
// { dg-options -std=c++1z }
template <class T> struct A { };
template <class T> requires false struct A<T*> { };
template <class T> struct A<T*> { static int i; };
int i = A<int*>::i;

View File

@ -0,0 +1,49 @@
// { dg-options -std=c++1z }
template <class T> concept bool is_int = __is_same_as(T,int);
template <class T> struct A { };
template <is_int T> struct A<T*> {
typedef int I1;
static const A<T*>::I1 j1 = 0;
static int f();
};
template <is_int T> int A<T*>::f()
{ A<T*>::I1 i; return j1; }
template <class T> struct A<T*> {
typedef int I2;
static const A<T*>::I2 j2 = 0;
static int f();
};
template <class T> int A<T*>::f()
{ A<T*>::I2 i; return j2; }
const int i1 = A<int*>::j1;
const int i2 = A<float*>::j2;
template <class T> struct B;
template <is_int T> struct B<T> {
typedef int I4;
static const B<T>::I4 j4 = 0;
static int f();
};
template <is_int T> int B<T>::f()
{ B<T>::I4 i; return j4; }
template <class T> struct B {
typedef int I5;
static const B<T>::I5 j5 = 0;
static int f();
};
template <class T> int B<T>::f()
{ B<T>::I5 i; return j5; }
int i4 = B<int>::j4;
int i5 = B<float>::j5;
int main()
{
B<int>::f();
B<float>::f();
}

Some files were not shown because too many files have changed in this diff Show More