summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>2015-08-07 05:44:49 +0000
committerjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>2015-08-07 05:44:49 +0000
commit56c12fd4ba064759724236ad896232603b8797ed (patch)
tree23bad22378f96699b757d5523bf73d4139ad66da
parent29890f2ede84580ebc07431f87e20cc787db8598 (diff)
downloadgcc-56c12fd4ba064759724236ad896232603b8797ed.tar.gz
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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@226713 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/c-family/ChangeLog12
-rw-r--r--gcc/c-family/c-common.c6
-rw-r--r--gcc/c-family/c-common.h7
-rw-r--r--gcc/c-family/c-cppbuiltin.c4
-rw-r--r--gcc/c-family/c-opts.c2
-rw-r--r--gcc/c-family/c.opt4
-rw-r--r--gcc/cp/ChangeLog183
-rw-r--r--gcc/cp/Make-lang.in5
-rw-r--r--gcc/cp/call.c79
-rw-r--r--gcc/cp/class.c29
-rw-r--r--gcc/cp/constexpr.c20
-rw-r--r--gcc/cp/constraint.cc2617
-rw-r--r--gcc/cp/cp-objcp-common.c4
-rw-r--r--gcc/cp/cp-tree.def88
-rw-r--r--gcc/cp/cp-tree.h317
-rw-r--r--gcc/cp/cxx-pretty-print.c339
-rw-r--r--gcc/cp/cxx-pretty-print.h17
-rw-r--r--gcc/cp/decl.c267
-rw-r--r--gcc/cp/decl2.c7
-rw-r--r--gcc/cp/error.c46
-rw-r--r--gcc/cp/lex.c2
-rw-r--r--gcc/cp/logic.cc497
-rw-r--r--gcc/cp/method.c8
-rw-r--r--gcc/cp/parser.c1240
-rw-r--r--gcc/cp/parser.h9
-rw-r--r--gcc/cp/pt.c863
-rw-r--r--gcc/cp/ptree.c13
-rw-r--r--gcc/cp/search.c6
-rw-r--r--gcc/cp/semantics.c90
-rw-r--r--gcc/cp/tree.c19
-rw-r--r--gcc/cp/typeck.c19
-rw-r--r--gcc/doc/extend.texi49
-rw-r--r--gcc/testsuite/g++.dg/concepts/alias1.C15
-rw-r--r--gcc/testsuite/g++.dg/concepts/alias2.C13
-rw-r--r--gcc/testsuite/g++.dg/concepts/alias3.C13
-rw-r--r--gcc/testsuite/g++.dg/concepts/alias4.C19
-rw-r--r--gcc/testsuite/g++.dg/concepts/class.C52
-rw-r--r--gcc/testsuite/g++.dg/concepts/class1.C14
-rw-r--r--gcc/testsuite/g++.dg/concepts/class2.C14
-rw-r--r--gcc/testsuite/g++.dg/concepts/class3.C14
-rw-r--r--gcc/testsuite/g++.dg/concepts/class4.C21
-rw-r--r--gcc/testsuite/g++.dg/concepts/class5.C26
-rw-r--r--gcc/testsuite/g++.dg/concepts/class6.C18
-rw-r--r--gcc/testsuite/g++.dg/concepts/constrained-parm.C13
-rw-r--r--gcc/testsuite/g++.dg/concepts/decl-diagnose.C32
-rw-r--r--gcc/testsuite/g++.dg/concepts/deduction-constraint1.C12
-rw-r--r--gcc/testsuite/g++.dg/concepts/disjunction1.C59
-rw-r--r--gcc/testsuite/g++.dg/concepts/dr1430.C34
-rw-r--r--gcc/testsuite/g++.dg/concepts/equiv.C34
-rw-r--r--gcc/testsuite/g++.dg/concepts/equiv2.C29
-rw-r--r--gcc/testsuite/g++.dg/concepts/explicit-inst1.C21
-rw-r--r--gcc/testsuite/g++.dg/concepts/explicit-inst2.C24
-rw-r--r--gcc/testsuite/g++.dg/concepts/explicit-inst3.C27
-rw-r--r--gcc/testsuite/g++.dg/concepts/explicit-inst4.C17
-rw-r--r--gcc/testsuite/g++.dg/concepts/explicit-spec1.C32
-rw-r--r--gcc/testsuite/g++.dg/concepts/explicit-spec2.C13
-rw-r--r--gcc/testsuite/g++.dg/concepts/explicit-spec3.C13
-rw-r--r--gcc/testsuite/g++.dg/concepts/explicit-spec4.C51
-rw-r--r--gcc/testsuite/g++.dg/concepts/explicit-spec5.C21
-rw-r--r--gcc/testsuite/g++.dg/concepts/explicit-spec6.C18
-rw-r--r--gcc/testsuite/g++.dg/concepts/expression.C20
-rw-r--r--gcc/testsuite/g++.dg/concepts/expression2.C42
-rw-r--r--gcc/testsuite/g++.dg/concepts/expression3.C23
-rw-r--r--gcc/testsuite/g++.dg/concepts/feature-macro.C5
-rw-r--r--gcc/testsuite/g++.dg/concepts/fn-concept1.C9
-rw-r--r--gcc/testsuite/g++.dg/concepts/fn-concept2.C7
-rw-r--r--gcc/testsuite/g++.dg/concepts/fn1.C25
-rw-r--r--gcc/testsuite/g++.dg/concepts/fn10.C92
-rw-r--r--gcc/testsuite/g++.dg/concepts/fn2.C18
-rw-r--r--gcc/testsuite/g++.dg/concepts/fn3.C25
-rw-r--r--gcc/testsuite/g++.dg/concepts/fn4.C17
-rw-r--r--gcc/testsuite/g++.dg/concepts/fn5.C23
-rw-r--r--gcc/testsuite/g++.dg/concepts/fn6.C16
-rw-r--r--gcc/testsuite/g++.dg/concepts/fn7.C8
-rw-r--r--gcc/testsuite/g++.dg/concepts/fn8.C27
-rw-r--r--gcc/testsuite/g++.dg/concepts/fn9.C35
-rw-r--r--gcc/testsuite/g++.dg/concepts/friend1.C34
-rw-r--r--gcc/testsuite/g++.dg/concepts/friend2.C20
-rw-r--r--gcc/testsuite/g++.dg/concepts/generic-fn-err.C49
-rw-r--r--gcc/testsuite/g++.dg/concepts/generic-fn.C157
-rw-r--r--gcc/testsuite/g++.dg/concepts/inherit-ctor1.C22
-rw-r--r--gcc/testsuite/g++.dg/concepts/inherit-ctor2.C18
-rw-r--r--gcc/testsuite/g++.dg/concepts/inherit-ctor3.C21
-rw-r--r--gcc/testsuite/g++.dg/concepts/inherit-ctor4.C18
-rw-r--r--gcc/testsuite/g++.dg/concepts/intro1.C38
-rw-r--r--gcc/testsuite/g++.dg/concepts/intro2.C27
-rw-r--r--gcc/testsuite/g++.dg/concepts/intro3.C17
-rw-r--r--gcc/testsuite/g++.dg/concepts/intro4.C32
-rw-r--r--gcc/testsuite/g++.dg/concepts/intro5.C15
-rw-r--r--gcc/testsuite/g++.dg/concepts/intro6.C12
-rw-r--r--gcc/testsuite/g++.dg/concepts/intro7.C13
-rw-r--r--gcc/testsuite/g++.dg/concepts/member-concept.C9
-rw-r--r--gcc/testsuite/g++.dg/concepts/memfun-err.C37
-rw-r--r--gcc/testsuite/g++.dg/concepts/memfun.C112
-rw-r--r--gcc/testsuite/g++.dg/concepts/partial-concept-id1.C33
-rw-r--r--gcc/testsuite/g++.dg/concepts/partial-concept-id2.C15
-rw-r--r--gcc/testsuite/g++.dg/concepts/partial-spec.C15
-rw-r--r--gcc/testsuite/g++.dg/concepts/partial-spec2.C32
-rw-r--r--gcc/testsuite/g++.dg/concepts/partial-spec3.C7
-rw-r--r--gcc/testsuite/g++.dg/concepts/partial-spec4.C49
-rw-r--r--gcc/testsuite/g++.dg/concepts/partial-spec5.C12
-rw-r--r--gcc/testsuite/g++.dg/concepts/placeholder1.C34
-rw-r--r--gcc/testsuite/g++.dg/concepts/placeholder2.C32
-rw-r--r--gcc/testsuite/g++.dg/concepts/placeholder3.C17
-rw-r--r--gcc/testsuite/g++.dg/concepts/placeholder4.C20
-rw-r--r--gcc/testsuite/g++.dg/concepts/placeholder5.C17
-rw-r--r--gcc/testsuite/g++.dg/concepts/pr65552.C19
-rw-r--r--gcc/testsuite/g++.dg/concepts/pr65575.C23
-rw-r--r--gcc/testsuite/g++.dg/concepts/pr65634.C19
-rw-r--r--gcc/testsuite/g++.dg/concepts/pr65636.C10
-rw-r--r--gcc/testsuite/g++.dg/concepts/pr65681.C106
-rw-r--r--gcc/testsuite/g++.dg/concepts/pr65848.C75
-rw-r--r--gcc/testsuite/g++.dg/concepts/pr65854.C24
-rw-r--r--gcc/testsuite/g++.dg/concepts/pr66091.C19
-rw-r--r--gcc/testsuite/g++.dg/concepts/req-neg1.C10
-rw-r--r--gcc/testsuite/g++.dg/concepts/req1.C29
-rw-r--r--gcc/testsuite/g++.dg/concepts/req10.C18
-rw-r--r--gcc/testsuite/g++.dg/concepts/req11.C28
-rw-r--r--gcc/testsuite/g++.dg/concepts/req12.C25
-rw-r--r--gcc/testsuite/g++.dg/concepts/req13.C13
-rw-r--r--gcc/testsuite/g++.dg/concepts/req14.C14
-rw-r--r--gcc/testsuite/g++.dg/concepts/req15.C21
-rw-r--r--gcc/testsuite/g++.dg/concepts/req16.C19
-rw-r--r--gcc/testsuite/g++.dg/concepts/req17.C20
-rw-r--r--gcc/testsuite/g++.dg/concepts/req18.C17
-rw-r--r--gcc/testsuite/g++.dg/concepts/req2.C19
-rw-r--r--gcc/testsuite/g++.dg/concepts/req3.C17
-rw-r--r--gcc/testsuite/g++.dg/concepts/req4.C18
-rw-r--r--gcc/testsuite/g++.dg/concepts/req5.C18
-rw-r--r--gcc/testsuite/g++.dg/concepts/req6.C13
-rw-r--r--gcc/testsuite/g++.dg/concepts/req7.C24
-rw-r--r--gcc/testsuite/g++.dg/concepts/req8.C17
-rw-r--r--gcc/testsuite/g++.dg/concepts/req9.C24
-rw-r--r--gcc/testsuite/g++.dg/concepts/template-parm1.C34
-rw-r--r--gcc/testsuite/g++.dg/concepts/template-parm10.C17
-rw-r--r--gcc/testsuite/g++.dg/concepts/template-parm11.C18
-rw-r--r--gcc/testsuite/g++.dg/concepts/template-parm12.C20
-rw-r--r--gcc/testsuite/g++.dg/concepts/template-parm2.C23
-rw-r--r--gcc/testsuite/g++.dg/concepts/template-parm3.C28
-rw-r--r--gcc/testsuite/g++.dg/concepts/template-parm4.C20
-rw-r--r--gcc/testsuite/g++.dg/concepts/template-parm5.C19
-rw-r--r--gcc/testsuite/g++.dg/concepts/template-parm6.C41
-rw-r--r--gcc/testsuite/g++.dg/concepts/template-parm7.C44
-rw-r--r--gcc/testsuite/g++.dg/concepts/template-parm8.C21
-rw-r--r--gcc/testsuite/g++.dg/concepts/template-parm9.C18
-rw-r--r--gcc/testsuite/g++.dg/concepts/template-template-parm1.C56
-rw-r--r--gcc/testsuite/g++.dg/concepts/traits1.C97
-rw-r--r--gcc/testsuite/g++.dg/concepts/traits2.C98
-rw-r--r--gcc/testsuite/g++.dg/concepts/var-concept1.C26
-rw-r--r--gcc/testsuite/g++.dg/concepts/var-concept2.C17
-rw-r--r--gcc/testsuite/g++.dg/concepts/var-concept3.C22
-rw-r--r--gcc/testsuite/g++.dg/concepts/var-concept4.C19
-rw-r--r--gcc/testsuite/g++.dg/concepts/var-concept5.C14
-rw-r--r--gcc/testsuite/g++.dg/concepts/var-concept6.C4
-rw-r--r--gcc/testsuite/g++.dg/concepts/var-templ1.C16
-rw-r--r--gcc/testsuite/g++.dg/concepts/var-templ2.C17
-rw-r--r--gcc/testsuite/g++.dg/concepts/variadic1.C25
-rw-r--r--gcc/testsuite/g++.dg/concepts/variadic2.C13
158 files changed, 10006 insertions, 243 deletions
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index 02374d33f82..d3a6a38ed7c 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -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):
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index a8e5353749c..f6c5ddd0496 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -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 },
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index ff74e53c708..d1f6cba20e3 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -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[];
diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c
index 1beb2dbfcb7..6e18a77f227 100644
--- a/gcc/c-family/c-cppbuiltin.c
+++ b/gcc/c-family/c-cppbuiltin.c
@@ -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");
}
diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c
index 73f5db0a664..3239a853546 100644
--- a/gcc/c-family/c-opts.c
+++ b/gcc/c-family/c-opts.c
@@ -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. */
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 4679038c90b..e86ee12acac 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -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
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 946c2c222d6..2ecb8d93764 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -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
diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in
index a0847be4a2d..a16f228146c 100644
--- a/gcc/cp/Make-lang.in
+++ b/gcc/cp/Make-lang.in
@@ -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
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 3b0fd692e9f..4823d37a9c4 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -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.
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index f659fd4d3a5..bf7b5c4bab6 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -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))
{
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index fc4a3f75a0d..218faeccf80 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -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:
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
new file mode 100644
index 00000000000..cf57cc010a4
--- /dev/null
+++ b/gcc/cp/constraint.cc
@@ -0,0 +1,2617 @@
+/* Processing 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"
+
+/*---------------------------------------------------------------------------
+ Operations on constraints
+---------------------------------------------------------------------------*/
+
+/* Returns true if C is a constraint tree code. Note that ERROR_MARK
+ is a valid constraint. */
+
+static inline bool
+constraint_p (tree_code c)
+{
+ return (PRED_CONSTR <= c && c <= DISJ_CONSTR) || c == ERROR_MARK;
+}
+
+/* Returns true if T is a constraint. Note that error_mark_node
+ is a valid constraint. */
+
+bool
+constraint_p (tree t)
+{
+ return constraint_p (TREE_CODE (t));
+}
+
+/* Make a predicate constraint from the given expression. */
+
+tree
+make_predicate_constraint (tree expr)
+{
+ return build_nt (PRED_CONSTR, expr);
+}
+
+/* Returns the conjunction of two constraints A and B. Note that
+ conjoining a non-null constraint with NULL_TREE is an identity
+ operation. That is, for non-null A,
+
+ conjoin_constraints(a, NULL_TREE) == a
+
+ and
+
+ conjoin_constraints (NULL_TREE, a) == a
+
+ If both A and B are NULL_TREE, the result is also NULL_TREE. */
+
+tree
+conjoin_constraints (tree a, tree b)
+{
+ gcc_assert (a ? constraint_p (a) : true);
+ gcc_assert (b ? constraint_p (b) : true);
+ if (a)
+ return b ? build_nt (CONJ_CONSTR, a, b) : a;
+ else if (b)
+ return b;
+ else
+ return NULL_TREE;
+}
+
+/* Transform the vector of expressions in the T into a conjunction
+ of requirements. T must be a TREE_VEC. */
+
+tree
+conjoin_constraints (tree t)
+{
+ gcc_assert (TREE_CODE (t) == TREE_VEC);
+ tree r = NULL_TREE;
+ for (int i = 0; i < TREE_VEC_LENGTH (t); ++i)
+ r = conjoin_constraints (r, TREE_VEC_ELT (t, i));
+ return r;
+}
+
+/* Returns true if T is a call expression to a function
+ concept. */
+
+bool
+function_concept_check_p (tree t)
+{
+ gcc_assert (TREE_CODE (t) == CALL_EXPR);
+ tree fn = CALL_EXPR_FN (t);
+ if (TREE_CODE (fn) == TEMPLATE_ID_EXPR
+ && TREE_CODE (TREE_OPERAND (fn, 0)) == OVERLOAD)
+ {
+ tree f1 = get_first_fn (fn);
+ if (TREE_CODE (f1) == TEMPLATE_DECL
+ && DECL_DECLARED_CONCEPT_P (DECL_TEMPLATE_RESULT (f1)))
+ return true;
+ }
+ return false;
+}
+
+/*---------------------------------------------------------------------------
+ Resolution of qualified concept names
+---------------------------------------------------------------------------*/
+
+/* This facility is used to resolve constraint checks from
+ requirement expressions. A constraint check is a call to
+ a function template declared with the keyword 'concept'.
+
+ The result of resolution is a pair (a TREE_LIST) whose value
+ is the matched declaration, and whose purpose contains the
+ coerced template arguments that can be substituted into the
+ call. */
+
+// Given an overload set OVL, try to find a unique definition that can be
+// instantiated by the template arguments ARGS.
+//
+// This function is not called for arbitrary call expressions. In particular,
+// the call expression must be written with explicit template arguments
+// and no function arguments. For example:
+//
+// f<T, U>()
+//
+// If a single match is found, this returns a TREE_LIST whose VALUE
+// is the constraint function (not the template), and its PURPOSE is
+// the complete set of arguments substituted into the parameter list.
+static tree
+resolve_constraint_check (tree ovl, tree args)
+{
+ tree cands = NULL_TREE;
+ for (tree p = ovl; p != NULL_TREE; p = OVL_NEXT (p))
+ {
+ // Get the next template overload.
+ tree tmpl = OVL_CURRENT (p);
+ if (TREE_CODE (tmpl) != TEMPLATE_DECL)
+ continue;
+
+ // Don't try to deduce checks for non-concepts. We often
+ // end up trying to resolve constraints in functional casts
+ // as part of a postfix-expression. We can save time and
+ // headaches by not instantiating those declarations.
+ //
+ // NOTE: This masks a potential error, caused by instantiating
+ // non-deduced contexts using placeholder arguments.
+ tree fn = DECL_TEMPLATE_RESULT (tmpl);
+ if (DECL_ARGUMENTS (fn))
+ continue;
+ if (!DECL_DECLARED_CONCEPT_P (fn))
+ continue;
+
+ // Remember the candidate if we can deduce a substitution.
+ ++processing_template_decl;
+ tree parms = TREE_VALUE (DECL_TEMPLATE_PARMS (tmpl));
+ if (tree subst = coerce_template_parms (parms, args, tmpl))
+ if (subst != error_mark_node)
+ cands = tree_cons (subst, fn, cands);
+ --processing_template_decl;
+ }
+
+ // If we didn't find a unique candidate, then this is
+ // not a constraint check.
+ if (!cands || TREE_CHAIN (cands))
+ return NULL_TREE;
+
+ return cands;
+}
+
+// Determine if the the call expression CALL is a constraint check, and
+// return the concept declaration and arguments being checked. If CALL
+// does not denote a constraint check, return NULL.
+tree
+resolve_constraint_check (tree call)
+{
+ gcc_assert (TREE_CODE (call) == CALL_EXPR);
+
+ // A constraint check must be only a template-id expression. If
+ // it's a call to a base-link, its function(s) should be a
+ // template-id expression. If this is not a template-id, then it
+ // cannot be a concept-check.
+ tree target = CALL_EXPR_FN (call);
+ if (BASELINK_P (target))
+ target = BASELINK_FUNCTIONS (target);
+ if (TREE_CODE (target) != TEMPLATE_ID_EXPR)
+ return NULL_TREE;
+
+ // Get the overload set and template arguments and try to
+ // resolve the target.
+ tree ovl = TREE_OPERAND (target, 0);
+
+ /* This is a function call of a variable concept... ill-formed. */
+ if (TREE_CODE (ovl) == TEMPLATE_DECL)
+ {
+ error_at (location_of (call),
+ "function call of variable concept %qE", call);
+ return error_mark_node;
+ }
+
+ tree args = TREE_OPERAND (target, 1);
+ return resolve_constraint_check (ovl, args);
+}
+
+/* Returns a pair containing the checked variable concept
+ and its associated prototype parameter. The result
+ is a TREE_LIST whose TREE_VALUE is the variable concept
+ and whose TREE_PURPOSE is the prototype parameter. */
+
+tree
+resolve_variable_concept_check (tree id)
+{
+ tree tmpl = TREE_OPERAND (id, 0);
+ tree args = TREE_OPERAND (id, 1);
+
+ if (!variable_concept_p (tmpl))
+ return NULL_TREE;
+
+ /* Make sure that we have the right parameters before
+ assuming that it works. Note that failing to deduce
+ will result in diagnostics. */
+ tree parms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl));
+ tree result = coerce_template_parms (parms, args, tmpl);
+ if (result != error_mark_node)
+ {
+ tree decl = DECL_TEMPLATE_RESULT (tmpl);
+ return build_tree_list (result, decl);
+ }
+ else
+ return NULL_TREE;
+}
+
+
+/* Given a call expression or template-id expression to
+ a concept EXPR possibly including a wildcard, deduce
+ the concept being checked and the prototype parameter.
+ Returns true if the constraint and prototype can be
+ deduced and false otherwise. Note that the CHECK and
+ PROTO arguments are set to NULL_TREE if this returns
+ false. */
+
+bool
+deduce_constrained_parameter (tree expr, tree& check, tree& proto)
+{
+ tree info = NULL_TREE;
+ if (TREE_CODE (expr) == TEMPLATE_ID_EXPR)
+ info = resolve_variable_concept_check (expr);
+ else if (TREE_CODE (expr) == CALL_EXPR)
+ info = resolve_constraint_check (expr);
+ else
+ gcc_unreachable ();
+
+ if (info && info != error_mark_node)
+ {
+ check = TREE_VALUE (info);
+ tree arg = TREE_VEC_ELT (TREE_PURPOSE (info), 0);
+ if (ARGUMENT_PACK_P (arg))
+ arg = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg), 0);
+ proto = TREE_TYPE (arg);
+ return true;
+ }
+ check = proto = NULL_TREE;
+ return false;
+}
+
+// Given a call expression or template-id expression to a concept, EXPR,
+// deduce the concept being checked and return the template arguments.
+// Returns NULL_TREE if deduction fails.
+static tree
+deduce_concept_introduction (tree expr)
+{
+ tree info = NULL_TREE;
+ if (TREE_CODE (expr) == TEMPLATE_ID_EXPR)
+ info = resolve_variable_concept_check (expr);
+ else if (TREE_CODE (expr) == CALL_EXPR)
+ info = resolve_constraint_check (expr);
+ else
+ gcc_unreachable ();
+
+ if (info && info != error_mark_node)
+ return TREE_PURPOSE (info);
+ return NULL_TREE;
+}
+
+namespace {
+
+/*---------------------------------------------------------------------------
+ Lifting of concept definitions
+---------------------------------------------------------------------------*/
+
+/* Part of constraint normalization. Whenever we find a reference to
+ a variable concept or a call to a function concept, we lift or
+ inline that concept's definition into the constraint. This ensures
+ that constraints are always checked in the immediate instantiation
+ context. */
+
+tree lift_expression (tree);
+
+/* If the tree T has operands, then lift any concepts out of them. */
+tree
+lift_operands (tree t)
+{
+ if (int n = tree_operand_length (t))
+ {
+ t = copy_node (t);
+ for (int i = 0; i < n; ++i)
+ TREE_OPERAND (t, i) = lift_expression (TREE_OPERAND (t, i));
+ }
+ return t;
+}
+
+/* Recursively lift all operands of the function call. Also, check
+ that the call target is not accidentally a variable concept
+ since that's ill-formed. */
+tree
+lift_function_call (tree t)
+{
+ gcc_assert (TREE_CODE (t) == CALL_EXPR);
+ gcc_assert (!VAR_P (CALL_EXPR_FN (t)));
+ return lift_operands (t);
+}
+
+/* Inline a function (concept) definition by substituting
+ ARGS into its body. */
+tree
+lift_function_definition (tree fn, tree args)
+{
+ /* Extract the body of the function minus the return expression. */
+ tree body = DECL_SAVED_TREE (fn);
+ if (!body)
+ return error_mark_node;
+ if (TREE_CODE (body) == BIND_EXPR)
+ body = BIND_EXPR_BODY (body);
+ if (TREE_CODE (body) != RETURN_EXPR)
+ return error_mark_node;
+
+ body = TREE_OPERAND (body, 0);
+
+ /* Substitute template arguments to produce our inline expression. */
+ tree result = tsubst_expr (body, args, tf_none, NULL_TREE, false);
+ if (result == error_mark_node)
+ return error_mark_node;
+
+ return lift_expression (result);
+}
+
+/* Inline a reference to a function concept. */
+tree
+lift_call_expression (tree t)
+{
+ /* Try to resolve this function call as a concept. If not, then
+ it can be returned as-is. */
+ tree check = resolve_constraint_check (t);
+ if (!check)
+ return lift_function_call (t);
+ if (check == error_mark_node)
+ return error_mark_node;
+
+ tree fn = TREE_VALUE (check);
+ tree args = TREE_PURPOSE (check);
+ return lift_function_definition (fn, args);
+}
+
+tree
+lift_variable_initializer (tree var, tree args)
+{
+ /* Extract the body from the variable initializer. */
+ tree init = DECL_INITIAL (var);
+ if (!init)
+ return error_mark_node;
+
+ /* Substitute the arguments to form our new inline expression. */
+ tree result = tsubst_expr (init, args, tf_none, NULL_TREE, false);
+ if (result == error_mark_node)
+ return error_mark_node;
+
+ return lift_expression (result);
+}
+
+/* Determine if a template-id is a variable concept and inline. */
+
+tree
+lift_template_id (tree t)
+{
+ if (tree info = resolve_variable_concept_check (t))
+ {
+ tree decl = TREE_VALUE (info);
+ tree args = TREE_PURPOSE (info);
+ return lift_variable_initializer (decl, args);
+ }
+
+ /* Check that we didn't refer to a function concept like
+ a variable.
+
+ TODO: Add a note on how to fix this. */
+ tree tmpl = TREE_OPERAND (t, 0);
+ if (TREE_CODE (tmpl) == OVERLOAD)
+ {
+ tree fn = OVL_FUNCTION (tmpl);
+ if (TREE_CODE (fn) == TEMPLATE_DECL
+ && DECL_DECLARED_CONCEPT_P (DECL_TEMPLATE_RESULT (fn)))
+ {
+ error_at (location_of (t),
+ "invalid reference to function concept %qD", fn);
+ return error_mark_node;
+ }
+ }
+
+ return t;
+}
+
+/* Lift any constraints appearing in a nested requirement of
+ a requires-expression. */
+tree
+lift_requires_expression (tree t)
+{
+ tree parms = TREE_OPERAND (t, 0);
+ tree reqs = TREE_OPERAND (t, 1);
+ tree result = NULL_TREE;
+ for (; reqs != NULL_TREE; reqs = TREE_CHAIN (reqs))
+ {
+ tree req = TREE_VALUE (reqs);
+ if (TREE_CODE (req) == NESTED_REQ)
+ {
+ tree expr = lift_expression (TREE_OPERAND (req, 0));
+ req = finish_nested_requirement (expr);
+ }
+ result = tree_cons (NULL_TREE, req, result);
+ }
+ return finish_requires_expr (parms, result);
+}
+
+/* Inline references to specializations of concepts. */
+tree
+lift_expression (tree t)
+{
+ if (t == NULL_TREE)
+ return NULL_TREE;
+
+ if (t == error_mark_node)
+ return error_mark_node;
+
+ /* Concepts can be referred to by call or variable. All other
+ nodes are preserved. */
+ switch (TREE_CODE (t))
+ {
+ case CALL_EXPR:
+ return lift_call_expression (t);
+
+ case TEMPLATE_ID_EXPR:
+ return lift_template_id (t);
+
+ case REQUIRES_EXPR:
+ return lift_requires_expression (t);
+
+ case EXPR_PACK_EXPANSION:
+ /* Use copy_node rather than make_pack_expansion so that
+ PACK_EXPANSION_PARAMETER_PACKS stays the same. */
+ t = copy_node (t);
+ SET_PACK_EXPANSION_PATTERN
+ (t, lift_expression (PACK_EXPANSION_PATTERN (t)));
+ return t;
+
+ case TREE_LIST:
+ {
+ t = copy_node (t);
+ TREE_VALUE (t) = lift_expression (TREE_VALUE (t));
+ TREE_CHAIN (t) = lift_expression (TREE_CHAIN (t));
+ return t;
+ }
+
+ default:
+ return lift_operands (t);
+ }
+}
+
+/*---------------------------------------------------------------------------
+ Transformation of expressions into constraints
+---------------------------------------------------------------------------*/
+
+/* Part of constraint normalization. The following functions rewrite
+ expressions as constraints. */
+
+tree transform_expression (tree);
+
+/* Check that the logical-or or logical-and expression does
+ not result in a call to a user-defined user-defined operator
+ (temp.constr.op). Returns true if the logical operator is
+ admissible and false otherwise. */
+
+bool
+check_logical_expr (tree t)
+{
+ /* We can't do much for type dependent expressions. */
+ if (type_dependent_expression_p (t))
+ return true;
+
+ /* Resolve the logical operator. Note that template processing is
+ disabled so we get the actual call or target expression back.
+ not_processing_template_sentinel sentinel.
+
+ TODO: This check is actually subsumed by the requirement that
+ constraint operands have type bool. I'm not sure we need it
+ unless we allow conversions. */
+ tree arg1 = TREE_OPERAND (t, 0);
+ tree arg2 = TREE_OPERAND (t, 1);
+ tree ovl = NULL_TREE;
+ tree expr = build_x_binary_op (EXPR_LOC_OR_LOC (arg2, input_location),
+ TREE_CODE (t),
+ arg1, TREE_CODE (arg1),
+ arg2, TREE_CODE (arg2),
+ &ovl,
+ tf_none);
+ if (TREE_CODE (expr) != TREE_CODE (t))
+ {
+ error ("user-defined operator %qs in constraint %q+E",
+ operator_name_info[TREE_CODE (t)].name, t);
+ return false;
+ }
+ return true;
+}
+
+/* Transform a logical-or or logical-and expression into either
+ a conjunction or disjunction. */
+
+tree
+xform_logical (tree t, tree_code c)
+{
+ if (!check_logical_expr (t))
+ return error_mark_node;
+ tree t0 = transform_expression (TREE_OPERAND (t, 0));
+ tree t1 = transform_expression (TREE_OPERAND (t, 1));
+ return build_nt (c, t0, t1);
+}
+
+/* A simple requirement T introduces an expression constraint
+ for its expression. */
+
+inline tree
+xform_simple_requirement (tree t)
+{
+ return build_nt (EXPR_CONSTR, TREE_OPERAND (t, 0));
+}
+
+/* A type requirement T introduce a type constraint for its type. */
+
+inline tree
+xform_type_requirement (tree t)
+{
+ return build_nt (TYPE_CONSTR, TREE_OPERAND (t, 0));
+}
+
+/* A compound requirement T introduces a conjunction of constraints
+ depending on its form. The conjunction always includes an
+ expression constraint for the expression of the requirement.
+ If a trailing return type was specified, the conjunction includes
+ either an implicit conversion constraint or an argument deduction
+ constraint. If the noexcept specifier is present, the conjunction
+ includes an exception constraint. */
+
+tree
+xform_compound_requirement (tree t)
+{
+ tree expr = TREE_OPERAND (t, 0);
+ tree constr = build_nt (EXPR_CONSTR, TREE_OPERAND (t, 0));
+
+ /* If a type is given, append an implicit conversion or
+ argument deduction constraint. */
+ if (tree type = TREE_OPERAND (t, 1))
+ {
+ tree type_constr;
+ /* TODO: We should be extracting a list of auto nodes
+ from type_uses_auto, not a single node */
+ if (tree placeholder = type_uses_auto (type))
+ type_constr = build_nt (DEDUCT_CONSTR, expr, type, placeholder);
+ else
+ type_constr = build_nt (ICONV_CONSTR, expr, type);
+ constr = conjoin_constraints (constr, type_constr);
+ }
+
+ /* If noexcept is present, append an exception constraint. */
+ if (COMPOUND_REQ_NOEXCEPT_P (t))
+ {
+ tree except = build_nt (EXCEPT_CONSTR, expr);
+ constr = conjoin_constraints (constr, except);
+ }
+
+ return constr;
+}
+
+/* A nested requirement T introduces a conjunction of constraints
+ corresponding to its constraint-expression.
+
+ If the result of transforming T is error_mark_node, the resulting
+ constraint is a predicate constraint whose operand is also
+ error_mark_node. This preserves the constraint structure, but
+ will guarantee that the constraint is never satisfied. */
+
+inline tree
+xform_nested_requirement (tree t)
+{
+ return transform_expression (TREE_OPERAND (t, 0));
+}
+
+/* Transform a requirement T into one or more constraints. */
+
+tree
+xform_requirement (tree t)
+{
+ switch (TREE_CODE (t))
+ {
+ case SIMPLE_REQ:
+ return xform_simple_requirement (t);
+
+ case TYPE_REQ:
+ return xform_type_requirement (t);
+
+ case COMPOUND_REQ:
+ return xform_compound_requirement (t);
+
+ case NESTED_REQ:
+ return xform_nested_requirement (t);
+
+ default:
+ gcc_unreachable ();
+ }
+ return error_mark_node;
+}
+
+/* Transform a sequence of requirements into a conjunction of
+ constraints. */
+
+tree
+xform_requirements (tree t)
+{
+ tree result = NULL_TREE;
+ for (; t; t = TREE_CHAIN (t))
+ {
+ tree constr = xform_requirement (TREE_VALUE (t));
+ result = conjoin_constraints (result, constr);
+ }
+ return result;
+}
+
+/* Transform a requires-expression into a parameterized constraint. */
+
+tree
+xform_requires_expr (tree t)
+{
+ tree operand = xform_requirements (TREE_OPERAND (t, 1));
+ if (tree parms = TREE_OPERAND (t, 0))
+ return build_nt (PARM_CONSTR, parms, operand);
+ else
+ return operand;
+}
+
+/* Transform an expression into an atomic predicate constraint.
+ After substitution, the expression of a predicate constraint
+ shall have type bool (temp.constr.pred). For non-type-dependent
+ expressions, we can check that now. */
+
+tree
+xform_atomic (tree t)
+{
+ if (TREE_TYPE (t) && !type_dependent_expression_p (t))
+ {
+ tree type = cv_unqualified (TREE_TYPE (t));
+ if (!same_type_p (type, boolean_type_node))
+ {
+ error ("predicate constraint %q+E does not have type %<bool%>", t);
+ return error_mark_node;
+ }
+ }
+ return build_nt (PRED_CONSTR, t);
+}
+
+/* Push down the pack expansion EXP into the leaves of the constraint PAT. */
+
+tree
+push_down_pack_expansion (tree exp, tree pat)
+{
+ switch (TREE_CODE (pat))
+ {
+ case CONJ_CONSTR:
+ case DISJ_CONSTR:
+ {
+ pat = copy_node (pat);
+ TREE_OPERAND (pat, 0)
+ = push_down_pack_expansion (exp, TREE_OPERAND (pat, 0));
+ TREE_OPERAND (pat, 1)
+ = push_down_pack_expansion (exp, TREE_OPERAND (pat, 1));
+ return pat;
+ }
+ default:
+ {
+ exp = copy_node (exp);
+ SET_PACK_EXPANSION_PATTERN (exp, pat);
+ return exp;
+ }
+ }
+}
+
+/* Transform a pack expansion into a constraint. First we transform the
+ pattern of the pack expansion, then we push the pack expansion down into the
+ leaves of the constraint so that partial ordering will work. */
+
+tree
+xform_pack_expansion (tree t)
+{
+ tree pat = transform_expression (PACK_EXPANSION_PATTERN (t));
+ return push_down_pack_expansion (t, pat);
+}
+
+/* Transform an expression into a constraint. */
+
+tree
+xform_expr (tree t)
+{
+ switch (TREE_CODE (t))
+ {
+ case TRUTH_ANDIF_EXPR:
+ return xform_logical (t, CONJ_CONSTR);
+
+ case TRUTH_ORIF_EXPR:
+ return xform_logical (t, DISJ_CONSTR);
+
+ case REQUIRES_EXPR:
+ return xform_requires_expr (t);
+
+ case BIND_EXPR:
+ return transform_expression (BIND_EXPR_BODY (t));
+
+ case EXPR_PACK_EXPANSION:
+ return xform_pack_expansion (t);
+
+ default:
+ /* All other constraints are atomic. */
+ return xform_atomic (t);
+ }
+}
+
+/* Transform a statement into an expression. */
+
+tree
+xform_stmt (tree t)
+{
+ switch (TREE_CODE (t))
+ {
+ case RETURN_EXPR:
+ return transform_expression (TREE_OPERAND (t, 0));
+ default:
+ gcc_unreachable ();
+ }
+ return error_mark_node;
+}
+
+/* Reduction rules for the declaration T. */
+
+tree
+xform_decl (tree t)
+{
+ switch (TREE_CODE (t))
+ {
+ case VAR_DECL:
+ return xform_atomic (t);
+ default:
+ gcc_unreachable ();
+ }
+ return error_mark_node;
+}
+
+/* Transform a lifted expression into a constraint. This either
+ returns a constraint, or it returns error_mark_node when
+ a constraint cannot be formed. */
+
+tree
+transform_expression (tree t)
+{
+ if (!t)
+ return NULL_TREE;
+
+ if (t == error_mark_node)
+ return error_mark_node;
+
+ switch (TREE_CODE_CLASS (TREE_CODE (t)))
+ {
+ case tcc_unary:
+ case tcc_binary:
+ case tcc_expression:
+ case tcc_vl_exp:
+ return xform_expr (t);
+
+ case tcc_statement:
+ return xform_stmt (t);
+
+ case tcc_declaration:
+ return xform_decl (t);
+
+ case tcc_exceptional:
+ case tcc_constant:
+ case tcc_reference:
+ case tcc_comparison:
+ /* These are all atomic predicate constraints. */
+ return xform_atomic (t);
+
+ default:
+ /* Unhandled node kind. */
+ gcc_unreachable ();
+ }
+ return error_mark_node;
+}
+
+/*---------------------------------------------------------------------------
+ Constraint normalization
+---------------------------------------------------------------------------*/
+
+tree normalize_constraint (tree);
+
+/* The normal form of the disjunction T0 /\ T1 is the conjunction
+ of the normal form of T0 and the normal form of T1. */
+
+inline tree
+normalize_conjunction (tree t)
+{
+ tree t0 = normalize_constraint (TREE_OPERAND (t, 0));
+ tree t1 = normalize_constraint (TREE_OPERAND (t, 1));
+ return build_nt (CONJ_CONSTR, t0, t1);
+}
+
+/* The normal form of the disjunction T0 \/ T1 is the disjunction
+ of the normal form of T0 and the normal form of T1. */
+
+inline tree
+normalize_disjunction (tree t)
+{
+ tree t0 = normalize_constraint (TREE_OPERAND (t, 0));
+ tree t1 = normalize_constraint (TREE_OPERAND (t, 1));
+ return build_nt (DISJ_CONSTR, t0, t1);
+}
+
+/* A predicate constraint is normalized in two stages. First all
+ references specializations of concepts are replaced by their
+ substituted definitions. Then, the resulting expression is
+ transformed into a constraint by transforming && expressions
+ into conjunctions and || into disjunctions. */
+
+tree
+normalize_predicate_constraint (tree t)
+{
+ ++processing_template_decl;
+ tree expr = PRED_CONSTR_EXPR (t);
+ tree lifted = lift_expression (expr);
+ tree constr = transform_expression (lifted);
+ --processing_template_decl;
+ return constr;
+}
+
+/* The normal form of a parameterized constraint is the normal
+ form of its operand. */
+
+tree
+normalize_parameterized_constraint (tree t)
+{
+ tree parms = PARM_CONSTR_PARMS (t);
+ tree operand = normalize_constraint (PARM_CONSTR_OPERAND (t));
+ return build_nt (PARM_CONSTR, parms, operand);
+}
+
+/* Normalize the constraint T by reducing it so that it is
+ comprised of only conjunctions and disjunctions of atomic
+ constraints. */
+
+tree
+normalize_constraint (tree t)
+{
+ if (!t)
+ return NULL_TREE;
+
+ if (t == error_mark_node)
+ return t;
+
+ switch (TREE_CODE (t))
+ {
+ case CONJ_CONSTR:
+ return normalize_conjunction (t);
+
+ case DISJ_CONSTR:
+ return normalize_disjunction (t);
+
+ case PRED_CONSTR:
+ return normalize_predicate_constraint (t);
+
+ case PARM_CONSTR:
+ return normalize_parameterized_constraint (t);
+
+ case EXPR_CONSTR:
+ case TYPE_CONSTR:
+ case ICONV_CONSTR:
+ case DEDUCT_CONSTR:
+ case EXCEPT_CONSTR:
+ /* These constraints are defined to be atomic. */
+ return t;
+
+ default:
+ /* CONSTR was not a constraint. */
+ gcc_unreachable();
+ }
+ return error_mark_node;
+}
+
+} /* namespace */
+
+
+// -------------------------------------------------------------------------- //
+// Constraint Semantic Processing
+//
+// The following functions are called by the parser and substitution rules
+// to create and evaluate constraint-related nodes.
+
+// The constraints associated with the current template parameters.
+tree
+current_template_constraints (void)
+{
+ if (!current_template_parms)
+ return NULL_TREE;
+ tree tmpl_constr = TEMPLATE_PARM_CONSTRAINTS (current_template_parms);
+ return build_constraints (tmpl_constr, NULL_TREE);
+}
+
+// If the recently parsed TYPE declares or defines a template or template
+// specialization, get its corresponding constraints from the current
+// template parameters and bind them to TYPE's declaration.
+tree
+associate_classtype_constraints (tree type)
+{
+ if (!type || type == error_mark_node || TREE_CODE (type) != RECORD_TYPE)
+ return type;
+
+ // An explicit class template specialization has no template
+ // parameters.
+ if (!current_template_parms)
+ return type;
+
+ if (CLASSTYPE_IS_TEMPLATE (type) || CLASSTYPE_TEMPLATE_SPECIALIZATION (type))
+ {
+ tree decl = TYPE_STUB_DECL (type);
+ tree ci = current_template_constraints ();
+
+ // An implicitly instantiated member template declaration already
+ // has associated constraints. If it is defined outside of its
+ // class, then we need match these constraints against those of
+ // original declaration.
+ if (tree orig_ci = get_constraints (decl))
+ {
+ if (!equivalent_constraints (ci, orig_ci))
+ {
+ // FIXME: Improve diagnostics.
+ error ("%qT does not match any declaration", type);
+ return error_mark_node;
+ }
+ return type;
+ }
+ set_constraints (decl, ci);
+ }
+ return type;
+}
+
+namespace {
+
+// Create an empty constraint info block.
+inline tree_constraint_info*
+build_constraint_info ()
+{
+ return (tree_constraint_info *)make_node (CONSTRAINT_INFO);
+}
+
+} // namespace
+
+/* Build a constraint-info object that contains the associated constraints
+ of a declaration. This also includes the declaration's template
+ requirements (TREQS) and any trailing requirements for a function
+ declarator (DREQS). Note that both TREQS and DREQS must be constraints.
+
+ If the declaration has neither template nor declaration requirements
+ this returns NULL_TREE, indicating an unconstrained declaration. */
+
+tree
+build_constraints (tree tmpl_reqs, tree decl_reqs)
+{
+ gcc_assert (tmpl_reqs ? constraint_p (tmpl_reqs) : true);
+ gcc_assert (decl_reqs ? constraint_p (decl_reqs) : true);
+
+ if (!tmpl_reqs && !decl_reqs)
+ return NULL_TREE;
+
+ tree_constraint_info* ci = build_constraint_info ();
+ ci->template_reqs = tmpl_reqs;
+ ci->declarator_reqs = decl_reqs;
+ ci->associated_constr = conjoin_constraints (tmpl_reqs, decl_reqs);
+
+ ++processing_template_decl;
+ ci->normalized_constr = normalize_constraint (ci->associated_constr);
+ --processing_template_decl;
+
+ ci->assumptions = decompose_assumptions (ci->normalized_constr);
+ return (tree)ci;
+}
+
+namespace {
+
+/* Returns true if any of the arguments in the template
+ argument list is a wildcard or wildcard pack. */
+bool
+contains_wildcard_p (tree args)
+{
+ for (int i = 0; i < TREE_VEC_LENGTH (args); ++i)
+ {
+ tree arg = TREE_VEC_ELT (args, i);
+ if (TREE_CODE (arg) == WILDCARD_DECL)
+ return true;
+ }
+ return false;
+}
+
+/* Build a new call expression, but don't actually generate
+ a new function call. We just want the tree, not the
+ semantics. */
+inline tree
+build_call_check (tree id)
+{
+ ++processing_template_decl;
+ vec<tree, va_gc> *fargs = make_tree_vector();
+ tree call = finish_call_expr (id, &fargs, false, false, tf_none);
+ release_tree_vector (fargs);
+ --processing_template_decl;
+ return call;
+}
+
+/* Build an expression that will check a variable concept. If any
+ argument contains a wildcard, don't try to finish the variable
+ template because we can't substitute into a non-existent
+ declaration. */
+tree
+build_variable_check (tree id)
+{
+ gcc_assert (TREE_CODE (id) == TEMPLATE_ID_EXPR);
+ if (contains_wildcard_p (TREE_OPERAND (id, 1)))
+ return id;
+
+ ++processing_template_decl;
+ tree var = finish_template_variable (id);
+ --processing_template_decl;
+ return var;
+}
+
+/* Construct a sequence of template arguments by prepending
+ ARG to REST. Either ARG or REST may be null. */
+tree
+build_concept_check_arguments (tree arg, tree rest)
+{
+ gcc_assert (rest ? TREE_CODE (rest) == TREE_VEC : true);
+ tree args;
+ if (arg)
+ {
+ int n = rest ? TREE_VEC_LENGTH (rest) : 0;
+ args = make_tree_vec (n + 1);
+ TREE_VEC_ELT (args, 0) = arg;
+ if (rest)
+ for (int i = 0; i < n; ++i)
+ TREE_VEC_ELT (args, i + 1) = TREE_VEC_ELT (rest, i);
+ int def = rest ? GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (rest) : 0;
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args, def + 1);
+ }
+ else
+ {
+ gcc_assert (rest != NULL_TREE);
+ args = rest;
+ }
+ return args;
+}
+
+} // namespace
+
+/* Construct an expression that checks the concept given by
+ TARGET. The TARGET must be:
+
+ - an OVERLOAD referring to one or more function concepts
+ - a BASELINK referring to an overload set of the above, or
+ - a TEMPLTATE_DECL referring to a variable concept.
+
+ ARG and REST are the explicit template arguments for the
+ eventual concept check. */
+tree
+build_concept_check (tree target, tree arg, tree rest)
+{
+ tree args = build_concept_check_arguments (arg, rest);
+ if (variable_template_p (target))
+ return build_variable_check (lookup_template_variable (target, args));
+ else
+ return build_call_check (lookup_template_function (target, args));
+}
+
+
+/* Returns a TYPE_DECL that contains sufficient information to
+ build a template parameter of the same kind as PROTO and
+ constrained by the concept declaration CNC. Note that PROTO
+ is the first template parameter of CNC.
+
+ If specified, ARGS provides additional arguments to the
+ constraint check. */
+tree
+build_constrained_parameter (tree cnc, tree proto, tree args)
+{
+ tree name = DECL_NAME (cnc);
+ tree type = TREE_TYPE (proto);
+ tree decl = build_decl (input_location, TYPE_DECL, name, type);
+ CONSTRAINED_PARM_PROTOTYPE (decl) = proto;
+ CONSTRAINED_PARM_CONCEPT (decl) = cnc;
+ CONSTRAINED_PARM_EXTRA_ARGS (decl) = args;
+ return decl;
+}
+
+/* Create a constraint expression for the given DECL that
+ evaluates the requirements specified by CONSTR, a TYPE_DECL
+ that contains all the information necessary to build the
+ requirements (see finish_concept_name for the layout of
+ that TYPE_DECL).
+
+ Note that the constraints are neither reduced nor decomposed.
+ That is done only after the requires clause has been parsed
+ (or not). */
+tree
+finish_shorthand_constraint (tree decl, tree constr)
+{
+ /* No requirements means no constraints. */
+ if (!constr)
+ return NULL_TREE;
+
+ tree proto = CONSTRAINED_PARM_PROTOTYPE (constr);
+ tree con = CONSTRAINED_PARM_CONCEPT (constr);
+ tree args = CONSTRAINED_PARM_EXTRA_ARGS (constr);
+
+ /* If the parameter declaration is variadic, but the concept
+ is not then we need to apply the concept to every element
+ in the pack. */
+ bool is_proto_pack = template_parameter_pack_p (proto);
+ bool is_decl_pack = template_parameter_pack_p (decl);
+ bool apply_to_all_p = is_decl_pack && !is_proto_pack;
+
+ /* Get the argument and overload used for the requirement
+ and adjust it if we're going to expand later. */
+ tree arg = template_parm_to_arg (build_tree_list (NULL_TREE, decl));
+ if (apply_to_all_p)
+ arg = PACK_EXPANSION_PATTERN (TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg), 0));
+
+ /* Build the concept check. If it the constraint needs to be
+ applied to all elements of the parameter pack, then make
+ the constraint an expansion. */
+ tree check;
+ tree tmpl = DECL_TI_TEMPLATE (con);
+ if (TREE_CODE (con) == VAR_DECL)
+ {
+ check = build_concept_check (tmpl, arg, args);
+ }
+ else
+ {
+ tree ovl = build_overload (tmpl, NULL_TREE);
+ check = build_concept_check (ovl, arg, args);
+ }
+
+ /* Make the check a pack expansion if needed.
+
+ FIXME: We should be making a fold expression. */
+ if (apply_to_all_p)
+ {
+ check = make_pack_expansion (check);
+ TREE_TYPE (check) = boolean_type_node;
+ }
+
+ return make_predicate_constraint (check);
+}
+
+/* Returns a conjunction of shorthand requirements for the template
+ parameter list PARMS. Note that the requirements are stored in
+ the TYPE of each tree node. */
+tree
+get_shorthand_constraints (tree parms)
+{
+ tree result = NULL_TREE;
+ parms = INNERMOST_TEMPLATE_PARMS (parms);
+ for (int i = 0; i < TREE_VEC_LENGTH (parms); ++i)
+ {
+ tree parm = TREE_VEC_ELT (parms, i);
+ tree constr = TEMPLATE_PARM_CONSTRAINTS (parm);
+ result = conjoin_constraints (result, constr);
+ }
+ return result;
+}
+
+// Returns and chains a new parameter for PARAMETER_LIST which will conform
+// to the prototype given by SRC_PARM. The new parameter will have its
+// identifier and location set according to IDENT and PARM_LOC respectively.
+static tree
+process_introduction_parm (tree parameter_list, tree src_parm)
+{
+ // If we have a pack, we should have a single pack argument which is the
+ // placeholder we want to look at.
+ bool is_parameter_pack = ARGUMENT_PACK_P (src_parm);
+ if (is_parameter_pack)
+ src_parm = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (src_parm), 0);
+
+ // At this point we should have a wildcard, but we want to
+ // grab the associated decl from it. Also grab the stored
+ // identifier and location that should be chained to it in
+ // a PARM_DECL.
+ gcc_assert (TREE_CODE (src_parm) == WILDCARD_DECL);
+
+ tree ident = DECL_NAME (src_parm);
+ location_t parm_loc = DECL_SOURCE_LOCATION (src_parm);
+
+ // If we expect a pack and the deduced template is not a pack, or if the
+ // template is using a pack and we didn't declare a pack, throw an error.
+ if (is_parameter_pack != WILDCARD_PACK_P (src_parm))
+ {
+ error_at (parm_loc, "cannot match pack for introduced parameter");
+ tree err_parm = build_tree_list (error_mark_node, error_mark_node);
+ return chainon (parameter_list, err_parm);
+ }
+
+ src_parm = TREE_TYPE (src_parm);
+
+ tree parm;
+ bool is_non_type;
+ if (TREE_CODE (src_parm) == TYPE_DECL)
+ {
+ is_non_type = false;
+ parm = finish_template_type_parm (class_type_node, ident);
+ }
+ else if (TREE_CODE (src_parm) == TEMPLATE_DECL)
+ {
+ is_non_type = false;
+ begin_template_parm_list ();
+ current_template_parms = DECL_TEMPLATE_PARMS (src_parm);
+ end_template_parm_list ();
+ parm = finish_template_template_parm (class_type_node, ident);
+ }
+ else
+ {
+ is_non_type = true;
+
+ // Since we don't have a declarator, so we can copy the source
+ // parameter and change the name and eventually the location.
+ parm = copy_decl (src_parm);
+ DECL_NAME (parm) = ident;
+ }
+
+ // Wrap in a TREE_LIST for process_template_parm. Introductions do not
+ // retain the defaults from the source template.
+ parm = build_tree_list (NULL_TREE, parm);
+
+ return process_template_parm (parameter_list, parm_loc, parm,
+ is_non_type, is_parameter_pack);
+}
+
+/* Associates a constraint check to the current template based
+ on the introduction parameters. INTRO_LIST must be a TREE_VEC
+ of WILDCARD_DECLs containing a chained PARM_DECL which
+ contains the identifier as well as the source location.
+ TMPL_DECL is the decl for the concept being used. If we
+ take a concept, C, this will form a check in the form of
+ C<INTRO_LIST> filling in any extra arguments needed by the
+ defaults deduced.
+
+ Returns NULL_TREE if no concept could be matched and
+ error_mark_node if an error occurred when matching. */
+tree
+finish_template_introduction (tree tmpl_decl, tree intro_list)
+{
+ /* Deduce the concept check. */
+ tree expr = build_concept_check (tmpl_decl, NULL_TREE, intro_list);
+ if (expr == error_mark_node)
+ return NULL_TREE;
+
+ tree parms = deduce_concept_introduction (expr);
+ if (!parms)
+ return NULL_TREE;
+
+ /* Build template parameter scope for introduction. */
+ tree parm_list = NULL_TREE;
+ begin_template_parm_list ();
+ int nargs = MIN (TREE_VEC_LENGTH (parms), TREE_VEC_LENGTH (intro_list));
+ for (int n = 0; n < nargs; ++n)
+ parm_list = process_introduction_parm (parm_list, TREE_VEC_ELT (parms, n));
+ parm_list = end_template_parm_list (parm_list);
+ for (int i = 0; i < TREE_VEC_LENGTH (parm_list); ++i)
+ if (TREE_VALUE (TREE_VEC_ELT (parm_list, i)) == error_mark_node)
+ {
+ end_template_decl ();
+ return error_mark_node;
+ }
+
+ /* Build a concept check for our constraint. */
+ tree check_args = make_tree_vec (TREE_VEC_LENGTH (parms));
+ int n = 0;
+ for (; n < TREE_VEC_LENGTH (parm_list); ++n)
+ {
+ tree parm = TREE_VEC_ELT (parm_list, n);
+ TREE_VEC_ELT (check_args, n) = template_parm_to_arg (parm);
+ }
+
+ /* If the template expects more parameters we should be able
+ to use the defaults from our deduced concept. */
+ for (; n < TREE_VEC_LENGTH (parms); ++n)
+ TREE_VEC_ELT (check_args, n) = TREE_VEC_ELT (parms, n);
+
+ /* Associate the constraint. */
+ tree check = build_concept_check (tmpl_decl, NULL_TREE, check_args);
+ tree constr = make_predicate_constraint (check);
+ TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = constr;
+
+ return parm_list;
+}
+
+
+/* Make a "constrained auto" type-specifier. This is an
+ auto type with constraints that must be associated after
+ deduction. The constraint is formed from the given
+ CONC and its optional sequence of arguments, which are
+ non-null if written as partial-concept-id. */
+tree
+make_constrained_auto (tree con, tree args)
+{
+ tree type = make_auto();
+
+ /* Build the constraint. */
+ tree tmpl = DECL_TI_TEMPLATE (con);
+ tree expr;
+ if (VAR_P (con))
+ expr = build_concept_check (tmpl, type, args);
+ else
+ expr = build_concept_check (build_overload (tmpl, NULL_TREE), type, args);
+
+ tree constr = make_predicate_constraint (expr);
+ PLACEHOLDER_TYPE_CONSTRAINTS (type) = constr;
+
+ /* Attach the constraint to the type declaration. */
+ tree decl = TYPE_NAME (type);
+ return decl;
+}
+
+
+/*---------------------------------------------------------------------------
+ Constraint substitution
+---------------------------------------------------------------------------*/
+
+/* The following functions implement substitution rules for constraints.
+ Substitution without checking constraints happens only in the
+ instantiation of class templates. For example:
+
+ template<C1 T> struct S {
+ void f(T) requires C2<T>;
+ void g(T) requires T::value;
+ };
+
+ S<int> s; // error instantiating S<int>::g(T)
+
+ When we instantiate S, we substitute into its member declarations,
+ including their constraints. However, those constraints are not
+ checked. Substituting int into C2<T> yields C2<int>, and substituting
+ into T::value yields a substitution failure, making the program
+ ill-formed.
+
+ Note that we only ever substitute into the associated constraints
+ of a declaration. That is, substitution is defined only for predicate
+ constraints and conjunctions. */
+
+/* Substitute into the predicate constraints. Returns error_mark_node
+ if the substitution into the expression fails. */
+tree
+tsubst_predicate_constraint (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ tree expr = PRED_CONSTR_EXPR (t);
+ ++processing_template_decl;
+ tree result = tsubst_expr (expr, args, complain, in_decl, false);
+ --processing_template_decl;
+ return build_nt (PRED_CONSTR, result);
+}
+
+/* Substitute into the conjunction of constraints. Returns
+ error_mark_node if substitution into either operand fails. */
+tree
+tsubst_conjunction (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ tree t0 = TREE_OPERAND (t, 0);
+ tree r0 = tsubst_constraint (t0, args, complain, in_decl);
+ tree t1 = TREE_OPERAND (t, 1);
+ tree r1 = tsubst_constraint (t1, args, complain, in_decl);
+ return build_nt (CONJ_CONSTR, r0, r1);
+}
+
+/* Substitute ARGS into the constraint T. */
+tree
+tsubst_constraint (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+{
+ if (t == NULL_TREE)
+ return t;
+ if (TREE_CODE (t) == CONJ_CONSTR)
+ return tsubst_conjunction (t, args, complain, in_decl);
+ else if (TREE_CODE (t) == PRED_CONSTR)
+ return tsubst_predicate_constraint (t, args, complain, in_decl);
+ else
+ gcc_unreachable ();
+ return error_mark_node;
+}
+
+namespace {
+
+/* A subroutine of tsubst_constraint_variables. Register local
+ specializations for each of parameter in PARMS and its
+ corresponding substituted constraint variable in VARS.
+ Returns VARS. */
+tree
+declare_constraint_vars (tree parms, tree vars)
+{
+ tree s = vars;
+ for (tree t = parms; t; t = DECL_CHAIN (t))
+ {
+ if (DECL_PACK_P (t))
+ {
+ tree pack = extract_fnparm_pack (t, &s);
+ register_local_specialization (pack, t);
+ }
+ else
+ {
+ register_local_specialization (s, t);
+ s = DECL_CHAIN (s);
+ }
+ }
+ return vars;
+}
+
+/* A subroutine of tsubst_parameterized_constraint. Substitute ARGS
+ into the parameter list T, producing a sequence of constraint
+ variables, declared in the current scope.
+
+ Note that the caller must establish a local specialization stack
+ prior to calling this function since this substitution will
+ declare the substituted parameters. */
+tree
+tsubst_constraint_variables (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ /* Clear cp_unevaluated_operand across tsubst so that we get a proper chain
+ of PARM_DECLs. */
+ int saved_unevaluated_operand = cp_unevaluated_operand;
+ cp_unevaluated_operand = 0;
+ tree vars = tsubst (t, args, complain, in_decl);
+ cp_unevaluated_operand = saved_unevaluated_operand;
+ if (vars == error_mark_node)
+ return error_mark_node;
+ return declare_constraint_vars (t, vars);
+}
+
+/* Substitute ARGS into the simple requirement T. Note that
+ substitution may result in an ill-formed expression without
+ causing the program to be ill-formed. In such cases, the
+ requirement wraps an error_mark_node. */
+inline tree
+tsubst_simple_requirement (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ ++processing_template_decl;
+ tree expr = tsubst_expr (TREE_OPERAND (t, 0), args, complain, in_decl, false);
+ --processing_template_decl;
+ return finish_simple_requirement (expr);
+}
+
+/* Substitute ARGS into the type requirement T. Note that
+ substitution may result in an ill-formed type without
+ causing the program to be ill-formed. In such cases, the
+ requirement wraps an error_mark_node. */
+
+inline tree
+tsubst_type_requirement (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ ++processing_template_decl;
+ tree type = tsubst (TREE_OPERAND (t, 0), args, complain, in_decl);
+ --processing_template_decl;
+ return finish_type_requirement (type);
+}
+
+/* Substitute args into the compound requirement T. If substituting
+ into either the expression or the type fails, the corresponding
+ operands in the resulting node will be error_mark_node. This
+ preserves a requirement for the purpose of partial ordering, but
+ it will never be satisfied. */
+
+tree
+tsubst_compound_requirement (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ ++processing_template_decl;
+ tree expr = tsubst_expr (TREE_OPERAND (t, 0), args, complain, in_decl, false);
+ tree type = tsubst (TREE_OPERAND (t, 1), args, complain, in_decl);
+ --processing_template_decl;
+ bool noexcept_p = COMPOUND_REQ_NOEXCEPT_P (t);
+ return finish_compound_requirement (expr, type, noexcept_p);
+}
+
+/* Substitute ARGS into the nested requirement T. */
+
+tree
+tsubst_nested_requirement (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ ++processing_template_decl;
+ tree expr = tsubst_expr (TREE_OPERAND (t, 0), args, complain, in_decl, false);
+ --processing_template_decl;
+ return finish_nested_requirement (expr);
+}
+
+inline tree
+tsubst_requirement (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+{
+ switch (TREE_CODE (t))
+ {
+ case SIMPLE_REQ:
+ return tsubst_simple_requirement (t, args, complain, in_decl);
+ case TYPE_REQ:
+ return tsubst_type_requirement (t, args, complain, in_decl);
+ case COMPOUND_REQ:
+ return tsubst_compound_requirement (t, args, complain, in_decl);
+ case NESTED_REQ:
+ return tsubst_nested_requirement (t, args, complain, in_decl);
+ default:
+ gcc_unreachable ();
+ }
+ return error_mark_node;
+}
+
+/* Substitute ARGS into the list of requirements T. Note that
+ substitution failures here result in ill-formed programs. */
+
+tree
+tsubst_requirement_body (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ tree r = NULL_TREE;
+ while (t)
+ {
+ tree e = tsubst_requirement (TREE_VALUE (t), args, complain, in_decl);
+ if (e == error_mark_node)
+ return error_mark_node;
+ r = tree_cons (NULL_TREE, e, r);
+ t = TREE_CHAIN (t);
+ }
+ return r;
+}
+
+} /* namespace */
+
+/* Substitute ARGS into the requires expression T. Note that this
+ results in the re-declaration of local parameters when
+ substituting through the parameter list. If either substitution
+ fails, the program is ill-formed. */
+
+tree
+tsubst_requires_expr (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ local_specialization_stack stack;
+
+ tree parms = TREE_OPERAND (t, 0);
+ if (parms)
+ {
+ parms = tsubst_constraint_variables (parms, args, complain, in_decl);
+ if (parms == error_mark_node)
+ return error_mark_node;
+ }
+
+ tree reqs = TREE_OPERAND (t, 1);
+ reqs = tsubst_requirement_body (reqs, args, complain, in_decl);
+ if (reqs == error_mark_node)
+ return error_mark_node;
+
+ return finish_requires_expr (parms, reqs);
+}
+
+/* Substitute ARGS into the constraint information CI, producing a new
+ constraint record. */
+tree
+tsubst_constraint_info (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ if (!t || t == error_mark_node || !check_constraint_info (t))
+ return NULL_TREE;
+
+ tree tmpl_constr = NULL_TREE;
+ if (tree r = CI_TEMPLATE_REQS (t))
+ tmpl_constr = tsubst_constraint (r, args, complain, in_decl);
+
+ tree decl_constr = NULL_TREE;
+ if (tree r = CI_DECLARATOR_REQS (t))
+ decl_constr = tsubst_constraint (r, args, complain, in_decl);
+
+ return build_constraints (tmpl_constr, decl_constr);
+}
+
+
+/*---------------------------------------------------------------------------
+ Constraint satisfaction
+---------------------------------------------------------------------------*/
+
+/* The following functions determine if a constraint, when
+ substituting template arguments, is satisfied. For convenience,
+ satisfaction reduces a constraint to either true or false (and
+ nothing else). */
+
+namespace {
+
+tree satisfy_constraint_1 (tree, tree, tsubst_flags_t, tree);
+
+/* Check the constraint pack expansion. */
+
+tree
+satisfy_pack_expansion (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ /* Get the vector of satisfaction results.
+ gen_elem_of_pack_expansion_instantiation will check that each element of
+ the expansion is satisfied. */
+ tree exprs = tsubst_pack_expansion (t, args, complain, in_decl);
+ if (exprs == error_mark_node)
+ return boolean_false_node;
+ int n = TREE_VEC_LENGTH (exprs);
+
+ for (int i = 0; i < n; ++i)
+ if (TREE_VEC_ELT (exprs, i) != boolean_true_node)
+ return boolean_false_node;
+ return boolean_true_node;
+}
+
+/* A predicate constraint is satisfied if its expression evaluates
+ to true. If substitution into that node fails, the constraint
+ is not satisfied ([temp.constr.pred]).
+
+ Note that a predicate constraint is a constraint expression
+ of type bool. If neither of those are true, the program is
+ ill-formed; they are not SFINAE'able errors. */
+
+tree
+satisfy_predicate_constraint (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ tree original = TREE_OPERAND (t, 0);
+
+ /* We should never have a naked pack expansion in a predicate constraint. */
+ gcc_assert (TREE_CODE (original) != EXPR_PACK_EXPANSION);
+
+ tree expr = tsubst_expr (original, args, complain, in_decl, false);
+ if (expr == error_mark_node)
+ return boolean_false_node;
+
+ /* A predicate constraint shall have type bool. In some
+ cases, substitution gives us const-qualified bool, which
+ is also acceptable. */
+ tree type = cv_unqualified (TREE_TYPE (expr));
+ if (!same_type_p (type, boolean_type_node))
+ {
+ error_at (EXPR_LOC_OR_LOC (expr, input_location),
+ "constraint %qE does not have type %qT",
+ expr, boolean_type_node);
+ return boolean_false_node;
+ }
+
+ tree value = cxx_constant_value (expr);
+ return value;
+}
+
+/* Check an expression constraint. The constraint is satisfied if
+ substitution succeeds ([temp.constr.expr]).
+
+ Note that the expression is unevaluated. */
+
+tree
+satisfy_expression_constraint (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ cp_unevaluated guard;
+ deferring_access_check_sentinel deferring;
+
+ tree expr = EXPR_CONSTR_EXPR (t);
+ tree check = tsubst_expr (expr, args, complain, in_decl, false);
+ if (check == error_mark_node)
+ return boolean_false_node;
+ if (!perform_deferred_access_checks (tf_none))
+ return boolean_false_node;
+
+ return boolean_true_node;
+}
+
+/* Check a type constraint. The constraint is satisfied if
+ substitution succeeds. */
+
+inline tree
+satisfy_type_constraint (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ deferring_access_check_sentinel deferring;
+ tree type = TYPE_CONSTR_TYPE (t);
+ gcc_assert (TYPE_P (type) || type == error_mark_node);
+ tree check = tsubst (type, args, complain, in_decl);
+ if (error_operand_p (check))
+ return boolean_false_node;
+ if (!perform_deferred_access_checks (complain))
+ return boolean_false_node;
+
+ return boolean_true_node;
+}
+
+/* Check an implicit conversion constraint. */
+
+tree
+satisfy_implicit_conversion_constraint (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ /* Don't tsubst as if we're processing a template. If we try
+ to we can end up generating template-like expressions
+ (e.g., modop-exprs) that aren't properly typed. */
+ tree expr =
+ tsubst_expr (ICONV_CONSTR_EXPR (t), args, complain, in_decl, false);
+ if (expr == error_mark_node)
+ return boolean_false_node;
+
+ /* Get the transformed target type. */
+ tree type = tsubst (ICONV_CONSTR_TYPE (t), args, complain, in_decl);
+ if (type == error_mark_node)
+ return boolean_false_node;
+
+ /* Attempt the conversion as a direct initialization
+ of the form TYPE <unspecified> = EXPR. */
+ tree conv =
+ perform_direct_initialization_if_possible (type, expr, false, complain);
+ if (conv == error_mark_node)
+ return boolean_false_node;
+ else
+ return boolean_true_node;
+}
+
+/* Check an argument deduction constraint. */
+
+tree
+satisfy_argument_deduction_constraint (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ /* Substitute through the expression. */
+ tree expr = DEDUCT_CONSTR_EXPR (t);
+ tree init = tsubst_expr (expr, args, complain, in_decl, false);
+ if (expr == error_mark_node)
+ return boolean_false_node;
+
+ /* Perform auto or decltype(auto) deduction to get the result. */
+ tree pattern = DEDUCT_CONSTR_PATTERN (t);
+ tree placeholder = DEDUCT_CONSTR_PLACEHOLDER (t);
+ tree constr = PLACEHOLDER_TYPE_CONSTRAINTS (placeholder);
+ PLACEHOLDER_TYPE_CONSTRAINTS (placeholder)
+ = tsubst_constraint (constr, args, complain|tf_partial, in_decl);
+ tree type = do_auto_deduction (pattern, init, placeholder,
+ complain, adc_requirement);
+ PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) = constr;
+ if (type == error_mark_node)
+ return boolean_false_node;
+
+ return boolean_true_node;
+}
+
+/* Check an exception constraint. An exception constraint for an
+ expression e is satisfied when noexcept(e) is true. */
+
+tree
+satisfy_exception_constraint (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ tree expr = EXCEPT_CONSTR_EXPR (t);
+ tree check = tsubst_expr (expr, args, complain, in_decl, false);
+ if (check == error_mark_node)
+ return boolean_false_node;
+
+ if (expr_noexcept_p (check, complain))
+ return boolean_true_node;
+ else
+ return boolean_false_node;
+}
+
+/* Check a parameterized constraint. */
+
+tree
+satisfy_parameterized_constraint (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ local_specialization_stack stack;
+ tree parms = PARM_CONSTR_PARMS (t);
+ tree vars = tsubst_constraint_variables (parms, args, complain, in_decl);
+ if (vars == error_mark_node)
+ return boolean_false_node;
+ tree constr = PARM_CONSTR_OPERAND (t);
+ return satisfy_constraint_1 (constr, args, complain, in_decl);
+}
+
+/* Check that the conjunction of constraints is satisfied. Note
+ that if left operand is not satisfied, the right operand
+ is not checked.
+
+ FIXME: Check that this wouldn't result in a user-defined
+ operator. Note that this error is partially diagnosed in
+ satisfy_predicate_constraint. It would be nice to diagnose
+ the overload, but I don't think it's strictly necessary. */
+
+tree
+satisfy_conjunction (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+{
+ tree t0 = satisfy_constraint_1 (TREE_OPERAND (t, 0), args, complain, in_decl);
+ if (t0 == boolean_false_node)
+ return t0;
+ tree t1 = satisfy_constraint_1 (TREE_OPERAND (t, 1), args, complain, in_decl);
+ if (t1 == boolean_false_node)
+ return t1;
+ return boolean_true_node;
+}
+
+/* Check that the disjunction of constraints is satisfied. Note
+ that if the left operand is satisfied, the right operand is not
+ checked. */
+
+tree
+satisfy_disjunction (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+{
+ tree t0 = satisfy_constraint_1 (TREE_OPERAND (t, 0), args, complain, in_decl);
+ if (t0 == boolean_true_node)
+ return boolean_true_node;
+ tree t1 = satisfy_constraint_1 (TREE_OPERAND (t, 1), args, complain, in_decl);
+ if (t1 == boolean_true_node)
+ return boolean_true_node;
+ return boolean_false_node;
+}
+
+/* Dispatch to an appropriate satisfaction routine depending on the
+ tree code of T. */
+
+tree
+satisfy_constraint_1 (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+{
+ gcc_assert (!processing_template_decl);
+
+ if (!t)
+ return boolean_false_node;
+
+ if (t == error_mark_node)
+ return boolean_false_node;
+
+ switch (TREE_CODE (t))
+ {
+ case PRED_CONSTR:
+ return satisfy_predicate_constraint (t, args, complain, in_decl);
+
+ case EXPR_CONSTR:
+ return satisfy_expression_constraint (t, args, complain, in_decl);
+
+ case TYPE_CONSTR:
+ return satisfy_type_constraint (t, args, complain, in_decl);
+
+ case ICONV_CONSTR:
+ return satisfy_implicit_conversion_constraint (t, args, complain, in_decl);
+
+ case DEDUCT_CONSTR:
+ return satisfy_argument_deduction_constraint (t, args, complain, in_decl);
+
+ case EXCEPT_CONSTR:
+ return satisfy_exception_constraint (t, args, complain, in_decl);
+
+ case PARM_CONSTR:
+ return satisfy_parameterized_constraint (t, args, complain, in_decl);
+
+ case CONJ_CONSTR:
+ return satisfy_conjunction (t, args, complain, in_decl);
+
+ case DISJ_CONSTR:
+ return satisfy_disjunction (t, args, complain, in_decl);
+
+ case EXPR_PACK_EXPANSION:
+ return satisfy_pack_expansion (t, args, complain, in_decl);
+
+ default:
+ gcc_unreachable ();
+ }
+ return boolean_false_node;
+}
+
+/* Check that the constraint is satisfied, according to the rules
+ for that constraint. Note that each satisfy_* function returns
+ true or false, depending on whether it is satisfied or not. */
+
+tree
+satisfy_constraint (tree t, tree args)
+{
+ /* Turn off template processing. Constraint satisfaction only applies
+ to non-dependent terms, so we want full checking here. */
+ processing_template_decl_sentinel sentinel (true);
+ /* Avoid early exit in tsubst and tsubst_copy from null args; since earlier
+ substitution was done with processing_template_decl forced on, there will
+ be expressions that still need semantic processing, possibly buried in
+ decltype or a template argument. */
+ if (args == NULL_TREE)
+ args = make_tree_vec (1);
+ return satisfy_constraint_1 (t, args, tf_none, NULL_TREE);
+}
+
+/* Check the associated constraints in CI against the given
+ ARGS, returning true when the constraints are satisfied
+ and false otherwise. */
+
+tree
+satisfy_associated_constraints (tree ci, tree args)
+{
+ /* If there are no constraints then this is trivially satisfied. */
+ if (!ci)
+ return boolean_true_node;
+
+ /* If any arguments depend on template parameters, we can't
+ check constraints. */
+ if (args && uses_template_parms (args))
+ return boolean_true_node;
+
+ /* Invalid requirements cannot be satisfied. */
+ if (!valid_constraints_p (ci))
+ return boolean_false_node;
+
+ return satisfy_constraint (CI_NORMALIZED_CONSTRAINTS (ci), args);
+}
+
+} /* namespace */
+
+/* Evaluate the given constraint, returning boolean_true_node
+ if the constraint is satisfied and boolean_false_node
+ otherwise. */
+
+tree
+evaluate_constraints (tree constr, tree args)
+{
+ gcc_assert (constraint_p (constr));
+ return satisfy_constraint (normalize_constraint (constr), args);
+}
+
+/* Evaluate the function concept FN by substituting its own args
+ into its definition and evaluating that as the result. Returns
+ boolean_true_node if the constraints are satisfied and
+ boolean_false_node otherwise. */
+
+tree
+evaluate_function_concept (tree fn, tree args)
+{
+ ++processing_template_decl;
+ /* We lift using DECL_TI_ARGS because we want to delay producing
+ non-dependent expressions until we're doing satisfaction. We can't just
+ go without any substitution because we need to lower the level of 'auto's
+ in type deduction constraints. */
+ tree constr = transform_expression (lift_function_definition
+ (fn, DECL_TI_ARGS (fn)));
+ --processing_template_decl;
+ return satisfy_constraint (constr, args);
+}
+
+/* Evaluate the variable concept VAR by substituting its own args into
+ its initializer and checking the resulting constraint. Returns
+ boolean_true_node if the constraints are satisfied and
+ boolean_false_node otherwise. */
+
+tree
+evaluate_variable_concept (tree decl, tree args)
+{
+ ++processing_template_decl;
+ tree constr = transform_expression (lift_variable_initializer
+ (decl, DECL_TI_ARGS (decl)));
+ --processing_template_decl;
+ return satisfy_constraint (constr, args);
+}
+
+/* Evaluate the given expression as if it were a predicate
+ constraint. Returns boolean_true_node if the constraint
+ is satisfied and boolean_false_node otherwise. */
+
+tree
+evaluate_constraint_expression (tree expr, tree args)
+{
+ ++processing_template_decl;
+ tree constr = transform_expression (lift_expression (expr));
+ --processing_template_decl;
+ return satisfy_constraint (constr, args);
+}
+
+/* Returns true if the DECL's constraints are satisfied.
+ This is used in cases where a declaration is formed but
+ before it is used (e.g., overload resolution). */
+
+bool
+constraints_satisfied_p (tree decl)
+{
+ /* Get the constraints to check for satisfaction. This depends
+ on whether we're looking at a template specialization or not. */
+ tree ci;
+ tree args = NULL_TREE;
+ if (tree ti = DECL_TEMPLATE_INFO (decl))
+ {
+ ci = get_constraints (TI_TEMPLATE (ti));
+ args = INNERMOST_TEMPLATE_ARGS (TI_ARGS (ti));
+ }
+ else
+ {
+ ci = get_constraints (decl);
+ }
+
+ tree eval = satisfy_associated_constraints (ci, args);
+ return eval == boolean_true_node;
+}
+
+/* Returns true if the constraints are satisfied by ARGS.
+ Here, T can be either a constraint or a constrained
+ declaration. */
+
+bool
+constraints_satisfied_p (tree t, tree args)
+{
+ tree eval;
+ if (constraint_p (t))
+ eval = evaluate_constraints (t, args);
+ else
+ eval = satisfy_associated_constraints (get_constraints (t), args);
+ return eval == boolean_true_node;
+}
+
+namespace
+{
+
+/* Normalize EXPR and determine if the resulting constraint is
+ satisfied by ARGS. Returns true if and only if the constraint
+ is satisfied. This is used extensively by diagnostics to
+ determine causes for failure. */
+
+inline bool
+constraint_expression_satisfied_p (tree expr, tree args)
+{
+ return evaluate_constraint_expression (expr, args) == boolean_true_node;
+}
+
+} /* namespace */
+
+
+/*---------------------------------------------------------------------------
+ Semantic analysis of requires-expressions
+---------------------------------------------------------------------------*/
+
+/* Finish a requires expression for the given PARMS (possibly
+ null) and the non-empty sequence of requirements. */
+tree
+finish_requires_expr (tree parms, tree reqs)
+{
+ /* Modify the declared parameters by removing their context
+ so they don't refer to the enclosing scope and explicitly
+ indicating that they are constraint variables. */
+ for (tree parm = parms; parm; parm = DECL_CHAIN (parm))
+ {
+ DECL_CONTEXT (parm) = NULL_TREE;
+ CONSTRAINT_VAR_P (parm) = true;
+ }
+
+ /* Build the node. */
+ tree r = build_min (REQUIRES_EXPR, boolean_type_node, parms, reqs);
+ TREE_SIDE_EFFECTS (r) = false;
+ TREE_CONSTANT (r) = true;
+ return r;
+}
+
+/* Construct a requirement for the validity of EXPR. */
+tree
+finish_simple_requirement (tree expr)
+{
+ return build_nt (SIMPLE_REQ, expr);
+}
+
+/* Construct a requirement for the validity of TYPE. */
+tree
+finish_type_requirement (tree type)
+{
+ return build_nt (TYPE_REQ, type);
+}
+
+/* Construct a requirement for the validity of EXPR, along with
+ its properties. if TYPE is non-null, then it specifies either
+ an implicit conversion or argument deduction constraint,
+ depending on whether any placeholders occur in the type name.
+ NOEXCEPT_P is true iff the noexcept keyword was specified. */
+tree
+finish_compound_requirement (tree expr, tree type, bool noexcept_p)
+{
+ tree req = build_nt (COMPOUND_REQ, expr, type);
+ COMPOUND_REQ_NOEXCEPT_P (req) = noexcept_p;
+ return req;
+}
+
+/* Finish a nested requirement. */
+tree
+finish_nested_requirement (tree expr)
+{
+ return build_nt (NESTED_REQ, expr);
+}
+
+// Check that FN satisfies the structural requirements of a
+// function concept definition.
+tree
+check_function_concept (tree fn)
+{
+ // Check that the function is comprised of only a single
+ // return statement.
+ tree body = DECL_SAVED_TREE (fn);
+ if (TREE_CODE (body) == BIND_EXPR)
+ body = BIND_EXPR_BODY (body);
+
+ // Sometimes a function call results in the creation of clean up
+ // points. Allow these to be preserved in the body of the
+ // constraint, as we might actually need them for some constexpr
+ // evaluations.
+ if (TREE_CODE (body) == CLEANUP_POINT_EXPR)
+ body = TREE_OPERAND (body, 0);
+
+ /* Check that the definition is written correctly. */
+ if (TREE_CODE (body) != RETURN_EXPR)
+ {
+ location_t loc = DECL_SOURCE_LOCATION (fn);
+ if (TREE_CODE (body) == STATEMENT_LIST && !STATEMENT_LIST_HEAD (body))
+ error_at (loc, "definition of concept %qD is empty", fn);
+ else
+ error_at (loc, "definition of concept %qD has multiple statements", fn);
+ }
+
+ return NULL_TREE;
+}
+
+
+// Check that a constrained friend declaration function declaration,
+// FN, is admissible. This is the case only when the declaration depends
+// on template parameters and does not declare a specialization.
+void
+check_constrained_friend (tree fn, tree reqs)
+{
+ if (fn == error_mark_node)
+ return;
+ gcc_assert (TREE_CODE (fn) == FUNCTION_DECL);
+
+ // If there are not constraints, this cannot be an error.
+ if (!reqs)
+ return;
+
+ // Constrained friend functions that don't depend on template
+ // arguments are effectively meaningless.
+ if (!uses_template_parms (TREE_TYPE (fn)))
+ {
+ error_at (location_of (fn),
+ "constrained friend does not depend on template parameters");
+ return;
+ }
+}
+
+/*---------------------------------------------------------------------------
+ Equivalence of constraints
+---------------------------------------------------------------------------*/
+
+/* Returns true when A and B are equivalent constraints. */
+bool
+equivalent_constraints (tree a, tree b)
+{
+ gcc_assert (!a || TREE_CODE (a) == CONSTRAINT_INFO);
+ gcc_assert (!b || TREE_CODE (b) == CONSTRAINT_INFO);
+ return cp_tree_equal (a, b);
+}
+
+/* Returns true if the template declarations A and B have equivalent
+ constraints. This is the case when A's constraints subsume B's and
+ when B's also constrain A's. */
+bool
+equivalently_constrained (tree d1, tree d2)
+{
+ gcc_assert (TREE_CODE (d1) == TREE_CODE (d2));
+ return equivalent_constraints (get_constraints (d1), get_constraints (d2));
+}
+
+/*---------------------------------------------------------------------------
+ Partial ordering of constraints
+---------------------------------------------------------------------------*/
+
+/* Returns true when the the constraints in A subsume those in B. */
+bool
+subsumes_constraints (tree a, tree b)
+{
+ gcc_assert (!a || TREE_CODE (a) == CONSTRAINT_INFO);
+ gcc_assert (!b || TREE_CODE (b) == CONSTRAINT_INFO);
+ return subsumes (a, b);
+}
+
+/* Determines which of the declarations, A or B, is more constrained.
+ That is, which declaration's constraints subsume but are not subsumed
+ by the other's?
+
+ Returns 1 if A is more constrained than B, -1 if B is more constrained
+ than A, and 0 otherwise. */
+int
+more_constrained (tree d1, tree d2)
+{
+ tree c1 = get_constraints (d1);
+ tree c2 = get_constraints (d2);
+ int winner = 0;
+ if (subsumes_constraints (c1, c2))
+ ++winner;
+ if (subsumes_constraints (c2, c1))
+ --winner;
+ return winner;
+}
+
+/* Returns true if D1 is at least as constrained as D2. That is, the
+ associated constraints of D1 subsume those of D2, or both declarations
+ are unconstrained. */
+bool
+at_least_as_constrained (tree d1, tree d2)
+{
+ tree c1 = get_constraints (d1);
+ tree c2 = get_constraints (d2);
+ return subsumes_constraints (c1, c2);
+}
+
+
+/*---------------------------------------------------------------------------
+ Constraint diagnostics
+---------------------------------------------------------------------------*/
+
+/* The diagnosis of constraints performs a combination of
+ normalization and satisfaction testing. We recursively
+ walk through the conjunction (or disjunctions) of associated
+ constraints, testing each sub-expression in turn.
+
+ We currently restrict diagnostics to just the top-level
+ conjunctions within the associated constraints. A fully
+ recursive walk is possible, but it can generate a lot
+ of errors. */
+
+
+namespace {
+
+void diagnose_expression (location_t, tree, tree);
+void diagnose_constraint (location_t, tree, tree);
+
+/* Diagnose a conjunction of constraints. */
+void
+diagnose_logical_operation (location_t loc, tree t, tree args)
+{
+ diagnose_expression (loc, TREE_OPERAND (t, 0), args);
+ diagnose_expression (loc, TREE_OPERAND (t, 0), args);
+}
+
+/* Determine if the trait expression T is satisfied by ARGS.
+ Emit a precise diagnostic if it is not. */
+void
+diagnose_trait_expression (location_t loc, tree t, tree args)
+{
+ if (constraint_expression_satisfied_p (t, args))
+ return;
+
+ /* Rebuild the trait expression so we can diagnose the
+ specific failure. */
+ ++processing_template_decl;
+ tree expr = tsubst_expr (t, args, tf_none, NULL_TREE, false);
+ --processing_template_decl;
+
+ tree t1 = TRAIT_EXPR_TYPE1 (expr);
+ tree t2 = TRAIT_EXPR_TYPE2 (expr);
+ switch (TRAIT_EXPR_KIND (t))
+ {
+ case CPTK_HAS_NOTHROW_ASSIGN:
+ inform (loc, " %qT is not nothrow copy assignable", t1);
+ break;
+ case CPTK_HAS_NOTHROW_CONSTRUCTOR:
+ inform (loc, " %qT is not nothrow default constructible", t1);
+ break;
+ case CPTK_HAS_NOTHROW_COPY:
+ inform (loc, " %qT is not nothrow copy constructible", t1);
+ break;
+ case CPTK_HAS_TRIVIAL_ASSIGN:
+ inform (loc, " %qT is not trivially copy assignable", t1);
+ break;
+ case CPTK_HAS_TRIVIAL_CONSTRUCTOR:
+ inform (loc, " %qT is not trivially default constructible", t1);
+ break;
+ case CPTK_HAS_TRIVIAL_COPY:
+ inform (loc, " %qT is not trivially copy constructible", t1);
+ break;
+ case CPTK_HAS_TRIVIAL_DESTRUCTOR:
+ inform (loc, " %qT is not trivially destructible", t1);
+ break;
+ case CPTK_HAS_VIRTUAL_DESTRUCTOR:
+ inform (loc, " %qT does not have a virtual destructor", t1);
+ break;
+ case CPTK_IS_ABSTRACT:
+ inform (loc, " %qT is not an abstract class", t1);
+ break;
+ case CPTK_IS_BASE_OF:
+ inform (loc, " %qT is not a base of %qT", t1, t2);
+ break;
+ case CPTK_IS_CLASS:
+ inform (loc, " %qT is not a class", t1);
+ break;
+ case CPTK_IS_EMPTY:
+ inform (loc, " %qT is not an empty class", t1);
+ break;
+ case CPTK_IS_ENUM:
+ inform (loc, " %qT is not an enum", t1);
+ break;
+ case CPTK_IS_FINAL:
+ inform (loc, " %qT is not a final class", t1);
+ break;
+ case CPTK_IS_LITERAL_TYPE:
+ inform (loc, " %qT is not a literal type", t1);
+ break;
+ case CPTK_IS_POD:
+ inform (loc, " %qT is not a POD type", t1);
+ break;
+ case CPTK_IS_POLYMORPHIC:
+ inform (loc, " %qT is not a polymorphic type", t1);
+ break;
+ case CPTK_IS_SAME_AS:
+ inform (loc, " %qT is not the same as %qT", t1, t2);
+ break;
+ case CPTK_IS_STD_LAYOUT:
+ inform (loc, " %qT is not an standard layout type", t1);
+ break;
+ case CPTK_IS_TRIVIAL:
+ inform (loc, " %qT is not a trivial type", t1);
+ break;
+ case CPTK_IS_UNION:
+ inform (loc, " %qT is not a union", t1);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Determine if the call expression T, when normalized as a constraint,
+ is satisfied by ARGS.
+
+ TODO: If T is refers to a concept, We could recursively analyze
+ its definition to identify the exact failure, but that could
+ emit a *lot* of error messages (defeating the purpose of
+ improved diagnostics). Consider adding a flag to control the
+ depth of diagnostics. */
+void
+diagnose_call_expression (location_t loc, tree t, tree args)
+{
+ if (constraint_expression_satisfied_p (t, args))
+ return;
+
+ /* Rebuild the expression for the purpose of diagnostics. */
+ ++processing_template_decl;
+ tree expr = tsubst_expr (t, args, tf_none, NULL_TREE, false);
+ --processing_template_decl;
+
+ /* If the function call is known to be a concept check, then
+ diagnose it differently (i.e., we may recurse). */
+ if (resolve_constraint_check (t))
+ inform (loc, " concept %qE was not satisfied", expr);
+ else
+ inform (loc, " %qE evaluated to false", expr);
+}
+
+/* Determine if the template-id T, when normalized as a constraint
+ is satisfied by ARGS. */
+void
+diagnose_template_id (location_t loc, tree t, tree args)
+{
+ /* Check for invalid template-ids. */
+ if (!variable_template_p (TREE_OPERAND (t, 0)))
+ {
+ inform (loc, " invalid constraint %qE", t);
+ return;
+ }
+
+ if (constraint_expression_satisfied_p (t, args))
+ return;
+
+ /* Rebuild the expression for the purpose of diagnostics. */
+ ++processing_template_decl;
+ tree expr = tsubst_expr (t, args, tf_none, NULL_TREE, false);
+ --processing_template_decl;
+
+ tree var = DECL_TEMPLATE_RESULT (TREE_OPERAND (t, 0));
+ if (DECL_DECLARED_CONCEPT_P (var))
+ inform (loc, " concept %qE was not satisfied", expr);
+ else
+ inform (loc, " %qE evaluated to false", expr);
+}
+
+/* Determine if the requires-expression, when normalized as a
+ constraint is satisfied by ARGS.
+
+ TODO: Build sets of expressions, types, and constraints
+ based on the requirements in T and emit specific diagnostics
+ for those. */
+void
+diagnose_requires_expression (location_t loc, tree t, tree args)
+{
+ if (constraint_expression_satisfied_p (t, args))
+ return;
+ inform (loc, "requirements not satisfied");
+}
+
+void
+diagnose_pack_expansion (location_t loc, tree t, tree args)
+{
+ if (constraint_expression_satisfied_p (t, args))
+ return;
+
+ /* Make sure that we don't have naked packs that we don't expect. */
+ if (!same_type_p (TREE_TYPE (t), boolean_type_node))
+ {
+ inform (loc, "invalid pack expansion in constraint %qE", t);
+ return;
+ }
+
+ inform (loc, " in the expansion of %qE", t);
+
+ /* Get the vector of expanded arguments. Note that n must not
+ be 0 since this constraint is not satisfied. */
+ ++processing_template_decl;
+ tree exprs = tsubst_pack_expansion (t, args, tf_none, NULL_TREE);
+ --processing_template_decl;
+ if (exprs == error_mark_node)
+ {
+ /* TODO: This error message could be better. */
+ inform (loc, " substitution failure occurred during expansion");
+ return;
+ }
+
+ /* Check each expanded constraint separately. */
+ int n = TREE_VEC_LENGTH (exprs);
+ for (int i = 0; i < n; ++i)
+ {
+ tree expr = TREE_VEC_ELT (exprs, i);
+ if (!constraint_expression_satisfied_p (expr, args))
+ inform (loc, " %qE was not satisfied", expr);
+ }
+}
+
+/* Diagnose an expression that would be characterized as
+ a predicate constraint. */
+void
+diagnose_other_expression (location_t loc, tree t, tree args)
+{
+ if (constraint_expression_satisfied_p (t, args))
+ return;
+ inform (loc, " %qE evaluated to false", t);
+}
+
+void
+diagnose_expression (location_t loc, tree t, tree args)
+{
+ switch (TREE_CODE (t))
+ {
+ case TRUTH_ANDIF_EXPR:
+ diagnose_logical_operation (loc, t, args);
+ break;
+
+ case TRUTH_ORIF_EXPR:
+ diagnose_logical_operation (loc, t, args);
+ break;
+
+ case CALL_EXPR:
+ diagnose_call_expression (loc, t, args);
+ break;
+
+ case TEMPLATE_ID_EXPR:
+ diagnose_template_id (loc, t, args);
+ break;
+
+ case REQUIRES_EXPR:
+ diagnose_requires_expression (loc, t, args);
+ break;
+
+ case TRAIT_EXPR:
+ diagnose_trait_expression (loc, t, args);
+ break;
+
+ case EXPR_PACK_EXPANSION:
+ diagnose_pack_expansion (loc, t, args);
+ break;
+
+ default:
+ diagnose_other_expression (loc, t, args);
+ break;
+ }
+}
+
+inline void
+diagnose_predicate_constraint (location_t loc, tree t, tree args)
+{
+ diagnose_expression (loc, PRED_CONSTR_EXPR (t), args);
+}
+
+inline void
+diagnose_conjunction (location_t loc, tree t, tree args)
+{
+ diagnose_constraint (loc, TREE_OPERAND (t, 0), args);
+ diagnose_constraint (loc, TREE_OPERAND (t, 1), args);
+}
+
+/* Diagnose the constraint T for the given ARGS. This is only
+ ever invoked on the associated constraints, so we can
+ only have conjunctions of predicate constraints. */
+void
+diagnose_constraint (location_t loc, tree t, tree args)
+{
+ switch (TREE_CODE (t))
+ {
+ case CONJ_CONSTR:
+ diagnose_conjunction (loc, t, args);
+ break;
+
+ case PRED_CONSTR:
+ diagnose_predicate_constraint (loc, t, args);
+ break;
+
+ default:
+ gcc_unreachable ();
+ break;
+ }
+}
+
+/* Diagnose the reason(s) why ARGS do not satisfy the constraints
+ of declaration DECL. */
+
+void
+diagnose_declaration_constraints (location_t loc, tree decl, tree args)
+{
+ inform (loc, " constraints not satisfied");
+
+ /* Constraints are attached to the template. */
+ if (tree ti = DECL_TEMPLATE_INFO (decl))
+ {
+ decl = TI_TEMPLATE (ti);
+ if (!args)
+ args = TI_ARGS (ti);
+ }
+
+ /* Check that the constraints are actually valid. */
+ tree ci = get_constraints (decl);
+ if (!valid_constraints_p (ci))
+ {
+ inform (loc, " invalid constraints");
+ return;
+ }
+
+ /* Recursively diagnose the associated constraints. */
+ diagnose_constraint (loc, CI_ASSOCIATED_CONSTRAINTS (ci), args);
+}
+
+} // namespace
+
+/* Emit diagnostics detailing the failure ARGS to satisfy the
+ constraints of T. Here, T can be either a constraint
+ or a declaration. */
+
+void
+diagnose_constraints (location_t loc, tree t, tree args)
+{
+ if (constraint_p (t))
+ diagnose_constraint (loc, t, args);
+ else
+ diagnose_declaration_constraints (loc, t, args);
+}
diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c
index 31f12c8b7a3..2cab89c1552 100644
--- a/gcc/cp/cp-objcp-common.c
+++ b/gcc/cp/cp-objcp-common.c
@@ -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"
diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index 6117771d7f0..61acf271885 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -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
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index e70dcb45a37..78fd4af0a2f 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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);
diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c
index 60be71ae981..879eb71fc3a 100644
--- a/gcc/cp/cxx-pretty-print.c
+++ b/gcc/cp/cxx-pretty-print.c
@@ -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;
diff --git a/gcc/cp/cxx-pretty-print.h b/gcc/cp/cxx-pretty-print.h
index 16f3238b707..e5161df0644 100644
--- a/gcc/cp/cxx-pretty-print.h
+++ b/gcc/cp/cxx-pretty-print.h
@@ -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 */
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 208173a8dfa..e8ed472293b 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -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;
}
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index a45755ec8de..068d79cd10c 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -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
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index b811df2b9be..4f85751711a 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -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;
diff --git a/gcc/cp/lex.c b/gcc/cp/lex.c
index a142410a295..f81c17b4a51 100644
--- a/gcc/cp/lex.c
+++ b/gcc/cp/lex.c
@@ -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)
diff --git a/gcc/cp/logic.cc b/gcc/cp/logic.cc
new file mode 100644
index 00000000000..7e016409c30
--- /dev/null
+++ b/gcc/cp/logic.cc
@@ -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);
+}
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index 7d7ec7da187..fdc76043e93 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -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;
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index bfad6089670..a0b249bbf5b 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -1343,7 +1343,7 @@ clear_decl_specs (cp_decl_specifier_seq *decl_specs)
VAR_DECLs or FUNCTION_DECLs) should do that directly. */
static cp_declarator *make_call_declarator
- (cp_declarator *, tree, cp_cv_quals, cp_virt_specifiers, cp_ref_qualifier, tree, tree);
+ (cp_declarator *, tree, cp_cv_quals, cp_virt_specifiers, cp_ref_qualifier, tree, tree, tree);
static cp_declarator *make_array_declarator
(cp_declarator *, tree);
static cp_declarator *make_pointer_declarator
@@ -1522,7 +1522,8 @@ make_call_declarator (cp_declarator *target,
cp_virt_specifiers virt_specifiers,
cp_ref_qualifier ref_qualifier,
tree exception_specification,
- tree late_return_type)
+ tree late_return_type,
+ tree requires_clause)
{
cp_declarator *declarator;
@@ -1534,6 +1535,7 @@ make_call_declarator (cp_declarator *target,
declarator->u.function.ref_qualifier = ref_qualifier;
declarator->u.function.exception_specification = exception_specification;
declarator->u.function.late_return_type = late_return_type;
+ declarator->u.function.requires_clause = requires_clause;
if (target)
{
declarator->id_loc = target->id_loc;
@@ -2082,6 +2084,8 @@ static tree cp_parser_type_specifier
static tree cp_parser_simple_type_specifier
(cp_parser *, cp_decl_specifier_seq *, cp_parser_flags);
static tree cp_parser_type_name
+ (cp_parser *, bool);
+static tree cp_parser_type_name
(cp_parser *);
static tree cp_parser_nonclass_name
(cp_parser* parser);
@@ -2136,7 +2140,7 @@ static cp_virt_specifiers cp_parser_virt_specifier_seq_opt
static cp_ref_qualifier cp_parser_ref_qualifier_opt
(cp_parser *);
static tree cp_parser_late_return_type_opt
- (cp_parser *, cp_declarator *, cp_cv_quals);
+ (cp_parser *, cp_declarator *, tree &, cp_cv_quals);
static tree cp_parser_declarator_id
(cp_parser *, bool);
static tree cp_parser_type_id
@@ -2177,7 +2181,7 @@ static tree cp_parser_late_parsing_cilk_simd_fn_info
(cp_parser *, tree);
static tree synthesize_implicit_template_parm
- (cp_parser *);
+ (cp_parser *, tree);
static tree finish_fully_implicit_template
(cp_parser *, tree);
@@ -2314,6 +2318,31 @@ static bool cp_parser_extension_opt
static void cp_parser_label_declaration
(cp_parser *);
+/* Concept Extensions */
+
+static tree cp_parser_requires_clause
+ (cp_parser *);
+static tree cp_parser_requires_clause_opt
+ (cp_parser *);
+static tree cp_parser_requires_expression
+ (cp_parser *);
+static tree cp_parser_requirement_parameter_list
+ (cp_parser *);
+static tree cp_parser_requirement_body
+ (cp_parser *);
+static tree cp_parser_requirement_list
+ (cp_parser *);
+static tree cp_parser_requirement
+ (cp_parser *);
+static tree cp_parser_simple_requirement
+ (cp_parser *);
+static tree cp_parser_compound_requirement
+ (cp_parser *);
+static tree cp_parser_type_requirement
+ (cp_parser *);
+static tree cp_parser_nested_requirement
+ (cp_parser *);
+
/* Transactional Memory Extensions */
static tree cp_parser_transaction
@@ -2396,7 +2425,7 @@ static tree cp_parser_function_definition_from_specifiers_and_declarator
(cp_parser *, cp_decl_specifier_seq *, tree, const cp_declarator *);
static tree cp_parser_function_definition_after_declarator
(cp_parser *, bool);
-static void cp_parser_template_declaration_after_export
+static bool cp_parser_template_declaration_after_export
(cp_parser *, bool);
static void cp_parser_perform_template_parameter_access_checks
(vec<deferred_access_check, va_gc> *);
@@ -2532,6 +2561,30 @@ static bool cp_parser_array_designator_p
static bool cp_parser_skip_to_closing_square_bracket
(cp_parser *);
+/* Concept-related syntactic transformations */
+
+static tree cp_parser_maybe_concept_name (cp_parser *, tree);
+static tree cp_parser_maybe_partial_concept_id (cp_parser *, tree, tree);
+
+// -------------------------------------------------------------------------- //
+// Unevaluated Operand Guard
+//
+// Implementation of an RAII helper for unevaluated operand parsing.
+cp_unevaluated::cp_unevaluated ()
+{
+ ++cp_unevaluated_operand;
+ ++c_inhibit_evaluation_warnings;
+}
+
+cp_unevaluated::~cp_unevaluated ()
+{
+ --c_inhibit_evaluation_warnings;
+ --cp_unevaluated_operand;
+}
+
+// -------------------------------------------------------------------------- //
+// Tentative Parsing
+
/* Returns nonzero if we are parsing tentatively. */
static inline bool
@@ -3552,6 +3605,9 @@ cp_parser_new (void)
parser->implicit_template_parms = 0;
parser->implicit_template_scope = 0;
+ /* Allow constrained-type-specifiers. */
+ parser->prevent_constrained_type_specifiers = 0;
+
return parser;
}
@@ -4591,6 +4647,7 @@ cp_parser_primary_expression (cp_parser *parser,
case RID_IS_LITERAL_TYPE:
case RID_IS_POD:
case RID_IS_POLYMORPHIC:
+ case RID_IS_SAME_AS:
case RID_IS_STD_LAYOUT:
case RID_IS_TRIVIAL:
case RID_IS_TRIVIALLY_ASSIGNABLE:
@@ -4599,6 +4656,10 @@ cp_parser_primary_expression (cp_parser *parser,
case RID_IS_UNION:
return cp_parser_trait_expr (parser, token->keyword);
+ // C++ concepts
+ case RID_REQUIRES:
+ return cp_parser_requires_expression (parser);
+
/* Objective-C++ expressions. */
case RID_AT_ENCODE:
case RID_AT_PROTOCOL:
@@ -5956,9 +6017,11 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
tree type;
/* The syntax permitted here is the same permitted for an
elaborated-type-specifier. */
+ ++parser->prevent_constrained_type_specifiers;
type = cp_parser_elaborated_type_specifier (parser,
/*is_friend=*/false,
/*is_declaration=*/false);
+ --parser->prevent_constrained_type_specifiers;
postfix_expression = cp_parser_functional_cast (parser, type);
}
break;
@@ -6056,9 +6119,11 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
that doesn't work we fall back to the primary-expression. */
cp_parser_parse_tentatively (parser);
/* Look for the simple-type-specifier. */
+ ++parser->prevent_constrained_type_specifiers;
type = cp_parser_simple_type_specifier (parser,
/*decl_specs=*/NULL,
CP_PARSER_FLAGS_NONE);
+ --parser->prevent_constrained_type_specifiers;
/* Parse the cast itself. */
if (!cp_parser_error_occurred (parser))
postfix_expression
@@ -8843,6 +8908,10 @@ cp_parser_trait_expr (cp_parser* parser, enum rid keyword)
case RID_IS_POLYMORPHIC:
kind = CPTK_IS_POLYMORPHIC;
break;
+ case RID_IS_SAME_AS:
+ kind = CPTK_IS_SAME_AS;
+ binary = true;
+ break;
case RID_IS_STD_LAYOUT:
kind = CPTK_IS_STD_LAYOUT;
break;
@@ -9440,7 +9509,8 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
VIRT_SPEC_UNSPECIFIED,
REF_QUAL_NONE,
exception_spec,
- /*late_return_type=*/NULL_TREE);
+ /*late_return_type=*/NULL_TREE,
+ /*requires_clause*/NULL_TREE);
declarator->id_loc = LAMBDA_EXPR_LOCATION (lambda_expr);
fco = grokmethod (&return_type_specs,
@@ -11371,8 +11441,12 @@ cp_parser_declaration (cp_parser* parser)
&& token1.keyword == RID_ATTRIBUTE
&& cp_parser_objc_valid_prefix_attributes (parser, &attributes))
cp_parser_objc_declaration (parser, attributes);
- /* We must have either a block declaration or a function
- definition. */
+ /* At this point we may have a template declared by a concept
+ introduction. */
+ else if (flag_concepts
+ && cp_parser_template_declaration_after_export (parser,
+ /*member_p=*/false))
+ /* We did. */;
else
/* Try to parse a block-declaration, or a function-definition. */
cp_parser_block_declaration (parser, /*statement_p=*/false);
@@ -11723,6 +11797,11 @@ cp_parser_simple_declaration (cp_parser* parser,
decl-specifier:
attributes
+ Concepts Extension:
+
+ decl-specifier:
+ concept
+
Set *DECL_SPECS to a representation of the decl-specifier-seq.
The parser flags FLAGS is used to control type-specifier parsing.
@@ -11850,6 +11929,11 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
cp_lexer_consume_token (parser->lexer);
break;
+ case RID_CONCEPT:
+ ds = ds_concept;
+ cp_lexer_consume_token (parser->lexer);
+ break;
+
/* function-specifier:
inline
virtual
@@ -13256,7 +13340,15 @@ cp_parser_operator (cp_parser* parser)
template-parameter-list-seq:
template-parameter-list-seq [opt]
- template < template-parameter-list > */
+ template < template-parameter-list >
+
+ Concept Extensions:
+
+ template-parameter-list-seq:
+ template < template-parameter-list > requires-clause [opt]
+
+ requires-clause:
+ requires logical-or-expression */
static void
cp_parser_template_declaration (cp_parser* parser, bool member_p)
@@ -13330,6 +13422,244 @@ cp_parser_template_parameter_list (cp_parser* parser)
return end_template_parm_list (parameter_list);
}
+/* Parse a introduction-list.
+
+ introduction-list:
+ introduced-parameter
+ introduction-list , introduced-parameter
+
+ introduced-parameter:
+ ...[opt] identifier
+
+ Returns a TREE_VEC of WILDCARD_DECLs. If the parameter is a pack
+ then the introduced parm will have WILDCARD_PACK_P set. In addition, the
+ WILDCARD_DECL will also have DECL_NAME set and token location in
+ DECL_SOURCE_LOCATION. */
+
+static tree
+cp_parser_introduction_list (cp_parser *parser)
+{
+ vec<tree, va_gc> *introduction_vec = make_tree_vector ();
+
+ while (true)
+ {
+ bool is_pack = cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS);
+ if (is_pack)
+ cp_lexer_consume_token (parser->lexer);
+
+ /* Build placeholder. */
+ tree parm = build_nt (WILDCARD_DECL);
+ DECL_SOURCE_LOCATION (parm)
+ = cp_lexer_peek_token (parser->lexer)->location;
+ DECL_NAME (parm) = cp_parser_identifier (parser);
+ WILDCARD_PACK_P (parm) = is_pack;
+ vec_safe_push (introduction_vec, parm);
+
+ /* If the next token is not a `,', we're done. */
+ if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA))
+ break;
+ /* Otherwise, consume the `,' token. */
+ cp_lexer_consume_token (parser->lexer);
+ }
+
+ /* Convert the vec into a TREE_VEC. */
+ tree introduction_list = make_tree_vec (introduction_vec->length ());
+ unsigned int n;
+ tree parm;
+ FOR_EACH_VEC_ELT (*introduction_vec, n, parm)
+ TREE_VEC_ELT (introduction_list, n) = parm;
+
+ release_tree_vector (introduction_vec);
+ return introduction_list;
+}
+
+/* Given a declarator, get the declarator-id part, or NULL_TREE if this
+ is an abstract declarator. */
+
+static inline cp_declarator*
+get_id_declarator (cp_declarator *declarator)
+{
+ cp_declarator *d = declarator;
+ while (d && d->kind != cdk_id)
+ d = d->declarator;
+ return d;
+}
+
+/* Get the unqualified-id from the DECLARATOR or NULL_TREE if this
+ is an abstract declarator. */
+
+static inline tree
+get_unqualified_id (cp_declarator *declarator)
+{
+ declarator = get_id_declarator (declarator);
+ if (declarator)
+ return declarator->u.id.unqualified_name;
+ else
+ return NULL_TREE;
+}
+
+/* Returns true if DECL represents a constrained-parameter. */
+
+static inline bool
+is_constrained_parameter (tree decl)
+{
+ return (decl
+ && TREE_CODE (decl) == TYPE_DECL
+ && CONSTRAINED_PARM_CONCEPT (decl)
+ && DECL_P (CONSTRAINED_PARM_CONCEPT (decl)));
+}
+
+/* Returns true if PARM declares a constrained-parameter. */
+
+static inline bool
+is_constrained_parameter (cp_parameter_declarator *parm)
+{
+ return is_constrained_parameter (parm->decl_specifiers.type);
+}
+
+/* Check that the type parameter is only a declarator-id, and that its
+ type is not cv-qualified. */
+
+bool
+cp_parser_check_constrained_type_parm (cp_parser *parser,
+ cp_parameter_declarator *parm)
+{
+ if (!parm->declarator)
+ return true;
+
+ if (parm->declarator->kind != cdk_id)
+ {
+ cp_parser_error (parser, "invalid constrained type parameter");
+ return false;
+ }
+
+ /* Don't allow cv-qualified type parameters. */
+ if (decl_spec_seq_has_spec_p (&parm->decl_specifiers, ds_const)
+ || decl_spec_seq_has_spec_p (&parm->decl_specifiers, ds_volatile))
+ {
+ cp_parser_error (parser, "cv-qualified type parameter");
+ return false;
+ }
+
+ return true;
+}
+
+/* Finish parsing/processing a template type parameter and checking
+ various restrictions. */
+
+static inline tree
+cp_parser_constrained_type_template_parm (cp_parser *parser,
+ tree id,
+ cp_parameter_declarator* parmdecl)
+{
+ if (cp_parser_check_constrained_type_parm (parser, parmdecl))
+ return finish_template_type_parm (class_type_node, id);
+ else
+ return error_mark_node;
+}
+
+/* Finish parsing/processing a template template parameter by borrowing
+ the template parameter list from the prototype parameter. */
+
+static tree
+cp_parser_constrained_template_template_parm (cp_parser *parser,
+ tree proto,
+ tree id,
+ cp_parameter_declarator *parmdecl)
+{
+ if (!cp_parser_check_constrained_type_parm (parser, parmdecl))
+ return error_mark_node;
+
+ /* FIXME: This should probably be copied, and we may need to adjust
+ the template parameter depths. */
+ tree saved_parms = current_template_parms;
+ begin_template_parm_list ();
+ current_template_parms = DECL_TEMPLATE_PARMS (proto);
+ end_template_parm_list ();
+
+ tree parm = finish_template_template_parm (class_type_node, id);
+ current_template_parms = saved_parms;
+
+ return parm;
+}
+
+/* Create a new non-type template parameter from the given PARM
+ declarator. */
+
+static tree
+constrained_non_type_template_parm (bool *is_non_type,
+ cp_parameter_declarator *parm)
+{
+ *is_non_type = true;
+ cp_declarator *decl = parm->declarator;
+ cp_decl_specifier_seq *specs = &parm->decl_specifiers;
+ specs->type = TREE_TYPE (DECL_INITIAL (specs->type));
+ return grokdeclarator (decl, specs, TPARM, 0, NULL);
+}
+
+/* Build a constrained template parameter based on the PARMDECL
+ declarator. The type of PARMDECL is the constrained type, which
+ refers to the prototype template parameter that ultimately
+ specifies the type of the declared parameter. */
+
+static tree
+finish_constrained_parameter (cp_parser *parser,
+ cp_parameter_declarator *parmdecl,
+ bool *is_non_type,
+ bool *is_parameter_pack)
+{
+ tree decl = parmdecl->decl_specifiers.type;
+ tree id = get_unqualified_id (parmdecl->declarator);
+ tree def = parmdecl->default_argument;
+ tree proto = DECL_INITIAL (decl);
+
+ /* A template parameter constrained by a variadic concept shall also
+ be declared as a template parameter pack. */
+ bool is_variadic = template_parameter_pack_p (proto);
+ if (is_variadic && !*is_parameter_pack)
+ cp_parser_error (parser, "variadic constraint introduced without %<...%>");
+
+ /* Build the parameter. Return an error if the declarator was invalid. */
+ tree parm;
+ if (TREE_CODE (proto) == TYPE_DECL)
+ parm = cp_parser_constrained_type_template_parm (parser, id, parmdecl);
+ else if (TREE_CODE (proto) == TEMPLATE_DECL)
+ parm = cp_parser_constrained_template_template_parm (parser, proto, id,
+ parmdecl);
+ else
+ parm = constrained_non_type_template_parm (is_non_type, parmdecl);
+ if (parm == error_mark_node)
+ return error_mark_node;
+
+ /* Finish the parameter decl and create a node attaching the
+ default argument and constraint. */
+ parm = build_tree_list (def, parm);
+ TEMPLATE_PARM_CONSTRAINTS (parm) = decl;
+
+ return parm;
+}
+
+/* Returns true if the parsed type actually represents the declaration
+ of a type template-parameter. */
+
+static inline bool
+declares_constrained_type_template_parameter (tree type)
+{
+ return (is_constrained_parameter (type)
+ && TREE_CODE (TREE_TYPE (type)) == TEMPLATE_TYPE_PARM);
+}
+
+
+/* Returns true if the parsed type actually represents the declaration of
+ a template template-parameter. */
+
+static bool
+declares_constrained_template_template_parameter (tree type)
+{
+ return (is_constrained_parameter (type)
+ && TREE_CODE (TREE_TYPE (type)) == TEMPLATE_TEMPLATE_PARM);
+}
+
/* Parse a default argument for a type template-parameter.
Note that diagnostics are handled in cp_parser_template_parameter. */
@@ -13454,7 +13784,7 @@ cp_parser_template_parameter (cp_parser* parser, bool *is_non_type,
return cp_parser_type_parameter (parser, is_parameter_pack);
}
- /* Otherwise, it is a non-type parameter.
+ /* Otherwise, it is a non-type parameter or a constrained parameter.
[temp.param]
@@ -13462,7 +13792,6 @@ cp_parser_template_parameter (cp_parser* parser, bool *is_non_type,
template-parameter, the first non-nested `>' is taken as the end
of the template parameter-list rather than a greater-than
operator. */
- *is_non_type = true;
parameter_declarator
= cp_parser_parameter_declaration (parser, /*template_parm_p=*/true,
/*parenthesized_p=*/NULL);
@@ -13483,6 +13812,16 @@ cp_parser_template_parameter (cp_parser* parser, bool *is_non_type,
cp_lexer_consume_token (parser->lexer);
}
+ // The parameter may have been constrained.
+ if (is_constrained_parameter (parameter_declarator))
+ return finish_constrained_parameter (parser,
+ parameter_declarator,
+ is_non_type,
+ is_parameter_pack);
+
+ // Now we're sure that the parameter is a non-type parameter.
+ *is_non_type = true;
+
parm = grokdeclarator (parameter_declarator->declarator,
&parameter_declarator->decl_specifiers,
TPARM, /*initialized=*/0,
@@ -13599,6 +13938,16 @@ cp_parser_type_parameter (cp_parser* parser, bool *is_parameter_pack)
cp_parser_template_parameter_list (parser);
/* Look for the `>'. */
cp_parser_require (parser, CPP_GREATER, RT_GREATER);
+
+ // If template requirements are present, parse them.
+ if (flag_concepts)
+ {
+ tree reqs = get_shorthand_constraints (current_template_parms);
+ if (tree r = cp_parser_requires_clause_opt (parser))
+ reqs = conjoin_constraints (reqs, make_predicate_constraint (r));
+ TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs;
+ }
+
/* Look for the `class' or 'typename' keywords. */
cp_parser_type_parameter_key (parser);
/* If the next token is an ellipsis, we have a template
@@ -13833,6 +14182,11 @@ cp_parser_template_id (cp_parser *parser,
template_id
= finish_template_type (templ, arguments, entering_scope);
}
+ /* A template-like identifier may be a partial concept id. */
+ else if (flag_concepts
+ && (template_id = (cp_parser_maybe_partial_concept_id
+ (parser, templ, arguments))))
+ return template_id;
else if (variable_template_p (templ))
{
template_id = lookup_template_variable (templ, arguments);
@@ -14844,6 +15198,11 @@ cp_parser_type_specifier (cp_parser* parser,
__typeof__ ( type-id )
__typeof__ ( type-id ) { initializer-list , [opt] }
+ Concepts Extension:
+
+ simple-type-specifier:
+ constrained-type-specifier
+
Returns the indicated TYPE_DECL. If DECL_SPECS is not NULL, it is
appropriately updated. */
@@ -14954,7 +15313,7 @@ cp_parser_simple_type_specifier (cp_parser* parser,
}
if (cxx_dialect >= cxx14)
- type = synthesize_implicit_template_parm (parser);
+ type = synthesize_implicit_template_parm (parser, NULL_TREE);
else
type = error_mark_node;
@@ -15195,18 +15554,34 @@ cp_parser_simple_type_specifier (cp_parser* parser,
typedef-name:
identifier
+ Concepts:
+
+ type-name:
+ concept-name
+ partial-concept-id
+
+ concept-name:
+ identifier
+
Returns a TYPE_DECL for the type. */
static tree
cp_parser_type_name (cp_parser* parser)
{
+ return cp_parser_type_name (parser, /*typename_keyword_p=*/false);
+}
+
+/* See above. */
+static tree
+cp_parser_type_name (cp_parser* parser, bool typename_keyword_p)
+{
tree type_decl;
/* We can't know yet whether it is a class-name or not. */
cp_parser_parse_tentatively (parser);
/* Try a class-name. */
type_decl = cp_parser_class_name (parser,
- /*typename_keyword_p=*/false,
+ typename_keyword_p,
/*template_keyword_p=*/false,
none_type,
/*check_dependency_p=*/true,
@@ -15236,11 +15611,16 @@ cp_parser_type_name (cp_parser* parser)
Whereas [temp.names]/7 says:
A simple-template-id that names a class template
- specialization is a class-name. */
+ specialization is a class-name.
+
+ With concepts, this could also be a partial-concept-id that
+ declares a non-type template parameter. */
if (type_decl != NULL_TREE
&& TREE_CODE (type_decl) == TYPE_DECL
&& TYPE_DECL_ALIAS_P (type_decl))
gcc_assert (DECL_TEMPLATE_INSTANTIATION (type_decl));
+ else if (is_constrained_parameter (type_decl))
+ /* Don't do anything. */ ;
else
cp_parser_simulate_error (parser);
@@ -15252,7 +15632,121 @@ cp_parser_type_name (cp_parser* parser)
return type_decl;
}
-/* Parse a non-class type-name, that is, either an enum-name or a typedef-name.
+/* Returns true if proto is a type parameter, but not a template
+ template parameter. */
+static bool
+check_type_concept (tree fn, tree proto)
+{
+ if (TREE_CODE (proto) != TYPE_DECL)
+ {
+ error ("invalid use of non-type concept %qD", fn);
+ return false;
+ }
+ return true;
+}
+
+/* Check if DECL and ARGS can form a constrained-type-specifier.
+ If ARGS is non-null, we try to form a concept check of the
+ form DECL<?, ARGS> where ? is a wildcard that matches any
+ kind of template argument. If ARGS is NULL, then we try to
+ form a concept check of the form DECL<?>. */
+
+static tree
+cp_parser_maybe_constrained_type_specifier (cp_parser *parser,
+ tree decl, tree args)
+{
+ gcc_assert (args ? TREE_CODE (args) == TREE_VEC : true);
+
+ /* If we a constrained-type-specifier cannot be deduced. */
+ if (parser->prevent_constrained_type_specifiers)
+ return NULL_TREE;
+
+ /* A constrained type specifier can only be found in an
+ overload set or as a reference to a template declaration.
+
+ FIXME: This might be masking a bug. It's possible that
+ that the deduction below is causing template specializations
+ to be formed with the wildcard as an argument. */
+ if (TREE_CODE (decl) != OVERLOAD && TREE_CODE (decl) != TEMPLATE_DECL)
+ return NULL_TREE;
+
+ /* Try to build a call expression that evaluates the
+ concept. This can fail if the overload set refers
+ only to non-templates. */
+ tree placeholder = build_nt (WILDCARD_DECL);
+ tree check = build_concept_check (decl, placeholder, args);
+ if (check == error_mark_node)
+ return NULL_TREE;
+
+ /* Deduce the checked constraint and the prototype parameter.
+
+ FIXME: In certain cases, failure to deduce should be a
+ diagnosable error. */
+ tree conc;
+ tree proto;
+ if (!deduce_constrained_parameter (check, conc, proto))
+ return NULL_TREE;
+
+ /* In template parameter scope, this results in a constrained
+ parameter. Return a descriptor of that parm. */
+ if (processing_template_parmlist)
+ return build_constrained_parameter (conc, proto, args);
+
+ /* In any other context, a concept must be a type concept.
+
+ FIXME: A constrained-type-specifier can be a placeholder
+ of any kind. */
+ if (!check_type_concept (conc, proto))
+ return error_mark_node;
+
+ /* In a parameter-declaration-clause, constrained-type
+ specifiers result in invented template parameters. */
+ if (parser->auto_is_implicit_function_template_parm_p)
+ {
+ tree x = build_constrained_parameter (conc, proto, args);
+ return synthesize_implicit_template_parm (parser, x);
+ }
+ else
+ {
+ /* Otherwise, we're in a context where the constrained
+ type name is deduced and the constraint applies
+ after deduction. */
+ return make_constrained_auto (conc, args);
+ }
+
+ return NULL_TREE;
+}
+
+/* If DECL refers to a concept, return a TYPE_DECL representing
+ the result of using the constrained type specifier in the
+ current context. DECL refers to a concept if
+
+ - it is an overload set containing a function concept taking a single
+ type argument, or
+
+ - it is a variable concept taking a single type argument. */
+
+static tree
+cp_parser_maybe_concept_name (cp_parser* parser, tree decl)
+{
+ return cp_parser_maybe_constrained_type_specifier (parser, decl, NULL_TREE);
+}
+
+/* Check if DECL and ARGS form a partial-concept-id. If so,
+ assign ID to the resulting constrained placeholder.
+
+ Returns true if the partial-concept-id designates a placeholder
+ and false otherwise. Note that *id is set to NULL_TREE in
+ this case. */
+
+static tree
+cp_parser_maybe_partial_concept_id (cp_parser *parser, tree decl, tree args)
+{
+ return cp_parser_maybe_constrained_type_specifier (parser, decl, args);
+}
+
+/* Parse a non-class type-name, that is, either an enum-name, a typedef-name,
+ or a concept-name.
enum-name:
identifier
@@ -15260,6 +15754,9 @@ cp_parser_type_name (cp_parser* parser)
typedef-name:
identifier
+ concept-name:
+ identifier
+
Returns a TYPE_DECL for the type. */
static tree
@@ -15278,6 +15775,17 @@ cp_parser_nonclass_name (cp_parser* parser)
type_decl = strip_using_decl (type_decl);
+ /* If we found an overload set, then it may refer to a concept-name. */
+ if (flag_concepts
+ && (TREE_CODE (type_decl) == OVERLOAD
+ || BASELINK_P (type_decl)
+ || variable_concept_p (type_decl)))
+ {
+ /* Determine whether the overload refers to a concept. */
+ if (tree decl = cp_parser_maybe_concept_name (parser, type_decl))
+ return decl;
+ }
+
if (TREE_CODE (type_decl) != TYPE_DECL
&& (objc_is_id (identifier) || objc_is_class_name (identifier)))
{
@@ -16637,6 +17145,14 @@ cp_parser_alias_declaration (cp_parser* parser)
if (decl == error_mark_node)
return decl;
+ // Attach constraints to the alias declaration.
+ if (flag_concepts && current_template_parms)
+ {
+ tree reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms);
+ tree constr = build_constraints (reqs, NULL_TREE);
+ set_constraints (decl, constr);
+ }
+
cp_finish_decl (decl, NULL_TREE, 0, NULL_TREE, 0);
if (pushed_scope)
@@ -17632,11 +18148,11 @@ cp_parser_direct_declarator (cp_parser* parser,
else
attrs = chainon (attr, attrs);
}
+ tree requires_clause = NULL_TREE;
late_return = (cp_parser_late_return_type_opt
- (parser, declarator,
+ (parser, declarator, requires_clause,
memfn ? cv_quals : -1));
-
/* Parse the virt-specifier-seq. */
virt_specifiers = cp_parser_virt_specifier_seq_opt (parser);
@@ -17647,7 +18163,8 @@ cp_parser_direct_declarator (cp_parser* parser,
virt_specifiers,
ref_qual,
exception_specification,
- late_return);
+ late_return,
+ requires_clause);
declarator->std_attributes = attrs;
/* Any subsequent parameter lists are to do with
return type, so are not those of the declared
@@ -18369,7 +18886,7 @@ parsing_nsdmi (void)
static tree
cp_parser_late_return_type_opt (cp_parser* parser, cp_declarator *declarator,
- cp_cv_quals quals)
+ tree& requires_clause, cp_cv_quals quals)
{
cp_token *token;
tree type = NULL_TREE;
@@ -18383,7 +18900,9 @@ cp_parser_late_return_type_opt (cp_parser* parser, cp_declarator *declarator,
/* Peek at the next token. */
token = cp_lexer_peek_token (parser->lexer);
/* A late-specified return type is indicated by an initial '->'. */
- if (token->type != CPP_DEREF && !(declare_simd_p || cilk_simd_fn_vector_p))
+ if (token->type != CPP_DEREF
+ && token->keyword != RID_REQUIRES
+ && !(declare_simd_p || cilk_simd_fn_vector_p))
return NULL_TREE;
tree save_ccp = current_class_ptr;
@@ -18402,6 +18921,10 @@ cp_parser_late_return_type_opt (cp_parser* parser, cp_declarator *declarator,
type = cp_parser_trailing_type_id (parser);
}
+ /* Function declarations may be followed by a trailing
+ requires-clause. */
+ requires_clause = cp_parser_requires_clause_opt (parser);
+
if (cilk_simd_fn_vector_p)
declarator->std_attributes
= cp_parser_late_parsing_cilk_simd_fn_info (parser,
@@ -18502,11 +19025,16 @@ cp_parser_type_id_1 (cp_parser* parser, bool is_template_arg,
&& type_uses_auto (type_specifier_seq.type))
{
/* A type-id with type 'auto' is only ok if the abstract declarator
- is a function declarator with a late-specified return type. */
+ is a function declarator with a late-specified return type.
+
+ A type-id with 'auto' is also valid in a trailing-return-type
+ in a compound-requirement. */
if (abstract_declarator
&& abstract_declarator->kind == cdk_function
&& abstract_declarator->u.function.late_return_type)
/* OK */;
+ else if (parser->in_result_type_constraint_p)
+ /* OK */;
else
{
error ("invalid use of %<auto%>");
@@ -18539,7 +19067,8 @@ static tree cp_parser_template_type_arg (cp_parser *parser)
return r;
}
-static tree cp_parser_trailing_type_id (cp_parser *parser)
+static tree
+cp_parser_trailing_type_id (cp_parser *parser)
{
return cp_parser_type_id_1 (parser, false, true);
}
@@ -19123,6 +19652,7 @@ cp_parser_parameter_declaration (cp_parser *parser,
/* If the next token is `=', then process a default argument. */
if (cp_lexer_next_token_is (parser->lexer, CPP_EQ))
{
+ tree type = decl_specifiers.type;
token = cp_lexer_peek_token (parser->lexer);
/* If we are defining a class, then the tokens that make up the
default argument must be saved and processed later. */
@@ -19130,6 +19660,17 @@ cp_parser_parameter_declaration (cp_parser *parser,
&& TYPE_BEING_DEFINED (current_class_type)
&& !LAMBDA_TYPE_P (current_class_type))
default_argument = cp_parser_cache_defarg (parser, /*nsdmi=*/false);
+
+ // A constrained-type-specifier may declare a type template-parameter.
+ else if (declares_constrained_type_template_parameter (type))
+ default_argument
+ = cp_parser_default_type_template_argument (parser);
+
+ // A constrained-type-specifier may declare a template-template-parameter.
+ else if (declares_constrained_template_template_parameter (type))
+ default_argument
+ = cp_parser_default_template_template_argument (parser);
+
/* Outside of a class definition, we can just parse the
assignment-expression. */
else
@@ -19872,6 +20413,10 @@ cp_parser_class_specifier_1 (cp_parser* parser)
= parser->in_unbraced_linkage_specification_p;
parser->in_unbraced_linkage_specification_p = false;
+ // Associate constraints with the type.
+ if (flag_concepts)
+ type = associate_classtype_constraints (type);
+
/* Start the class. */
if (nested_name_specifier_p)
{
@@ -20770,6 +21315,9 @@ cp_parser_member_declaration (cp_parser* parser)
return;
}
+ /* Check for a template introduction. */
+ else if (cp_parser_template_declaration_after_export (parser, true))
+ return;
/* Check for a using-declaration. */
if (cp_lexer_next_token_is_keyword (parser->lexer, RID_USING))
@@ -22764,6 +23312,324 @@ cp_parser_label_declaration (cp_parser* parser)
cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
}
+// -------------------------------------------------------------------------- //
+// Requires Clause
+
+// Parse a requires clause.
+//
+// requires-clause:
+// 'requires' logical-or-expression
+//
+// The required logical-or-expression must be a constant expression. Note
+// that we don't check that the expression is constepxr here. We defer until
+// we analyze constraints and then, we only check atomic constraints.
+static tree
+cp_parser_requires_clause (cp_parser *parser)
+{
+ // Parse the requires clause so that it is not automatically folded.
+ ++processing_template_decl;
+ tree expr = cp_parser_binary_expression (parser, false, false,
+ PREC_NOT_OPERATOR, NULL);
+ --processing_template_decl;
+ return expr;
+}
+
+// Optionally parse a requires clause:
+static tree
+cp_parser_requires_clause_opt (cp_parser *parser)
+{
+ if (!cp_lexer_next_token_is_keyword (parser->lexer, RID_REQUIRES))
+ return NULL_TREE;
+ cp_lexer_consume_token (parser->lexer);
+ return cp_parser_requires_clause (parser);
+}
+
+
+/*---------------------------------------------------------------------------
+ Requires expressions
+---------------------------------------------------------------------------*/
+
+/* Parse a requires expression
+
+ requirement-expression:
+ 'requires' requirement-parameter-list [opt] requirement-body */
+static tree
+cp_parser_requires_expression (cp_parser *parser)
+{
+ gcc_assert (cp_lexer_next_token_is_keyword (parser->lexer, RID_REQUIRES));
+ location_t loc = cp_lexer_consume_token (parser->lexer)->location;
+
+ /* A requires-expression shall appear only within a concept
+ definition or a requires-clause.
+
+ TODO: Implement this diagnostic correctly. */
+ if (!processing_template_decl)
+ {
+ error_at (loc, "a requires expression cannot appear outside a template");
+ cp_parser_skip_to_end_of_statement (parser);
+ return error_mark_node;
+ }
+
+ tree parms, reqs;
+ {
+ /* Local parameters are delared as variables within the scope
+ of the expression. They are not visible past the end of
+ the expression. Expressions within the requires-expression
+ are unevaluated. */
+ struct scope_sentinel
+ {
+ scope_sentinel ()
+ {
+ ++cp_unevaluated_operand;
+ begin_scope (sk_block, NULL_TREE);
+ }
+
+ ~scope_sentinel ()
+ {
+ pop_bindings_and_leave_scope ();
+ --cp_unevaluated_operand;
+ }
+ } s;
+
+ /* Parse the optional parameter list. */
+ if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
+ {
+ parms = cp_parser_requirement_parameter_list (parser);
+ if (parms == error_mark_node)
+ return error_mark_node;
+ }
+ else
+ parms = NULL_TREE;
+
+ /* Parse the requirement body. */
+ reqs = cp_parser_requirement_body (parser);
+ if (reqs == error_mark_node)
+ return error_mark_node;
+ }
+
+ /* This needs to happen after pop_bindings_and_leave_scope, as it reverses
+ the parm chain. */
+ grokparms (parms, &parms);
+ return finish_requires_expr (parms, reqs);
+}
+
+/* Parse a parameterized requirement.
+
+ requirement-parameter-list:
+ '(' parameter-declaration-clause ')' */
+static tree
+cp_parser_requirement_parameter_list (cp_parser *parser)
+{
+ if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+ return error_mark_node;
+
+ tree parms = cp_parser_parameter_declaration_clause (parser);
+
+ if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+ return error_mark_node;
+
+ return parms;
+}
+
+/* Parse the body of a requirement.
+
+ requirement-body:
+ '{' requirement-list '}' */
+static tree
+cp_parser_requirement_body (cp_parser *parser)
+{
+ if (!cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE))
+ return error_mark_node;
+
+ tree reqs = cp_parser_requirement_list (parser);
+
+ if (!cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE))
+ return error_mark_node;
+
+ return reqs;
+}
+
+/* Parse a list of requirements.
+
+ requirement-list:
+ requirement
+ requirement-list ';' requirement[opt] */
+static tree
+cp_parser_requirement_list (cp_parser *parser)
+{
+ tree result = NULL_TREE;
+ while (true)
+ {
+ tree req = cp_parser_requirement (parser);
+ if (req == error_mark_node)
+ return error_mark_node;
+
+ result = tree_cons (NULL_TREE, req, result);
+
+ /* If we see a semi-colon, consume it. */
+ if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+ cp_lexer_consume_token (parser->lexer);
+
+ /* Stop processing at the end of the list. */
+ if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE))
+ break;
+ }
+
+ /* Reverse the order of requirements so they are analyzed in
+ declaration order. */
+ return nreverse (result);
+}
+
+/* Parse a syntactic requirement or type requirement.
+
+ requirement:
+ simple-requirement
+ compound-requirement
+ type-requirement
+ nested-requirement */
+static tree
+cp_parser_requirement (cp_parser *parser)
+{
+ if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+ return cp_parser_compound_requirement (parser);
+ else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TYPENAME))
+ return cp_parser_type_requirement (parser);
+ else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_REQUIRES))
+ return cp_parser_nested_requirement (parser);
+ else
+ return cp_parser_simple_requirement (parser);
+}
+
+/* Parse a simple requirement.
+
+ simple-requirement:
+ expression ';' */
+static tree
+cp_parser_simple_requirement (cp_parser *parser)
+{
+ tree expr = cp_parser_expression (parser, NULL, false, false);
+ if (!expr || expr == error_mark_node)
+ return error_mark_node;
+
+ if (!cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON))
+ return error_mark_node;
+
+ return finish_simple_requirement (expr);
+}
+
+/* Parse a type requirement
+
+ type-requirement
+ nested-name-specifier [opt] required-type-name ';'
+
+ required-type-name:
+ type-name
+ 'template' [opt] simple-template-id */
+static tree
+cp_parser_type_requirement (cp_parser *parser)
+{
+ cp_lexer_consume_token (parser->lexer);
+
+ // Save the scope before parsing name specifiers.
+ tree saved_scope = parser->scope;
+ tree saved_object_scope = parser->object_scope;
+ tree saved_qualifying_scope = parser->qualifying_scope;
+ cp_parser_global_scope_opt (parser, /*current_scope_valid_p=*/true);
+ cp_parser_nested_name_specifier_opt (parser,
+ /*typename_keyword_p=*/true,
+ /*check_dependency_p=*/false,
+ /*type_p=*/true,
+ /*is_declaration=*/false);
+
+ tree type;
+ if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TEMPLATE))
+ {
+ cp_lexer_consume_token (parser->lexer);
+ type = cp_parser_template_id (parser,
+ /*template_keyword_p=*/true,
+ /*check_dependency=*/false,
+ /*tag_type=*/none_type,
+ /*is_declaration=*/false);
+ type = make_typename_type (parser->scope, type, typename_type,
+ /*complain=*/tf_error);
+ }
+ else
+ type = cp_parser_type_name (parser, /*typename_keyword_p=*/true);
+
+ if (TREE_CODE (type) == TYPE_DECL)
+ type = TREE_TYPE (type);
+
+ parser->scope = saved_scope;
+ parser->object_scope = saved_object_scope;
+ parser->qualifying_scope = saved_qualifying_scope;
+
+ if (type == error_mark_node)
+ cp_parser_skip_to_end_of_statement (parser);
+
+ if (!cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON))
+ return error_mark_node;
+ if (type == error_mark_node)
+ return error_mark_node;
+
+ return finish_type_requirement (type);
+}
+
+/* Parse a compound requirement
+
+ compound-requirement:
+ '{' expression '}' 'noexcept' [opt] trailing-return-type [opt] ';' */
+static tree
+cp_parser_compound_requirement (cp_parser *parser)
+{
+ /* Parse an expression enclosed in '{ }'s. */
+ if (!cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE))
+ return error_mark_node;
+
+ tree expr = cp_parser_expression (parser, NULL, false, false);
+ if (!expr || expr == error_mark_node)
+ return error_mark_node;
+
+ if (!cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE))
+ return error_mark_node;
+
+ /* Parse the optional noexcept. */
+ bool noexcept_p = false;
+ if (cp_lexer_next_token_is_keyword (parser->lexer, RID_NOEXCEPT))
+ {
+ cp_lexer_consume_token (parser->lexer);
+ noexcept_p = true;
+ }
+
+ /* Parse the optional trailing return type. */
+ tree type = NULL_TREE;
+ if (cp_lexer_next_token_is (parser->lexer, CPP_DEREF))
+ {
+ cp_lexer_consume_token (parser->lexer);
+ bool saved_result_type_constraint_p = parser->in_result_type_constraint_p;
+ parser->in_result_type_constraint_p = true;
+ type = cp_parser_trailing_type_id (parser);
+ parser->in_result_type_constraint_p = saved_result_type_constraint_p;
+ if (type == error_mark_node)
+ return error_mark_node;
+ }
+
+ return finish_compound_requirement (expr, type, noexcept_p);
+}
+
+/* Parse a nested requirement. This is the same as a requires clause.
+
+ nested-requirement:
+ requires-clause */
+static tree
+cp_parser_nested_requirement (cp_parser *parser)
+{
+ cp_lexer_consume_token (parser->lexer);
+ tree req = cp_parser_requires_clause (parser);
+ if (req == error_mark_node)
+ return error_mark_node;
+ return finish_nested_requirement (req);
+}
+
/* Support Functions */
/* Looks up NAME in the current scope, as given by PARSER->SCOPE.
@@ -23586,87 +24452,29 @@ cp_parser_function_definition_after_declarator (cp_parser* parser,
return fn;
}
-/* Parse a template-declaration, assuming that the `export' (and
- `extern') keywords, if present, has already been scanned. MEMBER_P
- is as for cp_parser_template_declaration. */
+/* Parse a template-declaration body (following argument list). */
static void
-cp_parser_template_declaration_after_export (cp_parser* parser, bool member_p)
+cp_parser_template_declaration_after_parameters (cp_parser* parser,
+ tree parameter_list,
+ bool member_p)
{
tree decl = NULL_TREE;
- vec<deferred_access_check, va_gc> *checks;
- tree parameter_list;
bool friend_p = false;
- bool need_lang_pop;
- cp_token *token;
-
- /* Look for the `template' keyword. */
- token = cp_lexer_peek_token (parser->lexer);
- if (!cp_parser_require_keyword (parser, RID_TEMPLATE, RT_TEMPLATE))
- return;
- /* And the `<'. */
- if (!cp_parser_require (parser, CPP_LESS, RT_LESS))
- return;
- if (at_class_scope_p () && current_function_decl)
- {
- /* 14.5.2.2 [temp.mem]
-
- A local class shall not have member templates. */
- error_at (token->location,
- "invalid declaration of member template in local class");
- cp_parser_skip_to_end_of_block_or_statement (parser);
- return;
- }
- /* [temp]
-
- A template ... shall not have C linkage. */
- if (current_lang_name == lang_name_c)
- {
- error_at (token->location, "template with C linkage");
- /* Give it C++ linkage to avoid confusing other parts of the
- front end. */
- push_lang_context (lang_name_cplusplus);
- need_lang_pop = true;
- }
- else
- need_lang_pop = false;
-
- /* We cannot perform access checks on the template parameter
- declarations until we know what is being declared, just as we
- cannot check the decl-specifier list. */
- push_deferring_access_checks (dk_deferred);
-
- /* If the next token is `>', then we have an invalid
- specialization. Rather than complain about an invalid template
- parameter, issue an error message here. */
- if (cp_lexer_next_token_is (parser->lexer, CPP_GREATER))
- {
- cp_parser_error (parser, "invalid explicit specialization");
- begin_specialization ();
- parameter_list = NULL_TREE;
- }
- else
- {
- /* Parse the template parameters. */
- parameter_list = cp_parser_template_parameter_list (parser);
- }
+ /* We just processed one more parameter list. */
+ ++parser->num_template_parameter_lists;
/* Get the deferred access checks from the parameter list. These
will be checked once we know what is being declared, as for a
member template the checks must be performed in the scope of the
class containing the member. */
- checks = get_deferred_access_checks ();
+ vec<deferred_access_check, va_gc> *checks = get_deferred_access_checks ();
- /* Look for the `>'. */
- cp_parser_skip_to_end_of_template_parameter_list (parser);
- /* We just processed one more parameter list. */
- ++parser->num_template_parameter_lists;
- /* If the next token is `template', there are more template
- parameters. */
- if (cp_lexer_next_token_is_keyword (parser->lexer,
- RID_TEMPLATE))
- cp_parser_template_declaration_after_export (parser, member_p);
+ /* Tentatively parse for a new template parameter list, which can either be
+ the template keyword or a template introduction. */
+ if (cp_parser_template_declaration_after_export (parser, member_p))
+ /* OK */;
else if (cxx_dialect >= cxx11
&& cp_lexer_next_token_is_keyword (parser->lexer, RID_USING))
decl = cp_parser_alias_declaration (parser);
@@ -23675,7 +24483,7 @@ cp_parser_template_declaration_after_export (cp_parser* parser, bool member_p)
/* There are no access checks when parsing a template, as we do not
know if a specialization will be a friend. */
push_deferring_access_checks (dk_no_check);
- token = cp_lexer_peek_token (parser->lexer);
+ cp_token *token = cp_lexer_peek_token (parser->lexer);
decl = cp_parser_single_declaration (parser,
checks,
member_p,
@@ -23750,13 +24558,10 @@ cp_parser_template_declaration_after_export (cp_parser* parser, bool member_p)
decl);
}
}
+
/* Register member declarations. */
if (member_p && !friend_p && decl && !DECL_CLASS_TEMPLATE_P (decl))
finish_member_declaration (decl);
- /* For the erroneous case of a template with C linkage, we pushed an
- implicit C++ linkage scope; exit that scope now. */
- if (need_lang_pop)
- pop_lang_context ();
/* If DECL is a function template, we must return to parse it later.
(Even though there is no definition, there might be default
arguments that need handling.) */
@@ -23765,6 +24570,182 @@ cp_parser_template_declaration_after_export (cp_parser* parser, bool member_p)
vec_safe_push (unparsed_funs_with_definitions, decl);
}
+/* Parse a template introduction header for a template-declaration. Returns
+ false if tentative parse fails. */
+
+static bool
+cp_parser_template_introduction (cp_parser* parser, bool member_p)
+{
+ cp_parser_parse_tentatively (parser);
+
+ tree saved_scope = parser->scope;
+ tree saved_object_scope = parser->object_scope;
+ tree saved_qualifying_scope = parser->qualifying_scope;
+
+ /* Look for the optional `::' operator. */
+ cp_parser_global_scope_opt (parser,
+ /*current_scope_valid_p=*/false);
+ /* Look for the nested-name-specifier. */
+ cp_parser_nested_name_specifier_opt (parser,
+ /*typename_keyword_p=*/false,
+ /*check_dependency_p=*/true,
+ /*type_p=*/false,
+ /*is_declaration=*/false);
+
+ cp_token *token = cp_lexer_peek_token (parser->lexer);
+ tree concept_name = cp_parser_identifier (parser);
+
+ /* Look up the concept for which we will be matching
+ template parameters. */
+ tree tmpl_decl = cp_parser_lookup_name_simple (parser, concept_name,
+ token->location);
+ parser->scope = saved_scope;
+ parser->object_scope = saved_object_scope;
+ parser->qualifying_scope = saved_qualifying_scope;
+
+ if (concept_name == error_mark_node)
+ cp_parser_simulate_error (parser);
+
+ /* Look for opening brace for introduction. */
+ cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE);
+
+ if (!cp_parser_parse_definitely (parser))
+ return false;
+
+ push_deferring_access_checks (dk_deferred);
+
+ /* Build vector of placeholder parameters and grab
+ matching identifiers. */
+ tree introduction_list = cp_parser_introduction_list (parser);
+
+ /* The introduction-list shall not be empty. */
+ int nargs = TREE_VEC_LENGTH (introduction_list);
+ if (nargs == 0)
+ {
+ error ("empty introduction-list");
+ return true;
+ }
+
+ /* Look for closing brace for introduction. */
+ if (!cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE))
+ return true;
+
+ if (tmpl_decl == error_mark_node)
+ {
+ cp_parser_name_lookup_error (parser, concept_name, tmpl_decl, NLE_NULL,
+ token->location);
+ return true;
+ }
+
+ /* Build and associate the constraint. */
+ tree parms = finish_template_introduction (tmpl_decl, introduction_list);
+ if (parms && parms != error_mark_node)
+ {
+ cp_parser_template_declaration_after_parameters (parser, parms,
+ member_p);
+ return true;
+ }
+
+ error_at (token->location, "no matching concept for template-introduction");
+ return true;
+}
+
+/* Parse a normal template-declaration following the template keyword. */
+
+static void
+cp_parser_explicit_template_declaration (cp_parser* parser, bool member_p)
+{
+ tree parameter_list;
+ bool need_lang_pop;
+ location_t location = input_location;
+
+ /* Look for the `<' token. */
+ if (!cp_parser_require (parser, CPP_LESS, RT_LESS))
+ return;
+ if (at_class_scope_p () && current_function_decl)
+ {
+ /* 14.5.2.2 [temp.mem]
+
+ A local class shall not have member templates. */
+ error_at (location,
+ "invalid declaration of member template in local class");
+ cp_parser_skip_to_end_of_block_or_statement (parser);
+ return;
+ }
+ /* [temp]
+
+ A template ... shall not have C linkage. */
+ if (current_lang_name == lang_name_c)
+ {
+ error_at (location, "template with C linkage");
+ /* Give it C++ linkage to avoid confusing other parts of the
+ front end. */
+ push_lang_context (lang_name_cplusplus);
+ need_lang_pop = true;
+ }
+ else
+ need_lang_pop = false;
+
+ /* We cannot perform access checks on the template parameter
+ declarations until we know what is being declared, just as we
+ cannot check the decl-specifier list. */
+ push_deferring_access_checks (dk_deferred);
+
+ /* If the next token is `>', then we have an invalid
+ specialization. Rather than complain about an invalid template
+ parameter, issue an error message here. */
+ if (cp_lexer_next_token_is (parser->lexer, CPP_GREATER))
+ {
+ cp_parser_error (parser, "invalid explicit specialization");
+ begin_specialization ();
+ parameter_list = NULL_TREE;
+ }
+ else
+ {
+ /* Parse the template parameters. */
+ parameter_list = cp_parser_template_parameter_list (parser);
+ }
+
+ /* Look for the `>'. */
+ cp_parser_skip_to_end_of_template_parameter_list (parser);
+
+ /* Manage template requirements */
+ if (flag_concepts)
+ {
+ tree reqs = get_shorthand_constraints (current_template_parms);
+ if (tree r = cp_parser_requires_clause_opt (parser))
+ reqs = conjoin_constraints (reqs, make_predicate_constraint (r));
+ TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs;
+ }
+
+ cp_parser_template_declaration_after_parameters (parser, parameter_list,
+ member_p);
+
+ /* For the erroneous case of a template with C linkage, we pushed an
+ implicit C++ linkage scope; exit that scope now. */
+ if (need_lang_pop)
+ pop_lang_context ();
+}
+
+/* Parse a template-declaration, assuming that the `export' (and
+ `extern') keywords, if present, has already been scanned. MEMBER_P
+ is as for cp_parser_template_declaration. */
+
+static bool
+cp_parser_template_declaration_after_export (cp_parser* parser, bool member_p)
+{
+ if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TEMPLATE))
+ {
+ cp_lexer_consume_token (parser->lexer);
+ cp_parser_explicit_template_declaration (parser, member_p);
+ return true;
+ }
+ else if (flag_concepts)
+ return cp_parser_template_introduction (parser, member_p);
+
+ return false;
+}
+
/* Perform the deferred access checks from a template-parameter-list.
CHECKS is a TREE_LIST of access checks, as returned by
get_deferred_access_checks. */
@@ -23832,6 +24813,12 @@ cp_parser_single_declaration (cp_parser* parser,
{
if (cp_parser_declares_only_class_p (parser))
{
+ // If this is a declaration, but not a definition, associate
+ // any constraints with the type declaration. Constraints
+ // are associated with definitions in cp_parser_class_specifier.
+ if (declares_class_or_enum == 1)
+ associate_classtype_constraints (decl_specifiers.type);
+
decl = shadow_tag (&decl_specifiers);
/* In this case:
@@ -33614,15 +34601,57 @@ tree_type_is_auto_or_concept (const_tree t)
return TREE_TYPE (t) && is_auto_or_concept (TREE_TYPE (t));
}
+/* Returns the template declaration being called or evaluated as
+ part of the constraint check. Note that T must be a predicate
+ constraint (it can't be any other kind of constraint). */
+static tree
+get_concept_from_constraint (tree t)
+{
+ gcc_assert (TREE_CODE (t) == PRED_CONSTR);
+ t = PRED_CONSTR_EXPR (t);
+ gcc_assert (TREE_CODE (t) == CALL_EXPR
+ || TREE_CODE (t) == TEMPLATE_ID_EXPR
+ || VAR_P (t));
+
+ if (TREE_CODE (t) == TEMPLATE_ID_EXPR)
+ return DECL_TEMPLATE_RESULT (TREE_OPERAND (t, 0));
+ if (VAR_P (t))
+ return DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (t));
+ else
+ {
+ tree fn = CALL_EXPR_FN (t);
+ tree ovl = TREE_OPERAND (fn, 0);
+ tree tmpl = OVL_FUNCTION (ovl);
+ return DECL_TEMPLATE_RESULT (tmpl);
+ }
+}
+
/* Add an implicit template type parameter to the CURRENT_TEMPLATE_PARMS
(creating a new template parameter list if necessary). Returns the newly
created template type parm. */
tree
-synthesize_implicit_template_parm (cp_parser *parser)
+synthesize_implicit_template_parm (cp_parser *parser, tree constr)
{
gcc_assert (current_binding_level->kind == sk_function_parms);
+ /* Before committing to modifying any scope, if we're in an
+ implicit template scope, and we're trying to synthesize a
+ constrained parameter, try to find a previous parameter with
+ the same name. This is the same-type rule for abbreviated
+ function templates. */
+ if (parser->implicit_template_scope && constr)
+ {
+ tree t = parser->implicit_template_parms;
+ while (t)
+ {
+ tree c = get_concept_from_constraint (TREE_TYPE (t));
+ if (c == CONSTRAINED_PARM_CONCEPT (constr))
+ return TREE_VALUE (t);
+ t = TREE_CHAIN (t);
+ }
+ }
+
/* We are either continuing a function template that already contains implicit
template parameters, creating a new fully-implicit function template, or
extending an existing explicit function template with implicit template
@@ -33731,14 +34760,18 @@ synthesize_implicit_template_parm (cp_parser *parser)
tree synth_id = make_generic_type_name ();
tree synth_tmpl_parm = finish_template_type_parm (class_type_node,
synth_id);
+
+ // Attach the constraint to the parm before processing.
+ tree node = build_tree_list (NULL_TREE, synth_tmpl_parm);
+ TREE_TYPE (node) = constr;
tree new_parm
= process_template_parm (parser->implicit_template_parms,
input_location,
- build_tree_list (NULL_TREE, synth_tmpl_parm),
+ node,
/*non_type=*/false,
/*param_pack=*/false);
-
+ // Chain the new parameter to the list of implicit parameters.
if (parser->implicit_template_parms)
parser->implicit_template_parms
= TREE_CHAIN (parser->implicit_template_parms);
@@ -33768,6 +34801,15 @@ synthesize_implicit_template_parm (cp_parser *parser)
TREE_VEC_ELT (new_parms, new_parm_idx) = parser->implicit_template_parms;
}
+ // If the new parameter was constrained, we need to add that to the
+ // constraints in the template parameter list.
+ if (tree req = TEMPLATE_PARM_CONSTRAINTS (tree_last (new_parm)))
+ {
+ tree reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms);
+ reqs = conjoin_constraints (reqs, req);
+ TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs;
+ }
+
current_binding_level = entry_scope;
return new_type;
diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h
index 4381bd16261..c7091c8e8e0 100644
--- a/gcc/cp/parser.h
+++ b/gcc/cp/parser.h
@@ -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 */
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index c3bafd3d37c..1b641746531 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -78,6 +78,23 @@ static GTY(()) tree saved_access_scope;
to the EXPR_STMT that is its result. */
static tree cur_stmt_expr;
+// -------------------------------------------------------------------------- //
+// Local Specialization Stack
+//
+// Implementation of the RAII helper for creating new local
+// specializations.
+local_specialization_stack::local_specialization_stack ()
+ : saved (local_specializations)
+{
+ local_specializations = new hash_map<tree, tree>;
+}
+
+local_specialization_stack::~local_specialization_stack ()
+{
+ delete local_specializations;
+ local_specializations = saved;
+}
+
/* True if we've recursed into fn_type_unification too many times. */
static bool excessive_deduction_depth;
@@ -156,8 +173,6 @@ static tree expand_template_argument_pack (tree);
static tree build_template_parm_index (int, int, int, tree, tree);
static bool inline_needs_template_parms (tree, bool);
static void push_inline_template_parms_recursive (tree, int);
-static tree retrieve_local_specialization (tree);
-static void register_local_specialization (tree, tree);
static tree reduce_template_parm_level (tree, tree, int, tree, tsubst_flags_t);
static int mark_template_parm (tree, void *);
static int template_parm_this_level_p (tree, void *);
@@ -196,17 +211,13 @@ static int invalid_nontype_parm_type_p (tree, tsubst_flags_t);
static bool dependent_template_arg_p (tree);
static bool any_template_arguments_need_structural_equality_p (tree);
static bool dependent_type_p_r (tree);
-static tree tsubst_expr (tree, tree, tsubst_flags_t, tree, bool);
static tree tsubst_copy (tree, tree, tsubst_flags_t, tree);
-static tree tsubst_pack_expansion (tree, tree, tsubst_flags_t, tree);
static tree tsubst_decl (tree, tree, tsubst_flags_t);
static void perform_typedefs_access_check (tree tmpl, tree targs);
static void append_type_to_template_for_access_check_1 (tree, tree, tree,
location_t);
static tree listify (tree);
static tree listify_autos (tree, tree);
-static tree template_parm_to_arg (tree t);
-static tree current_template_args (void);
static tree tsubst_template_parm (tree, tree, tsubst_flags_t);
static tree instantiate_alias_template (tree, tree, tsubst_flags_t);
static bool complex_alias_template_p (const_tree tmpl);
@@ -818,6 +829,98 @@ check_explicit_instantiation_namespace (tree spec)
spec, current_namespace, ns);
}
+// Returns the type of a template specialization only if that
+// specialization needs to be defined. Otherwise (e.g., if the type has
+// already been defined), the function returns NULL_TREE.
+static tree
+maybe_new_partial_specialization (tree type)
+{
+ // An implicit instantiation of an incomplete type implies
+ // the definition of a new class template.
+ //
+ // template<typename T>
+ // struct S;
+ //
+ // template<typename T>
+ // struct S<T*>;
+ //
+ // Here, S<T*> is an implicit instantiation of S whose type
+ // is incomplete.
+ if (CLASSTYPE_IMPLICIT_INSTANTIATION (type) && !COMPLETE_TYPE_P (type))
+ return type;
+
+ // It can also be the case that TYPE is a completed specialization.
+ // Continuing the previous example, suppose we also declare:
+ //
+ // template<typename T>
+ // requires Integral<T>
+ // struct S<T*>;
+ //
+ // Here, S<T*> refers to the specialization S<T*> defined
+ // above. However, we need to differentiate definitions because
+ // we intend to define a new partial specialization. In this case,
+ // we rely on the fact that the constraints are different for
+ // this declaration than that above.
+ //
+ // Note that we also get here for injected class names and
+ // late-parsed template definitions. We must ensure that we
+ // do not create new type declarations for those cases.
+ if (flag_concepts && CLASSTYPE_TEMPLATE_SPECIALIZATION (type))
+ {
+ tree tmpl = CLASSTYPE_TI_TEMPLATE (type);
+ tree args = CLASSTYPE_TI_ARGS (type);
+
+ // If there are no template parameters, this cannot be a new
+ // partial template specializtion?
+ if (!current_template_parms)
+ return NULL_TREE;
+
+ // If the constraints are not the same as those of the primary
+ // then, we can probably create a new specialization.
+ tree type_constr = current_template_constraints ();
+
+ if (type == TREE_TYPE (tmpl))
+ if (tree main_constr = get_constraints (tmpl))
+ if (equivalent_constraints (type_constr, main_constr))
+ return NULL_TREE;
+
+ // Also, if there's a pre-existing specialization with matching
+ // constraints, then this also isn't new.
+ tree specs = DECL_TEMPLATE_SPECIALIZATIONS (tmpl);
+ while (specs)
+ {
+ tree spec_tmpl = TREE_VALUE (specs);
+ tree spec_args = TREE_PURPOSE (specs);
+ tree spec_constr = get_constraints (spec_tmpl);
+ if (comp_template_args (args, spec_args)
+ && equivalent_constraints (type_constr, spec_constr))
+ return NULL_TREE;
+ specs = TREE_CHAIN (specs);
+ }
+
+ // Create a new type node (and corresponding type decl)
+ // for the newly declared specialization.
+ tree t = make_class_type (TREE_CODE (type));
+ CLASSTYPE_DECLARED_CLASS (t) = CLASSTYPE_DECLARED_CLASS (type);
+ TYPE_FOR_JAVA (t) = TYPE_FOR_JAVA (type);
+ SET_TYPE_TEMPLATE_INFO (t, build_template_info (tmpl, args));
+
+ /* We only need a separate type node for storing the definition of this
+ partial specialization; uses of S<T*> are unconstrained, so all are
+ equivalent. So keep TYPE_CANONICAL the same. */
+ TYPE_CANONICAL (t) = TYPE_CANONICAL (type);
+
+ // Build the corresponding type decl.
+ tree d = create_implicit_typedef (DECL_NAME (tmpl), t);
+ DECL_CONTEXT (d) = TYPE_CONTEXT (t);
+ DECL_SOURCE_LOCATION (d) = input_location;
+
+ return t;
+ }
+
+ return NULL_TREE;
+}
+
/* The TYPE is being declared. If it is a template type, that means it
is a partial specialization. Do appropriate error-checking. */
@@ -866,19 +969,19 @@ maybe_process_partial_specialization (tree type)
Make sure that `C<int>' and `C<T*>' are implicit instantiations. */
- if (CLASSTYPE_IMPLICIT_INSTANTIATION (type)
- && !COMPLETE_TYPE_P (type))
+ if (tree t = maybe_new_partial_specialization (type))
{
- if (!check_specialization_namespace (CLASSTYPE_TI_TEMPLATE (type))
+ if (!check_specialization_namespace (CLASSTYPE_TI_TEMPLATE (t))
&& !at_namespace_scope_p ())
return error_mark_node;
- SET_CLASSTYPE_TEMPLATE_SPECIALIZATION (type);
- DECL_SOURCE_LOCATION (TYPE_MAIN_DECL (type)) = input_location;
+ SET_CLASSTYPE_TEMPLATE_SPECIALIZATION (t);
+ DECL_SOURCE_LOCATION (TYPE_MAIN_DECL (t)) = input_location;
if (processing_template_decl)
{
- if (push_template_decl (TYPE_MAIN_DECL (type))
- == error_mark_node)
+ tree decl = push_template_decl (TYPE_MAIN_DECL (t));
+ if (decl == error_mark_node)
return error_mark_node;
+ return TREE_TYPE (decl);
}
}
else if (CLASSTYPE_TEMPLATE_INSTANTIATION (type))
@@ -1154,7 +1257,7 @@ retrieve_specialization (tree tmpl, tree args, hashval_t hash)
/* Like retrieve_specialization, but for local declarations. */
-static tree
+tree
retrieve_local_specialization (tree tmpl)
{
if (local_specializations == NULL)
@@ -1546,8 +1649,7 @@ register_specialization (tree spec, tree tmpl, tree args, bool is_friend,
return spec;
}
-/* Returns true iff two spec_entry nodes are equivalent. Only compares the
- TMPL and ARGS members, ignores SPEC. */
+/* Returns true iff two spec_entry nodes are equivalent. */
int comparing_specializations;
@@ -1559,6 +1661,16 @@ spec_hasher::equal (spec_entry *e1, spec_entry *e2)
++comparing_specializations;
equal = (e1->tmpl == e2->tmpl
&& comp_template_args (e1->args, e2->args));
+ if (equal && flag_concepts
+ && VAR_P (DECL_TEMPLATE_RESULT (e1->tmpl))
+ && uses_template_parms (e1->args))
+ {
+ /* Partial specializations of a variable template can be distinguished by
+ constraints. */
+ tree c1 = e1->spec ? get_constraints (e1->spec) : NULL_TREE;
+ tree c2 = e2->spec ? get_constraints (e2->spec) : NULL_TREE;
+ equal = equivalent_constraints (c1, c2);
+ }
--comparing_specializations;
return equal;
@@ -1785,7 +1897,7 @@ reregister_specialization (tree spec, tree tinfo, tree new_spec)
/* Like register_specialization, but for local declarations. We are
registering SPEC, an instantiation of TMPL. */
-static void
+void
register_local_specialization (tree spec, tree tmpl)
{
local_specializations->put (tmpl, spec);
@@ -1863,6 +1975,52 @@ print_candidates (tree fns)
gcc_assert (str == NULL);
}
+/* Get a (possibly) constrained template declaration for the
+ purpose of ordering candidates. */
+static tree
+get_template_for_ordering (tree list)
+{
+ gcc_assert (TREE_CODE (list) == TREE_LIST);
+ tree f = TREE_VALUE (list);
+ if (tree ti = DECL_TEMPLATE_INFO (f))
+ return TI_TEMPLATE (ti);
+ return f;
+}
+
+/* Among candidates having the same signature, return the
+ most constrained or NULL_TREE if there is no best candidate.
+ If the signatures of candidates vary (e.g., template
+ specialization vs. member function), then there can be no
+ most constrained.
+
+ Note that we don't compare constraints on the functions
+ themselves, but rather those of their templates. */
+static tree
+most_constrained_function (tree candidates)
+{
+ // Try to find the best candidate in a first pass.
+ tree champ = candidates;
+ for (tree c = TREE_CHAIN (champ); c; c = TREE_CHAIN (c))
+ {
+ int winner = more_constrained (get_template_for_ordering (champ),
+ get_template_for_ordering (c));
+ if (winner == -1)
+ champ = c; // The candidate is more constrained
+ else if (winner == 0)
+ return NULL_TREE; // Neither is more constrained
+ }
+
+ // Verify that the champ is better than previous candidates.
+ for (tree c = candidates; c != champ; c = TREE_CHAIN (c)) {
+ if (!more_constrained (get_template_for_ordering (champ),
+ get_template_for_ordering (c)))
+ return NULL_TREE;
+ }
+
+ return champ;
+}
+
+
/* Returns the template (one of the functions given by TEMPLATE_ID)
which can be specialized to match the indicated DECL with the
explicit template args given in TEMPLATE_ID. The DECL may be
@@ -1899,6 +2057,7 @@ determine_specialization (tree template_id,
tree targs;
tree explicit_targs;
tree candidates = NULL_TREE;
+
/* A TREE_LIST of templates of which DECL may be a specialization.
The TREE_VALUE of each node is a TEMPLATE_DECL. The
corresponding TREE_PURPOSE is the set of template arguments that,
@@ -1960,7 +2119,8 @@ determine_specialization (tree template_id,
targs = coerce_template_parms (parms, explicit_targs, fns,
tf_warning_or_error,
/*req_all*/true, /*use_defarg*/true);
- templates = tree_cons (targs, fns, templates);
+ if (targs != error_mark_node)
+ templates = tree_cons (targs, fns, templates);
}
else for (; fns; fns = OVL_NEXT (fns))
{
@@ -2035,7 +2195,11 @@ determine_specialization (tree template_id,
/* Function templates cannot be specializations; there are
no partial specializations of functions. Therefore, if
the type of DECL does not match FN, there is no
- match. */
+ match.
+
+ Note that it should never be the case that we have both
+ candidates added here, and for regular member functions
+ below. */
if (tsk == tsk_template)
{
if (compparms (fn_arg_types, decl_arg_types))
@@ -2056,7 +2220,12 @@ determine_specialization (tree template_id,
specialize TMPL will produce DECL. */
continue;
- /* Make sure that the deduced arguments actually work. */
+ /* Remove, from the set of candidates, all those functions
+ whose constraints are not satisfied. */
+ if (flag_concepts && !constraints_satisfied_p (fn, targs))
+ continue;
+
+ // Then, try to form the new function type.
insttype = tsubst (TREE_TYPE (fn), targs, tf_none, NULL_TREE);
if (insttype == error_mark_node)
continue;
@@ -2114,10 +2283,17 @@ determine_specialization (tree template_id,
&& DECL_NONSTATIC_MEMBER_FUNCTION_P (decl))
decl_arg_types = TREE_CHAIN (decl_arg_types);
- if (compparms (TYPE_ARG_TYPES (TREE_TYPE (fn)),
+ if (!compparms (TYPE_ARG_TYPES (TREE_TYPE (fn)),
decl_arg_types))
- /* They match! */
- candidates = tree_cons (NULL_TREE, fn, candidates);
+ continue;
+
+ // If the deduced arguments do not satisfy the constraints,
+ // this is not a candidate.
+ if (flag_concepts && !constraints_satisfied_p (fn))
+ continue;
+
+ // Add the candidate.
+ candidates = tree_cons (NULL_TREE, fn, candidates);
}
}
@@ -2162,6 +2338,19 @@ determine_specialization (tree template_id,
}
}
+ // Concepts allows multiple declarations of member functions
+ // with the same signature. Like above, we need to rely on
+ // on the partial ordering of those candidates to determine which
+ // is the best.
+ if (flag_concepts && candidates && TREE_CHAIN (candidates))
+ {
+ if (tree cand = most_constrained_function (candidates))
+ {
+ candidates = cand;
+ TREE_CHAIN (cand) = NULL_TREE;
+ }
+ }
+
if (templates == NULL_TREE && candidates == NULL_TREE)
{
error ("template-id %qD for %q+D does not match any template "
@@ -2190,6 +2379,10 @@ determine_specialization (tree template_id,
{
tree fn = TREE_VALUE (candidates);
*targs_out = copy_node (DECL_TI_ARGS (fn));
+
+ // Propagate the candidate's constraints to the declaration.
+ set_constraints (decl, get_constraints (fn));
+
/* DECL is a re-declaration or partial instantiation of a template
function. */
if (TREE_CODE (fn) == TEMPLATE_DECL)
@@ -2448,6 +2641,7 @@ check_explicit_specialization (tree declarator,
{
int have_def = flags & 2;
int is_friend = flags & 4;
+ bool is_concept = flags & 8;
int specialization = 0;
int explicit_instantiation = 0;
int member_specialization = 0;
@@ -2523,6 +2717,9 @@ check_explicit_specialization (tree declarator,
/* Fall through. */
case tsk_expl_spec:
+ if (is_concept)
+ error ("explicit specialization declared %<concept%>");
+
if (VAR_P (decl) && TREE_CODE (declarator) != TEMPLATE_ID_EXPR)
/* In cases like template<> constexpr bool v = true;
We'll give an error in check_template_variable. */
@@ -2862,6 +3059,14 @@ check_explicit_specialization (tree declarator,
tree tmpl_func = DECL_TEMPLATE_RESULT (gen_tmpl);
gcc_assert (TREE_CODE (tmpl_func) == FUNCTION_DECL);
+ /* A concept cannot be specialized. */
+ if (DECL_DECLARED_CONCEPT_P (tmpl_func))
+ {
+ error ("explicit specialization of function concept %qD",
+ gen_tmpl);
+ return error_mark_node;
+ }
+
/* This specialization has the same linkage and visibility as
the function template it specializes. */
TREE_PUBLIC (decl) = TREE_PUBLIC (tmpl_func);
@@ -2904,9 +3109,12 @@ check_explicit_specialization (tree declarator,
else if (VAR_P (decl))
DECL_COMDAT (decl) = false;
- /* Register this specialization so that we can find it
- again. */
- decl = register_specialization (decl, gen_tmpl, targs, is_friend, 0);
+ /* If this is a full specialization, register it so that we can find
+ it again. Partial specializations will be registered in
+ process_partial_specialization. */
+ if (!processing_template_decl)
+ decl = register_specialization (decl, gen_tmpl, targs,
+ is_friend, 0);
/* A 'structor should already have clones. */
gcc_assert (decl == error_mark_node
@@ -3789,11 +3997,11 @@ process_template_parm (tree list, location_t parm_loc, tree parm,
bool is_non_type, bool is_parameter_pack)
{
tree decl = 0;
- tree defval;
int idx = 0;
gcc_assert (TREE_CODE (parm) == TREE_LIST);
- defval = TREE_PURPOSE (parm);
+ tree defval = TREE_PURPOSE (parm);
+ tree constr = TREE_TYPE (parm);
if (list)
{
@@ -3890,8 +4098,19 @@ process_template_parm (tree list, location_t parm_loc, tree parm,
}
DECL_ARTIFICIAL (decl) = 1;
SET_DECL_TEMPLATE_PARM_P (decl);
+
+ /* Build requirements for the type/template parameter.
+ This must be done after SET_DECL_TEMPLATE_PARM_P or
+ process_template_parm could fail. */
+ tree reqs = finish_shorthand_constraint (parm, constr);
+
pushdecl (decl);
+
+ /* Build the parameter node linking the parameter declaration,
+ its default argument (if any), and its constraints (if any). */
parm = build_tree_list (defval, parm);
+ TEMPLATE_PARM_CONSTRAINTS (parm) = reqs;
+
return chainon (list, parm);
}
@@ -3926,6 +4145,16 @@ end_template_parm_list (tree parms)
return saved_parmlist;
}
+// Explicitly indicate the end of the template parameter list. We assume
+// that the current template parameters have been constructed and/or
+// managed explicitly, as when creating new template template parameters
+// from a shorthand constraint.
+void
+end_template_parm_list ()
+{
+ --processing_template_parmlist;
+}
+
/* end_template_decl is called after a template declaration is seen. */
void
@@ -3948,7 +4177,7 @@ end_template_decl (void)
functions. Note that If the TREE_LIST contains an error_mark
node, the returned argument is error_mark_node. */
-static tree
+tree
template_parm_to_arg (tree t)
{
@@ -4109,10 +4338,10 @@ maybe_update_decl_type (tree orig_type, tree scope)
}
/* Return a TEMPLATE_DECL corresponding to DECL, using the indicated
- template PARMS. If MEMBER_TEMPLATE_P is true, the new template is
- a member template. Used by push_template_decl below. */
+ template PARMS and constraints, CONSTR. If MEMBER_TEMPLATE_P is true,
+ the new template is a member template. */
-static tree
+tree
build_template_decl (tree decl, tree parms, bool member_template_p)
{
tree tmpl = build_lang_decl (TEMPLATE_DECL, DECL_NAME (decl), NULL_TREE);
@@ -4193,6 +4422,13 @@ process_partial_specialization (tree decl)
gcc_assert (current_template_parms);
+ /* A concept cannot be specialized. */
+ if (flag_concepts && variable_concept_p (maintmpl))
+ {
+ error ("specialization of variable concept %q#D", maintmpl);
+ return error_mark_node;
+ }
+
inner_parms = INNERMOST_TEMPLATE_PARMS (current_template_parms);
ntparms = TREE_VEC_LENGTH (inner_parms);
@@ -4266,9 +4502,19 @@ process_partial_specialization (tree decl)
the implicit argument list of the primary template. */
tree main_args
= TI_ARGS (get_template_info (DECL_TEMPLATE_RESULT (maintmpl)));
- if (comp_template_args (inner_args, INNERMOST_TEMPLATE_ARGS (main_args)))
- error ("partial specialization %qD does not specialize "
- "any template arguments", decl);
+ if (comp_template_args (inner_args, INNERMOST_TEMPLATE_ARGS (main_args))
+ && (!flag_concepts
+ || !subsumes_constraints (current_template_constraints (),
+ get_constraints (maintmpl))))
+ {
+ if (!flag_concepts)
+ error ("partial specialization %q+D does not specialize "
+ "any template arguments", decl);
+ else
+ error ("partial specialization %q+D does not specialize any "
+ "template arguments and is not more constrained than", decl);
+ inform (DECL_SOURCE_LOCATION (maintmpl), "primary template here");
+ }
/* A partial specialization that replaces multiple parameters of the
primary template with a pack expansion is less specialized for those
@@ -4411,6 +4657,7 @@ process_partial_specialization (tree decl)
if (TREE_CODE (decl) == TYPE_DECL)
gcc_assert (!COMPLETE_TYPE_P (type));
+ // Build the template decl.
tree tmpl = build_template_decl (decl, current_template_parms,
DECL_MEMBER_TEMPLATE_P (maintmpl));
TREE_TYPE (tmpl) = type;
@@ -4419,6 +4666,11 @@ process_partial_specialization (tree decl)
DECL_TEMPLATE_INFO (tmpl) = build_template_info (maintmpl, specargs);
DECL_PRIMARY_TEMPLATE (tmpl) = maintmpl;
+ if (VAR_P (decl))
+ /* We didn't register this in check_explicit_specialization so we could
+ wait until the constraints were set. */
+ decl = register_specialization (decl, maintmpl, specargs, false, 0);
+
DECL_TEMPLATE_SPECIALIZATIONS (maintmpl)
= tree_cons (specargs, tmpl,
DECL_TEMPLATE_SPECIALIZATIONS (maintmpl));
@@ -4965,7 +5217,8 @@ push_template_decl_real (tree decl, bool is_friend)
class-type, we must be redeclaring it here. Make sure
that the redeclaration is valid. */
redeclare_class_template (TREE_TYPE (decl),
- current_template_parms);
+ current_template_parms,
+ current_template_constraints ());
/* We don't need to create a new TEMPLATE_DECL; just use the
one we already had. */
tmpl = TYPE_TI_TEMPLATE (TREE_TYPE (decl));
@@ -5212,7 +5465,7 @@ add_inherited_template_parms (tree fn, tree inherited)
template <class T> struct S {}; */
bool
-redeclare_class_template (tree type, tree parms)
+redeclare_class_template (tree type, tree parms, tree cons)
{
tree tmpl;
tree tmpl_parms;
@@ -5320,6 +5573,15 @@ redeclare_class_template (tree type, tree parms)
}
}
+ // Cannot redeclare a class template with a different set of constraints.
+ if (!equivalent_constraints (get_constraints (tmpl), cons))
+ {
+ error_at (input_location, "redeclaration %q#D with different "
+ "constraints", tmpl);
+ inform (DECL_SOURCE_LOCATION (tmpl),
+ "original declaration appeared here");
+ }
+
return true;
}
@@ -6622,6 +6884,51 @@ canonicalize_type_argument (tree arg, tsubst_flags_t complain)
return canon;
}
+// A template declaration can be substituted for a constrained
+// template template parameter only when the argument is more
+// constrained than the parameter.
+static bool
+is_compatible_template_arg (tree parm, tree arg)
+{
+ tree parm_cons = get_constraints (parm);
+
+ /* For now, allow constrained template template arguments
+ and unconstrained template template parameters. */
+ if (parm_cons == NULL_TREE)
+ return true;
+
+ tree arg_cons = get_constraints (arg);
+
+ // If the template parameter is constrained, we need to rewrite its
+ // constraints in terms of the ARG's template parameters. This ensures
+ // that all of the template parameter types will have the same depth.
+ //
+ // Note that this is only valid when coerce_template_template_parm is
+ // true for the innermost template parameters of PARM and ARG. In other
+ // words, because coercion is successful, this conversion will be valid.
+ if (parm_cons)
+ {
+ tree args = template_parms_to_args (DECL_TEMPLATE_PARMS (arg));
+ parm_cons = tsubst_constraint_info (parm_cons,
+ INNERMOST_TEMPLATE_ARGS (args),
+ tf_none, NULL_TREE);
+ if (parm_cons == error_mark_node)
+ return false;
+ }
+
+ return subsumes (parm_cons, arg_cons);
+}
+
+// Convert a placeholder argument into a binding to the original
+// parameter. The original parameter is saved as the TREE_TYPE of
+// ARG.
+static inline tree
+convert_wildcard_argument (tree parm, tree arg)
+{
+ TREE_TYPE (arg) = parm;
+ return arg;
+}
+
/* Convert the indicated template ARG as necessary to match the
indicated template PARM. Returns the converted ARG, or
error_mark_node if the conversion was unsuccessful. Error and
@@ -6644,6 +6951,10 @@ convert_template_argument (tree parm,
if (parm == error_mark_node)
return error_mark_node;
+ /* Trivially convert placeholders. */
+ if (TREE_CODE (arg) == WILDCARD_DECL)
+ return convert_wildcard_argument (parm, arg);
+
if (TREE_CODE (arg) == TREE_LIST
&& TREE_CODE (TREE_VALUE (arg)) == OFFSET_REF)
{
@@ -6811,6 +7122,22 @@ convert_template_argument (tree parm,
val = error_mark_node;
}
+
+ // Check that the constraints are compatible before allowing the
+ // substitution.
+ if (val != error_mark_node)
+ if (!is_compatible_template_arg (parm, arg))
+ {
+ if (in_decl && (complain & tf_error))
+ {
+ error ("constraint mismatch at argument %d in "
+ "template parameter list for %qD",
+ i + 1, in_decl);
+ inform (input_location, " expected %qD but got %qD",
+ parm, arg);
+ }
+ val = error_mark_node;
+ }
}
}
else
@@ -6948,6 +7275,16 @@ coerce_template_parameter_pack (tree parms,
packed_args = make_tree_vec (TREE_VEC_LENGTH (packed_parms));
}
+ /* Check if we have a placeholder pack, which indicates we're
+ in the context of a introduction list. In that case we want
+ to match this pack to the single placeholder. */
+ else if (arg_idx < nargs
+ && TREE_CODE (TREE_VEC_ELT (inner_args, arg_idx)) == WILDCARD_DECL
+ && WILDCARD_PACK_P (TREE_VEC_ELT (inner_args, arg_idx)))
+ {
+ nargs = arg_idx + 1;
+ packed_args = make_tree_vec (1);
+ }
else
packed_args = make_tree_vec (nargs - arg_idx);
@@ -7133,7 +7470,9 @@ coerce_template_parms (tree parms,
}
/* We can't pass a pack expansion to a non-pack parameter of an alias
template (DR 1430). */
- else if (in_decl && DECL_ALIAS_TEMPLATE_P (in_decl)
+ else if (in_decl
+ && (DECL_ALIAS_TEMPLATE_P (in_decl)
+ || concept_template_p (in_decl))
&& variadic_args_p
&& nargs - variadic_args_p < nparms - variadic_p)
{
@@ -7147,8 +7486,14 @@ coerce_template_parms (tree parms,
if (PACK_EXPANSION_P (arg)
&& !template_parameter_pack_p (parm))
{
- error ("pack expansion argument for non-pack parameter "
- "%qD of alias template %qD", parm, in_decl);
+ if (DECL_ALIAS_TEMPLATE_P (in_decl))
+ error_at (location_of (arg),
+ "pack expansion argument for non-pack parameter "
+ "%qD of alias template %qD", parm, in_decl);
+ else
+ error_at (location_of (arg),
+ "pack expansion argument for non-pack parameter "
+ "%qD of concept %qD", parm, in_decl);
inform (DECL_SOURCE_LOCATION (parm), "declared here");
goto found;
}
@@ -7318,6 +7663,30 @@ coerce_template_parms (tree parms,
return new_inner_args;
}
+/* Convert all template arguments to their appropriate types, and
+ return a vector containing the innermost resulting template
+ arguments. If any error occurs, return error_mark_node. Error and
+ warning messages are not issued.
+
+ Note that no function argument deduction is performed, and default
+ arguments are used to fill in unspecified arguments. */
+tree
+coerce_template_parms (tree parms, tree args, tree in_decl)
+{
+ return coerce_template_parms (parms, args, in_decl, tf_none, true, true);
+}
+
+/* Convert all template arguments to their appropriate type, and
+ instantiate default arguments as needed. This returns a vector
+ containing the innermost resulting template arguments, or
+ error_mark_node if unsuccessful. */
+tree
+coerce_template_parms (tree parms, tree args, tree in_decl,
+ tsubst_flags_t complain)
+{
+ return coerce_template_parms (parms, args, in_decl, complain, true, true);
+}
+
/* Like coerce_template_parms. If PARMS represents all template
parameters levels, this function returns a vector of vectors
representing all the resulting argument levels. Note that in this
@@ -7887,6 +8256,22 @@ lookup_template_class_1 (tree d1, tree arglist, tree in_decl, tree context,
if (entry)
return entry->spec;
+ /* If the the template's constraints are not satisfied,
+ then we cannot form a valid type.
+
+ Note that the check is deferred until after the hash
+ lookup. This prevents redundant checks on previously
+ instantiated specializations. */
+ if (flag_concepts && !constraints_satisfied_p (gen_tmpl, arglist))
+ {
+ if (complain & tf_error)
+ {
+ error ("template constraint failure");
+ diagnose_constraints (input_location, gen_tmpl, arglist);
+ }
+ return error_mark_node;
+ }
+
is_dependent_type = uses_template_parms (arglist);
/* If the deduced arguments are invalid, then the binding
@@ -8130,6 +8515,7 @@ lookup_template_class_1 (tree d1, tree arglist, tree in_decl, tree context,
: CLASSTYPE_TI_TEMPLATE (found);
}
+ // Build template info for the new specialization.
SET_TYPE_TEMPLATE_INFO (t, build_template_info (found, arglist));
elt.spec = t;
@@ -8184,14 +8570,17 @@ lookup_template_class (tree d1, tree arglist, tree in_decl, tree context,
return ret;
}
-/* Return a TEMPLATE_ID_EXPR for the given variable template and ARGLIST.
- The type of the expression is the unknown_type_node since the
- template-id could refer to an explicit or partial specialization. */
+/* Return a TEMPLATE_ID_EXPR for the given variable template and ARGLIST. */
tree
lookup_template_variable (tree templ, tree arglist)
{
+ /* The type of the expression is NULL_TREE since the template-id could refer
+ to an explicit or partial specialization. */
tree type = NULL_TREE;
+ if (flag_concepts && variable_concept_p (templ))
+ /* Except that concepts are always bool. */
+ type = boolean_type_node;
return build2 (TEMPLATE_ID_EXPR, type, templ, arglist);
}
@@ -8201,8 +8590,14 @@ tree
finish_template_variable (tree var, tsubst_flags_t complain)
{
tree templ = TREE_OPERAND (var, 0);
-
tree arglist = TREE_OPERAND (var, 1);
+
+ /* We never want to return a VAR_DECL for a variable concept, since they
+ aren't instantiated. In a template, leave the TEMPLATE_ID_EXPR alone. */
+ bool concept_p = flag_concepts && variable_concept_p (templ);
+ if (concept_p && processing_template_decl)
+ return var;
+
tree tmpl_args = DECL_TI_ARGS (DECL_TEMPLATE_RESULT (templ));
arglist = add_outermost_template_args (tmpl_args, arglist);
@@ -8211,6 +8606,29 @@ finish_template_variable (tree var, tsubst_flags_t complain)
/*req_all*/true,
/*use_default*/true);
+ if (flag_concepts && !constraints_satisfied_p (templ, arglist))
+ {
+ if (complain & tf_error)
+ {
+ error ("constraints for %qD not satisfied", templ);
+ diagnose_constraints (location_of (var), templ, arglist);
+ }
+ return error_mark_node;
+ }
+
+ /* If a template-id refers to a specialization of a variable
+ concept, then the expression is true if and only if the
+ concept's constraints are satisfied by the given template
+ arguments.
+
+ NOTE: This is an extension of Concepts Lite TS that
+ allows constraints to be used in expressions. */
+ if (concept_p)
+ {
+ tree decl = DECL_TEMPLATE_RESULT (templ);
+ return evaluate_variable_concept (decl, arglist);
+ }
+
return instantiate_template (templ, arglist, complain);
}
@@ -9013,7 +9431,8 @@ tsubst_friend_class (tree friend_tmpl, tree args)
saved_input_location = input_location;
input_location = DECL_SOURCE_LOCATION (friend_tmpl);
- redeclare_class_template (TREE_TYPE (tmpl), parms);
+ tree cons = get_constraints (tmpl);
+ redeclare_class_template (TREE_TYPE (tmpl), parms, cons);
input_location = saved_input_location;
}
@@ -9831,7 +10250,7 @@ tsubst_template_arg (tree t, tree args, tsubst_flags_t complain, tree in_decl)
instantiated from it at *SPEC_P, return a NONTYPE_ARGUMENT_PACK of them
and set *SPEC_P to point at the next point in the list. */
-static tree
+tree
extract_fnparm_pack (tree tmpl_parm, tree *spec_p)
{
/* Collect all of the extra "packed" parameters into an
@@ -10044,6 +10463,16 @@ gen_elem_of_pack_expansion_instantiation (tree pattern,
/* Expanding a fixed parameter pack from
coerce_template_parameter_pack. */
t = tsubst_decl (pattern, args, complain);
+ else if (pattern == error_mark_node)
+ t = error_mark_node;
+ else if (constraint_p (pattern))
+ {
+ if (processing_template_decl)
+ t = tsubst_constraint (pattern, args, complain, in_decl);
+ else
+ t = (constraints_satisfied_p (pattern, args)
+ ? boolean_true_node : boolean_false_node);
+ }
else if (!TYPE_P (pattern))
t = tsubst_expr (pattern, args, complain, in_decl,
/*integral_constant_expression_p=*/false);
@@ -10108,7 +10537,11 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
}
if (TREE_CODE (parm_pack) == PARM_DECL)
{
- if (PACK_EXPANSION_LOCAL_P (t))
+ /* We know we have correct local_specializations if this
+ expansion is at function scope, or if we're dealing with a
+ local parameter in a requires expression; for the latter,
+ tsubst_requires_expr set it up appropriately. */
+ if (PACK_EXPANSION_LOCAL_P (t) || CONSTRAINT_VAR_P (parm_pack))
arg_pack = retrieve_local_specialization (parm_pack);
else
{
@@ -10788,6 +11221,7 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
gcc_assert (DECL_LANG_SPECIFIC (r) != 0);
DECL_CHAIN (r) = NULL_TREE;
+ // Build new template info linking to the original template decl.
DECL_TEMPLATE_INFO (r) = build_template_info (t, args);
if (TREE_CODE (decl) == TYPE_DECL
@@ -11041,6 +11475,15 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
&& !grok_op_properties (r, /*complain=*/false))
RETURN (error_mark_node);
+ /* When instantiating a constrained member, substitute
+ into the constraints to create a new constraint. */
+ if (tree ci = get_constraints (t))
+ if (member)
+ {
+ ci = tsubst_constraint_info (ci, argvec, complain, NULL_TREE);
+ set_constraints (r, ci);
+ }
+
/* Set up the DECL_TEMPLATE_INFO for R. There's no need to do
this in the special friend case mentioned above where
GEN_TMPL is NULL. */
@@ -12098,6 +12541,12 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
int levels;
tree arg = NULL_TREE;
+ /* Early in template argument deduction substitution, we don't
+ want to reduce the level of 'auto', or it will be confused
+ with a normal template parm in subsequent deduction. */
+ if (is_auto (t) && (complain & tf_partial))
+ return t;
+
r = NULL_TREE;
gcc_assert (TREE_VEC_LENGTH (args) > 0);
@@ -12238,12 +12687,6 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
about the template parameter in question. */
return t;
- /* Early in template argument deduction substitution, we don't
- want to reduce the level of 'auto', or it will be confused
- with a normal template parm in subsequent deduction. */
- if (is_auto (t) && (complain & tf_partial))
- return t;
-
/* If we get here, we must have been looking at a parm for a
more deeply nested template. Make a new version of this
template parameter, but with a lower level. */
@@ -12260,6 +12703,14 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
complain | (code == TEMPLATE_TYPE_PARM
? tf_ignore_bad_quals : 0));
}
+ else if (TREE_CODE (t) == TEMPLATE_TYPE_PARM
+ && PLACEHOLDER_TYPE_CONSTRAINTS (t)
+ && (r = (TEMPLATE_PARM_DESCENDANTS
+ (TEMPLATE_TYPE_PARM_INDEX (t))))
+ && (r = TREE_TYPE (r))
+ && !PLACEHOLDER_TYPE_CONSTRAINTS (r))
+ /* Break infinite recursion when substituting the constraints
+ of a constrained placeholder. */;
else
{
r = copy_type (t);
@@ -12285,6 +12736,12 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
else
TYPE_CANONICAL (r) = canonical_type_parameter (r);
+ /* Propagate constraints on placeholders. */
+ if (TREE_CODE (t) == TEMPLATE_TYPE_PARM)
+ if (tree constr = PLACEHOLDER_TYPE_CONSTRAINTS (t))
+ PLACEHOLDER_TYPE_CONSTRAINTS (r)
+ = tsubst_constraint (constr, args, complain, in_decl);
+
if (code == BOUND_TEMPLATE_TEMPLATE_PARM)
{
tree argvec = tsubst (TYPE_TI_ARGS (t), args,
@@ -13934,7 +14391,7 @@ tsubst_omp_for_iterator (tree t, int i, tree declv, tree initv,
/* Like tsubst_copy for expressions, etc. but also does semantic
processing. */
-static tree
+tree
tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
bool integral_constant_expression_p)
{
@@ -15911,6 +16368,9 @@ tsubst_copy_and_build (tree t,
complain));
}
+ case REQUIRES_EXPR:
+ RETURN (tsubst_requires_expr (t, args, complain, in_decl));
+
default:
/* Handle Objective-C++ constructs, if appropriate. */
{
@@ -19194,6 +19654,16 @@ more_specialized_fn (tree pat1, tree pat2, int len)
processing_template_decl--;
+ /* If both deductions succeed, the partial ordering selects the more
+ constrained template. */
+ if (!lose1 && !lose2)
+ {
+ tree c1 = get_constraints (DECL_TEMPLATE_RESULT (pat1));
+ tree c2 = get_constraints (DECL_TEMPLATE_RESULT (pat2));
+ lose1 = !subsumes_constraints (c1, c2);
+ lose2 = !subsumes_constraints (c2, c1);
+ }
+
/* All things being equal, if the next argument is a pack expansion
for one function but not for the other, prefer the
non-variadic function. FIXME this is bogus; see c++/41958. */
@@ -19262,6 +19732,11 @@ more_specialized_partial_spec (tree tmpl, tree pat1, tree pat2)
}
--processing_template_decl;
+ /* If both deductions succeed, the partial ordering selects the more
+ constrained template. */
+ if (!winner && any_deductions)
+ return more_constrained (tmpl1, tmpl2);
+
/* In the case of a tie where at least one of the templates
has a parameter pack at the end, the template with the most
non-packed parameters wins. */
@@ -19409,6 +19884,34 @@ get_partial_spec_bindings (tree tmpl, tree tparms, tree spec_args, tree args)
return deduced_args;
}
+// Compare two function templates T1 and T2 by deducing bindings
+// from one against the other. If both deductions succeed, compare
+// constraints to see which is more constrained.
+static int
+more_specialized_inst (tree t1, tree t2)
+{
+ int fate = 0;
+ int count = 0;
+
+ if (get_bindings (t1, DECL_TEMPLATE_RESULT (t2), NULL_TREE, true))
+ {
+ --fate;
+ ++count;
+ }
+
+ if (get_bindings (t2, DECL_TEMPLATE_RESULT (t1), NULL_TREE, true))
+ {
+ ++fate;
+ ++count;
+ }
+
+ // If both deductions succeed, then one may be more constrained.
+ if (count == 2 && fate == 0)
+ fate = more_constrained (t1, t2);
+
+ return fate;
+}
+
/* TEMPLATES is a TREE_LIST. Each TREE_VALUE is a TEMPLATE_DECL.
Return the TREE_LIST node with the most specialized template, if
any. If there is no most specialized template, the error_mark_node
@@ -19430,18 +19933,7 @@ most_specialized_instantiation (tree templates)
champ = templates;
for (fn = TREE_CHAIN (templates); fn; fn = TREE_CHAIN (fn))
{
- int fate = 0;
-
- if (get_bindings (TREE_VALUE (champ),
- DECL_TEMPLATE_RESULT (TREE_VALUE (fn)),
- NULL_TREE, /*check_ret=*/true))
- fate--;
-
- if (get_bindings (TREE_VALUE (fn),
- DECL_TEMPLATE_RESULT (TREE_VALUE (champ)),
- NULL_TREE, /*check_ret=*/true))
- fate++;
-
+ int fate = more_specialized_inst (TREE_VALUE (champ), TREE_VALUE (fn));
if (fate == -1)
champ = fn;
else if (!fate)
@@ -19458,17 +19950,13 @@ most_specialized_instantiation (tree templates)
if (champ)
/* Now verify that champ is better than everything earlier in the
instantiation list. */
- for (fn = templates; fn != champ; fn = TREE_CHAIN (fn))
- if (get_bindings (TREE_VALUE (champ),
- DECL_TEMPLATE_RESULT (TREE_VALUE (fn)),
- NULL_TREE, /*check_ret=*/true)
- || !get_bindings (TREE_VALUE (fn),
- DECL_TEMPLATE_RESULT (TREE_VALUE (champ)),
- NULL_TREE, /*check_ret=*/true))
- {
- champ = NULL_TREE;
- break;
- }
+ for (fn = templates; fn != champ; fn = TREE_CHAIN (fn)) {
+ if (more_specialized_inst (TREE_VALUE (champ), TREE_VALUE (fn)) != 1)
+ {
+ champ = NULL_TREE;
+ break;
+ }
+ }
processing_template_decl--;
@@ -19629,8 +20117,15 @@ most_specialized_partial_spec (tree target, tsubst_flags_t complain)
{
if (outer_args)
spec_args = add_to_template_args (outer_args, spec_args);
- list = tree_cons (spec_args, TREE_VALUE (t), list);
- TREE_TYPE (list) = TREE_TYPE (t);
+
+ /* Keep the candidate only if the constraints are satisfied,
+ or if we're not compiling with concepts. */
+ if (!flag_concepts
+ || constraints_satisfied_p (spec_tmpl, spec_args))
+ {
+ list = tree_cons (spec_args, TREE_VALUE (t), list);
+ TREE_TYPE (list) = TREE_TYPE (t);
+ }
}
}
@@ -20308,6 +20803,9 @@ instantiate_decl (tree d, int defer_ok,
functions and static member variables. */
gcc_assert (VAR_OR_FUNCTION_DECL_P (d));
+ /* A concept is never instantiated. */
+ gcc_assert (!DECL_DECLARED_CONCEPT_P (d));
+
/* Variables are never deferred; if instantiation is required, they
are instantiated right away. That allows for better code in the
case that an expression refers to the value of the variable --
@@ -21417,6 +21915,14 @@ value_dependent_expression_p (tree expression)
|| has_value_dependent_address (op));
}
+ case REQUIRES_EXPR:
+ /* Treat all requires-expressions as value-dependent so
+ we don't try to fold them. */
+ return true;
+
+ case TYPE_REQ:
+ return dependent_type_p (TREE_OPERAND (expression, 0));
+
case CALL_EXPR:
{
tree fn = get_callee_fndecl (expression);
@@ -21444,7 +21950,8 @@ value_dependent_expression_p (tree expression)
case TEMPLATE_ID_EXPR:
/* If a TEMPLATE_ID_EXPR involves a dependent name, it will be
type-dependent. */
- return type_dependent_expression_p (expression);
+ return type_dependent_expression_p (expression)
+ || variable_concept_p (TREE_OPERAND (expression, 0));
case CONSTRUCTOR:
{
@@ -21516,7 +22023,9 @@ type_dependent_expression_p (tree expression)
return false;
/* An unresolved name is always dependent. */
- if (identifier_p (expression) || TREE_CODE (expression) == USING_DECL)
+ if (identifier_p (expression)
+ || TREE_CODE (expression) == USING_DECL
+ || TREE_CODE (expression) == WILDCARD_DECL)
return true;
/* Some expression forms are never type-dependent. */
@@ -21529,7 +22038,8 @@ type_dependent_expression_p (tree expression)
|| TREE_CODE (expression) == TYPEID_EXPR
|| TREE_CODE (expression) == DELETE_EXPR
|| TREE_CODE (expression) == VEC_DELETE_EXPR
- || TREE_CODE (expression) == THROW_EXPR)
+ || TREE_CODE (expression) == THROW_EXPR
+ || TREE_CODE (expression) == REQUIRES_EXPR)
return false;
/* The types of these expressions depends only on the type to which
@@ -21779,6 +22289,22 @@ instantiation_dependent_r (tree *tp, int *walk_subtrees,
case BIND_EXPR:
return *tp;
+ /* Treat requires-expressions as dependent. */
+ case REQUIRES_EXPR:
+ return *tp;
+
+ case CALL_EXPR:
+ /* Treat calls to function concepts as dependent. */
+ if (function_concept_check_p (*tp))
+ return *tp;
+ break;
+
+ case TEMPLATE_ID_EXPR:
+ /* And variable concepts. */
+ if (variable_concept_p (TREE_OPERAND (*tp, 0)))
+ return *tp;
+ break;
+
default:
break;
}
@@ -22380,6 +22906,20 @@ listify_autos (tree type, tree auto_node)
tree
do_auto_deduction (tree type, tree init, tree auto_node)
{
+ return do_auto_deduction (type, init, auto_node,
+ tf_warning_or_error,
+ adc_unspecified);
+}
+
+/* Replace occurrences of 'auto' in TYPE with the appropriate type deduced
+ from INIT. AUTO_NODE is the TEMPLATE_TYPE_PARM used for 'auto' in TYPE.
+ The CONTEXT determines the context in which auto deduction is performed
+ and is used to control error diagnostics. */
+
+tree
+do_auto_deduction (tree type, tree init, tree auto_node,
+ tsubst_flags_t complain, auto_deduction_context context)
+{
tree targs;
if (init == error_mark_node)
@@ -22402,11 +22942,14 @@ do_auto_deduction (tree type, tree init, tree auto_node)
init = CONSTRUCTOR_ELT (init, 0)->value;
else
{
- if (permerror (input_location, "direct-list-initialization of "
- "%<auto%> requires exactly one element"))
- inform (input_location,
- "for deduction to %<std::initializer_list%>, use copy-"
- "list-initialization (i.e. add %<=%> before the %<{%>)");
+ if (complain & tf_warning_or_error)
+ {
+ if (permerror (input_location, "direct-list-initialization of "
+ "%<auto%> requires exactly one element"))
+ inform (input_location,
+ "for deduction to %<std::initializer_list%>, use copy-"
+ "list-initialization (i.e. add %<=%> before the %<{%>)");
+ }
type = listify_autos (type, auto_node);
}
}
@@ -22422,7 +22965,8 @@ do_auto_deduction (tree type, tree init, tree auto_node)
= finish_decltype_type (init, id, tf_warning_or_error);
if (type != auto_node)
{
- error ("%qT as type rather than plain %<decltype(auto)%>", type);
+ if (complain & tf_error)
+ error ("%qT as type rather than plain %<decltype(auto)%>", type);
return error_mark_node;
}
}
@@ -22442,7 +22986,8 @@ do_auto_deduction (tree type, tree init, tree auto_node)
if (processing_template_decl)
/* Try again at instantiation time. */
return type;
- if (type && type != error_mark_node)
+ if (type && type != error_mark_node
+ && (complain & tf_error))
/* If type is error_mark_node a diagnostic must have been
emitted by now. Also, having a mention to '<type error>'
in the diagnostic is not really useful to the user. */
@@ -22474,11 +23019,45 @@ do_auto_deduction (tree type, tree init, tree auto_node)
auto_node, TREE_TYPE (auto_node), TREE_VEC_ELT (targs, 0));
return error_mark_node;
}
- TREE_TYPE (auto_node) = TREE_VEC_ELT (targs, 0);
+ if (context != adc_requirement)
+ TREE_TYPE (auto_node) = TREE_VEC_ELT (targs, 0);
+
+ /* Check any placeholder constraints against the deduced type. */
+ if (flag_concepts && !processing_template_decl)
+ if (tree constr = PLACEHOLDER_TYPE_CONSTRAINTS (auto_node))
+ {
+ /* Use the deduced type to check the associated constraints. */
+ if (!constraints_satisfied_p (constr, targs))
+ {
+ if (complain & tf_warning_or_error)
+ {
+ switch (context)
+ {
+ case adc_unspecified:
+ error("placeholder constraints not satisfied");
+ break;
+ case adc_variable_type:
+ error ("deduced initializer does not satisfy "
+ "placeholder constraints");
+ break;
+ case adc_return_type:
+ error ("deduced return type does not satisfy "
+ "placeholder constraints");
+ break;
+ case adc_requirement:
+ error ("deduced expression type does not saatisy "
+ "placeholder constraints");
+ break;
+ }
+ diagnose_constraints (input_location, constr, targs);
+ }
+ return error_mark_node;
+ }
+ }
if (processing_template_decl)
targs = add_to_template_args (current_template_args (), targs);
- return tsubst (type, targs, tf_warning_or_error, NULL_TREE);
+ return tsubst (type, targs, complain, NULL_TREE);
}
/* Substitutes LATE_RETURN_TYPE for 'auto' in TYPE and returns the
@@ -22716,6 +23295,108 @@ convert_generic_types_to_packs (tree parm, int start_idx, int end_idx)
return tsubst (parm, replacement, tf_none, NULL_TREE);
}
+/* Entries in the decl_constraint hash table. */
+struct GTY((for_user)) constr_entry
+{
+ tree decl;
+ tree ci;
+};
+
+/* Hashing function and equality for constraint entries. */
+struct constr_hasher : ggc_ptr_hash<constr_entry>
+{
+ static hashval_t hash (constr_entry *e)
+ {
+ return (hashval_t)DECL_UID (e->decl);
+ }
+
+ static bool equal (constr_entry *e1, constr_entry *e2)
+ {
+ return e1->decl == e2->decl;
+ }
+};
+
+/* A mapping from declarations to constraint information. Note that
+ both templates and their underlying declarations are mapped to the
+ same constraint information.
+
+ FIXME: This is defined in pt.c because garbage collection
+ code is not being generated for constraint.cc. */
+
+static GTY (()) hash_table<constr_hasher> *decl_constraints;
+
+/* Returns true iff cinfo contains a valid set of constraints.
+ This is the case when the associated requirements have been
+ successfully decomposed into lists of atomic constraints.
+ That is, when the saved assumptions are not error_mark_node. */
+
+bool
+valid_constraints_p (tree cinfo)
+{
+ gcc_assert (cinfo);
+ return CI_ASSUMPTIONS (cinfo) != error_mark_node;
+}
+
+/* Returns the template constraints of declaration T. If T is not
+ constrained, return NULL_TREE. Note that T must be non-null. */
+
+tree
+get_constraints (tree t)
+{
+ gcc_assert (DECL_P (t));
+ if (TREE_CODE (t) == TEMPLATE_DECL)
+ t = DECL_TEMPLATE_RESULT (t);
+ constr_entry elt = { t, NULL_TREE };
+ constr_entry* found = decl_constraints->find (&elt);
+ if (found)
+ return found->ci;
+ else
+ return NULL_TREE;
+}
+
+/* Associate the given constraint information CI with the declaration
+ T. If T is a template, then the constraints are associated with
+ its underlying declaration. Don't build associations if CI is
+ NULL_TREE. */
+
+void
+set_constraints (tree t, tree ci)
+{
+ if (!ci)
+ return;
+ gcc_assert (t);
+ if (TREE_CODE (t) == TEMPLATE_DECL)
+ t = DECL_TEMPLATE_RESULT (t);
+ gcc_assert (!get_constraints (t));
+ constr_entry elt = {t, ci};
+ constr_entry** slot = decl_constraints->find_slot (&elt, INSERT);
+ constr_entry* entry = ggc_alloc<constr_entry> ();
+ *entry = elt;
+ *slot = entry;
+}
+
+/* Remove the associated constraints of the declaration T. */
+
+void
+remove_constraints (tree t)
+{
+ gcc_assert (DECL_P (t));
+ if (TREE_CODE (t) == TEMPLATE_DECL)
+ t = DECL_TEMPLATE_RESULT (t);
+
+ constr_entry elt = {t, NULL_TREE};
+ constr_entry** slot = decl_constraints->find_slot (&elt, NO_INSERT);
+ if (slot)
+ decl_constraints->clear_slot (slot);
+}
+
+/* Set up the hash table for constraint association. */
+
+void
+init_constraint_processing (void)
+{
+ decl_constraints = hash_table<constr_hasher>::create_ggc(37);
+}
/* Set up the hash tables for template instantiations. */
diff --git a/gcc/cp/ptree.c b/gcc/cp/ptree.c
index 8abdb729ced..b0281df19b9 100644
--- a/gcc/cp/ptree.c
+++ b/gcc/cp/ptree.c
@@ -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);
diff --git a/gcc/cp/search.c b/gcc/cp/search.c
index 1176b3e6f4e..90cd2438b36 100644
--- a/gcc/cp/search.c
+++ b/gcc/cp/search.c
@@ -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);
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 17b0a1457b4..7bbae06f717 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -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 ();
}
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 46818b43e81..987ebe8e2aa 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -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;
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 5d754f1228a..83fd34ca80a 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -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;
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 2a47943574e..64d9a6ab0da 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -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
diff --git a/gcc/testsuite/g++.dg/concepts/alias1.C b/gcc/testsuite/g++.dg/concepts/alias1.C
new file mode 100644
index 00000000000..03b3ceae928
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/alias1.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/concepts/alias2.C b/gcc/testsuite/g++.dg/concepts/alias2.C
new file mode 100644
index 00000000000..d81188ed435
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/alias2.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/concepts/alias3.C b/gcc/testsuite/g++.dg/concepts/alias3.C
new file mode 100644
index 00000000000..e6ab66976a1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/alias3.C
@@ -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" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/alias4.C b/gcc/testsuite/g++.dg/concepts/alias4.C
new file mode 100644
index 00000000000..4227a44c042
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/alias4.C
@@ -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 *-*-* } }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/class.C b/gcc/testsuite/g++.dg/concepts/class.C
new file mode 100644
index 00000000000..ea74a54da6c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/class.C
@@ -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() { }
diff --git a/gcc/testsuite/g++.dg/concepts/class1.C b/gcc/testsuite/g++.dg/concepts/class1.C
new file mode 100644
index 00000000000..b213cb58b7e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/class1.C
@@ -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() { }
diff --git a/gcc/testsuite/g++.dg/concepts/class2.C b/gcc/testsuite/g++.dg/concepts/class2.C
new file mode 100644
index 00000000000..2c3ea4483ab
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/class2.C
@@ -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() { }
diff --git a/gcc/testsuite/g++.dg/concepts/class3.C b/gcc/testsuite/g++.dg/concepts/class3.C
new file mode 100644
index 00000000000..e3a1d2a949b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/class3.C
@@ -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() { }
diff --git a/gcc/testsuite/g++.dg/concepts/class4.C b/gcc/testsuite/g++.dg/concepts/class4.C
new file mode 100644
index 00000000000..7ba82508552
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/class4.C
@@ -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() { }
diff --git a/gcc/testsuite/g++.dg/concepts/class5.C b/gcc/testsuite/g++.dg/concepts/class5.C
new file mode 100644
index 00000000000..903bf249955
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/class5.C
@@ -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() { }
diff --git a/gcc/testsuite/g++.dg/concepts/class6.C b/gcc/testsuite/g++.dg/concepts/class6.C
new file mode 100644
index 00000000000..fe6b42d5557
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/class6.C
@@ -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() { }
diff --git a/gcc/testsuite/g++.dg/concepts/constrained-parm.C b/gcc/testsuite/g++.dg/concepts/constrained-parm.C
new file mode 100644
index 00000000000..fd21c43a037
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/constrained-parm.C
@@ -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" }
diff --git a/gcc/testsuite/g++.dg/concepts/decl-diagnose.C b/gcc/testsuite/g++.dg/concepts/decl-diagnose.C
new file mode 100644
index 00000000000..67b56f962d0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/decl-diagnose.C
@@ -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" }
+};
diff --git a/gcc/testsuite/g++.dg/concepts/deduction-constraint1.C b/gcc/testsuite/g++.dg/concepts/deduction-constraint1.C
new file mode 100644
index 00000000000..6ff3be94ba9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/deduction-constraint1.C
@@ -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);
diff --git a/gcc/testsuite/g++.dg/concepts/disjunction1.C b/gcc/testsuite/g++.dg/concepts/disjunction1.C
new file mode 100644
index 00000000000..f67fa0beacb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/disjunction1.C
@@ -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();
diff --git a/gcc/testsuite/g++.dg/concepts/dr1430.C b/gcc/testsuite/g++.dg/concepts/dr1430.C
new file mode 100644
index 00000000000..7f857fe4ecc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/dr1430.C
@@ -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 "" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/equiv.C b/gcc/testsuite/g++.dg/concepts/equiv.C
new file mode 100644
index 00000000000..c2ac7410f01
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/equiv.C
@@ -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>() { }
diff --git a/gcc/testsuite/g++.dg/concepts/equiv2.C b/gcc/testsuite/g++.dg/concepts/equiv2.C
new file mode 100644
index 00000000000..675fe2105f1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/equiv2.C
@@ -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) { }
diff --git a/gcc/testsuite/g++.dg/concepts/explicit-inst1.C b/gcc/testsuite/g++.dg/concepts/explicit-inst1.C
new file mode 100644
index 00000000000..3079ca50f64
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/explicit-inst1.C
@@ -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() { }
diff --git a/gcc/testsuite/g++.dg/concepts/explicit-inst2.C b/gcc/testsuite/g++.dg/concepts/explicit-inst2.C
new file mode 100644
index 00000000000..5e75f4ff631
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/explicit-inst2.C
@@ -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();
+}
diff --git a/gcc/testsuite/g++.dg/concepts/explicit-inst3.C b/gcc/testsuite/g++.dg/concepts/explicit-inst3.C
new file mode 100644
index 00000000000..a471657a750
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/explicit-inst3.C
@@ -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() { }
diff --git a/gcc/testsuite/g++.dg/concepts/explicit-inst4.C b/gcc/testsuite/g++.dg/concepts/explicit-inst4.C
new file mode 100644
index 00000000000..b075c1009e8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/explicit-inst4.C
@@ -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() { }
diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec1.C b/gcc/testsuite/g++.dg/concepts/explicit-spec1.C
new file mode 100644
index 00000000000..6316410b950
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/explicit-spec1.C
@@ -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);
+}
diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec2.C b/gcc/testsuite/g++.dg/concepts/explicit-spec2.C
new file mode 100644
index 00000000000..4f196243717
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/explicit-spec2.C
@@ -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();
+}
diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec3.C b/gcc/testsuite/g++.dg/concepts/explicit-spec3.C
new file mode 100644
index 00000000000..29546b3b37c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/explicit-spec3.C
@@ -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() { }
diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec4.C b/gcc/testsuite/g++.dg/concepts/explicit-spec4.C
new file mode 100644
index 00000000000..e9aacd51bbb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/explicit-spec4.C
@@ -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);
+}
diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec5.C b/gcc/testsuite/g++.dg/concepts/explicit-spec5.C
new file mode 100644
index 00000000000..8047278aa77
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/explicit-spec5.C
@@ -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" }
diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec6.C b/gcc/testsuite/g++.dg/concepts/explicit-spec6.C
new file mode 100644
index 00000000000..3eba9ff7bfd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/explicit-spec6.C
@@ -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);
diff --git a/gcc/testsuite/g++.dg/concepts/expression.C b/gcc/testsuite/g++.dg/concepts/expression.C
new file mode 100644
index 00000000000..5ae04e43a12
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/expression.C
@@ -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);
+}
diff --git a/gcc/testsuite/g++.dg/concepts/expression2.C b/gcc/testsuite/g++.dg/concepts/expression2.C
new file mode 100644
index 00000000000..40c2034764a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/expression2.C
@@ -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" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/expression3.C b/gcc/testsuite/g++.dg/concepts/expression3.C
new file mode 100644
index 00000000000..eb8406f13b0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/expression3.C
@@ -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" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/feature-macro.C b/gcc/testsuite/g++.dg/concepts/feature-macro.C
new file mode 100644
index 00000000000..7bc787559ec
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/feature-macro.C
@@ -0,0 +1,5 @@
+// { dg-options -std=c++1z }
+
+#ifndef __cpp_concepts
+#error __cpp_concepts not defined
+#endif
diff --git a/gcc/testsuite/g++.dg/concepts/fn-concept1.C b/gcc/testsuite/g++.dg/concepts/fn-concept1.C
new file mode 100644
index 00000000000..385dcbc13cf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/fn-concept1.C
@@ -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&);
diff --git a/gcc/testsuite/g++.dg/concepts/fn-concept2.C b/gcc/testsuite/g++.dg/concepts/fn-concept2.C
new file mode 100644
index 00000000000..092c91c5b84
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/fn-concept2.C
@@ -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" }
diff --git a/gcc/testsuite/g++.dg/concepts/fn1.C b/gcc/testsuite/g++.dg/concepts/fn1.C
new file mode 100644
index 00000000000..b2bdaf91885
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/fn1.C
@@ -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);
+}
diff --git a/gcc/testsuite/g++.dg/concepts/fn10.C b/gcc/testsuite/g++.dg/concepts/fn10.C
new file mode 100644
index 00000000000..f4cd4c5ce34
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/fn10.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/concepts/fn2.C b/gcc/testsuite/g++.dg/concepts/fn2.C
new file mode 100644
index 00000000000..0aee852ce38
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/fn2.C
@@ -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" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/fn3.C b/gcc/testsuite/g++.dg/concepts/fn3.C
new file mode 100644
index 00000000000..06402e02c3b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/fn3.C
@@ -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);
+}
diff --git a/gcc/testsuite/g++.dg/concepts/fn4.C b/gcc/testsuite/g++.dg/concepts/fn4.C
new file mode 100644
index 00000000000..5ced6a7f390
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/fn4.C
@@ -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" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/fn5.C b/gcc/testsuite/g++.dg/concepts/fn5.C
new file mode 100644
index 00000000000..b3c3f70a878
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/fn5.C
@@ -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" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/fn6.C b/gcc/testsuite/g++.dg/concepts/fn6.C
new file mode 100644
index 00000000000..73ef19a20a7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/fn6.C
@@ -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() { }
diff --git a/gcc/testsuite/g++.dg/concepts/fn7.C b/gcc/testsuite/g++.dg/concepts/fn7.C
new file mode 100644
index 00000000000..2abd34a1e78
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/fn7.C
@@ -0,0 +1,8 @@
+// { dg-do link }
+// { dg-options "-std=c++1z" }
+
+// FIXME: What is this actually testing?
+
+void f() requires true { }
+
+int main() { }
diff --git a/gcc/testsuite/g++.dg/concepts/fn8.C b/gcc/testsuite/g++.dg/concepts/fn8.C
new file mode 100644
index 00000000000..71141f6ff55
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/fn8.C
@@ -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" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/fn9.C b/gcc/testsuite/g++.dg/concepts/fn9.C
new file mode 100644
index 00000000000..b7ac4e10a8d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/fn9.C
@@ -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);
+}
diff --git a/gcc/testsuite/g++.dg/concepts/friend1.C b/gcc/testsuite/g++.dg/concepts/friend1.C
new file mode 100644
index 00000000000..286e7697c73
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/friend1.C
@@ -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" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/friend2.C b/gcc/testsuite/g++.dg/concepts/friend2.C
new file mode 100644
index 00000000000..38b230c4ff7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/friend2.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/concepts/generic-fn-err.C b/gcc/testsuite/g++.dg/concepts/generic-fn-err.C
new file mode 100644
index 00000000000..c6b74571954
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/generic-fn-err.C
@@ -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" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/generic-fn.C b/gcc/testsuite/g++.dg/concepts/generic-fn.C
new file mode 100644
index 00000000000..778356db180
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/generic-fn.C
@@ -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; }
diff --git a/gcc/testsuite/g++.dg/concepts/inherit-ctor1.C b/gcc/testsuite/g++.dg/concepts/inherit-ctor1.C
new file mode 100644
index 00000000000..29433ade7b4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/inherit-ctor1.C
@@ -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);
+}
diff --git a/gcc/testsuite/g++.dg/concepts/inherit-ctor2.C b/gcc/testsuite/g++.dg/concepts/inherit-ctor2.C
new file mode 100644
index 00000000000..4f39203db4c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/inherit-ctor2.C
@@ -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" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/inherit-ctor3.C b/gcc/testsuite/g++.dg/concepts/inherit-ctor3.C
new file mode 100644
index 00000000000..3d0ddf210d1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/inherit-ctor3.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/concepts/inherit-ctor4.C b/gcc/testsuite/g++.dg/concepts/inherit-ctor4.C
new file mode 100644
index 00000000000..cd9565f660f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/inherit-ctor4.C
@@ -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" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/intro1.C b/gcc/testsuite/g++.dg/concepts/intro1.C
new file mode 100644
index 00000000000..1b5f5d14b7e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/intro1.C
@@ -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>
+ {
+ }
diff --git a/gcc/testsuite/g++.dg/concepts/intro2.C b/gcc/testsuite/g++.dg/concepts/intro2.C
new file mode 100644
index 00000000000..91a1cacf9c0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/intro2.C
@@ -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); }
diff --git a/gcc/testsuite/g++.dg/concepts/intro3.C b/gcc/testsuite/g++.dg/concepts/intro3.C
new file mode 100644
index 00000000000..5dd95c698e1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/intro3.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/concepts/intro4.C b/gcc/testsuite/g++.dg/concepts/intro4.C
new file mode 100644
index 00000000000..6d8aec3ff61
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/intro4.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/concepts/intro5.C b/gcc/testsuite/g++.dg/concepts/intro5.C
new file mode 100644
index 00000000000..64771cd6e25
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/intro5.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/concepts/intro6.C b/gcc/testsuite/g++.dg/concepts/intro6.C
new file mode 100644
index 00000000000..4e168ef3c7d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/intro6.C
@@ -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>(); }
diff --git a/gcc/testsuite/g++.dg/concepts/intro7.C b/gcc/testsuite/g++.dg/concepts/intro7.C
new file mode 100644
index 00000000000..d92eafcfbd5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/intro7.C
@@ -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>;
+};
diff --git a/gcc/testsuite/g++.dg/concepts/member-concept.C b/gcc/testsuite/g++.dg/concepts/member-concept.C
new file mode 100644
index 00000000000..46404e3ad4f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/member-concept.C
@@ -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" }
+};
diff --git a/gcc/testsuite/g++.dg/concepts/memfun-err.C b/gcc/testsuite/g++.dg/concepts/memfun-err.C
new file mode 100644
index 00000000000..a62fe5ef220
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/memfun-err.C
@@ -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" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/memfun.C b/gcc/testsuite/g++.dg/concepts/memfun.C
new file mode 100644
index 00000000000..a96c0181653
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/memfun.C
@@ -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; }
diff --git a/gcc/testsuite/g++.dg/concepts/partial-concept-id1.C b/gcc/testsuite/g++.dg/concepts/partial-concept-id1.C
new file mode 100644
index 00000000000..114c12f85bc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/partial-concept-id1.C
@@ -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);
+}
diff --git a/gcc/testsuite/g++.dg/concepts/partial-concept-id2.C b/gcc/testsuite/g++.dg/concepts/partial-concept-id2.C
new file mode 100644
index 00000000000..fb754483db4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/partial-concept-id2.C
@@ -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;
diff --git a/gcc/testsuite/g++.dg/concepts/partial-spec.C b/gcc/testsuite/g++.dg/concepts/partial-spec.C
new file mode 100644
index 00000000000..fe612d4ada0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/partial-spec.C
@@ -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() { }
diff --git a/gcc/testsuite/g++.dg/concepts/partial-spec2.C b/gcc/testsuite/g++.dg/concepts/partial-spec2.C
new file mode 100644
index 00000000000..db7545586a9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/partial-spec2.C
@@ -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*>,"");
diff --git a/gcc/testsuite/g++.dg/concepts/partial-spec3.C b/gcc/testsuite/g++.dg/concepts/partial-spec3.C
new file mode 100644
index 00000000000..d73d7879a30
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/partial-spec3.C
@@ -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;
diff --git a/gcc/testsuite/g++.dg/concepts/partial-spec4.C b/gcc/testsuite/g++.dg/concepts/partial-spec4.C
new file mode 100644
index 00000000000..d4b8e9c92f7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/partial-spec4.C
@@ -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();
+}
diff --git a/gcc/testsuite/g++.dg/concepts/partial-spec5.C b/gcc/testsuite/g++.dg/concepts/partial-spec5.C
new file mode 100644
index 00000000000..dcf167bc4ae
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/partial-spec5.C
@@ -0,0 +1,12 @@
+// PR c++/67138
+// { dg-options -std=c++1z }
+
+template <class T>
+concept bool _Auto = true;
+
+template <_Auto T>
+struct test {};
+
+template <_Auto T>
+ requires requires (T t) { t + t; }
+struct test<T> {};
diff --git a/gcc/testsuite/g++.dg/concepts/placeholder1.C b/gcc/testsuite/g++.dg/concepts/placeholder1.C
new file mode 100644
index 00000000000..0b5a7cf2785
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/placeholder1.C
@@ -0,0 +1,34 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T, typename U>
+struct is_same
+{
+ static constexpr bool value = false;
+};
+
+template<typename T>
+struct is_same<T, T>
+{
+ static constexpr bool value = true;
+};
+
+template<class T, class U>
+concept bool Same = is_same<T, U>::value;
+
+template<typename T>
+concept bool C1 = true;
+
+template<typename T, typename U>
+concept bool C2 = true;
+
+template<typename T>
+concept bool C3() { return true; }
+
+template<typename T, typename U>
+concept bool C4() { return true; }
+
+C1 c1 = 0;
+C2<int> c2 = 0;
+C3 c3 = 0;
+C4<int> c4 = 0;
+Same<int> s1 = 'a'; // { dg-error "does not satisfy|is_same" }
diff --git a/gcc/testsuite/g++.dg/concepts/placeholder2.C b/gcc/testsuite/g++.dg/concepts/placeholder2.C
new file mode 100644
index 00000000000..8165c8134f9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/placeholder2.C
@@ -0,0 +1,32 @@
+// { dg-options "-std=c++1z" }
+
+// Check argument deduction constraints.
+// TODO: We shoul have more of these...
+
+template<typename T>
+concept bool C1 = sizeof(T) == 0;
+
+template<typename T, typename U>
+concept bool C2 = __is_same_as(T, U);
+
+
+template<typename T>
+concept bool D1()
+{
+ return requires (T t) { { t } -> C1; };
+}
+
+template<typename T>
+concept bool D2()
+{
+ return requires (T t) { { t } -> C2<void>; };
+}
+
+void f1(D1) { }
+void f2(D2) { }
+
+int main()
+{
+ f1(0); // { dg-error "cannot call" }
+ f2(0); // { dg-error "cannot call" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/placeholder3.C b/gcc/testsuite/g++.dg/concepts/placeholder3.C
new file mode 100644
index 00000000000..bd89551aa5b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/placeholder3.C
@@ -0,0 +1,17 @@
+// PR c++/66218
+// { dg-options "-std=c++1z" }
+
+template <class T, class U>
+concept bool Same = __is_same_as(T, U);
+
+template <class T>
+concept bool C =
+ requires {
+ { 0 } -> Same<T>;
+ };
+
+template <C c>
+constexpr bool f() { return true; }
+
+static_assert(f<double>(), ""); // { dg-error "" }
+static_assert(f<int>(), "");
diff --git a/gcc/testsuite/g++.dg/concepts/placeholder4.C b/gcc/testsuite/g++.dg/concepts/placeholder4.C
new file mode 100644
index 00000000000..e34fc94e6cf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/placeholder4.C
@@ -0,0 +1,20 @@
+// PR c++/66218
+// { dg-options "-std=c++1z" }
+
+template <class T, class U>
+concept bool Same = __is_same_as(T, U);
+
+template <class T>
+concept bool C =
+ requires {
+ { 0 } -> Same<T>;
+ };
+
+template <class T>
+struct A {
+ template <T t, C c>
+ constexpr static bool f() { return true; }
+};
+
+static_assert(A<int>::f<1,double>(), ""); // { dg-error "" }
+static_assert(A<char>::f<'a',int>(), "");
diff --git a/gcc/testsuite/g++.dg/concepts/placeholder5.C b/gcc/testsuite/g++.dg/concepts/placeholder5.C
new file mode 100644
index 00000000000..6aaf57b3287
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/placeholder5.C
@@ -0,0 +1,17 @@
+// { dg-options "-std=c++1z" }
+
+template <class T, class U>
+concept bool Same = __is_same_as(T, U);
+
+const int i = 0;
+template <class T>
+concept bool C =
+ requires {
+ { &i } -> const Same<T>*;
+ };
+
+template <C c>
+constexpr bool f() { return true; }
+
+static_assert(f<double>(), ""); // { dg-error "" }
+static_assert(f<int>(), "");
diff --git a/gcc/testsuite/g++.dg/concepts/pr65552.C b/gcc/testsuite/g++.dg/concepts/pr65552.C
new file mode 100644
index 00000000000..c72e6c638f5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/pr65552.C
@@ -0,0 +1,19 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+concept bool Concept() {
+ return requires () {
+ typename T::member_type1;
+ typename T::member_type2;
+ };
+}
+
+struct model {
+ using member_type1 = int;
+ using member_type2 = int;
+};
+
+template<Concept C>
+struct S {};
+
+S<model> s;
diff --git a/gcc/testsuite/g++.dg/concepts/pr65575.C b/gcc/testsuite/g++.dg/concepts/pr65575.C
new file mode 100644
index 00000000000..fa24b7be0e0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/pr65575.C
@@ -0,0 +1,23 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+concept bool C = false;
+
+int f1() requires false;
+int& f2() requires false;
+int* f3() requires false;
+auto f4() -> int& requires false;
+auto f5() -> int* requires false;
+auto f6() -> int requires false;
+
+int (*p)() requires true; // { dg-error "" }
+int (&p)() requires true; // { dg-error "" }
+int g(int (*)() requires true); // { dg-error "" }
+
+int f() { }
+
+int
+main()
+{
+ f1(); // { dg-error "cannot call" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/pr65634.C b/gcc/testsuite/g++.dg/concepts/pr65634.C
new file mode 100644
index 00000000000..31dc87aa6c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/pr65634.C
@@ -0,0 +1,19 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+concept bool C1() {
+ return requires () {
+ { T::smf() } noexcept;
+ };
+}
+struct M1 {
+ static void smf() noexcept;
+};
+template<typename T>
+concept bool C2() {
+ return C1<typename T::type>();
+}
+struct M2 {
+ using type = M1;
+};
+static_assert(C2<M2>(), "");
diff --git a/gcc/testsuite/g++.dg/concepts/pr65636.C b/gcc/testsuite/g++.dg/concepts/pr65636.C
new file mode 100644
index 00000000000..8b6f2a1ad6e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/pr65636.C
@@ -0,0 +1,10 @@
+// { dg-options "-std=c++1z" }
+
+using TD = int;
+
+template<typename T>
+concept bool C() {
+ return requires () { typename TD; };
+}
+
+static_assert(C<int>(), "");
diff --git a/gcc/testsuite/g++.dg/concepts/pr65681.C b/gcc/testsuite/g++.dg/concepts/pr65681.C
new file mode 100644
index 00000000000..bdbbd66e747
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/pr65681.C
@@ -0,0 +1,106 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+concept bool C()
+{
+ return requires (T t) { t.mf(); };
+}
+
+template<typename T>
+concept bool CA1()
+{
+ return C<typename T::ca1_type>();
+}
+
+template<typename T>
+concept bool CA2()
+{
+ return CA1<T>() && requires () { typename T::ca2_type; };
+}
+
+template<typename T>
+concept bool CA3()
+{
+ return CA2<T>() && requires () { typename T::ca3_type; };
+}
+
+template<typename T>
+concept bool CB1()
+{
+ return requires () { typename T::cb1_type; };
+}
+
+template<typename T>
+concept bool CB2()
+{
+ return CB1<T>() && requires () { typename T::cb2_type; };
+}
+
+template<typename T>
+concept bool CB3()
+{
+ return CB2<T>() && requires () { typename T::cb3_type; };
+}
+
+
+struct MC { void mf(); };
+static_assert(C<MC>(), "");
+
+
+struct MA1 { using ca1_type = MC; };
+struct MA2 : MA1 { using ca2_type = int; };
+struct MA3 : MA2 { using ca3_type = int; };
+static_assert(CA1<MA1>(), "");
+static_assert(CA2<MA2>(), "");
+static_assert(CA3<MA3>(), "");
+
+struct MB1 { using cb1_type = int; };
+struct MB2 : MB1 { using cb2_type = int; };
+struct MB3 : MB2 { using cb3_type = int; };
+static_assert(CB1<MB1>(), "");
+static_assert(CB2<MB2>(), "");
+static_assert(CB3<MB3>(), "");
+
+
+template<typename T1, typename T2>
+struct S;
+
+template<CA1 T1, CB1 T2>
+struct S<T1, T2> // Specialization #1
+{
+ static constexpr int value = 1;
+};
+
+template<CA1 T1, CB2 T2>
+ requires !CA2<T1>()
+struct S<T1, T2> // Specialization #2
+{
+ static constexpr int value = 2;
+};
+
+template<CA2 T1, CB3 T2>
+ requires !CA3<T1>()
+struct S<T1, T2> // Specialization #3
+{
+ static constexpr int value = 3;
+};
+
+S<MA1,MB1> s11;
+S<MA1,MB2> s12;
+S<MA1,MB3> s13;
+S<MA2,MB1> s21;
+S<MA2,MB2> s22;
+S<MA2,MB3> s23;
+S<MA3,MB1> s31;
+S<MA3,MB2> s32;
+S<MA3,MB3> s33;
+
+static_assert(S<MA1,MB1>::value == 1, "");
+static_assert(S<MA1,MB2>::value == 2, "");
+static_assert(S<MA1,MB3>::value == 2, "");
+static_assert(S<MA2,MB1>::value == 1, "");
+static_assert(S<MA2,MB2>::value == 1, "");
+static_assert(S<MA2,MB3>::value == 3, "");
+static_assert(S<MA3,MB1>::value == 1, "");
+static_assert(S<MA3,MB2>::value == 1, "");
+static_assert(S<MA3,MB3>::value == 1, "");
diff --git a/gcc/testsuite/g++.dg/concepts/pr65848.C b/gcc/testsuite/g++.dg/concepts/pr65848.C
new file mode 100644
index 00000000000..d0bb6a2afb8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/pr65848.C
@@ -0,0 +1,75 @@
+// { dg-options "-std=c++1z" }
+
+// Performance test... This should be fast.
+
+#include <type_traits>
+
+template<typename T>
+concept bool Destructible() {
+ return std::is_destructible<T>::value;
+}
+template<typename T, typename... Args>
+concept bool Constructible() {
+ return Destructible<T>() && std::is_constructible<T, Args...>::value;
+}
+template<typename T>
+concept bool Move_constructible() {
+ return Constructible<T, T&&>();
+}
+template<typename T>
+concept bool Copy_constructible() {
+ return Move_constructible<T>() && Constructible<T, const T&>();
+}
+template<typename T, typename U>
+concept bool Assignable() {
+ return std::is_assignable<T, U>::value;
+}
+template<typename T>
+concept bool Move_assignable() {
+ return Assignable<T&, T&&>();
+}
+template<typename T>
+concept bool Copy_assignable() {
+ return Move_assignable<T>() && Assignable<T&, const T&>();
+}
+template<typename T>
+concept bool Copyable() {
+ return Copy_constructible<T>() && Copy_assignable<T>();
+}
+
+template<typename T>
+concept bool C1() { return Copyable<T>(); }
+template<typename T>
+concept bool C2() { return C1<T>(); }
+template<typename T>
+concept bool C3() { return C2<T>(); }
+template<typename T>
+concept bool C4() { return C3<T>(); }
+template<typename T>
+concept bool C5() { return C4<T>(); }
+template<typename T>
+concept bool C6() { return C5<T>(); }
+template<typename T>
+concept bool C7() { return C6<T>(); }
+template<typename T>
+concept bool C8() { return C7<T>(); }
+template<typename T>
+concept bool C9() { return C8<T>(); }
+template<typename T>
+concept bool C10() { return C9<T>(); }
+template<typename T>
+concept bool C11() { return C10<T>(); }
+
+struct S1 {};
+struct S2 {};
+struct S3 {};
+struct S4 {};
+struct S5 {};
+struct S6 {};
+
+static_assert(C11<S1>(), "");
+static_assert(C11<S2>(), "");
+static_assert(C11<S3>(), "");
+static_assert(C11<S4>(), "");
+static_assert(C11<S5>(), "");
+static_assert(C11<S6>(), "");
diff --git a/gcc/testsuite/g++.dg/concepts/pr65854.C b/gcc/testsuite/g++.dg/concepts/pr65854.C
new file mode 100644
index 00000000000..4b0befa5599
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/pr65854.C
@@ -0,0 +1,24 @@
+// { dg-options "-std=c++1z" }
+
+// Handle alias templates in type requirements.
+
+template<typename T1, typename T2>
+struct BTT { };
+
+template<typename T>
+struct BTT<T,T> { using type = int; };
+
+template<typename T1, typename T2>
+using Alias1 = typename BTT<T1, T2>::type;
+
+template<typename T1, typename T2>
+concept bool C()
+{
+ return requires() { typename Alias1<T1, T2>; };
+}
+
+template<typename T1, typename T2>
+ requires C<T1, T2>()
+int f();
+
+auto i = f<char, int>(); // { dg-error "cannot call function" }
diff --git a/gcc/testsuite/g++.dg/concepts/pr66091.C b/gcc/testsuite/g++.dg/concepts/pr66091.C
new file mode 100644
index 00000000000..ee9d1150fdb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/pr66091.C
@@ -0,0 +1,19 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+concept bool C1()
+{
+ return requires() { typename T::type1; };
+}
+
+template<typename T>
+concept bool C2()
+{
+ return C1<T>() && requires() { typename T::type2; };
+}
+
+template<C1 T>
+struct S {
+ S& operator++() { return *this; }
+ S& operator++() requires C2<T>() { return *this; }
+};
diff --git a/gcc/testsuite/g++.dg/concepts/req-neg1.C b/gcc/testsuite/g++.dg/concepts/req-neg1.C
new file mode 100644
index 00000000000..0b7520e185c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/req-neg1.C
@@ -0,0 +1,10 @@
+// { dg-options "-std=c++1z" }
+
+void f1(int a) requires true; // OK
+auto f2(int a) -> bool requires true; // OK
+auto f3(int a) requires true -> bool; // { dg-error "" } requires-clause precedes trailing-return-type
+typedef void fn_t() requires true; // { dg-error "typedef" }
+void (*pf)() requires true; // { dg-error "non-function" }
+void (*fn(int))() requires false; // { dg-error "return type" }
+void g(int (*)() requires true); // { dg-error "parameter|non-function" }
+auto* p = new (void(*)(char) requires true); // { dg-error "type-id" }
diff --git a/gcc/testsuite/g++.dg/concepts/req1.C b/gcc/testsuite/g++.dg/concepts/req1.C
new file mode 100644
index 00000000000..02d5de09ec9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/req1.C
@@ -0,0 +1,29 @@
+// { dg-do compile }
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+ concept bool Class () { return __is_class(T); }
+
+// Allow a requires-expression with no parms.
+template<typename T>
+ concept bool C = requires { typename T::type; };
+
+void f1(auto a) requires Class<decltype(a)>() { }
+void f2(auto a) requires requires (decltype(a) x) { -x; } { }
+
+struct S { } s;
+
+// Allow non-type template parms as constraints.
+template<bool B> requires B struct S0; // OK
+
+template<int N> requires N struct S1 { }; // { dg-error "does not have type" }
+template<int N> requires N == 0 struct S2 { }; // OK
+
+template<typename T, T X> requires X struct S3 { }; // OK
+S3<int, 0> s3a; // { dg-error "constraint failure|does not have type" }
+S3<bool, false> s3b; // { dg-error "constraint failure" }
+
+int main() {
+ f1(s);
+ f2(0);
+}
diff --git a/gcc/testsuite/g++.dg/concepts/req10.C b/gcc/testsuite/g++.dg/concepts/req10.C
new file mode 100644
index 00000000000..bd3b4e3e2f5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/req10.C
@@ -0,0 +1,18 @@
+// { dg-options "-std=c++1z" }
+
+// Test that standard conversions are checked with
+// implicit conversion constraints.
+
+template<typename T, typename U>
+concept bool C()
+{
+ return requires(T& t) { {t} -> U&; };
+}
+
+struct B { };
+class D : B { };
+
+int main()
+{
+ static_assert(C<D, B>(), ""); // { dg-error "failed" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/req11.C b/gcc/testsuite/g++.dg/concepts/req11.C
new file mode 100644
index 00000000000..3e21c11113f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/req11.C
@@ -0,0 +1,28 @@
+// { dg-options "-std=c++1z" }
+
+// Check that we can evaluate constant requires-expressions
+// as constant expressions, for the curious case when they
+// appear within predicate constraints.
+
+template<typename... Ts> struct variant { };
+
+template<typename T>
+concept bool Streamable()
+{
+ return requires (T t) { t; };
+}
+
+template<typename T>
+concept bool Range()
+{
+ return requires (T t) { t; };
+}
+
+template<class T>
+ requires Streamable<T>() and not Range<T>()
+void print(const T& x) { }
+
+int main()
+{
+ print("hello"); // { dg-error "cannot call" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/req12.C b/gcc/testsuite/g++.dg/concepts/req12.C
new file mode 100644
index 00000000000..7b23d113cb2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/req12.C
@@ -0,0 +1,25 @@
+// PR c++/66218
+// { dg-options "-std=c++1z" }
+
+#include <type_traits>
+
+template <class T, class U>
+concept bool Same =
+ std::is_same<T, U>::value;
+
+template <class T>
+concept bool C =
+ requires(T t) {
+ { t } -> Same<T>;
+ };
+
+template <class>
+constexpr bool f() { return false; }
+template <C>
+constexpr bool f() { return true; }
+
+static_assert(f<char>(), "");
+static_assert(f<int>(), "");
+static_assert(f<double>(), "");
+
+int main() {}
diff --git a/gcc/testsuite/g++.dg/concepts/req13.C b/gcc/testsuite/g++.dg/concepts/req13.C
new file mode 100644
index 00000000000..54fcd8b0678
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/req13.C
@@ -0,0 +1,13 @@
+// PR c++/66758
+// { dg-options "-std=c++1z" }
+
+template <class T, class...Args>
+concept bool Constructible =
+ requires(Args&&...args) {
+ T{ ((Args&&)(args))... };
+ new T{((Args&&)(args))...};
+ };
+
+template <Constructible T> struct A { };
+A<int> a;
+
diff --git a/gcc/testsuite/g++.dg/concepts/req14.C b/gcc/testsuite/g++.dg/concepts/req14.C
new file mode 100644
index 00000000000..e86281a3931
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/req14.C
@@ -0,0 +1,14 @@
+// PR c++/66758
+// { dg-options "-std=c++1z" }
+
+template <class T, class U>
+concept bool C = requires (T t, U u) { t + u; };
+
+template <class T, class U>
+requires C<T,U>
+void f(T t, U u) { t + u; }
+
+int main()
+{
+ using T = decltype(f(42, 24));
+}
diff --git a/gcc/testsuite/g++.dg/concepts/req15.C b/gcc/testsuite/g++.dg/concepts/req15.C
new file mode 100644
index 00000000000..09ebf89ef1b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/req15.C
@@ -0,0 +1,21 @@
+// PR c++/66832
+// { dg-options "-std=c++1z" }
+
+template <class T, class U, unsigned N>
+ requires requires (T& t, U &u) { t.foo(); u.foo(); }
+void foo_all( T (&t)[N], U (&u)[N] ) {
+ for(auto& x : t)
+ x.foo();
+ for(auto& x : u)
+ x.foo();
+}
+
+struct S {
+ void foo() {}
+};
+
+int main() {
+ S rg[4] {};
+ foo_all(rg, rg);
+}
+
diff --git a/gcc/testsuite/g++.dg/concepts/req16.C b/gcc/testsuite/g++.dg/concepts/req16.C
new file mode 100644
index 00000000000..67ae5b5126c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/req16.C
@@ -0,0 +1,19 @@
+// PR c++/66988
+// { dg-options "-std=c++1z" }
+
+#include <type_traits>
+
+template <template <class> class T, class U>
+concept bool _Valid = requires { typename T<U>; };
+
+template <class T>
+using __t = typename T::type;
+
+template <class T>
+struct __has_type : std::false_type { };
+
+template <class T>
+ requires _Valid<__t, T>
+struct __has_type<T> : std::true_type { };
+
+static_assert(!__has_type<int>(), "");
diff --git a/gcc/testsuite/g++.dg/concepts/req17.C b/gcc/testsuite/g++.dg/concepts/req17.C
new file mode 100644
index 00000000000..48bd0a8c2cd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/req17.C
@@ -0,0 +1,20 @@
+// PR c++/67018
+// { dg-options "-std=c++1z" }
+
+template <typename T>
+constexpr bool Val = true;
+
+template <class I>
+concept bool InputIterator = requires (I i) {
+ requires Val <decltype(i++)>;
+};
+
+template <class I>
+concept bool ForwardIterator = InputIterator<I> && true;
+
+template<InputIterator>
+constexpr bool f() { return false; }
+template<ForwardIterator>
+constexpr bool f() { return true; }
+
+static_assert(f<int*>());
diff --git a/gcc/testsuite/g++.dg/concepts/req18.C b/gcc/testsuite/g++.dg/concepts/req18.C
new file mode 100644
index 00000000000..9a474376137
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/req18.C
@@ -0,0 +1,17 @@
+// { dg-options "-std=c++1z" }
+
+template <class> struct all_same {
+ static constexpr bool value = 1;
+};
+template <class T> concept bool Assignable
+= requires(T t)
+{
+ requires all_same<decltype(t = 0)>::value;
+};
+
+template <class I> requires !Assignable<I>
+int dispatch();
+template <Assignable>
+void dispatch();
+
+int main() { dispatch<int *>(); }
diff --git a/gcc/testsuite/g++.dg/concepts/req2.C b/gcc/testsuite/g++.dg/concepts/req2.C
new file mode 100644
index 00000000000..a28f30b5b18
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/req2.C
@@ -0,0 +1,19 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+ concept bool Class () { return __is_class(T); }
+
+void f1(auto a) requires Class<decltype(a)>() { }
+
+// FIXME: This is generating excess errors related to pretty
+// printing the trailing requires expression.
+void f2(auto a)
+ requires requires (decltype(a) x) { -x; }
+{ }
+
+struct S { } s;
+
+int main() {
+ f1(0); // { dg-error "cannot call" }
+ f2((void*)0); // { dg-error "cannot call" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/req3.C b/gcc/testsuite/g++.dg/concepts/req3.C
new file mode 100644
index 00000000000..69581ee43ea
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/req3.C
@@ -0,0 +1,17 @@
+// { dg-do run }
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+ concept bool Class () { return __is_class(T); }
+
+struct Test {
+ void f(auto a) requires Class<decltype(a)>();
+} test;
+
+struct S { }s;
+
+int main() {
+ test.f(s);
+}
+
+void Test::f(auto a) requires Class<decltype(a)>() { }
diff --git a/gcc/testsuite/g++.dg/concepts/req4.C b/gcc/testsuite/g++.dg/concepts/req4.C
new file mode 100644
index 00000000000..20778342e52
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/req4.C
@@ -0,0 +1,18 @@
+// { dg-options "-std=c++1z" }
+
+struct fool {
+ constexpr fool operator&&(fool) const { return {}; }
+ constexpr fool operator||(fool) const { return {}; }
+};
+
+template<typename T> constexpr fool p1() { return {}; }
+template<typename T> constexpr fool p2() { return {}; }
+
+template<typename T>
+ concept bool C() { return p1<T>() && p2<T>(); } // { dg-error "does not have type" }
+
+template<C T> void f(T x) { }
+
+int main() {
+ f(0); // { dg-error "cannot call" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/req5.C b/gcc/testsuite/g++.dg/concepts/req5.C
new file mode 100644
index 00000000000..c0af1f88495
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/req5.C
@@ -0,0 +1,18 @@
+// { dg-options "-std=c++1z" }
+
+struct fool { };
+
+constexpr fool operator&&(fool, fool) { return {}; }
+constexpr fool operator||(fool, fool) { return {}; }
+
+template<typename T> constexpr fool p1() { return {}; }
+template<typename T> constexpr fool p2() { return {}; }
+
+template<typename T>
+ concept bool C() { return p1<T>() && p2<T>(); } // { dg-error "does not have type" }
+
+template<C T> void f(T x) { }
+
+int main() {
+ f(0); // { dg-error "cannot call" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/req6.C b/gcc/testsuite/g++.dg/concepts/req6.C
new file mode 100644
index 00000000000..e4f20ecb67c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/req6.C
@@ -0,0 +1,13 @@
+// { dg-options "-std=c++1z" }
+
+struct X { };
+int operator==(X, X) { return 0; }
+
+template<typename T>
+ concept bool C1() { return X(); }
+
+template<C1 T>
+ void h(T) { } // { dg-error "not|bool" }
+
+template<typename T>
+ concept bool C2() { return X() == X(); } // OK
diff --git a/gcc/testsuite/g++.dg/concepts/req7.C b/gcc/testsuite/g++.dg/concepts/req7.C
new file mode 100644
index 00000000000..51aba7b2e6f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/req7.C
@@ -0,0 +1,24 @@
+// { dg-options "-std=c++1z" }
+
+#include <vector>
+
+using namespace std;
+
+template<typename T>
+ struct Sequence : std::false_type { };
+
+template<typename T>
+ struct Predicate : std::false_type { };
+
+template<typename Seq, typename Fn>
+ requires Sequence<Seq>{} and Predicate<Fn>{}
+ bool all(const Seq& seq, Fn fn) {
+ for(const auto& x : seq)
+ if (not fn(x))
+ return false;
+ return true;
+ }
+
+int main() {
+ all(vector<int>{0, 2}, true); // { dg-error "not|bool" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/req8.C b/gcc/testsuite/g++.dg/concepts/req8.C
new file mode 100644
index 00000000000..35c08541b60
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/req8.C
@@ -0,0 +1,17 @@
+// { dg-do compile }
+// { dg-options "-std=c++1z" }
+
+// Check that type requirements are normalized correctly.
+
+template<typename T>
+ concept bool Has_member_type() {
+ return requires() { typename T::type; };
+ }
+
+template<typename T>
+ concept bool Concept() {
+ return true && Has_member_type<T>();
+ }
+
+template<Concept T>
+ void foo( T t ) { }
diff --git a/gcc/testsuite/g++.dg/concepts/req9.C b/gcc/testsuite/g++.dg/concepts/req9.C
new file mode 100644
index 00000000000..5654be0a0fe
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/req9.C
@@ -0,0 +1,24 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+struct S1 {};
+
+template<typename T>
+concept bool C() { return requires(T x) { { x.fn() } -> S1<T>; }; }
+
+template<C U>
+void fn(U x)
+{
+ x.fn();
+}
+
+struct S2
+{
+ auto fn() const { return S1<S2>(); }
+};
+
+int main()
+{
+ fn(S2{});
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/concepts/template-parm1.C b/gcc/testsuite/g++.dg/concepts/template-parm1.C
new file mode 100644
index 00000000000..20a7fb6b1c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/template-parm1.C
@@ -0,0 +1,34 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+ concept bool C1 = __is_same_as(T, int);
+
+template<int N>
+ concept bool C2 = N == 0;
+
+template<template<typename> class X>
+ concept bool C3 = true;
+
+template<typename> struct Foo;
+
+// Type template parameters
+template<C1 T = int> struct S1 { };
+template<C1 = int> struct S2;
+template<C1 T> struct S2 { };
+
+// Non-type template parameters
+template<C2 N = 0> struct S3 { };
+template<C2 = 0> struct S4;
+template<C2 N> struct S4 { };
+
+// Template template parameters
+template<C3 X = Foo> struct S5 { };
+template<C3 = Foo> struct S6;
+template<C3 X> struct S6 { };
+
+S1<> s1;
+S2<> s2;
+S3<> s3;
+S4<> s4;
+S5<> s5;
+S6<> s6;
diff --git a/gcc/testsuite/g++.dg/concepts/template-parm10.C b/gcc/testsuite/g++.dg/concepts/template-parm10.C
new file mode 100644
index 00000000000..b80328995b0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/template-parm10.C
@@ -0,0 +1,17 @@
+// { dg-options "-std=c++1z" }
+
+template<int N, class T>
+ concept bool P() { return true; }
+
+template<template<typename> class X, class T>
+ concept bool Q() { return true; }
+
+template<P<int> N> void f() { }
+template<Q<int> X> void g() { }
+
+template<typename> struct S { };
+
+int main() {
+ f<0>();
+ g<S>();
+}
diff --git a/gcc/testsuite/g++.dg/concepts/template-parm11.C b/gcc/testsuite/g++.dg/concepts/template-parm11.C
new file mode 100644
index 00000000000..95ce7845116
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/template-parm11.C
@@ -0,0 +1,18 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+concept bool NameProvider()
+{
+ return requires(){
+ typename T::_name_t::template _member_t<int>;
+ };
+}
+
+template<NameProvider... ColSpec>
+void getTable(const ColSpec&...)
+{}
+
+int f()
+{
+ getTable(7, 'a'); // { dg-error "cannot call" }
+};
diff --git a/gcc/testsuite/g++.dg/concepts/template-parm12.C b/gcc/testsuite/g++.dg/concepts/template-parm12.C
new file mode 100644
index 00000000000..1639f26dbf1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/template-parm12.C
@@ -0,0 +1,20 @@
+// Conceptized version of template/ttp23.C
+// { dg-options "-std=c++1z" }
+
+template <class T> concept bool Foo = true;
+
+template <typename T> struct A {};
+
+template <template <Foo> class P>
+struct B {
+ template <template <Foo> class Q>
+ friend bool foo (const B<Q>& a);
+};
+
+template <template <typename> class Q>
+bool foo (const B<Q>& a);
+
+void bar () {
+ B<A> a;
+ foo (a);
+}
diff --git a/gcc/testsuite/g++.dg/concepts/template-parm2.C b/gcc/testsuite/g++.dg/concepts/template-parm2.C
new file mode 100644
index 00000000000..ad715e11a32
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/template-parm2.C
@@ -0,0 +1,23 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+ concept bool C1 = __is_same_as(T, int);
+
+template<int N>
+ concept bool C2 = N == 0;
+
+template<template<typename> class X>
+ concept bool C3 = false;
+
+template<typename> struct Foo;
+
+// Instantiation of default arguments happens at the point of
+// instantiation for the class.
+
+template<C1 T = char> struct S1 { };
+template<C2 N = 1> struct S2 { };
+template<C3 X = Foo> struct S3 { };
+
+S1<> s1; // { dg-error "constraint failure|invalid type" }
+S2<> s2; // { dg-error "constraint failure|invalid type" }
+S3<> s3; // { dg-error "constraint failure|invalid type" }
diff --git a/gcc/testsuite/g++.dg/concepts/template-parm3.C b/gcc/testsuite/g++.dg/concepts/template-parm3.C
new file mode 100644
index 00000000000..e412fb4ca12
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/template-parm3.C
@@ -0,0 +1,28 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+ concept bool C1 = __is_same_as(T, int);
+
+template<int N>
+ concept bool C2 = N == 0;
+
+template<template<typename> class X>
+ concept bool C3 = true;
+
+template<typename> struct Foo;
+
+template<C1...> struct S1;
+template<C1... Ts> struct S1 { };
+
+template<C2...> struct S2;
+template<C2... Ns> struct S2 { };
+
+template<C3...> struct S3;
+template<C3... Xs> struct S3 { };
+
+S1<int, int, int> s1; // OK
+S1<> s11;
+S2<0, 0, 0> s2;
+S2<> s22;
+S3<Foo, Foo> s3;
+S3<> s33;
diff --git a/gcc/testsuite/g++.dg/concepts/template-parm4.C b/gcc/testsuite/g++.dg/concepts/template-parm4.C
new file mode 100644
index 00000000000..a29bb11a679
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/template-parm4.C
@@ -0,0 +1,20 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+ concept bool C1 = __is_same_as(T, int);
+
+template<int N>
+ concept bool C2 = N == 0;
+
+template<template<typename> class X>
+ concept bool C3 = false;
+
+template<typename> struct Foo;
+
+template<C1... Ts> struct S1 { };
+template<C2... Ns> struct S2 { };
+template<C3... Xs> struct S3 { };
+
+S1<int, int, bool> s1; // { dg-error "constraint failure|invalid type" }
+S2<0, 1, 2> s2; // { dg-error "constraint failure|invalid type" }
+S3<Foo> s3; // { dg-error "constraint failure|invalid type" }
diff --git a/gcc/testsuite/g++.dg/concepts/template-parm5.C b/gcc/testsuite/g++.dg/concepts/template-parm5.C
new file mode 100644
index 00000000000..f33742becd2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/template-parm5.C
@@ -0,0 +1,19 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+ concept bool C1 = __is_same_as(T, int);
+
+template<int N>
+ concept bool C2 = N == 0;
+
+template<template<typename> class X>
+ concept bool C3 = true;
+
+template<typename> struct Foo;
+
+template<C1... Ts = int> struct S1; // { dg-error "default argument" }
+template<C1... = int> struct S2; // { dg-error "default argument" }
+template<C2... Ns = 0> struct S3; // { dg-error "default argument" }
+template<C2... = 0> struct S4; // { dg-error "default argument" }
+template<C3... Ts = Foo> struct S5; // { dg-error "default argument" }
+template<C3... = Foo> struct S6; // { dg-error "default argument" }
diff --git a/gcc/testsuite/g++.dg/concepts/template-parm6.C b/gcc/testsuite/g++.dg/concepts/template-parm6.C
new file mode 100644
index 00000000000..d23f2594b46
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/template-parm6.C
@@ -0,0 +1,41 @@
+// { dg-options "-std=c++1z" }
+
+template<typename... Ts> struct are_same;
+
+template<>
+ struct are_same<> {
+ static constexpr bool value = true;
+ };
+
+template<typename T>
+ struct are_same<T> {
+ static constexpr bool value = true;
+ };
+
+template<typename T, typename U, typename... Ts>
+ struct are_same<T, U, Ts...> {
+ static constexpr bool value =
+ __is_same_as(T, U) && are_same<U, Ts...>::value;
+ };
+
+constexpr bool all_of() { return true; }
+constexpr bool all_of(bool b) { return b; }
+
+template<typename... Ts>
+ constexpr bool all_of(bool a, bool b, Ts... args) {
+ return (a && b) && all_of(b, args...);
+ }
+
+template<typename... Ts>
+ concept bool C1 = are_same<Ts...>::value;
+
+template<bool... Bs>
+ concept bool C2 = all_of(Bs...);
+
+template<C1... Ts> struct S1 { };
+template<C1...> struct S2 { };
+template<C2... Bs> struct S4 { };
+template<C2...> struct S5 { };
+
+S1<int, int, int> s1;
+S4<true, true, true> s4;
diff --git a/gcc/testsuite/g++.dg/concepts/template-parm7.C b/gcc/testsuite/g++.dg/concepts/template-parm7.C
new file mode 100644
index 00000000000..10869840fdd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/template-parm7.C
@@ -0,0 +1,44 @@
+// { dg-options "-std=c++1z" }
+
+template<typename... Ts> struct are_same;
+
+template<>
+ struct are_same<> {
+ static constexpr bool value = true;
+ };
+
+template<typename T>
+ struct are_same<T> {
+ static constexpr bool value = true;
+ };
+
+template<typename T, typename U, typename... Ts>
+ struct are_same<T, U, Ts...> {
+ static constexpr bool value =
+ __is_same_as(T, U) && are_same<U, Ts...>::value;
+ };
+
+constexpr bool all_of() { return true; }
+constexpr bool all_of(bool b) { return b; }
+
+template<typename... Ts>
+ constexpr bool all_of(bool a, bool b, Ts... args) {
+ return (a && b) && all_of(b, args...);
+ }
+
+
+template<typename... Ts>
+ concept bool C1 = are_same<Ts...>::value;
+
+template<bool... Bs>
+ concept bool C2 = all_of(Bs...);
+
+template<C1... Ts> struct S1 { }; // OK
+S1<int, int, char> s1; // { dg-error "constraint failure|invalid type" }
+template<C1 Ts> struct S2 { }; // { dg-error "variadic constraint" }
+
+template<C2... Bs> struct S3 { }; // OK
+S3<true, true, false> s3; // { dg-error "constraint failure|invalid type" }
+template<C2 Bs> struct S4 { }; // { dg-error "variadic constraint" }
+
+int main() { }
diff --git a/gcc/testsuite/g++.dg/concepts/template-parm8.C b/gcc/testsuite/g++.dg/concepts/template-parm8.C
new file mode 100644
index 00000000000..f604d5a6a2b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/template-parm8.C
@@ -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>() and __is_empty(T); }
+
+template<template<typename Q> requires C<Q>() class X>
+ struct S { };
+
+// An unconstrained template can be used as an argument for any
+// constrained template template parameter.
+template<typename A> struct T0 { };
+S<T0> x1;
+
+// Matching constraints are valid.
+template<typename A> requires C<A>() struct T1 { };
+S<T1> x2;
+
+int main() { }
diff --git a/gcc/testsuite/g++.dg/concepts/template-parm9.C b/gcc/testsuite/g++.dg/concepts/template-parm9.C
new file mode 100644
index 00000000000..7e6898405ae
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/template-parm9.C
@@ -0,0 +1,18 @@
+// { 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); }
+
+template<template<typename Q> requires C<Q>() class X>
+ struct S { };
+
+template<typename A> requires true struct T0 { };
+template<typename A> requires D<A>() struct T1 { };
+
+S<T0> x3; // { dg-error "constraint mismatch|invalid type" }
+S<T1> x4; // { dg-error "constraint mismatch|invalid type" }
+
+int main() { }
diff --git a/gcc/testsuite/g++.dg/concepts/template-template-parm1.C b/gcc/testsuite/g++.dg/concepts/template-template-parm1.C
new file mode 100644
index 00000000000..40912843714
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/template-template-parm1.C
@@ -0,0 +1,56 @@
+// PR c++/66937
+// { dg-options -std=c++1z }
+
+#include <tuple>
+
+namespace detail
+{
+ template<typename T, template<typename...> class Sink>
+ struct copy_tuple_args_impl;
+
+ template<typename... Args, template<typename...> class Sink>
+ struct copy_tuple_args_impl<std::tuple<Args...>, Sink>
+ {
+ using type = Sink<Args...>;
+ };
+}
+
+// copy_tuple_args copies the template arguments of a tuple into another template
+// copy_tuple_args does not care about constraints whatsoever.
+template<typename Tuple, template<typename...> class Sink>
+using copy_tuple_args = typename detail::copy_tuple_args_impl<Tuple, Sink>::type;
+
+// A concept of a column
+template <typename T>
+concept bool Column()
+{
+ return requires()
+ {
+ typename T::_name_t;
+ };
+}
+
+// column_list is constrained to Column arguments
+template<Column... C>
+struct column_list
+{
+};
+
+// Here are some columns
+struct A
+{
+ using _name_t = int;
+};
+
+struct B
+{
+ using _name_t = int;
+};
+
+
+int main()
+{
+ using ColumnTuple = std::tuple<A, B>;
+ using ColumnList = copy_tuple_args<ColumnTuple, column_list>; // This fails, but should not
+}
+
diff --git a/gcc/testsuite/g++.dg/concepts/traits1.C b/gcc/testsuite/g++.dg/concepts/traits1.C
new file mode 100644
index 00000000000..f07c8787472
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/traits1.C
@@ -0,0 +1,97 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+ concept bool Nothrow_assignable() { return __has_nothrow_assign(T); }
+
+template<typename T>
+ concept bool Nothrow_constructible() { return __has_nothrow_constructor(T); }
+
+template<typename T>
+ concept bool Nothrow_copyable() { return __has_nothrow_copy(T); }
+
+template<typename T>
+ concept bool Trivially_assignable() { return __has_trivial_assign(T); }
+
+template<typename T>
+ concept bool Trivially_constructible() { return __has_trivial_constructor(T); }
+
+template<typename T>
+ concept bool Trivially_copyable() { return __has_trivial_copy(T); }
+
+template<typename T>
+ concept bool Trivially_destructible() { return __has_trivial_destructor(T); }
+
+template<typename T>
+ concept bool Dynamically_destructible() { return __has_virtual_destructor(T); }
+
+template<typename T>
+ concept bool Abstract() { return __is_abstract(T); }
+
+template<typename T>
+ concept bool Polymorphic() { return __is_polymorphic(T); }
+
+template<typename T>
+ concept bool Class() { return __is_class(T); }
+
+template<typename T>
+ concept bool Empty() { return __is_empty(T); }
+
+template<typename T>
+ concept bool Enum() { return __is_enum(T); }
+
+template<typename T>
+ concept bool Final() { return __is_final(T); }
+
+template<typename T>
+ concept bool Literal_type() { return __is_literal_type(T); }
+
+template<typename T>
+ concept bool Pod() { return __is_pod(T); }
+
+template<typename T>
+ concept bool Standard_layout() { return __is_standard_layout(T); }
+
+template<typename T>
+ concept bool Trivial() { return __is_trivial(T); }
+
+template<typename T>
+ concept bool Union() { return __is_union(T); }
+
+template<Nothrow_assignable T> void f1() { }
+template<Nothrow_copyable T> void f2() { }
+template<Nothrow_constructible T> void f3() { }
+template<Trivially_assignable T> void f4() { }
+template<Trivially_copyable T> void f5() { }
+template<Trivially_constructible T> void f6() { }
+template<Trivially_destructible T> void f7() { }
+template<Dynamically_destructible T> void f8() { }
+template<Class T> void f9() { }
+template<Empty T> void f10() { }
+template<Standard_layout T> void f11() { }
+template<Pod T> void f12() { }
+template<Trivial T> void f13() { }
+template<Polymorphic T> void f14() { }
+template<Abstract T> void f15() { }
+template<Final T> void f16() { }
+template<Union T> void f17() { }
+template<Enum T> void f18() { }
+
+int main() {
+ f1<void>(); // { dg-error "cannot call" }
+ f2<void>(); // { dg-error "cannot call" }
+ f3<void>(); // { dg-error "cannot call" }
+ f4<void>(); // { dg-error "cannot call" }
+ f5<void>(); // { dg-error "cannot call" }
+ f6<void>(); // { dg-error "cannot call" }
+ f7<void>(); // { dg-error "cannot call" }
+ f8<void>(); // { dg-error "cannot call" }
+ f9<void>(); // { dg-error "cannot call" }
+ f10<void>(); // { dg-error "cannot call" }
+ f11<void>(); // { dg-error "cannot call" }
+ f12<void>(); // { dg-error "cannot call" }
+ f13<void>(); // { dg-error "cannot call" }
+ f14<void>(); // { dg-error "cannot call" }
+ f15<void>(); // { dg-error "cannot call" }
+ f16<void>(); // { dg-error "cannot call" }
+ f17<void>(); // { dg-error "cannot call" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/traits2.C b/gcc/testsuite/g++.dg/concepts/traits2.C
new file mode 100644
index 00000000000..28ced34c63e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/traits2.C
@@ -0,0 +1,98 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+ concept bool Nothrow_assignable() { return __has_nothrow_assign(T); }
+
+template<typename T>
+ concept bool Nothrow_constructible() { return __has_nothrow_constructor(T); }
+
+template<typename T>
+ concept bool Nothrow_copyable() { return __has_nothrow_copy(T); }
+
+template<typename T>
+ concept bool Trivially_assignable() { return __has_trivial_assign(T); }
+
+template<typename T>
+ concept bool Trivially_constructible() { return __has_trivial_constructor(T); }
+
+template<typename T>
+ concept bool Trivially_copyable() { return __has_trivial_copy(T); }
+
+template<typename T>
+ concept bool Trivially_destructible() { return __has_trivial_destructor(T); }
+
+template<typename T>
+ concept bool Dynamically_destructible() { return __has_virtual_destructor(T); }
+
+template<typename T>
+ concept bool Abstract() { return __is_abstract(T); }
+
+template<typename T>
+ concept bool Polymorphic() { return __is_polymorphic(T); }
+
+template<typename T>
+ concept bool Class() { return __is_class(T); }
+
+template<typename T>
+ concept bool Empty() { return __is_empty(T); }
+
+template<typename T>
+ concept bool Enum() { return __is_enum(T); }
+
+template<typename T>
+ concept bool Final() { return __is_final(T); }
+
+template<typename T>
+ concept bool Literal_type() { return __is_literal_type(T); }
+
+template<typename T>
+ concept bool Pod() { return __is_pod(T); }
+
+template<typename T>
+ concept bool Standard_layout() { return __is_standard_layout(T); }
+
+template<typename T>
+ concept bool Trivial() { return __is_trivial(T); }
+
+template<typename T>
+ concept bool Union() { return __is_union(T); }
+
+template<Nothrow_assignable T> void f1();
+template<Nothrow_copyable T> void f2();
+template<Nothrow_constructible T> void f3();
+template<Trivially_assignable T> void f4();
+template<Trivially_copyable T> void f5();
+template<Trivially_constructible T> void f6();
+template<Trivially_destructible T> void f7();
+template<Dynamically_destructible T> void f8();
+template<Class T> void f9();
+template<Empty T> void f10();
+template<Standard_layout T> void f11();
+template<Pod T> void f12();
+template<Trivial T> void f13();
+template<Polymorphic T> void f14();
+template<Abstract T> void f15();
+template<Final T> void f16();
+template<Union T> void f17();
+template<Enum T> void f18();
+
+
+int main() {
+ f1<void>(); // { dg-error "cannot call" }
+ f2<void>(); // { dg-error "cannot call" }
+ f3<void>(); // { dg-error "cannot call" }
+ f4<void>(); // { dg-error "cannot call" }
+ f5<void>(); // { dg-error "cannot call" }
+ f6<void>(); // { dg-error "cannot call" }
+ f7<void>(); // { dg-error "cannot call" }
+ f8<void>(); // { dg-error "cannot call" }
+ f9<void>(); // { dg-error "cannot call" }
+ f10<void>(); // { dg-error "cannot call" }
+ f11<void>(); // { dg-error "cannot call" }
+ f12<void>(); // { dg-error "cannot call" }
+ f13<void>(); // { dg-error "cannot call" }
+ f14<void>(); // { dg-error "cannot call" }
+ f15<void>(); // { dg-error "cannot call" }
+ f16<void>(); // { dg-error "cannot call" }
+ f17<void>(); // { dg-error "cannot call" }
+}
diff --git a/gcc/testsuite/g++.dg/concepts/var-concept1.C b/gcc/testsuite/g++.dg/concepts/var-concept1.C
new file mode 100644
index 00000000000..eb8a2b797a3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/var-concept1.C
@@ -0,0 +1,26 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+concept bool C1 = __is_class(T);
+
+template<typename T>
+concept bool C2 = requires (T t) { t; };
+
+void f1(C1, C1) { }
+
+template<typename T>
+ requires C2<T>
+void f2(T) { }
+
+void f3(C2) { }
+
+struct S1 {};
+
+int main ()
+{
+ f1(S1(), S1());
+ f2(0);
+ f3(0);
+
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/concepts/var-concept2.C b/gcc/testsuite/g++.dg/concepts/var-concept2.C
new file mode 100644
index 00000000000..71663bdec5f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/var-concept2.C
@@ -0,0 +1,17 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+ concept bool C1 = __is_class(T);
+
+template<typename U>
+ requires C1<U>
+ void f1(U, U) { }
+
+void f2(C1) {}
+
+int main ()
+{
+ f1(0, 0); // { dg-error "cannot call" }
+ f2(1); // { dg-error "cannot call" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/concepts/var-concept3.C b/gcc/testsuite/g++.dg/concepts/var-concept3.C
new file mode 100644
index 00000000000..c90102848d4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/var-concept3.C
@@ -0,0 +1,22 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+ concept bool C1 = __is_class(T);
+
+template<typename T>
+ concept bool C2() { return __is_class(T); }
+
+template<typename T>
+ constexpr bool C3 = __is_class(T);
+
+
+template<typename U>
+ requires C1<U>() // { dg-error "" }
+ void f1(U) { }
+
+template<typename U>
+ requires C2<U> // { dg-error "invalid reference" }
+ void f2(U) { }
+
+template<C3 T> // { dg-error "not a type" }
+ void f(T) { } // { dg-error "" }
diff --git a/gcc/testsuite/g++.dg/concepts/var-concept4.C b/gcc/testsuite/g++.dg/concepts/var-concept4.C
new file mode 100644
index 00000000000..b3983539616
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/var-concept4.C
@@ -0,0 +1,19 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T, typename U>
+concept bool Same = __is_same_as(T, U);
+
+template<typename T0, typename T1, typename T2, typename... T3toN>
+concept bool Same<T0, T1, T2, T3toN...> = true; // { dg-error "wrong number|does not match" }
+
+template<typename T>
+concept bool C1 = true;
+
+template<typename T>
+concept bool C1<T*> = true; // { dg-error "specialization of variable concept" }
+
+template<typename T>
+concept bool C2 = true;
+
+template<>
+concept bool C2<int> = true; // { dg-error "non-template variable" }
diff --git a/gcc/testsuite/g++.dg/concepts/var-concept5.C b/gcc/testsuite/g++.dg/concepts/var-concept5.C
new file mode 100644
index 00000000000..13f86dd3124
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/var-concept5.C
@@ -0,0 +1,14 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T1, typename T2>
+concept bool C1 = true;
+
+template<typename T1, typename T2, typename T3>
+concept bool C2 = true;
+
+
+template<C1 T> // { dg-error "not a type" }
+constexpr bool f1( ) { return true; }
+
+template<C2<int> T> // { dg-error "expected" }
+constexpr bool f2( ) { return true; }
diff --git a/gcc/testsuite/g++.dg/concepts/var-concept6.C b/gcc/testsuite/g++.dg/concepts/var-concept6.C
new file mode 100644
index 00000000000..2f775fd62e0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/var-concept6.C
@@ -0,0 +1,4 @@
+// { dg-options -std=c++1z }
+
+template <class T>
+concept int C = true; // { dg-error "bool" }
diff --git a/gcc/testsuite/g++.dg/concepts/var-templ1.C b/gcc/testsuite/g++.dg/concepts/var-templ1.C
new file mode 100644
index 00000000000..79476c35416
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/var-templ1.C
@@ -0,0 +1,16 @@
+// PR c++/67117
+// { dg-options -std=c++1z }
+
+template <class T>
+ requires false
+constexpr bool v = true;
+
+template <class T>
+constexpr bool f() { return true; }
+
+template <class T>
+ requires v<T>
+constexpr bool f() { return false; }
+
+static_assert(f<void>());
+static_assert(v<void>); // { dg-error "constraints" }
diff --git a/gcc/testsuite/g++.dg/concepts/var-templ2.C b/gcc/testsuite/g++.dg/concepts/var-templ2.C
new file mode 100644
index 00000000000..e615f21457d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/var-templ2.C
@@ -0,0 +1,17 @@
+// PR c++/67139
+// { dg-options -std=c++1z }
+
+template <class T>
+constexpr typename T::type::value_type _v = T::type::value;
+
+template <class T> concept bool IsTrue_() { return _v<T>; }
+
+template <class T> concept bool Unpossible() {
+ return IsTrue_<T &&>();
+}
+
+template <class> constexpr bool unpossible() { return false; }
+Unpossible{ T }
+constexpr bool unpossible() { return true; }
+
+static_assert((!unpossible<void>()), "");
diff --git a/gcc/testsuite/g++.dg/concepts/variadic1.C b/gcc/testsuite/g++.dg/concepts/variadic1.C
new file mode 100644
index 00000000000..ed3d4f1e01d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/variadic1.C
@@ -0,0 +1,25 @@
+// PR c++/66712
+// { dg-options "-std=c++1z" }
+
+template <class T, class...Args>
+concept bool _Constructible_ =
+ requires (Args&&...args)
+ {
+ T{ ((Args&&)(args))... };
+ };
+
+template <class T, class...Args>
+constexpr bool _constructible_() { return false; }
+
+_Constructible_{T, ...Args}
+constexpr bool _constructible_() { return true; }
+
+struct S
+{
+ S(int, char const *);
+};
+
+int main()
+{
+ static_assert(_constructible_<S, int, char const *>(), "");
+}
diff --git a/gcc/testsuite/g++.dg/concepts/variadic2.C b/gcc/testsuite/g++.dg/concepts/variadic2.C
new file mode 100644
index 00000000000..6c552771d59
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/variadic2.C
@@ -0,0 +1,13 @@
+// { dg-options "-std=c++1z" }
+
+template <class T> concept bool Copyable = requires (T t) { T(t); };
+template <class T> concept bool Constructable = requires { T(); };
+template <class T> concept bool Both = Copyable<T> && Constructable<T>;
+
+template <Copyable... Ts> void f(Ts...) { }
+template <Both... Ts> void f(Ts...) { }
+
+int main()
+{
+ f(42);
+}