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

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

158 files changed:
gcc/c-family/ChangeLog
gcc/c-family/c-common.c
gcc/c-family/c-common.h
gcc/c-family/c-cppbuiltin.c
gcc/c-family/c-opts.c
gcc/c-family/c.opt
gcc/cp/ChangeLog
gcc/cp/Make-lang.in
gcc/cp/call.c
gcc/cp/class.c
gcc/cp/constexpr.c
gcc/cp/constraint.cc [new file with mode: 0644]
gcc/cp/cp-objcp-common.c
gcc/cp/cp-tree.def
gcc/cp/cp-tree.h
gcc/cp/cxx-pretty-print.c
gcc/cp/cxx-pretty-print.h
gcc/cp/decl.c
gcc/cp/decl2.c
gcc/cp/error.c
gcc/cp/lex.c
gcc/cp/logic.cc [new file with mode: 0644]
gcc/cp/method.c
gcc/cp/parser.c
gcc/cp/parser.h
gcc/cp/pt.c
gcc/cp/ptree.c
gcc/cp/search.c
gcc/cp/semantics.c
gcc/cp/tree.c
gcc/cp/typeck.c
gcc/doc/extend.texi
gcc/testsuite/g++.dg/concepts/alias1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/alias2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/alias3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/alias4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/class.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/class1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/class2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/class3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/class4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/class5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/class6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/constrained-parm.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/decl-diagnose.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/deduction-constraint1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/disjunction1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/dr1430.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/equiv.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/equiv2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/explicit-inst1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/explicit-inst2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/explicit-inst3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/explicit-inst4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/explicit-spec1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/explicit-spec2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/explicit-spec3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/explicit-spec4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/explicit-spec5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/explicit-spec6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/expression.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/expression2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/expression3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/feature-macro.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/fn-concept1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/fn-concept2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/fn1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/fn10.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/fn2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/fn3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/fn4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/fn5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/fn6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/fn7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/fn8.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/fn9.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/friend1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/friend2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/generic-fn-err.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/generic-fn.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/inherit-ctor1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/inherit-ctor2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/inherit-ctor3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/inherit-ctor4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/intro1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/intro2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/intro3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/intro4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/intro5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/intro6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/intro7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/member-concept.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/memfun-err.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/memfun.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/partial-concept-id1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/partial-concept-id2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/partial-spec.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/partial-spec2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/partial-spec3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/partial-spec4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/partial-spec5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/placeholder1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/placeholder2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/placeholder3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/placeholder4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/placeholder5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/pr65552.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/pr65575.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/pr65634.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/pr65636.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/pr65681.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/pr65848.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/pr65854.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/pr66091.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/req-neg1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/req1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/req10.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/req11.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/req12.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/req13.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/req14.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/req15.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/req16.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/req17.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/req18.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/req2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/req3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/req4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/req5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/req6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/req7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/req8.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/req9.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/template-parm1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/template-parm10.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/template-parm11.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/template-parm12.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/template-parm2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/template-parm3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/template-parm4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/template-parm5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/template-parm6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/template-parm7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/template-parm8.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/template-parm9.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/template-template-parm1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/traits1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/traits2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/var-concept1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/var-concept2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/var-concept3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/var-concept4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/var-concept5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/var-concept6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/var-templ1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/var-templ2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/variadic1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/variadic2.C [new file with mode: 0644]

index 02374d3..d3a6a38 100644 (file)
@@ -1,3 +1,15 @@
+2015-08-06  Andrew Sutton  <andrew.n.sutton@gmail.com>
+           Braden Obrzut  <admin@maniacsvault.net>
+           Jason Merrill  <jason@redhat.com>
+
+       Add C++ Concepts TS support.
+       * c-common.c (c_common_reswords): Add __is_same_as, concept, requires.
+       * c-common.h (enum rid): Add RID_IS_SAME_AS, RID_CONCEPT, RID_REQUIRES.
+       (D_CXX_CONCEPTS, D_CXX_CONCEPTS_FLAGS): New.
+       * c-cppbuiltin.c (c_cpp_builtins): Define __cpp_concepts.
+       * c-opts.c (set_std_cxx1z): Set flag_concepts.
+       * c.opt (fconcepts): New.
+
 2015-08-02  Patrick Palka  <ppalka@gcc.gnu.org>
 
        * c-indentation.c (should_warn_for_misleading_indentation):
index a8e5353..f6c5ddd 100644 (file)
@@ -491,6 +491,7 @@ const struct c_common_resword c_common_reswords[] =
   { "__is_literal_type", RID_IS_LITERAL_TYPE, D_CXXONLY },
   { "__is_pod",                RID_IS_POD,     D_CXXONLY },
   { "__is_polymorphic",        RID_IS_POLYMORPHIC, D_CXXONLY },
+  { "__is_same_as",     RID_IS_SAME_AS, D_CXXONLY },
   { "__is_standard_layout", RID_IS_STD_LAYOUT, D_CXXONLY },
   { "__is_trivial",     RID_IS_TRIVIAL, D_CXXONLY },
   { "__is_trivially_assignable", RID_IS_TRIVIALLY_ASSIGNABLE, D_CXXONLY },
@@ -589,6 +590,11 @@ const struct c_common_resword c_common_reswords[] =
   { "volatile",                RID_VOLATILE,   0 },
   { "wchar_t",         RID_WCHAR,      D_CXXONLY },
   { "while",           RID_WHILE,      0 },
+
+  /* Concepts-related keywords */
+  { "concept",         RID_CONCEPT,    D_CXX_CONCEPTS_FLAGS | D_CXXWARN },
+  { "requires",        RID_REQUIRES,   D_CXX_CONCEPTS_FLAGS | D_CXXWARN },
+
   /* These Objective-C keywords are recognized only immediately after
      an '@'.  */
   { "compatibility_alias", RID_AT_ALIAS,       D_OBJC },
index ff74e53..d1f6cba 100644 (file)
@@ -142,6 +142,7 @@ enum rid
   RID_IS_EMPTY,                RID_IS_ENUM,
   RID_IS_FINAL,                RID_IS_LITERAL_TYPE,
   RID_IS_POD,                  RID_IS_POLYMORPHIC,
+  RID_IS_SAME_AS,
   RID_IS_STD_LAYOUT,           RID_IS_TRIVIAL,
   RID_IS_TRIVIALLY_ASSIGNABLE, RID_IS_TRIVIALLY_CONSTRUCTIBLE,
   RID_IS_TRIVIALLY_COPYABLE,
@@ -150,6 +151,9 @@ enum rid
   /* C++11 */
   RID_CONSTEXPR, RID_DECLTYPE, RID_NOEXCEPT, RID_NULLPTR, RID_STATIC_ASSERT,
 
+  /* C++ concepts */
+  RID_CONCEPT, RID_REQUIRES,
+
   /* Cilk Plus keywords.  */
   RID_CILK_SPAWN, RID_CILK_SYNC, RID_CILK_FOR,
   
@@ -386,6 +390,9 @@ extern machine_mode c_default_pointer_mode;
 #define D_OBJC         0x080   /* In Objective C and neither C nor C++.  */
 #define D_CXX_OBJC     0x100   /* In Objective C, and C++, but not C.  */
 #define D_CXXWARN      0x200   /* In C warn with -Wcxx-compat.  */
+#define D_CXX_CONCEPTS  0x400   /* In C++, only with concepts. */
+
+#define D_CXX_CONCEPTS_FLAGS D_CXXONLY | D_CXX_CONCEPTS
 
 /* The reserved keyword table.  */
 extern const struct c_common_resword c_common_reswords[];
index 1beb2db..6e18a77 100644 (file)
@@ -865,6 +865,10 @@ c_cpp_builtins (cpp_reader *pfile)
          cpp_define (pfile, "__cpp_variable_templates=201304");
          cpp_define (pfile, "__cpp_digit_separators=201309");
        }
+      if (flag_concepts)
+       /* Use a value smaller than the 201507 specified in
+          the TS, since we don't yet support extended auto.  */
+       cpp_define (pfile, "__cpp_concepts=201500");
       if (flag_sized_deallocation)
        cpp_define (pfile, "__cpp_sized_deallocation=201309");
     }
index 73f5db0..3239a85 100644 (file)
@@ -1558,6 +1558,8 @@ set_std_cxx1z (int iso)
   /* C++11 includes the C99 standard library.  */
   flag_isoc94 = 1;
   flag_isoc99 = 1;
+  /* Enable concepts by default. */
+  flag_concepts = 1;
   flag_isoc11 = 1;
   cxx_dialect = cxx1z;
   lang_hooks.name = "GNU C++14"; /* Pretend C++14 till standarization.  */
index 4679038..e86ee12 100644 (file)
@@ -1086,6 +1086,10 @@ fcilkplus
 C ObjC C++ ObjC++ LTO Report Var(flag_cilkplus) Init(0)
 Enable Cilk Plus
 
+fconcepts
+C++ ObjC++ Var(flag_concepts)
+Enable support for C++ concepts
+
 fcond-mismatch
 C ObjC C++ ObjC++
 Allow the arguments of the '?' operator to have different types
index 946c2c2..2ecb8d9 100644 (file)
@@ -1,3 +1,186 @@
+2015-08-06  Andrew Sutton  <andrew.n.sutton@gmail.com>
+           Braden Obrzut  <admin@maniacsvault.net>
+           Jason Merrill  <jason@redhat.com>
+
+       Add C++ Concepts TS support.
+       * constraint.cc, logic.cc: New files.
+       * Make-lang.in (CXX_AND_OBJCXX_OBJS): Add constraint.o and logic.o.
+       (c++.tags): Also process .cc files.
+       * call.c (enum rejection_reason_code): Add rr_constraint_failure.
+       (print_z_candidate): Handle it.
+       (constraint_failure): New.
+       (add_function_candidate): Check constraints.
+       (build_new_function_call): Handle evaluating concepts.
+       (joust): Check more_constrained.
+       * class.c (add_method): Check equivalently_constrained.
+       (build_clone): Copy constraints.
+       (currently_open_class): Return tree.
+       (resolve_address_of_overloaded_function): Check constraints.
+       * constexpr.c (cxx_eval_constant_expression): Handle REQUIRES_EXPR.
+       (potential_constant_expression_1): Likewise.
+       * cp-objcp-common.c (cp_tree_size): Handle CONSTRAINT_INFO.
+       (cp_common_init_ts): Handle WILDCARD_DECL and REQUIRES_EXPR.
+       * cp-tree.def: Add CONSTRAINT_INFO, WILDCARD_DECL, REQUIRES_EXPR,
+       SIMPLE_REQ, TYPE_REQ, COMPOUND_REQ, NESTED_REQ, PRED_CONSTR,
+       EXPR_CONSTR, TYPE_CONSTR, ICONV_CONSTR, DEDUCT_CONSTR,
+       EXCEPT_CONSTR, PARM_CONSTR, CONJ_CONSTR, DISJ_CONSTR.
+       * cp-tree.h (struct tree_constraint_info, check_nonnull)
+       (check_constraint_info, CI_TEMPLATE_REQS, CI_DECLARATOR_REQS)
+       (CI_ASSOCIATED_CONSTRAINTS, CI_NORMALIZED_CONSTRAINTS)
+       (CI_ASSUMPTIONS, TEMPLATE_PARMS_CONSTRAINTS)
+       (TEMPLATE_PARM_CONSTRAINTS, COMPOUND_REQ_NOEXCEPT_P)
+       (PLACEHOLDER_TYPE_CONSTRAINTS, PRED_CONSTR_EXPR, EXPR_CONSTR_EXPR)
+       (TYPE_CONSTR_TYPE, ICONV_CONSTR_EXPR, ICONV_CONSTR_TYPE)
+       (DEDUCT_CONSTR_EXPR, DEDUCT_CONSTR_PATTERN)
+       (DEDUCT_CONSTR_PLACEHOLDER, EXCEPT_CONSTR_EXPR, PARM_CONSTR_PARMS)
+       (PARM_CONSTR_OPERAND, CONSTRAINT_VAR_P, CONSTRAINED_PARM_CONCEPT)
+       (CONSTRAINED_PARM_EXTRA_ARGS, CONSTRAINED_PARM_PROTOTYPE)
+       (DECL_DECLARED_CONCEPT_P, WILDCARD_PACK_P, struct cp_unevaluated)
+       (struct local_specialization_stack, enum auto_deduction_context)
+       (variable_concept_p, concept_template_p)
+       (struct deferring_access_check_sentinel): New.
+       (enum cp_tree_node_structure_enum): Add TS_CP_CONSTRAINT_INFO.
+       (union lang_tree_node): Add constraint_info field.
+       (struct lang_decl_base): Add concept_p flag.
+       (enum cp_decl_spec): Add ds_concept.
+       (struct cp_declarator): Add requires_clause.
+       * cxx-pretty-print.c (cxx_pretty_printer::primary_expression)
+       (cxx_pretty_printer::expression): Handle REQUIRES_EXPR,
+       TRAIT_EXPR, *_CONSTR.
+       (pp_cxx_parameter_declaration_clause): Accept a chain of
+       PARM_DECLs.
+       (cxx_pretty_printer::declarator): Print requires-clause.
+       (pp_cxx_template_declaration): Likewise.
+       (pp_cxx_trait_expression): Handle CPTK_IS_SAME_AS.
+       (pp_cxx_requires_clause, pp_cxx_requirement)
+       (pp_cxx_requirement_list, pp_cxx_requirement_body)
+       (pp_cxx_requires_expr, pp_cxx_simple_requirement)
+       (pp_cxx_type_requirement, pp_cxx_compound_requirement)
+       (pp_cxx_nested_requirement, pp_cxx_predicate_constraint)
+       (pp_cxx_expression_constraint, pp_cxx_type_constraint)
+       (pp_cxx_implicit_conversion_constraint)
+       (pp_cxx_argument_deduction_constraint)
+       (pp_cxx_exception_constraint, pp_cxx_parameterized_constraint)
+       (pp_cxx_conjunction, pp_cxx_disjunction, pp_cxx_constraint): New.
+       * cxx-pretty-print.h: Declare them.
+       * decl.c (decls_match): Compare constraints.
+       (duplicate_decls): Likewise.  Remove constraints before freeing.
+       (cxx_init_decl_processing): Call init_constraint_processing.
+       (cp_finish_decl): Diagnose concept without initializer.
+       (grokfndecl, grokvardecl): Handle concepts and constraints.
+       (grokdeclarator): Handle concept, requires-clause.
+       (grokparms): No longer static.
+       (xref_tag_1): Check constraints.
+       (finish_function): Call check_function_concept.
+       (cp_tree_node_structure): Handle CONSTRAINT_INFO.
+       (check_concept_refinement, is_concept_var, check_concept_fn): New.
+       * decl2.c (check_classfn): Compare constraints.
+       (mark_used): Don't instantiate concepts.
+       * error.c (dump_template_decl): Print constraints.
+       (dump_function_decl): Likewise.
+       (dump_expr): Handle REQUIRES_EXPR, *_REQ, *_CONSTR.
+       * lex.c (init_reswords): Set D_CXX_CONCEPTS.
+       * method.c (implicitly_declare_fn): Copy constraints from
+       inherited ctor.
+       * parser.h (struct cp_parser): Add in_result_type_constraint_p and
+       prevent_constrained_type_specifiers fields.
+       * parser.c (make_call_declarator): Add requires_clause parm.
+       (cp_parser_new): Clear prevent_constrained_type_specifiers.
+       (cp_parser_primary_expression): Handle RID_IS_SAME_AS, RID_REQUIRES.
+       (cp_parser_postfix_expression): Set prevent_constrained_type_specifiers.
+       (cp_parser_trait_expr): Handle RID_IS_SAME_AS.
+       (cp_parser_declaration): Handle concept introduction.
+       (cp_parser_member_declaration): Likewise.
+       (cp_parser_template_parameter): Handle constrained parameter.
+       (cp_parser_type_parameter): Handle constraints.
+       (cp_parser_decl_specifier_seq): Handle RID_CONCEPT.
+       (cp_parser_template_id): Handle partial concept id.
+       (cp_parser_type_name): Add overload that takes typename_keyword_p.
+       Handle constrained parameter.
+       (cp_parser_nonclass_name): Handle concept names.
+       (cp_parser_alias_declaration): Handle constraints.
+       (cp_parser_late_return_type_opt): Also handle requires-clause.
+       (cp_parser_type_id_1): Handle deduction constraint.
+       (cp_parser_parameter_declaration): Handle constrained parameters.
+       (cp_parser_class_specifier_1): Handle constraints.
+       (cp_parser_template_declaration_after_parameters): Split out from
+       cp_parser_template_declaration_after_export.
+       (cp_parser_single_declaration): Handle constraints.
+       (synthesize_implicit_template_parm): Handle constraints.
+       (cp_parser_maybe_concept_name, cp_parser_maybe_partial_concept_id)
+       (cp_parser_introduction_list, get_id_declarator)
+       (get_unqualified_id, is_constrained_parameter)
+       (cp_parser_check_constrained_type_parm)
+       (cp_parser_constrained_type_template_parm)
+       (cp_parser_constrained_template_template_parm)
+       (constrained_non_type_template_parm, finish_constrained_parameter)
+       (declares_constrained_type_template_parameter)
+       (declares_constrained_template_template_parameter)
+       (check_type_concept, cp_parser_maybe_constrained_type_specifier)
+       (cp_parser_maybe_concept_name, cp_parser_maybe_partial_concept_id)
+       (cp_parser_requires_clause, cp_parser_requires_clause_opt)
+       (cp_parser_requires_expression)
+       (cp_parser_requirement_parameter_list, cp_parser_requirement_body)
+       (cp_parser_requirement_list, cp_parser_requirement)
+       (cp_parser_simple_requirement, cp_parser_type_requirement)
+       (cp_parser_compound_requirement, cp_parser_nested_requirement)
+       (cp_parser_template_introduction)
+       (cp_parser_explicit_template_declaration)
+       (get_concept_from_constraint): New.
+       * pt.c (local_specialization_stack): Implement.
+       (maybe_new_partial_specialization): New.
+       (maybe_process_partial_specialization): Use it.
+       (retrieve_local_specialization, register_local_specialization)
+       (template_parm_to_arg, build_template_decl, extract_fnparm_pack)
+       (tsubst_expr): No longer static.
+       (spec_hasher::equal): Compare constraints.
+       (determine_specialization): Handle constraints.
+       (check_explicit_specialization): Handle concepts.
+       (process_template_parm): Handle constraints.
+       (end_template_parm_list): Add overload taking no arguments.
+       (process_partial_specialization): Handle concepts and constraints.
+       Register partial specializations of variable templates.
+       (redeclare_class_template): Handle constraints.
+       (convert_template_argument): Handle WILDCARD_DECL.  Check
+       is_compatible_template_arg.
+       (coerce_template_parameter_pack): Handle wildcard packs.
+       (coerce_template_parms): DR 1430 also applies to concepts.  Add
+       overloads taking fewer parameters.
+       (lookup_template_class_1): Handle constraints.
+       (lookup_template_variable): Concepts are always bool.
+       (finish_template_variable): Handle concepts and constraints.
+       (tsubst_friend_class): Handle constraints.
+       (gen_elem_of_pack_expansion_instantiation): Handle constraints.
+       (tsubst_pack_expansion): Handle local parameters.
+       (tsubst_decl) [FUNCTION_DECL]: Handle constraints.
+       (tsubst) [TEMPLATE_TYPE_PARM]: Handle deduction constraints.
+       (tsubst_copy_and_build): Handle REQUIRES_EXPR.
+       (more_specialized_fn, more_specialized_partial_spec): Check constraints.
+       (more_specialized_inst): Split out from most_specialized_instantiation.
+       (most_specialized_partial_spec): Check constraints.
+       (instantiate_decl): Never instantiate a concept.
+       (value_dependent_expression_p): Handle REQUIRES_EXPR, TYPE_REQ,
+       variable concepts.
+       (type_dependent_expression_p): Handle WILDCARD_DECL, REQUIRES_EXPR.
+       (instantiation_dependent_r): Handle REQUIRES_EXPR and concepts.
+       (do_auto_deduction): Add overload taking tsubst flags and context enum.
+       Handle constraints.
+       (get_template_for_ordering, most_constrained_function)
+       (is_compatible_template_arg, convert_wildcard_argument)
+       (struct constr_entry, struct constr_hasher, decl_constraints)
+       (valid_constraints_p, get_constraints, set_constraints)
+       (remove_constraints, init_constraint_processing): New.
+       * ptree.c (cxx_print_xnode): Handle CONSTRAINT_INFO.
+       * search.c (lookup_member): Do lookup in the open partial
+       instantiation.
+       * semantics.c (finish_template_template_parm): Handle constraints.
+       (fixup_template_type): New.
+       (finish_template_type): Call it.
+       (trait_expr_value, finish_trait_expr): Handle CPTK_IS_SAME_AS.
+       * tree.c (cp_tree_equal): Handle local parameters, CONSTRAINT_INFO.
+       (cp_walk_subtrees): Handle REQUIRES_EXPR.
+       * typeck.c (cp_build_function_call_vec): Check constraints.
+
 2015-08-06  Jason Merrill  <jason@redhat.com>
 
        PR c++/66533
index a0847be..a16f228 100644 (file)
@@ -78,7 +78,8 @@ CXX_AND_OBJCXX_OBJS = cp/call.o cp/decl.o cp/expr.o cp/pt.o cp/typeck2.o \
  cp/mangle.o cp/cp-objcp-common.o cp/name-lookup.o cp/cxx-pretty-print.o \
  cp/cp-cilkplus.o \
  cp/cp-gimplify.o cp/cp-array-notation.o cp/lambda.o \
- cp/vtable-class-hierarchy.o cp/constexpr.o cp/cp-ubsan.o $(CXX_C_OBJS)
+ cp/vtable-class-hierarchy.o cp/constexpr.o cp/cp-ubsan.o \
+ cp/constraint.o cp/logic.o $(CXX_C_OBJS)
 
 # Language-specific object files for C++.
 CXX_OBJS = cp/cp-lang.o c-family/stub-objc.o $(CXX_AND_OBJCXX_OBJS)
@@ -131,7 +132,7 @@ c++.srcinfo:
 c++.srcextra:
 
 c++.tags: force
-       cd $(srcdir)/cp; etags -o TAGS.sub *.c *.h --language=none \
+       cd $(srcdir)/cp; etags -o TAGS.sub *.c *.cc *.h --language=none \
          --regex='/DEFTREECODE [(]\([A-Z_]+\)/\1/' cp-tree.def; \
        etags --include TAGS.sub --include ../TAGS.sub
 
index 3b0fd69..4823d37 100644 (file)
@@ -425,7 +425,8 @@ enum rejection_reason_code {
   rr_arg_conversion,
   rr_bad_arg_conversion,
   rr_template_unification,
-  rr_invalid_copy
+  rr_invalid_copy,
+  rr_constraint_failure
 };
 
 struct conversion_info {
@@ -688,6 +689,27 @@ invalid_copy_with_fn_template_rejection (void)
   return r;
 }
 
+// Build a constraint failure record, saving information into the
+// template_instantiation field of the rejection. If FN is not a template
+// declaration, the TMPL member is the FN declaration and TARGS is empty.
+
+static struct rejection_reason *
+constraint_failure (tree fn)
+{
+  struct rejection_reason *r = alloc_rejection (rr_constraint_failure);
+  if (tree ti = DECL_TEMPLATE_INFO (fn))
+    {
+      r->u.template_instantiation.tmpl = TI_TEMPLATE (ti);
+      r->u.template_instantiation.targs = TI_ARGS (ti);
+    }
+  else
+    {
+      r->u.template_instantiation.tmpl = fn;
+      r->u.template_instantiation.targs = NULL_TREE;
+    }
+  return r;
+}
+
 /* Dynamically allocate a conversion.  */
 
 static conversion *
@@ -1957,10 +1979,20 @@ add_function_candidate (struct z_candidate **candidates,
       viable = 0;
       reason = arity_rejection (first_arg, i + remaining, len);
     }
+
+  /* Second, for a function to be viable, its constraints must be
+     satisfied. */
+  if (flag_concepts && viable
+      && !constraints_satisfied_p (fn))
+    {
+      reason = constraint_failure (fn);
+      viable = false;
+    }
+
   /* When looking for a function from a subobject from an implicit
      copy/move constructor/operator=, don't consider anything that takes (a
      reference to) an unrelated type.  See c++/44909 and core 1092.  */
-  else if (parmlist && (flags & LOOKUP_DEFAULTED))
+  if (viable && parmlist && (flags & LOOKUP_DEFAULTED))
     {
       if (DECL_CONSTRUCTOR_P (fn))
        i = 1;
@@ -1984,7 +2016,7 @@ add_function_candidate (struct z_candidate **candidates,
   if (! viable)
     goto out;
 
-  /* Second, for F to be a viable function, there shall exist for each
+  /* Third, for F to be a viable function, there shall exist for each
      argument an implicit conversion sequence that converts that argument
      to the corresponding parameter of F.  */
 
@@ -3387,6 +3419,13 @@ print_z_candidate (location_t loc, const char *msgstr,
                  "  a constructor taking a single argument of its own "
                  "class type is invalid");
          break;
+       case rr_constraint_failure:
+         {
+           tree tmpl = r->u.template_instantiation.tmpl;
+           tree args = r->u.template_instantiation.targs;
+           diagnose_constraints (cloc, tmpl, args);
+         }
+         break;
        case rr_none:
        default:
          /* This candidate didn't have any issues or we failed to
@@ -4044,9 +4083,13 @@ build_new_function_call (tree fn, vec<tree, va_gc> **args, bool koenig_p,
     {
       if (complain & tf_error)
        {
+         // If there is a single (non-viable) function candidate,
+         // let the error be diagnosed by cp_build_function_call_vec.
          if (!any_viable_p && candidates && ! candidates->next
              && (TREE_CODE (candidates->fn) == FUNCTION_DECL))
            return cp_build_function_call_vec (candidates->fn, args, complain);
+
+         // Otherwise, emit notes for non-viable candidates.
          if (TREE_CODE (fn) == TEMPLATE_ID_EXPR)
            fn = TREE_OPERAND (fn, 0);
          print_error_for_call_failure (fn, *args, candidates);
@@ -4061,7 +4104,26 @@ build_new_function_call (tree fn, vec<tree, va_gc> **args, bool koenig_p,
          through flags so that later we can use it to decide whether to warn
          about peculiar null pointer conversion.  */
       if (TREE_CODE (fn) == TEMPLATE_ID_EXPR)
-        flags |= LOOKUP_EXPLICIT_TMPL_ARGS;
+        {
+          /* If overload resolution selects a specialization of a
+             function concept for non-dependent template arguments,
+             the expression is true if the constraints are satisfied
+             and false otherwise.
+
+             NOTE: This is an extension of Concepts Lite TS that
+             allows constraints to be used in expressions. */
+          if (flag_concepts && !processing_template_decl)
+            {
+              tree tmpl = DECL_TI_TEMPLATE (cand->fn);
+              tree targs = DECL_TI_ARGS (cand->fn);
+              tree decl = DECL_TEMPLATE_RESULT (tmpl);
+              if (DECL_DECLARED_CONCEPT_P (decl))
+                return evaluate_function_concept (decl, targs);
+            }
+
+          flags |= LOOKUP_EXPLICIT_TMPL_ARGS;
+        }
+
       result = build_over_call (cand, flags, complain);
     }
 
@@ -9095,6 +9157,15 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
        return winner;
     }
 
+    // C++ Concepts
+    // or, if not that, F1 is more constrained than F2.
+    if (flag_concepts)
+      {
+        winner = more_constrained (cand1->fn, cand2->fn);
+        if (winner)
+          return winner;
+      }
+
   /* Check whether we can discard a builtin candidate, either because we
      have two identical ones or matching builtin and non-builtin candidates.
 
index f659fd4..bf7b5c4 100644 (file)
@@ -1138,7 +1138,8 @@ add_method (tree type, tree method, tree using_decl)
       if (compparms (parms1, parms2)
          && (!DECL_CONV_FN_P (fn)
              || same_type_p (TREE_TYPE (fn_type),
-                             TREE_TYPE (method_type))))
+                             TREE_TYPE (method_type)))
+          && equivalently_constrained (fn, method))
        {
          /* For function versions, their parms and types match
             but they are not duplicates.  Record function versions
@@ -4602,6 +4603,14 @@ build_clone (tree fn, tree name)
       TREE_TYPE (clone) = TREE_TYPE (result);
       return clone;
     }
+  else
+    {
+      // Clone constraints.
+      if (flag_concepts)
+        if (tree ci = get_constraints (fn))
+          set_constraints (clone, copy_node (ci));
+    }
+
 
   SET_DECL_ASSEMBLER_NAME (clone, NULL_TREE);
   DECL_CLONED_FUNCTION (clone) = fn;
@@ -7281,15 +7290,17 @@ pop_class_stack (void)
 }
 
 /* Returns 1 if the class type currently being defined is either T or
-   a nested type of T.  */
+   a nested type of T.  Returns the type from the current_class_stack,
+   which might be equivalent to but not equal to T in case of
+   constrained partial specializations.  */
 
-bool
+tree
 currently_open_class (tree t)
 {
   int i;
 
   if (!CLASS_TYPE_P (t))
-    return false;
+    return NULL_TREE;
 
   t = TYPE_MAIN_VARIANT (t);
 
@@ -7309,9 +7320,9 @@ currently_open_class (tree t)
       if (!c)
        continue;
       if (same_type_p (c, t))
-       return true;
+       return c;
     }
-  return false;
+  return NULL_TREE;
 }
 
 /* If either current_class_type or one of its enclosing classes are derived
@@ -7661,6 +7672,12 @@ resolve_address_of_overloaded_function (tree target_type,
            /* Instantiation failed.  */
            continue;
 
+         /* Constraints must be satisfied. This is done before
+            return type deduction since that instantiates the
+            function. */
+         if (flag_concepts && !constraints_satisfied_p (instantiation))
+           continue;
+
          /* And now force instantiation to do return type deduction.  */
          if (undeduced_auto_decl (instantiation))
            {
index fc4a3f7..218faec 100644 (file)
@@ -3525,6 +3525,25 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
                            non_constant_p, overflow_p, jump_target);
       break;
 
+    case REQUIRES_EXPR:
+      /* It's possible to get a requires-expression in a constant
+         expression. For example:
+
+             template<typename T> concept bool C() {
+               return requires (T t) { t; };
+             }
+
+             template<typename T> requires !C<T>() void f(T);
+
+         Normalization leaves f with the associated constraint
+         '!requires (T t) { ... }' which is not transformed into
+         a constraint.  */
+      if (!processing_template_decl)
+        return evaluate_constraint_expression (t, NULL_TREE);
+      else
+        *non_constant_p = true;
+      return t;
+
     default:
       if (STATEMENT_CODE_P (TREE_CODE (t)))
        {
@@ -3897,6 +3916,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
     case PLACEHOLDER_EXPR:
     case BREAK_STMT:
     case CONTINUE_STMT:
+    case REQUIRES_EXPR:
       return true;
 
     case AGGR_INIT_EXPR:
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
new file mode 100644 (file)
index 0000000..cf57cc0
--- /dev/null
@@ -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);
+}
index 31f12c8..2cab89c 100644 (file)
@@ -100,6 +100,8 @@ cp_tree_size (enum tree_code code)
 
     case TEMPLATE_INFO:         return sizeof (struct tree_template_info);
 
+    case CONSTRAINT_INFO:       return sizeof (struct tree_constraint_info);
+
     case USERDEF_LITERAL:      return sizeof (struct tree_userdef_literal);
 
     case TEMPLATE_DECL:                return sizeof (struct tree_template_decl);
@@ -240,6 +242,7 @@ cp_common_init_ts (void)
 {
   MARK_TS_DECL_NON_COMMON (USING_DECL);
   MARK_TS_DECL_COMMON (TEMPLATE_DECL);
+  MARK_TS_DECL_COMMON (WILDCARD_DECL);
 
   MARK_TS_COMMON (TEMPLATE_TEMPLATE_PARM);
   MARK_TS_COMMON (TEMPLATE_TYPE_PARM);
@@ -311,6 +314,7 @@ cp_common_init_ts (void)
   MARK_TS_TYPED (LAMBDA_EXPR);
   MARK_TS_TYPED (CTOR_INITIALIZER);
   MARK_TS_TYPED (ARRAY_NOTATION_REF);
+  MARK_TS_TYPED (REQUIRES_EXPR);
 }
 
 #include "gt-cp-cp-objcp-common.h"
index 6117771..61acf27 100644 (file)
@@ -475,6 +475,94 @@ DEFTREECODE (BASES, "bases", tcc_type, 0)
    instantiation time.  */
 DEFTREECODE (TEMPLATE_INFO, "template_info", tcc_exceptional, 0)
 
+/* Extensions for Concepts. */
+
+/* Used to represent information associated with constrained declarations. */
+DEFTREECODE (CONSTRAINT_INFO, "constraint_info", tcc_exceptional, 0)
+
+/* A wildcard declaration is a placeholder for a template parameter
+   used to resolve constrained-type-names in concepts.  During
+   resolution, the matching argument is saved as the TREE_TYPE
+   of the wildcard.  */
+DEFTREECODE (WILDCARD_DECL, "wildcard_decl", tcc_declaration, 0)
+
+/* A requires-expr is a binary expression. The first operand is
+   its parameter list (possibly NULL). The second is a list of
+   requirements, which are denoted by the _REQ* tree codes
+   below. */
+DEFTREECODE (REQUIRES_EXPR,   "requires_expr", tcc_expression, 2)
+
+/* A requirement for an expression. */
+DEFTREECODE (SIMPLE_REQ, "simple_req", tcc_expression, 1)
+
+/* A requirement for a type. */
+DEFTREECODE (TYPE_REQ, "type_req", tcc_expression, 1)
+
+/* A requirement for an expression and its properties. The
+   first operand is the expression, and the 2nd is its type.
+   The accessor COMPOUND_REQ_NOEXCEPT determines whether
+   the noexcept keyword was present. */
+DEFTREECODE (COMPOUND_REQ, "compound_req", tcc_expression, 2)
+
+/* A requires clause within a requires expression. */
+DEFTREECODE (NESTED_REQ, "nested_req", tcc_expression, 1)
+
+/* Constraints are modeled as kinds of expressions.
+   The operands of a constraint can be either types or expressions.
+   Unlike expressions, constraints do not have a type. */
+
+/* A predicate constraint evaluates an expression E.
+
+   PRED_CONSTR_EXPR has the expression to be evaluated. */
+DEFTREECODE (PRED_CONSTR, "pred_constr", tcc_expression, 1)
+
+/* An expression constraint determines the validity of a expression E.
+
+   EXPR_CONST_EXPR has the expression being validated. */
+DEFTREECODE (EXPR_CONSTR, "expr_constr", tcc_expression, 1)
+
+/* A type constraint determines the validity of a type T. Note that
+
+   TYPE_CONST_TYPE has the type being validated */
+DEFTREECODE (TYPE_CONSTR, "type_constr", tcc_expression, 1)
+
+/* An implicit conversion constraint determines if an expression
+   E is implicitly convertible to a type T. Note that T may
+   be dependent but does not contain any placeholders.
+
+   ICONV_CONSTR_EXPR has the expression E.
+   ICONV_CONSTR_TYPE has the type T.
+   */
+DEFTREECODE (ICONV_CONSTR, "iconv_constr", tcc_expression, 2)
+
+/* An argument deduction constraint determines if the type of an
+   expression E can be deduced from a type pattern T. Note that
+   T must contain at least one place holder.
+
+   DEDUCT_CONSTR_EXPR has the expression E
+   DEDUCT_CONSTR_PATTERN has the type patter T.
+   DEDUCT_CONSTR_PLACEHOLDERS has the list of placeholder nodes in T. */
+DEFTREECODE (DEDUCT_CONSTR, "deduct_constr", tcc_expression, 3)
+
+/* An exception constraint determines if, for an expression E,
+   noexcept(E) is true.
+
+   EXCEPT_CONSTR_EXPR has the expression E. */
+DEFTREECODE (EXCEPT_CONSTR, "except_constr", tcc_expression, 1)
+
+/* A parameterized constraint declares constraint variables, which
+   are used in expression, type, and exception constraints.
+
+   PARM_CONSTR_PARMS has a TREE_LIST of parameter declarations.
+   PARM_CONSTR_OPERAND has the nested constraint. */
+DEFTREECODE (PARM_CONSTR, "parm_constr", tcc_expression, 2)
+
+/* The conjunction and disjunction of two constraints, respectively.
+   Operands are accessed using TREE_OPERAND. */
+DEFTREECODE (CONJ_CONSTR, "conj_constr", tcc_expression, 2)
+DEFTREECODE (DISJ_CONSTR, "disj_constr", tcc_expression, 2)
+
+
 /*
 Local variables:
 mode:c
index e70dcb4..78fd4af 100644 (file)
@@ -77,6 +77,8 @@ c-common.h, not after.
       PACK_EXPANSION_LOCAL_P (in *_PACK_EXPANSION)
       TINFO_HAS_ACCESS_ERRORS (in TEMPLATE_INFO)
       SIZEOF_EXPR_TYPE_P (in SIZEOF_EXPR)
+      COMPOUND_REQ_NOEXCEPT_P (in COMPOUND_REQ)
+      WILDCARD_PACK_P (in WILDCARD_DECL)
       BLOCK_OUTER_CURLY_BRACE_P (in BLOCK)
    1: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE)
       TI_PENDING_TEMPLATE_FLAG.
@@ -154,6 +156,7 @@ c-common.h, not after.
       LABEL_DECL_CONTINUE (in LABEL_DECL)
    2: DECL_THIS_EXTERN (in VAR_DECL or FUNCTION_DECL).
       DECL_IMPLICIT_TYPEDEF_P (in a TYPE_DECL)
+      DECL_CONSTRAINT_VAR_P (in a PARM_DECL)
       TEMPLATE_DECL_COMPLEX_ALIAS_P (in TEMPLATE_DECL)
       DECL_INSTANTIATING_NSDMI_P (in a FIELD_DECL)
    3: DECL_IN_AGGR_P.
@@ -660,6 +663,7 @@ typedef enum cp_trait_kind
   CPTK_IS_LITERAL_TYPE,
   CPTK_IS_POD,
   CPTK_IS_POLYMORPHIC,
+  CPTK_IS_SAME_AS,
   CPTK_IS_STD_LAYOUT,
   CPTK_IS_TRIVIAL,
   CPTK_IS_TRIVIALLY_ASSIGNABLE,
@@ -812,6 +816,154 @@ struct GTY(()) tree_template_info {
   vec<qualified_typedef_usage_t, va_gc> *typedefs_needing_access_checking;
 };
 
+// Constraint information for a C++ declaration. Constraint information is
+// comprised of:
+//
+// - a constraint expression introduced by the template header
+// - a constraint expression introduced by a function declarator
+// - the associated constraints, which are the conjunction of those,
+//   and used for declaration matching
+// - the cached normalized associated constraints which are used
+//   to support satisfaction and subsumption.
+// - assumptions which is the result of decomposing the normalized
+//   constraints.
+//
+// The template and declarator requirements are kept to support pretty
+// printing constrained declarations.
+struct GTY(()) tree_constraint_info {
+  struct tree_base base;
+  tree template_reqs;
+  tree declarator_reqs;
+  tree associated_constr;
+  tree normalized_constr;
+  tree assumptions;
+};
+
+// Require that pointer P is non-null before returning.
+template<typename T>
+inline T*
+check_nonnull (T* p)
+{
+  gcc_assert (p);
+  return p;
+}
+
+// Returns true iff T is non-null and represents constraint info.
+inline tree_constraint_info *
+check_constraint_info (tree t)
+{
+  if (t && TREE_CODE (t) == CONSTRAINT_INFO)
+    return (tree_constraint_info *)t;
+  return NULL;
+}
+
+// Access the expression describing the template constraints. This may be
+// null if no constraints were introduced in the template parameter list,
+// a requirements clause after the template parameter list, or constraints
+// through a constrained-type-specifier.
+#define CI_TEMPLATE_REQS(NODE) \
+  check_constraint_info (check_nonnull(NODE))->template_reqs
+
+// Access the expression describing the trailing constraints. This is non-null
+// for any implicit instantiation of a constrained declaration. For a
+// templated declaration it is non-null only when a trailing requires-clause
+// was specified.
+#define CI_DECLARATOR_REQS(NODE) \
+  check_constraint_info (check_nonnull(NODE))->declarator_reqs
+
+// The computed associated constraint expression for a declaration.
+#define CI_ASSOCIATED_CONSTRAINTS(NODE) \
+  check_constraint_info (check_nonnull(NODE))->associated_constr
+
+// The normalized associated constraints.
+#define CI_NORMALIZED_CONSTRAINTS(NODE) \
+  check_constraint_info (check_nonnull(NODE))->normalized_constr
+
+// Get the set of assumptions associated with the constraint info node.
+#define CI_ASSUMPTIONS(NODE) \
+  check_constraint_info (check_nonnull(NODE))->assumptions
+
+// Access the logical constraints on the template parameters introduced
+// at a given template parameter list level indicated by NODE.
+#define TEMPLATE_PARMS_CONSTRAINTS(NODE) \
+  TREE_TYPE (TREE_LIST_CHECK (NODE))
+
+// Access the logical constraints on the template parameter declaration
+// indicated by NODE.
+#define TEMPLATE_PARM_CONSTRAINTS(NODE) \
+  TREE_TYPE (TREE_LIST_CHECK (NODE))
+
+/* Non-zero if the noexcept is present in a compound requirement. */
+#define COMPOUND_REQ_NOEXCEPT_P(NODE) \
+  TREE_LANG_FLAG_0 (TREE_CHECK (NODE, COMPOUND_REQ))
+
+/* The constraints on an 'auto' placeholder type, used in an argument deduction
+   constraint.  */
+#define PLACEHOLDER_TYPE_CONSTRAINTS(NODE) \
+  DECL_SIZE_UNIT (TYPE_NAME (NODE))
+
+/* The expression evaluated by the predicate constraint. */
+#define PRED_CONSTR_EXPR(NODE) \
+  TREE_OPERAND (TREE_CHECK (NODE, PRED_CONSTR), 0)
+
+/* The expression validated by the predicate constraint. */
+#define EXPR_CONSTR_EXPR(NODE) \
+  TREE_OPERAND (TREE_CHECK (NODE, EXPR_CONSTR), 0)
+
+/* The type validated by the predicate constraint. */
+#define TYPE_CONSTR_TYPE(NODE) \
+  TREE_OPERAND (TREE_CHECK (NODE, TYPE_CONSTR), 0)
+
+/* In an implicit conversion constraint, the source expression. */
+#define ICONV_CONSTR_EXPR(NODE) \
+  TREE_OPERAND (TREE_CHECK (NODE, ICONV_CONSTR), 0)
+
+/* In an implicit conversion constraint, the target type. */
+#define ICONV_CONSTR_TYPE(NODE) \
+  TREE_OPERAND (TREE_CHECK (NODE, ICONV_CONSTR), 1)
+
+/* In an argument deduction constraint, the source expression. */
+#define DEDUCT_CONSTR_EXPR(NODE) \
+  TREE_OPERAND (TREE_CHECK (NODE, DEDUCT_CONSTR), 0)
+
+/* In an argument deduction constraint, the target type pattern. */
+#define DEDUCT_CONSTR_PATTERN(NODE) \
+  TREE_OPERAND (TREE_CHECK (NODE, DEDUCT_CONSTR), 1)
+
+/* In an argument deduction constraint, the list of placeholder nodes. */
+#define DEDUCT_CONSTR_PLACEHOLDER(NODE) \
+  TREE_OPERAND (TREE_CHECK (NODE, DEDUCT_CONSTR), 2)
+
+/* The expression of an exception constraint. */
+#define EXCEPT_CONSTR_EXPR(NODE) \
+  TREE_OPERAND (TREE_CHECK (NODE, EXCEPT_CONSTR), 0)
+
+/* In a parameterized constraint, the local parameters. */
+#define PARM_CONSTR_PARMS(NODE) \
+  TREE_OPERAND (TREE_CHECK (NODE, PARM_CONSTR), 0)
+
+/* In a parameterized constraint, the operand. */
+#define PARM_CONSTR_OPERAND(NODE) \
+  TREE_OPERAND (TREE_CHECK (NODE, PARM_CONSTR), 1)
+
+/* Whether a PARM_DECL represents a local parameter in a
+   requires-expression.  */
+#define CONSTRAINT_VAR_P(NODE) \
+  DECL_LANG_FLAG_2 (TREE_CHECK (NODE, PARM_DECL))
+
+/* The concept constraining this constrained template-parameter.  */
+#define CONSTRAINED_PARM_CONCEPT(NODE) \
+  DECL_SIZE_UNIT (TYPE_DECL_CHECK (NODE))
+/* Any extra template arguments specified for a constrained
+   template-parameter.  */
+#define CONSTRAINED_PARM_EXTRA_ARGS(NODE) \
+  DECL_SIZE (TYPE_DECL_CHECK (NODE))
+/* The first template parameter of CONSTRAINED_PARM_CONCEPT to be used as a
+   prototype for the constrained parameter in finish_shorthand_constraint,
+   attached for convenience.  */
+#define CONSTRAINED_PARM_PROTOTYPE(NODE) \
+  DECL_INITIAL (TYPE_DECL_CHECK (NODE))
+
 enum cp_tree_node_structure_enum {
   TS_CP_GENERIC,
   TS_CP_IDENTIFIER,
@@ -829,6 +981,7 @@ enum cp_tree_node_structure_enum {
   TS_CP_TRAIT_EXPR,
   TS_CP_LAMBDA_EXPR,
   TS_CP_TEMPLATE_INFO,
+  TS_CP_CONSTRAINT_INFO,
   TS_CP_USERDEF_LITERAL,
   LAST_TS_CP_ENUM
 };
@@ -856,6 +1009,8 @@ union GTY((desc ("cp_tree_node_structure (&%h)"),
     lambda_expression;
   struct tree_template_info GTY ((tag ("TS_CP_TEMPLATE_INFO")))
     template_info;
+  struct tree_constraint_info GTY ((tag ("TS_CP_CONSTRAINT_INFO")))
+    constraint_info;
   struct tree_userdef_literal GTY ((tag ("TS_CP_USERDEF_LITERAL")))
     userdef_literal;
 };
@@ -2021,7 +2176,8 @@ struct GTY(()) lang_decl_base {
   unsigned template_conv_p : 1;                   /* var or template */
   unsigned odr_used : 1;                  /* var or fn */
   unsigned u2sel : 1;
-  /* 1 spare bit */
+  unsigned concept_p : 1;                  /* applies to vars and functions */
+  /* 0 spare bits */
 };
 
 /* True for DECL codes which have template info and access.  */
@@ -2569,6 +2725,12 @@ struct GTY(()) lang_decl {
 #define DECL_DECLARED_CONSTEXPR_P(DECL) \
   DECL_LANG_FLAG_8 (VAR_OR_FUNCTION_DECL_CHECK (STRIP_TEMPLATE (DECL)))
 
+// True if NODE was declared as 'concept'.  The flag implies that the
+// declaration is constexpr, that the declaration cannot be specialized or
+// refined, and that the result type must be convertible to bool.
+#define DECL_DECLARED_CONCEPT_P(NODE) \
+  (DECL_LANG_SPECIFIC (NODE)->u.base.concept_p)
+
 /* Nonzero if this DECL is the __PRETTY_FUNCTION__ variable in a
    template function.  */
 #define DECL_PRETTY_FUNCTION_P(NODE) \
@@ -3036,6 +3198,9 @@ extern void decl_shadowed_for_var_insert (tree, tree);
 /* True iff this pack expansion is within a function context.  */
 #define PACK_EXPANSION_LOCAL_P(NODE) TREE_LANG_FLAG_0 (NODE)
 
+/* True iff the wildcard can match a template parameter pack.  */
+#define WILDCARD_PACK_P(NODE) TREE_LANG_FLAG_0 (NODE)
+
 /* Determine if this is an argument pack.  */
 #define ARGUMENT_PACK_P(NODE)                          \
   (TREE_CODE (NODE) == TYPE_ARGUMENT_PACK              \
@@ -4504,6 +4669,15 @@ extern int comparing_specializations;
 
 extern int cp_unevaluated_operand;
 
+/* RAII class used to inhibit the evaluation of operands during parsing
+   and template instantiation. Evaluation warnings are also inhibited. */
+
+struct cp_unevaluated
+{
+  cp_unevaluated ();
+  ~cp_unevaluated ();
+};
+
 /* in pt.c  */
 
 /* These values are used for the `STRICT' parameter to type_unification and
@@ -4516,6 +4690,17 @@ typedef enum unification_kind_t {
   DEDUCE_EXACT
 } unification_kind_t;
 
+// An RAII class used to create a new pointer map for local
+// specializations. When the stack goes out of scope, the
+// previous pointer map is restored.
+struct local_specialization_stack
+{
+  local_specialization_stack ();
+  ~local_specialization_stack ();
+
+  hash_map<tree, tree> *saved;
+};
+
 /* in class.c */
 
 extern int current_class_depth;
@@ -4810,6 +4995,17 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
 #define TEMPLATE_TYPE_PARAMETER_PACK(NODE) \
   (TEMPLATE_PARM_PARAMETER_PACK (TEMPLATE_TYPE_PARM_INDEX (NODE)))
 
+/* Contexts in which auto deduction occurs. These flags are
+   used to control diagnostics in do_auto_deduction.  */
+
+enum auto_deduction_context
+{
+  adc_unspecified,   /* Not given */
+  adc_variable_type, /* Variable initializer deduction */
+  adc_return_type,   /* Return type deduction */
+  adc_requirement    /* Argument dedution constraint */
+};
+
 /* True iff this TEMPLATE_TYPE_PARM represents decltype(auto).  */
 #define AUTO_IS_DECLTYPE(NODE) \
   (TYPE_LANG_FLAG_5 (TEMPLATE_TYPE_PARM_CHECK (NODE)))
@@ -4963,6 +5159,7 @@ typedef enum cp_decl_spec {
   ds_std_attribute,
   ds_storage_class,
   ds_long_long,
+  ds_concept,
   ds_last /* This enumerator must always be the last one.  */
 } cp_decl_spec;
 
@@ -5092,6 +5289,8 @@ struct cp_declarator {
       tree exception_specification;
       /* The late-specified return type, if any.  */
       tree late_return_type;
+      /* The trailing requires-clause, if any. */
+      tree requires_clause;
     } function;
     /* For arrays.  */
     struct {
@@ -5157,7 +5356,7 @@ class_of_this_parm (const_tree fntype)
   return TREE_TYPE (type_of_this_parm (fntype));
 }
 
-/* True if T designates a variable template declaration.  */
+/* True iff T is a variable template declaration. */
 inline bool
 variable_template_p (tree t)
 {
@@ -5170,6 +5369,30 @@ variable_template_p (tree t)
   return false;
 }
 
+/* True iff T is a variable concept definition. That is, T is
+   a variable template declared with the concept specifier. */
+inline bool
+variable_concept_p (tree t)
+{
+  if (TREE_CODE (t) != TEMPLATE_DECL)
+    return false;
+  if (tree r = DECL_TEMPLATE_RESULT (t))
+    return VAR_P (r) && DECL_DECLARED_CONCEPT_P (r);
+  return false;
+}
+
+/* True iff T is a concept definition. That is, T is a variable or function
+   template declared with the concept specifier. */
+inline bool
+concept_template_p (tree t)
+{
+  if (TREE_CODE (t) != TEMPLATE_DECL)
+    return false;
+  if (tree r = DECL_TEMPLATE_RESULT (t))
+    return VAR_OR_FUNCTION_DECL_P (r) && DECL_DECLARED_CONCEPT_P (r);
+  return false;
+}
+
 /* A parameter list indicating for a function with no parameters,
    e.g  "int f(void)".  */
 extern cp_parameter_declarator *no_parameters;
@@ -5266,7 +5489,7 @@ extern tree get_vtable_decl                       (tree, int);
 extern void resort_type_method_vec             (void *, void *,
                                                 gt_pointer_operator, void *);
 extern bool add_method                         (tree, tree, tree);
-extern bool currently_open_class               (tree);
+extern tree currently_open_class               (tree);
 extern tree currently_open_derived_class       (tree);
 extern tree outermost_open_class               (void);
 extern tree current_nonlambda_class_type       (void);
@@ -5402,6 +5625,7 @@ extern tree build_ptrmemfunc_type         (tree);
 extern tree build_ptrmem_type                  (tree, tree);
 /* the grokdeclarator prototype is in decl.h */
 extern tree build_this_parm                    (tree, cp_cv_quals);
+extern tree grokparms                          (tree, tree *);
 extern int copy_fn_p                           (const_tree);
 extern bool move_fn_p                           (const_tree);
 extern bool move_signature_fn_p                 (const_tree);
@@ -5679,7 +5903,10 @@ extern int num_template_headers_for_class        (tree);
 extern void check_template_variable            (tree);
 extern tree make_auto                          (void);
 extern tree make_decltype_auto                 (void);
-extern tree do_auto_deduction                  (tree, tree, tree);
+extern tree do_auto_deduction                   (tree, tree, tree);
+extern tree do_auto_deduction                   (tree, tree, tree,
+                                                 tsubst_flags_t,
+                                                 auto_deduction_context);
 extern tree type_uses_auto                     (tree);
 extern tree type_uses_auto_or_concept          (tree);
 extern void append_type_to_template_for_access_check (tree, tree, tree,
@@ -5691,13 +5918,14 @@ extern bool is_auto_or_concept                  (const_tree);
 extern tree process_template_parm              (tree, location_t, tree, 
                                                 bool, bool);
 extern tree end_template_parm_list             (tree);
+extern void end_template_parm_list             (void);
 extern void end_template_decl                  (void);
 extern tree maybe_update_decl_type             (tree, tree);
 extern bool check_default_tmpl_args             (tree, tree, bool, bool, int);
 extern tree push_template_decl                 (tree);
 extern tree push_template_decl_real            (tree, bool);
 extern tree add_inherited_template_parms       (tree, tree);
-extern bool redeclare_class_template           (tree, tree);
+extern bool redeclare_class_template           (tree, tree, tree);
 extern tree lookup_template_class              (tree, tree, tree, tree,
                                                 int, tsubst_flags_t);
 extern tree lookup_template_function           (tree, tree);
@@ -5742,6 +5970,9 @@ extern tree tsubst_default_argument               (tree, tree, tree,
 extern tree tsubst (tree, tree, tsubst_flags_t, tree);
 extern tree tsubst_copy_and_build              (tree, tree, tsubst_flags_t,
                                                 tree, bool, bool);
+extern tree tsubst_expr                         (tree, tree, tsubst_flags_t,
+                                                 tree, bool);
+extern tree tsubst_pack_expansion               (tree, tree, tsubst_flags_t, tree);
 extern tree most_general_template              (tree);
 extern tree get_mostly_instantiated_function_type (tree);
 extern bool problematic_instantiation_changed  (void);
@@ -5792,6 +6023,12 @@ extern tree get_template_argument_pack_elems     (const_tree);
 extern tree get_function_template_decl         (const_tree);
 extern tree resolve_nondeduced_context         (tree);
 extern hashval_t iterative_hash_template_arg (tree arg, hashval_t val);
+extern tree coerce_template_parms               (tree, tree, tree);
+extern tree coerce_template_parms               (tree, tree, tree, tsubst_flags_t);
+extern void register_local_specialization       (tree, tree);
+extern tree retrieve_local_specialization       (tree);
+extern tree extract_fnparm_pack                 (tree, tree *);
+extern tree template_parm_to_arg                (tree);
 
 /* in repo.c */
 extern void init_repo                          (void);
@@ -5882,6 +6119,22 @@ extern bool perform_access_checks (vec<deferred_access_check, va_gc> *,
 extern bool perform_deferred_access_checks     (tsubst_flags_t);
 extern bool perform_or_defer_access_check      (tree, tree, tree,
                                                 tsubst_flags_t);
+
+/* RAII sentinel to ensures that deferred access checks are popped before
+  a function returns.  */
+
+struct deferring_access_check_sentinel
+{
+  deferring_access_check_sentinel ()
+  {
+    push_deferring_access_checks (dk_deferred);
+  }
+  ~deferring_access_check_sentinel ()
+  {
+    pop_deferring_access_checks ();
+  }
+};
+
 extern int stmts_are_full_exprs_p              (void);
 extern void init_cp_semantics                  (void);
 extern tree do_poplevel                                (tree);
@@ -6392,6 +6645,60 @@ extern bool cxx_omp_privatize_by_reference       (const_tree);
 extern void suggest_alternatives_for            (location_t, tree);
 extern tree strip_using_decl                    (tree);
 
+/* in constraint.cc */
+extern void init_constraint_processing          ();
+extern bool constraint_p                        (tree);
+extern tree make_predicate_constraint           (tree);
+extern tree conjoin_constraints                 (tree, tree);
+extern tree conjoin_constraints                 (tree);
+extern bool valid_constraints_p                 (tree);
+extern tree get_constraints                     (tree);
+extern void set_constraints                     (tree, tree);
+extern void remove_constraints                  (tree);
+extern tree current_template_constraints       (void);
+extern tree associate_classtype_constraints     (tree);
+extern tree build_constraints                   (tree, tree);
+extern tree get_shorthand_constraints           (tree);
+extern tree build_concept_check                 (tree, tree, tree = NULL_TREE);
+extern tree build_constrained_parameter         (tree, tree, tree = NULL_TREE);
+extern tree make_constrained_auto               (tree, tree);
+extern bool deduce_constrained_parameter        (tree, tree&, tree&);
+extern tree resolve_constraint_check            (tree);
+extern tree check_function_concept              (tree);
+extern tree finish_template_introduction        (tree, tree);
+extern bool valid_requirements_p                (tree);
+extern tree finish_concept_name                 (tree);
+extern tree finish_shorthand_constraint         (tree, tree);
+extern tree finish_requires_expr                (tree, tree);
+extern tree finish_simple_requirement           (tree);
+extern tree finish_type_requirement             (tree);
+extern tree finish_compound_requirement         (tree, tree, bool);
+extern tree finish_nested_requirement           (tree);
+extern void check_constrained_friend            (tree, tree);
+extern tree tsubst_requires_expr                (tree, tree, tsubst_flags_t, tree);
+extern tree tsubst_constraint                   (tree, tree, tsubst_flags_t, tree);
+extern tree tsubst_constraint_info              (tree, tree, tsubst_flags_t, tree);
+extern bool function_concept_check_p            (tree);
+
+extern tree evaluate_constraints                (tree, tree);
+extern tree evaluate_function_concept           (tree, tree);
+extern tree evaluate_variable_concept           (tree, tree);
+extern tree evaluate_constraint_expression      (tree, tree);
+extern bool constraints_satisfied_p             (tree);
+extern bool constraints_satisfied_p             (tree, tree);
+
+extern bool equivalent_constraints              (tree, tree);
+extern bool equivalently_constrained            (tree, tree);
+extern bool subsumes_constraints                (tree, tree);
+extern int more_constrained                     (tree, tree);
+
+extern void diagnose_constraints                (location_t, tree, tree);
+
+/* in logic.cc */
+extern tree decompose_assumptions               (tree);
+extern tree decompose_conclusions               (tree);
+extern bool subsumes                            (tree, tree);
+
 /* in vtable-class-hierarchy.c */
 extern void vtv_compute_class_hierarchy_transitive_closure (void);
 extern void vtv_generate_init_routine           (void);
index 60be71a..879eb71 100644 (file)
@@ -451,6 +451,10 @@ cxx_pretty_printer::primary_expression (tree t)
       pp_cxx_offsetof_expression (this, t);
       break;
 
+    case REQUIRES_EXPR:
+      pp_cxx_requires_expr (this, t);
+      break;
+
     default:
       c_pretty_printer::primary_expression (t);
       break;
@@ -1064,6 +1068,7 @@ cxx_pretty_printer::expression (tree t)
     case TEMPLATE_PARM_INDEX:
     case TEMPLATE_TEMPLATE_PARM:
     case STMT_EXPR:
+    case REQUIRES_EXPR:
       primary_expression (t);
       break;
 
@@ -1158,6 +1163,22 @@ cxx_pretty_printer::expression (tree t)
       pp_cxx_ws_string (this, "<lambda>");
       break;
 
+    case TRAIT_EXPR:
+      pp_cxx_trait_expression (this, t);
+      break;
+
+    case PRED_CONSTR:
+    case EXPR_CONSTR:
+    case TYPE_CONSTR:
+    case ICONV_CONSTR:
+    case DEDUCT_CONSTR:
+    case EXCEPT_CONSTR:
+    case PARM_CONSTR:
+    case CONJ_CONSTR:
+    case DISJ_CONSTR:
+      pp_cxx_constraint (this, t);
+      break;
+
     case PAREN_EXPR:
       pp_cxx_left_paren (this);
       expression (TREE_OPERAND (t, 0));
@@ -1423,10 +1444,26 @@ pp_cxx_parameter_declaration (cxx_pretty_printer *pp, tree t)
 static void
 pp_cxx_parameter_declaration_clause (cxx_pretty_printer *pp, tree t)
 {
-  tree args = TYPE_P (t) ? NULL : FUNCTION_FIRST_USER_PARM (t);
-  tree types =
-    TYPE_P (t) ? TYPE_ARG_TYPES (t) : FUNCTION_FIRST_USER_PARMTYPE (t);
-  const bool abstract = args == NULL || pp->flags & pp_c_flag_abstract;
+  tree args;
+  tree types;
+  bool abstract;
+
+  // For a requires clause or the explicit printing of a parameter list
+  // we expect T to be a chain of PARM_DECLs. Otherwise, the list of
+  // args and types are taken from the function decl T.
+  if (TREE_CODE (t) == PARM_DECL)
+    {
+      args = t;
+      types = t;
+      abstract = false;
+    }
+  else
+    {
+      bool type_p = TYPE_P (t);
+      args = type_p ? NULL : FUNCTION_FIRST_USER_PARM (t);
+      types = type_p ? TYPE_ARG_TYPES (t) : FUNCTION_FIRST_USER_PARMTYPE (t);
+      abstract = args == NULL || pp->flags & pp_c_flag_abstract;
+    }
   bool first = true;
 
   /* Skip artificial parameter for nonstatic member functions.  */
@@ -1574,6 +1611,12 @@ void
 cxx_pretty_printer::declarator (tree t)
 {
   direct_declarator (t);
+
+  // Print a requires clause.
+  if (flag_concepts)
+    if (tree ci = get_constraints (t))
+      if (tree reqs = CI_DECLARATOR_REQS (ci))
+        pp_cxx_requires_clause (this, reqs);
 }
 
 /* ctor-initializer:
@@ -2154,7 +2197,13 @@ pp_cxx_canonical_template_parameter (cxx_pretty_printer *pp, tree parm)
 
 /*
   template-declaration:
-     export(opt) template < template-parameter-list > declaration   */
+     export(opt) template < template-parameter-list > declaration
+
+  Concept extensions:
+
+  template-declaration:
+     export(opt) template < template-parameter-list >
+       requires-clause(opt) declaration */
 
 static void
 pp_cxx_template_declaration (cxx_pretty_printer *pp, tree t)
@@ -2171,6 +2220,15 @@ pp_cxx_template_declaration (cxx_pretty_printer *pp, tree t)
       pp_cxx_end_template_argument_list (pp);
       pp_newline_and_indent (pp, 3);
     }
+
+  if (flag_concepts)
+    if (tree ci = get_constraints (t))
+      if (tree reqs = CI_TEMPLATE_REQS (ci))
+         {
+            pp_cxx_requires_clause (pp, reqs);
+            pp_newline_and_indent (pp, 6);
+         }
+
   if (TREE_CODE (t) == FUNCTION_DECL && DECL_SAVED_TREE (t))
     pp_cxx_function_definition (pp, t);
   else
@@ -2387,6 +2445,9 @@ pp_cxx_trait_expression (cxx_pretty_printer *pp, tree t)
     case CPTK_IS_POLYMORPHIC:
       pp_cxx_ws_string (pp, "__is_polymorphic");
       break;
+    case CPTK_IS_SAME_AS:
+      pp_cxx_ws_string (pp, "__is_same_as");
+      break;
     case CPTK_IS_STD_LAYOUT:
       pp_cxx_ws_string (pp, "__is_std_layout");
       break;
@@ -2416,7 +2477,7 @@ pp_cxx_trait_expression (cxx_pretty_printer *pp, tree t)
   pp_cxx_left_paren (pp);
   pp->type_id (TRAIT_EXPR_TYPE1 (t));
 
-  if (kind == CPTK_IS_BASE_OF)
+  if (kind == CPTK_IS_BASE_OF || kind == CPTK_IS_SAME_AS)
     {
       pp_cxx_separate_with (pp, ',');
       pp->type_id (TRAIT_EXPR_TYPE2 (t));
@@ -2424,6 +2485,272 @@ pp_cxx_trait_expression (cxx_pretty_printer *pp, tree t)
 
   pp_cxx_right_paren (pp);
 }
+
+// requires-clause:
+//    'requires' logical-or-expression
+void
+pp_cxx_requires_clause (cxx_pretty_printer *pp, tree t)
+{
+  if (!t)
+    return;
+  pp->padding = pp_before;
+  pp_cxx_ws_string (pp, "requires");
+  pp_space (pp);
+  pp->expression (t);
+}
+
+/* requirement:
+     simple-requirement
+     compound-requirement
+     type-requirement
+     nested-requirement */
+static void
+pp_cxx_requirement (cxx_pretty_printer *pp, tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case SIMPLE_REQ:
+      pp_cxx_simple_requirement (pp, t);
+      break;
+
+    case TYPE_REQ:
+      pp_cxx_type_requirement (pp, t);
+      break;
+
+    case COMPOUND_REQ:
+      pp_cxx_compound_requirement (pp, t);
+      break;
+
+    case NESTED_REQ:
+      pp_cxx_nested_requirement (pp, t);
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+// requirement-list:
+//    requirement
+//    requirement-list ';' requirement[opt]
+//
+static void
+pp_cxx_requirement_list (cxx_pretty_printer *pp, tree t)
+{
+  for (; t; t = TREE_CHAIN (t))
+    pp_cxx_requirement (pp, TREE_VALUE (t));
+}
+
+// requirement-body:
+//    '{' requirement-list '}'
+static void
+pp_cxx_requirement_body (cxx_pretty_printer *pp, tree t)
+{
+  pp_cxx_left_brace (pp);
+  pp_cxx_requirement_list (pp, t);
+  pp_cxx_right_brace (pp);
+}
+
+// requires-expression:
+//    'requires' requirement-parameter-list requirement-body
+void
+pp_cxx_requires_expr (cxx_pretty_printer *pp, tree t)
+{
+  pp_string (pp, "requires");
+  if (tree parms = TREE_OPERAND (t, 0))
+    {
+      pp_cxx_parameter_declaration_clause (pp, parms);
+      pp_cxx_whitespace (pp);
+    }
+  pp_cxx_requirement_body (pp, TREE_OPERAND (t, 1));
+}
+
+/* simple-requirement:
+     expression ';' */
+void
+pp_cxx_simple_requirement (cxx_pretty_printer *pp, tree t)
+{
+  pp->expression (TREE_OPERAND (t, 0));
+  pp_cxx_semicolon (pp);
+}
+
+/* type-requirement:
+     typename type-name ';' */
+void
+pp_cxx_type_requirement (cxx_pretty_printer *pp, tree t)
+{
+  pp->type_id (TREE_OPERAND (t, 0));
+  pp_cxx_semicolon (pp);
+}
+
+/* compound-requirement:
+     '{' expression '}' 'noexcept' [opt] trailing-return-type [opt] */
+void
+pp_cxx_compound_requirement (cxx_pretty_printer *pp, tree t)
+{
+  pp_cxx_left_brace (pp);
+  pp->expression (TREE_OPERAND (t, 0));
+  pp_cxx_right_brace (pp);
+
+  if (COMPOUND_REQ_NOEXCEPT_P (t))
+    pp_cxx_ws_string (pp, "noexcept");
+
+  if (tree type = TREE_OPERAND (t, 1))
+    {
+      pp_cxx_ws_string (pp, "->");
+      pp->type_id (type);
+    }
+}
+
+/* nested requirement:
+     'requires' constraint-expression */
+void
+pp_cxx_nested_requirement (cxx_pretty_printer *pp, tree t)
+{
+  pp_cxx_ws_string (pp, "requires");
+  pp->expression (TREE_OPERAND (t, 0));
+  pp_cxx_semicolon (pp);
+}
+
+void
+pp_cxx_predicate_constraint (cxx_pretty_printer *pp, tree t)
+{
+  pp_string (pp, "predicate");
+  pp_left_paren (pp);
+  pp->expression (TREE_OPERAND (t, 0));
+  pp_right_paren (pp);
+}
+
+void
+pp_cxx_expression_constraint (cxx_pretty_printer *pp, tree t)
+{
+  pp_string (pp, "valid_expr");
+  pp_left_paren (pp);
+  pp->expression (TREE_OPERAND (t, 0));
+  pp_right_paren (pp);
+}
+
+void
+pp_cxx_type_constraint (cxx_pretty_printer *pp, tree t)
+{
+  pp_string (pp, "valid_type");
+  pp_left_paren (pp);
+  pp->type_id (TREE_OPERAND (t, 0));
+  pp_right_paren (pp);
+}
+
+void
+pp_cxx_implicit_conversion_constraint (cxx_pretty_printer *pp, tree t)
+{
+  pp_string (pp, "convertible");
+  pp_left_paren (pp);
+  pp->expression (ICONV_CONSTR_EXPR (t));
+  pp_cxx_separate_with (pp, ',');
+  pp->expression (ICONV_CONSTR_TYPE (t));
+  pp_right_paren (pp);
+}
+
+void
+pp_cxx_argument_deduction_constraint (cxx_pretty_printer *pp, tree t)
+{
+  pp_string (pp, "deducible");
+  pp_left_paren (pp);
+  pp->expression (DEDUCT_CONSTR_EXPR (t));
+  pp_cxx_separate_with (pp, ',');
+  pp->expression (DEDUCT_CONSTR_PATTERN (t));
+  pp_right_paren (pp);
+}
+
+void
+pp_cxx_exception_constraint (cxx_pretty_printer *pp, tree t)
+{
+  pp_cxx_ws_string (pp, "noexcept");
+  pp_left_paren (pp);
+  pp->expression (TREE_OPERAND (t, 0));
+  pp_right_paren (pp);
+}
+
+void
+pp_cxx_parameterized_constraint (cxx_pretty_printer *pp, tree t)
+{
+  pp_left_paren (pp);
+  pp_string (pp, "forall");
+  if (tree parms = PARM_CONSTR_PARMS (t))
+    {
+      if (parms)
+       pp_cxx_parameter_declaration_clause (pp, parms);
+      pp_cxx_whitespace (pp);
+    }
+  pp_cxx_constraint (pp, PARM_CONSTR_OPERAND (t));
+  pp_right_paren (pp);
+}
+
+void
+pp_cxx_conjunction (cxx_pretty_printer *pp, tree t)
+{
+  pp_cxx_constraint (pp, TREE_OPERAND (t, 0));
+  pp_string (pp, " and ");
+  pp_cxx_constraint (pp, TREE_OPERAND (t, 1));
+}
+
+void
+pp_cxx_disjunction (cxx_pretty_printer *pp, tree t)
+{
+  pp_cxx_constraint (pp, TREE_OPERAND (t, 0));
+  pp_string (pp, " or ");
+  pp_cxx_constraint (pp, TREE_OPERAND (t, 1));
+}
+
+void
+pp_cxx_constraint (cxx_pretty_printer *pp, tree t)
+{
+  if (t == error_mark_node)
+    return pp->expression (t);
+
+  switch (TREE_CODE (t))
+    {
+    case PRED_CONSTR:
+      pp_cxx_predicate_constraint (pp, t);
+      break;
+
+    case EXPR_CONSTR:
+      pp_cxx_expression_constraint (pp, t);
+      break;
+
+    case TYPE_CONSTR:
+      pp_cxx_type_constraint (pp, t);
+      break;
+
+    case ICONV_CONSTR:
+      pp_cxx_implicit_conversion_constraint (pp, t);
+      break;
+
+    case DEDUCT_CONSTR:
+      pp_cxx_argument_deduction_constraint (pp, t);
+      break;
+
+    case EXCEPT_CONSTR:
+      pp_cxx_exception_constraint (pp, t);
+      break;
+
+    case PARM_CONSTR:
+      pp_cxx_parameterized_constraint (pp, t);
+      break;
+
+    case CONJ_CONSTR:
+      pp_cxx_conjunction (pp, t);
+      break;
+
+    case DISJ_CONSTR:
+      pp_cxx_disjunction (pp, t);
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+
 \f
 typedef c_pretty_print_fn pp_fun;
 
index 16f3238..e5161df 100644 (file)
@@ -91,6 +91,21 @@ void pp_cxx_trait_expression (cxx_pretty_printer *, tree);
 void pp_cxx_va_arg_expression (cxx_pretty_printer *, tree);
 void pp_cxx_offsetof_expression (cxx_pretty_printer *, tree);
 void pp_cxx_userdef_literal (cxx_pretty_printer *, tree);
-
+void pp_cxx_requires_clause (cxx_pretty_printer *, tree);
+void pp_cxx_requires_expr (cxx_pretty_printer *, tree);
+void pp_cxx_simple_requirement (cxx_pretty_printer *, tree);
+void pp_cxx_type_requirement (cxx_pretty_printer *, tree);
+void pp_cxx_compound_requirement (cxx_pretty_printer *, tree);
+void pp_cxx_nested_requirement (cxx_pretty_printer *, tree);
+void pp_cxx_predicate_constraint (cxx_pretty_printer *, tree);
+void pp_cxx_expression_constraint (cxx_pretty_printer *, tree);
+void pp_cxx_type_constraint (cxx_pretty_printer *, tree);
+void pp_cxx_implicit_conversion_constraint (cxx_pretty_printer *, tree);
+void pp_cxx_argument_deduction_constraint (cxx_pretty_printer *, tree);
+void pp_cxx_exception_constraint (cxx_pretty_printer *, tree);
+void pp_cxx_parameterized_constraint (cxx_pretty_printer *, tree);
+void pp_cxx_conjunction (cxx_pretty_printer *, tree);
+void pp_cxx_disjunction (cxx_pretty_printer *, tree);
+void pp_cxx_constraint (cxx_pretty_printer *, tree);
 
 #endif /* GCC_CXX_PRETTY_PRINT_H */
index 208173a..e8ed472 100644 (file)
@@ -72,7 +72,6 @@ enum bad_spec_place {
   BSP_FIELD   /* field */
 };
 
-static tree grokparms (tree parmlist, tree *);
 static const char *redeclaration_error_message (tree, tree);
 
 static int decl_jump_unsafe (tree);
@@ -1079,8 +1078,10 @@ decls_match (tree newdecl, tree olddecl)
     }
   else if (TREE_CODE (newdecl) == TEMPLATE_DECL)
     {
-      if (TREE_CODE (DECL_TEMPLATE_RESULT (newdecl))
-         != TREE_CODE (DECL_TEMPLATE_RESULT (olddecl)))
+      tree oldres = DECL_TEMPLATE_RESULT (olddecl);
+      tree newres = DECL_TEMPLATE_RESULT (newdecl);
+
+      if (TREE_CODE (newres) != TREE_CODE (oldres))
        return 0;
 
       if (!comp_template_parms (DECL_TEMPLATE_PARMS (newdecl),
@@ -1088,11 +1089,12 @@ decls_match (tree newdecl, tree olddecl)
        return 0;
 
       if (TREE_CODE (DECL_TEMPLATE_RESULT (newdecl)) == TYPE_DECL)
-       types_match = same_type_p (TREE_TYPE (DECL_TEMPLATE_RESULT (olddecl)),
-                                  TREE_TYPE (DECL_TEMPLATE_RESULT (newdecl)));
+       types_match = (same_type_p (TREE_TYPE (oldres), TREE_TYPE (newres))
+                      && equivalently_constrained (olddecl, newdecl));
       else
-       types_match = decls_match (DECL_TEMPLATE_RESULT (olddecl),
-                                  DECL_TEMPLATE_RESULT (newdecl));
+       // We don't need to check equivalently_constrained for variable and
+       // function templates because we check it on the results.
+       types_match = decls_match (oldres, newres);
     }
   else
     {
@@ -1120,6 +1122,11 @@ decls_match (tree newdecl, tree olddecl)
                                 COMPARE_REDECLARATION);
     }
 
+  // Normal functions can be constrained, as can variable partial
+  // specializations.
+  if (types_match && VAR_OR_FUNCTION_DECL_P (newdecl))
+    types_match = equivalently_constrained (newdecl, olddecl);
+
   return types_match;
 }
 
@@ -1243,6 +1250,36 @@ validate_constexpr_redeclaration (tree old_decl, tree new_decl)
   return true;
 }
 
+// If OLDDECL and NEWDECL are concept declarations with the same type
+// (i.e., and template parameters), but different requirements,
+// emit diagnostics and return true. Otherwise, return false.
+static inline bool
+check_concept_refinement (tree olddecl, tree newdecl)
+{
+  if (!DECL_DECLARED_CONCEPT_P (olddecl) || !DECL_DECLARED_CONCEPT_P (newdecl))
+    return false;
+
+  tree d1 = DECL_TEMPLATE_RESULT (olddecl);
+  tree d2 = DECL_TEMPLATE_RESULT (newdecl);
+  if (TREE_CODE (d1) != TREE_CODE (d2))
+    return false;
+
+  tree t1 = TREE_TYPE (d1);
+  tree t2 = TREE_TYPE (d2);
+  if (TREE_CODE (d1) == FUNCTION_DECL)
+    {
+      if (compparms (TYPE_ARG_TYPES (t1), TYPE_ARG_TYPES (t2))
+          && comp_template_parms (DECL_TEMPLATE_PARMS (olddecl),
+                                  DECL_TEMPLATE_PARMS (newdecl))
+          && !equivalently_constrained (olddecl, newdecl))
+        {
+          error ("cannot specialize concept %q#D", olddecl);
+          return true;
+        }
+    }
+  return false;
+}
+
 /* DECL is a redeclaration of a function or function template.  If
    it does have default arguments issue a diagnostic.  Note: this
    function is used to enforce the requirements in C++11 8.3.6 about
@@ -1571,12 +1608,17 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
                   /* Template functions can be disambiguated by
                      return type.  */
                   && same_type_p (TREE_TYPE (TREE_TYPE (newdecl)),
-                                  TREE_TYPE (TREE_TYPE (olddecl))))
+                                  TREE_TYPE (TREE_TYPE (olddecl)))
+                   // Template functions can also be disambiguated by
+                   // constraints.
+                   && equivalently_constrained (olddecl, newdecl))
            {
              error ("ambiguating new declaration %q+#D", newdecl);
              inform (DECL_SOURCE_LOCATION (olddecl),
                      "old declaration %q#D", olddecl);
            }
+          else if (check_concept_refinement (olddecl, newdecl))
+           return error_mark_node;
          return NULL_TREE;
        }
       if (TREE_CODE (newdecl) == FUNCTION_DECL)
@@ -1593,8 +1635,11 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
             are not ambiguous.  */
          else if ((!DECL_FUNCTION_VERSIONED (newdecl)
                    && !DECL_FUNCTION_VERSIONED (olddecl))
+                   // The functions have the same parameter types.
                   && compparms (TYPE_ARG_TYPES (TREE_TYPE (newdecl)),
-                             TYPE_ARG_TYPES (TREE_TYPE (olddecl))))
+                                TYPE_ARG_TYPES (TREE_TYPE (olddecl)))
+                   // And the same constraints.
+                   && equivalently_constrained (newdecl, olddecl))
            {
              error ("ambiguating new declaration of %q+#D", newdecl);
              inform (DECL_SOURCE_LOCATION (olddecl),
@@ -2576,6 +2621,12 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
       if (snode)
        snode->remove ();
     }
+
+  /* Remove the associated constraints for newdecl, if any, before
+     reclaiming memory. */
+  if (flag_concepts)
+    remove_constraints (newdecl);
+
   ggc_free (newdecl);
 
   return olddecl;
@@ -3981,6 +4032,10 @@ cxx_init_decl_processing (void)
 
     /* Ensure attribs.c is initialized.  */
     init_attributes ();
+
+    /* Ensure constraint.cc is initialized. */
+    init_constraint_processing ();
+
     extvisattr = build_tree_list (get_identifier ("externally_visible"),
                                  NULL_TREE);
     newattrs = tree_cons (get_identifier ("alloc_size"),
@@ -6368,6 +6423,16 @@ value_dependent_init_p (tree init)
   return false;
 }
 
+// Returns true if a DECL is VAR_DECL with the concept specifier.
+static inline bool
+is_concept_var (tree decl)
+{
+  return (VAR_P (decl)
+         // Not all variables have DECL_LANG_SPECIFIC.
+          && DECL_LANG_SPECIFIC (decl)
+          && DECL_DECLARED_CONCEPT_P (decl));
+}
+
 /* Finish processing of a declaration;
    install its line number and initial value.
    If the length of an array type is not known before,
@@ -6446,7 +6511,9 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
                                                  tf_warning_or_error);
       d_init = resolve_nondeduced_context (d_init);
       type = TREE_TYPE (decl) = do_auto_deduction (type, d_init,
-                                                  auto_node);
+                                                  auto_node,
+                                                   tf_warning_or_error,
+                                                   adc_variable_type);
       if (type == error_mark_node)
        return;
       cp_apply_type_quals_to_decl (cp_type_quals (type), decl);
@@ -6544,6 +6611,8 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
            init = NULL_TREE;
          release_tree_vector (cleanups);
        }
+      else if (!init && is_concept_var (decl))
+        error ("variable concept has no initializer");
       else if (!DECL_PRETTY_FUNCTION_P (decl))
        {
          /* Deduce array size even if the initializer is dependent.  */
@@ -7606,6 +7675,23 @@ check_static_quals (tree decl, cp_cv_quals quals)
           decl);
 }
 
+// Check that FN takes no arguments and returns bool.
+static void
+check_concept_fn (tree fn)
+{
+  // A constraint is nullary.
+  if (DECL_ARGUMENTS (fn))
+    error ("concept %q#D declared with function parameters", fn);
+
+  // The declared return type of the concept shall be bool, and
+  // it shall not be deduced from it definition.
+  tree type = TREE_TYPE (TREE_TYPE (fn));
+  if (is_auto (type))
+    error ("concept %q#D declared with a deduced return type", fn);
+  else if (type != boolean_type_node)
+    error ("concept %q#D with non-%<bool%> return type %qT", fn, type);
+}
+
 /* Helper function.  Replace the temporary this parameter injected
    during cp_finish_omp_declare_simd with the real this parameter.  */
 
@@ -7646,6 +7732,7 @@ grokfndecl (tree ctype,
            tree declarator,
            tree parms,
            tree orig_declarator,
+           tree decl_reqs,
            int virtualp,
            enum overload_flags flags,
            cp_cv_quals quals,
@@ -7667,6 +7754,16 @@ grokfndecl (tree ctype,
   int staticp = ctype && TREE_CODE (type) == FUNCTION_TYPE;
   tree t;
 
+  // Was the concept specifier present?
+  bool concept_p = inlinep & 4;
+
+  // Concept declarations must have a corresponding definition.
+  if (concept_p && !funcdef_flag)
+    {
+      error ("concept %qD has no definition", declarator);
+      return NULL_TREE;
+    }
+
   if (rqual)
     type = build_ref_qualified_type (type, rqual);
   if (raises)
@@ -7674,6 +7771,21 @@ grokfndecl (tree ctype,
 
   decl = build_lang_decl (FUNCTION_DECL, declarator, type);
 
+  /* Set the constraints on the declaration. */
+  if (flag_concepts)
+    {
+      tree tmpl_reqs = NULL_TREE;
+      if (processing_template_decl > template_class_depth (ctype))
+        tmpl_reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms);
+
+      /* Adjust the required expression into a constraint. */
+      if (decl_reqs)
+        decl_reqs = make_predicate_constraint (decl_reqs);
+
+      tree ci = build_constraints (tmpl_reqs, decl_reqs);
+      set_constraints (decl, ci);
+    }
+
   /* If we have an explicit location, use it, otherwise use whatever
      build_lang_decl used (probably input_location).  */
   if (location != UNKNOWN_LOCATION)
@@ -7848,6 +7960,14 @@ grokfndecl (tree ctype,
   if (inlinep & 2)
     DECL_DECLARED_CONSTEXPR_P (decl) = true;
 
+  // If the concept declaration specifier was found, check
+  // that the declaration satisfies the necessary requirements.
+  if (concept_p)
+    {
+      DECL_DECLARED_CONCEPT_P (decl) = true;
+      check_concept_fn (decl);
+    }
+
   DECL_EXTERNAL (decl) = 1;
   if (TREE_CODE (type) == FUNCTION_TYPE)
     {
@@ -7967,7 +8087,8 @@ grokfndecl (tree ctype,
   decl = check_explicit_specialization (orig_declarator, decl,
                                        template_count,
                                        2 * funcdef_flag +
-                                       4 * (friendp != 0));
+                                       4 * (friendp != 0) +
+                                        8 * concept_p);
   if (decl == error_mark_node)
     return NULL_TREE;
 
@@ -8125,7 +8246,7 @@ grokvardecl (tree type,
             tree orig_declarator,
             const cp_decl_specifier_seq *declspecs,
             int initialized,
-            int constp,
+            int flags,
             int template_count,
             tree scope)
 {
@@ -8134,6 +8255,9 @@ grokvardecl (tree type,
 
   gcc_assert (!name || identifier_p (name));
 
+  bool constp = flags&1;
+  bool conceptp = flags&2;
+
   /* Compute the scope in which to place the variable, but remember
      whether or not that scope was explicitly specified by the user.   */
   explicit_scope = scope;
@@ -8231,10 +8355,33 @@ grokvardecl (tree type,
   else
     DECL_INTERFACE_KNOWN (decl) = 1;
 
+  /* Check that the variable can be safely declared as a concept.
+     Note that this also forbids explicit specializations.  */
+  if (conceptp)
+    {
+      if (!processing_template_decl)
+        {
+          error ("a non-template variable cannot be %<concept%>");
+          return NULL_TREE;
+        }
+      else
+        DECL_DECLARED_CONCEPT_P (decl) = true;
+      if (!same_type_ignoring_top_level_qualifiers_p (type, boolean_type_node))
+       error_at (declspecs->locations[ds_type_spec],
+                 "concept must have type %<bool%>");
+    }
+  else if (flag_concepts
+          && processing_template_decl > template_class_depth (scope))
+    {
+      tree reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms);
+      tree ci = build_constraints (reqs, NULL_TREE);
+      set_constraints (decl, ci);
+    }
+
   // Handle explicit specializations and instantiations of variable templates.
   if (orig_declarator)
     decl = check_explicit_specialization (orig_declarator, decl,
-                                         template_count, 0);
+                                         template_count, conceptp * 8);
 
   return decl != error_mark_node ? decl : NULL_TREE;
 }
@@ -8969,6 +9116,7 @@ grokdeclarator (const cp_declarator *declarator,
   bool array_parameter_p = false;
   source_location saved_loc = input_location;
   const char *errmsg;
+  tree reqs = NULL_TREE;
 
   signed_p = decl_spec_seq_has_spec_p (declspecs, ds_signed);
   unsigned_p = decl_spec_seq_has_spec_p (declspecs, ds_unsigned);
@@ -8978,6 +9126,12 @@ grokdeclarator (const cp_declarator *declarator,
   explicit_intN = declspecs->explicit_intN_p;
   thread_p = decl_spec_seq_has_spec_p (declspecs, ds_thread);
 
+  // Was concept_p specified? Note that ds_concept
+  // implies ds_constexpr!
+  bool concept_p = decl_spec_seq_has_spec_p (declspecs, ds_concept);
+  if (concept_p)
+    constexpr_p = true;
+
   if (decl_context == FUNCDEF)
     funcdef_flag = true, decl_context = NORMAL;
   else if (decl_context == MEMFUNCDEF)
@@ -9220,6 +9374,12 @@ grokdeclarator (const cp_declarator *declarator,
   if (name == NULL)
     name = decl_context == PARM ? "parameter" : "type name";
 
+  if (concept_p && typedef_p)
+    {
+      error ("%<concept%> cannot appear in a typedef declaration");
+      return error_mark_node;
+    }
+
   if (constexpr_p && typedef_p)
     {
       error ("%<constexpr%> cannot appear in a typedef declaration");
@@ -9548,9 +9708,12 @@ grokdeclarator (const cp_declarator *declarator,
               || thread_p)
        error ("storage class specifiers invalid in parameter declarations");
 
+      /* Function parameters cannot be concept. */
+      if (concept_p)
+          error ("a parameter cannot be declared %<concept%>");
       /* Function parameters cannot be constexpr.  If we saw one, moan
          and pretend it wasn't there.  */
-      if (constexpr_p)
+      else if (constexpr_p)
         {
           error ("a parameter cannot be declared %<constexpr%>");
           constexpr_p = 0;
@@ -9778,6 +9941,10 @@ grokdeclarator (const cp_declarator *declarator,
            if (raises == error_mark_node)
              raises = NULL_TREE;
 
+           if (reqs)
+             error_at (location_of (reqs), "requires-clause on return type");
+           reqs = declarator->u.function.requires_clause;
+
            /* Say it's a definition only for the CALL_EXPR
               closest to the identifier.  */
            funcdecl_p = inner_declarator && inner_declarator->kind == cdk_id;
@@ -10373,6 +10540,9 @@ grokdeclarator (const cp_declarator *declarator,
          type = error_mark_node;
        }
 
+      if (reqs)
+       error_at (location_of (reqs), "requires-clause on typedef");
+
       if (decl_context == FIELD)
        decl = build_lang_decl (TYPE_DECL, unqualified_id, type);
       else
@@ -10566,6 +10736,9 @@ grokdeclarator (const cp_declarator *declarator,
            error ("invalid qualifiers on non-member function type");
        }
 
+      if (reqs)
+       error_at (location_of (reqs), "requires-clause on type-id");
+
       return type;
     }
   else if (unqualified_id == NULL_TREE && decl_context != PARM
@@ -10587,6 +10760,13 @@ grokdeclarator (const cp_declarator *declarator,
       return error_mark_node;
     }
 
+  if (reqs
+      && TREE_CODE (type) != FUNCTION_TYPE
+      && TREE_CODE (type) != METHOD_TYPE)
+    error_at (location_of (reqs),
+             "requires-clause on declaration of non-function type %qT",
+             type);
+
   /* We don't check parameter types here because we can emit a better
      error message later.  */
   if (decl_context != PARM)
@@ -10744,6 +10924,11 @@ grokdeclarator (const cp_declarator *declarator,
                           uqname, ctype);
                    return error_mark_node;
                  }
+                if (concept_p)
+                  {
+                    error ("a destructor cannot be %<concept%>");
+                    return error_mark_node;
+                  }
                 if (constexpr_p)
                   {
                     error ("a destructor cannot be %<constexpr%>");
@@ -10757,6 +10942,17 @@ grokdeclarator (const cp_declarator *declarator,
                       id_declarator->u.id.unqualified_name);
                return error_mark_node;
              }
+           if (sfk == sfk_constructor)
+             if (concept_p)
+               {
+                 error ("a constructor cannot be %<concept%>");
+                 return error_mark_node;
+               }
+           if (concept_p)
+             {
+               error ("a concept cannot be a member function");
+               concept_p = false;
+             }
 
            if (TREE_CODE (unqualified_id) == TEMPLATE_ID_EXPR)
              {
@@ -10785,9 +10981,10 @@ grokdeclarator (const cp_declarator *declarator,
                               ? unqualified_id : dname,
                               parms,
                               unqualified_id,
+                              reqs,
                               virtualp, flags, memfn_quals, rqual, raises,
                               friendp ? -1 : 0, friendp, publicp,
-                               inlinep | (2 * constexpr_p),
+                               inlinep | (2 * constexpr_p) | (4 * concept_p),
                               initialized == SD_DELETED, sfk,
                               funcdef_flag, template_count, in_namespace,
                               attrlist, declarator->id_loc);
@@ -10890,8 +11087,10 @@ grokdeclarator (const cp_declarator *declarator,
                    if (declspecs->gnu_thread_keyword_p)
                      SET_DECL_GNU_TLS_P (decl);
                  }
-
-               if (constexpr_p && !initialized)
+               if (concept_p)
+                   error ("static data member %qE declared %<concept%>",
+                          unqualified_id);
+               else if (constexpr_p && !initialized)
                  {
                    error ("constexpr static data member %qD must have an "
                           "initializer", decl);
@@ -10900,7 +11099,10 @@ grokdeclarator (const cp_declarator *declarator,
              }
            else
              {
-                if (constexpr_p)
+               if (concept_p)
+                 error ("non-static data member %qE declared %<concept%>",
+                        unqualified_id);
+                else if (constexpr_p)
                  {
                    error ("non-static data member %qE declared %<constexpr%>",
                           unqualified_id);
@@ -11010,10 +11212,12 @@ grokdeclarator (const cp_declarator *declarator,
          TYPE_HAS_LATE_RETURN_TYPE (type) = 1;
 
        decl = grokfndecl (ctype, type, original_name, parms, unqualified_id,
-                          virtualp, flags, memfn_quals, rqual, raises,
+                           reqs, virtualp, flags, memfn_quals, rqual, raises,
                           1, friendp,
-                          publicp, inlinep | (2 * constexpr_p),
-                          initialized == SD_DELETED, sfk,
+                          publicp,
+                           inlinep | (2 * constexpr_p) | (4 * concept_p),
+                          initialized == SD_DELETED,
+                           sfk,
                            funcdef_flag,
                           template_count, in_namespace, attrlist,
                           declarator->id_loc);
@@ -11054,7 +11258,7 @@ grokdeclarator (const cp_declarator *declarator,
        decl = grokvardecl (type, dname, unqualified_id,
                            declspecs,
                            initialized,
-                           (type_quals & TYPE_QUAL_CONST) != 0,
+                           ((type_quals & TYPE_QUAL_CONST) != 0) | (2 * concept_p),
                            template_count,
                            ctype ? ctype : in_namespace);
        if (decl == NULL_TREE)
@@ -11304,7 +11508,7 @@ type_is_deprecated (tree type)
 
    *PARMS is set to the chain of PARM_DECLs created.  */
 
-static tree
+tree
 grokparms (tree parmlist, tree *parms)
 {
   tree result = NULL_TREE;
@@ -12399,8 +12603,16 @@ xref_tag_1 (enum tag_types tag_code, tree name,
     {
       if (template_header_p && MAYBE_CLASS_TYPE_P (t))
         {
-         if (!redeclare_class_template (t, current_template_parms))
-            return error_mark_node;
+          /* Check that we aren't trying to overload a class with different
+             constraints.  */
+          tree constr = NULL_TREE;
+          if (current_template_parms)
+            {
+              tree reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms);
+              constr = build_constraints (reqs, NULL_TREE);
+            }
+         if (!redeclare_class_template (t, current_template_parms, constr))
+           return error_mark_node;
         }
       else if (!processing_template_decl
               && CLASS_TYPE_P (t)
@@ -14252,6 +14464,10 @@ finish_function (int flags)
       fntype = TREE_TYPE (fndecl);
     }
 
+  // If this is a concept, check that the definition is reasonable.
+  if (DECL_DECLARED_CONCEPT_P (fndecl))
+    check_function_concept (fndecl);
+
   /* Save constexpr function body before it gets munged by
      the NRV transformation.   */
   maybe_save_function_definition (fndecl);
@@ -14742,6 +14958,7 @@ cp_tree_node_structure (union lang_tree_node * t)
     case TRAIT_EXPR:           return TS_CP_TRAIT_EXPR;
     case LAMBDA_EXPR:          return TS_CP_LAMBDA_EXPR;
     case TEMPLATE_INFO:                return TS_CP_TEMPLATE_INFO;
+    case CONSTRAINT_INFO:       return TS_CP_CONSTRAINT_INFO;
     case USERDEF_LITERAL:      return TS_CP_USERDEF_LITERAL;
     default:                   return TS_CP_GENERIC;
     }
index a45755e..068d79c 100644 (file)
@@ -714,6 +714,10 @@ check_classfn (tree ctype, tree function, tree template_parms)
              != type_memfn_rqual (TREE_TYPE (fndecl)))
            continue;
 
+         // Include constraints in the match.
+         tree c1 = get_constraints (function);
+         tree c2 = get_constraints (fndecl);
+
          /* While finding a match, same types and params are not enough
             if the function is versioned.  Also check version ("target")
             attributes.  */
@@ -724,6 +728,7 @@ check_classfn (tree ctype, tree function, tree template_parms)
              && (!is_template
                  || comp_template_parms (template_parms,
                                          DECL_TEMPLATE_PARMS (fndecl)))
+             && equivalent_constraints (c1, c2)
              && (DECL_TEMPLATE_SPECIALIZATION (function)
                  == DECL_TEMPLATE_SPECIALIZATION (fndecl))
              && (!DECL_TEMPLATE_SPECIALIZATION (function)
@@ -5081,6 +5086,7 @@ mark_used (tree decl, tsubst_flags_t complain)
          || (TREE_CODE (decl) == FUNCTION_DECL
              && DECL_OMP_DECLARE_REDUCTION_P (decl))
          || undeduced_auto_decl (decl))
+      && !DECL_DECLARED_CONCEPT_P (decl)
       && !uses_template_parms (DECL_TI_ARGS (decl)))
     {
       /* Instantiating a function will result in garbage collection.  We
@@ -5179,6 +5185,7 @@ mark_used (tree decl, tsubst_flags_t complain)
     }
   else if (VAR_OR_FUNCTION_DECL_P (decl)
           && DECL_TEMPLATE_INFO (decl)
+           && !DECL_DECLARED_CONCEPT_P (decl)
           && (!DECL_EXPLICIT_INSTANTIATION (decl)
               || always_instantiate_p (decl)))
     /* If this is a function or variable that is an instance of some
index b811df2..4f85751 100644 (file)
@@ -1333,6 +1333,15 @@ dump_template_decl (cxx_pretty_printer *pp, tree t, int flags)
        }
     }
 
+  if (flag_concepts)
+    if (tree ci = get_constraints (t))
+      if (check_constraint_info (ci))
+        if (tree reqs = CI_TEMPLATE_REQS (ci))
+         {
+           pp_cxx_requires_clause (pp, reqs);
+           pp_cxx_whitespace (pp);
+         }
+
   if (DECL_CLASS_TEMPLATE_P (t))
     dump_type (pp, TREE_TYPE (t),
               ((flags & ~TFF_CLASS_KEY_OR_ENUM) | TFF_TEMPLATE_NAME
@@ -1564,6 +1573,11 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
       if (show_return)
        dump_type_suffix (pp, TREE_TYPE (fntype), flags);
 
+      if (flag_concepts)
+        if (tree ci = get_constraints (t))
+          if (tree reqs = CI_DECLARATOR_REQS (ci))
+            pp_cxx_requires_clause (pp, reqs);
+
       dump_substitution (pp, t, template_parms, template_args, flags);
     }
   else if (template_args)
@@ -2689,6 +2703,38 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
       pp_cxx_right_paren (pp);
       break;
 
+    case REQUIRES_EXPR:
+      pp_cxx_requires_expr (cxx_pp, t);
+      break;
+
+    case SIMPLE_REQ:
+      pp_cxx_simple_requirement (cxx_pp, t);
+      break;
+
+    case TYPE_REQ:
+      pp_cxx_type_requirement (cxx_pp, t);
+      break;
+
+    case COMPOUND_REQ:
+      pp_cxx_compound_requirement (cxx_pp, t);
+      break;
+
+    case NESTED_REQ:
+      pp_cxx_nested_requirement (cxx_pp, t);
+      break;
+
+    case PRED_CONSTR:
+    case EXPR_CONSTR:
+    case TYPE_CONSTR:
+    case ICONV_CONSTR:
+    case DEDUCT_CONSTR:
+    case EXCEPT_CONSTR:
+    case PARM_CONSTR:
+    case CONJ_CONSTR:
+    case DISJ_CONSTR:
+      pp_cxx_constraint (cxx_pp, t);
+      break;
+
     case PLACEHOLDER_EXPR:
       pp_string (pp, M_("*this"));
       break;
index a142410..f81c17b 100644 (file)
@@ -174,6 +174,8 @@ init_reswords (void)
 
   if (cxx_dialect < cxx11)
     mask |= D_CXX11;
+  if (!flag_concepts)
+    mask |= D_CXX_CONCEPTS;
   if (flag_no_asm)
     mask |= D_ASM | D_EXT;
   if (flag_no_gnu_keywords)
diff --git a/gcc/cp/logic.cc b/gcc/cp/logic.cc
new file mode 100644 (file)
index 0000000..7e01640
--- /dev/null
@@ -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);
+}
index 7d7ec7d..fdc7604 100644 (file)
@@ -1924,6 +1924,14 @@ implicitly_declare_fn (special_function_kind kind, tree type,
   rest_of_decl_compilation (fn, toplevel_bindings_p (), at_eof);
   gcc_assert (!TREE_USED (fn));
 
+  /* Propagate constraints from the inherited constructor. */
+  if (flag_concepts && inherited_ctor)
+    if (tree orig_ci = get_constraints (inherited_ctor))
+      {
+        tree new_ci = copy_node (orig_ci);
+        set_constraints (fn, new_ci);
+      }
+
   /* Restore PROCESSING_TEMPLATE_DECL.  */
   processing_template_decl = saved_processing_template_decl;
 
index bfad608..a0b249b 100644 (file)
@@ -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,18 +23312,336 @@ cp_parser_label_declaration (cp_parser* parser)
   cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
 }
 
-/* Support Functions */
+// -------------------------------------------------------------------------- //
+// Requires Clause
 
-/* Looks up NAME in the current scope, as given by PARSER->SCOPE.
-   NAME should have one of the representations used for an
-   id-expression.  If NAME is the ERROR_MARK_NODE, the ERROR_MARK_NODE
-   is returned.  If PARSER->SCOPE is a dependent type, then a
-   SCOPE_REF is returned.
+// 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;
+}
 
-   If NAME is a TEMPLATE_ID_EXPR, then it will be immediately
-   returned; the name was already resolved when the TEMPLATE_ID_EXPR
-   was formed.  Abstractly, such entities should not be passed to this
-   function, because they do not need to be looked up, but it is
+// 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.
+   NAME should have one of the representations used for an
+   id-expression.  If NAME is the ERROR_MARK_NODE, the ERROR_MARK_NODE
+   is returned.  If PARSER->SCOPE is a dependent type, then a
+   SCOPE_REF is returned.
+
+   If NAME is a TEMPLATE_ID_EXPR, then it will be immediately
+   returned; the name was already resolved when the TEMPLATE_ID_EXPR
+   was formed.  Abstractly, such entities should not be passed to this
+   function, because they do not need to be looked up, but it is
    simpler to check for this special case here, rather than at the
    call-sites.
 
@@ -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;
index 4381bd1..c7091c8 100644 (file)
@@ -397,6 +397,15 @@ typedef struct GTY(()) cp_parser {
      member definition using a generic type, it is the sk_class scope.  */
   cp_binding_level* implicit_template_scope;
 
+  /* True if parsing a result type in a compound requirement. This permits
+     constrained-type-specifiers inside what would normally be a trailing
+     return type. */
+  bool in_result_type_constraint_p;
+
+  /* True if a constrained-type-specifier is not allowed in this
+     context e.g., because they could never be deduced.  */
+  int prevent_constrained_type_specifiers;
+
 } cp_parser;
 
 /* In parser.c  */
index c3bafd3..1b64174 100644 (file)
@@ -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);
 }
 \f
@@ -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.  */
 
index 8abdb72..b0281df 100644 (file)
@@ -253,6 +253,19 @@ cxx_print_xnode (FILE *file, tree node, int indent)
          fprintf (file, "pending_template");
        }
       break;
+    case CONSTRAINT_INFO:
+      {
+        tree_constraint_info *cinfo = (tree_constraint_info *)node;
+        if (cinfo->template_reqs)
+          print_node (file, "template_reqs", cinfo->template_reqs, indent+4);
+        if (cinfo->declarator_reqs)
+          print_node (file, "declarator_reqs", cinfo->declarator_reqs,
+                     indent+4);
+        print_node (file, "associated_constr",
+                          cinfo->associated_constr, indent+4);
+        print_node_brief (file, "assumptions", cinfo->assumptions, indent+4);
+        break;
+      }
     case ARGUMENT_PACK_SELECT:
       print_node (file, "pack", ARGUMENT_PACK_SELECT_FROM_PACK (node),
                  indent+4);
index 1176b3e..90cd243 100644 (file)
@@ -1203,6 +1203,12 @@ lookup_member (tree xbasetype, tree name, int protect, bool want_type,
     }
 
   type = complete_type (type);
+
+  /* Make sure we're looking for a member of the current instantiation in the
+     right partial specialization.  */
+  if (flag_concepts && dependent_type_p (type))
+    type = currently_open_class (type);
+
   if (!basetype_path)
     basetype_path = TYPE_BINFO (type);
 
index 17b0a14..7bbae06 100644 (file)
@@ -2710,10 +2710,18 @@ finish_template_template_parm (tree aggr, tree identifier)
 {
   tree decl = build_decl (input_location,
                          TYPE_DECL, identifier, NULL_TREE);
+
   tree tmpl = build_lang_decl (TEMPLATE_DECL, identifier, NULL_TREE);
   DECL_TEMPLATE_PARMS (tmpl) = current_template_parms;
   DECL_TEMPLATE_RESULT (tmpl) = decl;
   DECL_ARTIFICIAL (decl) = 1;
+
+  // Associate the constraints with the underlying declaration,
+  // not the template.
+  tree reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms);
+  tree constr = build_constraints (reqs, NULL_TREE);
+  set_constraints (decl, constr);
+
   end_template_decl ();
 
   gcc_assert (DECL_TEMPLATE_PARMS (tmpl));
@@ -2977,6 +2985,72 @@ finish_template_decl (tree parms)
     end_specialization ();
 }
 
+// Returns the template type of the class scope being entered. If we're
+// entering a constrained class scope. TYPE is the class template
+// scope being entered and we may need to match the intended type with
+// a constrained specialization. For example:
+//
+//    template<Object T>
+//      struct S { void f(); }; #1
+//
+//    template<Object T>
+//      void S<T>::f() { }      #2
+//
+// We check, in #2, that S<T> refers precisely to the type declared by
+// #1 (i.e., that the constraints match). Note that the following should
+// be an error since there is no specialization of S<T> that is
+// unconstrained, but this is not diagnosed here.
+//
+//    template<typename T>
+//      void S<T>::f() { }
+//
+// We cannot diagnose this problem here since this function also matches
+// qualified template names that are not part of a definition. For example:
+//
+//    template<Integral T, Floating_point U>
+//      typename pair<T, U>::first_type void f(T, U);
+//
+// Here, it is unlikely that there is a partial specialization of
+// pair constrained for for Integral and Floating_point arguments.
+//
+// The general rule is: if a constrained specialization with matching
+// constraints is found return that type. Also note that if TYPE is not a
+// class-type (e.g. a typename type), then no fixup is needed.
+
+static tree
+fixup_template_type (tree type)
+{
+  // Find the template parameter list at the a depth appropriate to
+  // the scope we're trying to enter.
+  tree parms = current_template_parms;
+  int depth = template_class_depth (type);
+  for (int n = processing_template_decl; n > depth && parms; --n)
+    parms = TREE_CHAIN (parms);
+  if (!parms)
+    return type;
+  tree cur_reqs = TEMPLATE_PARMS_CONSTRAINTS (parms);
+  tree cur_constr = build_constraints (cur_reqs, NULL_TREE);
+
+  // Search for a specialization whose type and constraints match.
+  tree tmpl = CLASSTYPE_TI_TEMPLATE (type);
+  tree specs = DECL_TEMPLATE_SPECIALIZATIONS (tmpl);
+  while (specs)
+    {
+      tree spec_constr = get_constraints (TREE_VALUE (specs));
+
+      // If the type and constraints match a specialization, then we
+      // are entering that type.
+      if (same_type_p (type, TREE_TYPE (specs))
+         && equivalent_constraints (cur_constr, spec_constr))
+        return TREE_TYPE (specs);
+      specs = TREE_CHAIN (specs);
+    }
+
+  // If no specialization matches, then must return the type
+  // previously found.
+  return type;
+}
+
 /* Finish processing a template-id (which names a type) of the form
    NAME < ARGS >.  Return the TYPE_DECL for the type named by the
    template-id.  If ENTERING_SCOPE is nonzero we are about to enter
@@ -2990,6 +3064,16 @@ finish_template_type (tree name, tree args, int entering_scope)
   type = lookup_template_class (name, args,
                                NULL_TREE, NULL_TREE, entering_scope,
                                tf_warning_or_error | tf_user);
+
+  /* If we might be entering the scope of a partial specialization,
+     find the one with the right constraints.  */
+  if (flag_concepts
+      && entering_scope
+      && CLASS_TYPE_P (type)
+      && dependent_type_p (type)
+      && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (type)))
+    type = fixup_template_type (type);
+
   if (type == error_mark_node)
     return type;
   else if (CLASS_TYPE_P (type) && !alias_type_or_template_p (type))
@@ -7442,6 +7526,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_POLYMORPHIC:
       return (CLASS_TYPE_P (type1) && TYPE_POLYMORPHIC_P (type1));
 
+    case CPTK_IS_SAME_AS:
+      return same_type_p (type1, type2);
+
     case CPTK_IS_STD_LAYOUT:
       return (std_layout_type_p (type1));
 
@@ -7549,8 +7636,9 @@ finish_trait_expr (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_CLASS:
     case CPTK_IS_ENUM:
     case CPTK_IS_UNION:
+    case CPTK_IS_SAME_AS:
       break;
-    
+
     default:
       gcc_unreachable ();
     }
index 46818b4..987ebe8 100644 (file)
@@ -2955,16 +2955,19 @@ cp_tree_equal (tree t1, tree t2)
         up for expressions that involve 'this' in a member function
         template.  */
 
-      if (comparing_specializations)
+      if (comparing_specializations && !CONSTRAINT_VAR_P (t1))
        /* When comparing hash table entries, only an exact match is
           good enough; we don't want to replace 'this' with the
-          version from another function.  */
+          version from another function.  But be more flexible
+          with local parameters in a requires-expression.  */
        return false;
 
       if (same_type_p (TREE_TYPE (t1), TREE_TYPE (t2)))
        {
          if (DECL_ARTIFICIAL (t1) ^ DECL_ARTIFICIAL (t2))
            return false;
+         if (CONSTRAINT_VAR_P (t1) ^ CONSTRAINT_VAR_P (t2))
+           return false;
          if (DECL_ARTIFICIAL (t1)
              || (DECL_PARM_LEVEL (t1) == DECL_PARM_LEVEL (t2)
                  && DECL_PARM_INDEX (t1) == DECL_PARM_INDEX (t2)))
@@ -3000,6 +3003,10 @@ cp_tree_equal (tree t1, tree t2)
       return (cp_tree_equal (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0))
              && cp_tree_equal (TREE_OPERAND (t1, 1), TREE_OPERAND (t2, 1)));
 
+    case CONSTRAINT_INFO:
+      return cp_tree_equal (CI_ASSOCIATED_CONSTRAINTS (t1),
+                            CI_ASSOCIATED_CONSTRAINTS (t2));
+
     case TREE_VEC:
       {
        unsigned ix;
@@ -3876,6 +3883,14 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func,
       *walk_subtrees_p = 0;
       break;
  
+    case REQUIRES_EXPR:
+      // Only recurse through the nested expression. Do not
+      // walk the parameter list. Doing so causes false
+      // positives in the pack expansion checker since the
+      // requires parameters are introduced as pack expansions.
+      WALK_SUBTREE (TREE_OPERAND (*tp, 1));
+      *walk_subtrees_p = 0;
+      break;
 
     default:
       return NULL_TREE;
index 5d754f1..83fd34c 100644 (file)
@@ -3474,6 +3474,25 @@ cp_build_function_call_vec (tree function, vec<tree, va_gc> **params,
 
   if (TREE_CODE (function) == FUNCTION_DECL)
     {
+      /* If the function is a non-template member function
+         or a non-template friend, then we need to check the
+         constraints.
+
+        Note that if overload resolution failed with a single
+        candidate this function will be used to explicitly diagnose
+        the failure for the single call expression. The check is
+        technically redundant since we also would have failed in
+        add_function_candidate. */
+      if (flag_concepts
+          && (complain & tf_error)
+          && !constraints_satisfied_p (function))
+        {
+          error ("cannot call function %qD", function);
+          location_t loc = DECL_SOURCE_LOCATION (function);
+          diagnose_constraints (loc, function, NULL_TREE);
+          return error_mark_node;
+        }
+
       if (!mark_used (function, complain) && !(complain & tf_error))
        return error_mark_node;
       fndecl = function;
index 2a47943..64d9a6a 100644 (file)
@@ -19280,7 +19280,8 @@ Predefined Macros,cpp,The GNU C Preprocessor}).
 * C++ Attributes::      Variable, function, and type attributes for C++ only.
 * Function Multiversioning::   Declaring multiple function versions.
 * Namespace Association:: Strong using-directives for namespace association.
-* Type Traits::         Compiler support for type traits
+* Type Traits::         Compiler support for type traits.
+* C++ Concepts::        Improved support for generic programming.
 * Java Exceptions::     Tweaking exception handling to work with Java.
 * Deprecated Features:: Things will disappear from G++.
 * Backwards Compatibility:: Compatibilities with earlier definitions of C++.
@@ -20076,6 +20077,52 @@ an enumeration type ([dcl.enum]).
 
 @end table
 
+
+@node C++ Concepts
+@section C++ Concepts
+
+C++ concepts provide much-improved support for generic programming. In
+particular, they allow the specification of constraints on template arguments.
+The constraints are used to extend the usual overloading and partial
+specialization capabilities of the language, allowing generic data structures
+and algorithms to be ``refined'' based on their properties rather than their
+type names.
+
+The following keywords are reserved for concepts.
+
+@table @code
+@item assumes
+States an expression as an assumption, and if possible, verifies that the
+assumption is valid. For example, @code{assume(n > 0)}.
+
+@item axiom
+Introduces an axiom definition. Axioms introduce requirements on values.
+
+@item forall
+Introduces a universally quantified object in an axiom. For example,
+@code{forall (int n) n + 0 == n}).
+
+@item concept
+Introduces a concept definition. Concepts are sets of syntactic and semantic
+requirements on types and their values.
+
+@item requires
+Introduces constraints on template arguments or requirements for a member
+function of a class template.
+
+@end table
+
+The front end also exposes a number of internal mechanism that can be used
+to simplify the writing of type traits. Note that some of these traits are
+likely to be removed in the future.
+
+@table @code
+@item __is_same (type1, type2)
+A binary type trait: true whenever the type arguments are the same.
+
+@end table
+
+
 @node Java Exceptions
 @section Java Exceptions
 
diff --git a/gcc/testsuite/g++.dg/concepts/alias1.C b/gcc/testsuite/g++.dg/concepts/alias1.C
new file mode 100644 (file)
index 0000000..03b3cea
--- /dev/null
@@ -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 (file)
index 0000000..d81188e
--- /dev/null
@@ -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 (file)
index 0000000..e6ab669
--- /dev/null
@@ -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 (file)
index 0000000..4227a44
--- /dev/null
@@ -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 (file)
index 0000000..ea74a54
--- /dev/null
@@ -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 (file)
index 0000000..b213cb5
--- /dev/null
@@ -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 (file)
index 0000000..2c3ea44
--- /dev/null
@@ -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 (file)
index 0000000..e3a1d2a
--- /dev/null
@@ -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 (file)
index 0000000..7ba8250
--- /dev/null
@@ -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 (file)
index 0000000..903bf24
--- /dev/null
@@ -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 (file)
index 0000000..fe6b42d
--- /dev/null
@@ -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 (file)
index 0000000..fd21c43
--- /dev/null
@@ -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 (file)
index 0000000..67b56f9
--- /dev/null
@@ -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 (file)
index 0000000..6ff3be9
--- /dev/null
@@ -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 (file)
index 0000000..f67fa0b
--- /dev/null
@@ -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 (file)
index 0000000..7f857fe
--- /dev/null
@@ -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 (file)
index 0000000..c2ac741
--- /dev/null
@@ -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 (file)
index 0000000..675fe21
--- /dev/null
@@ -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 (file)
index 0000000..3079ca5
--- /dev/null
@@ -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 (file)
index 0000000..5e75f4f
--- /dev/null
@@ -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 (file)
index 0000000..a471657
--- /dev/null
@@ -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 (file)
index 0000000..b075c10
--- /dev/null
@@ -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 (file)
index 0000000..6316410
--- /dev/null
@@ -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 (file)
index 0000000..4f19624
--- /dev/null
@@ -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 (file)
index 0000000..29546b3
--- /dev/null
@@ -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 (file)
index 0000000..e9aacd5
--- /dev/null
@@ -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 (file)
index 0000000..8047278
--- /dev/null
@@ -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 (file)
index 0000000..3eba9ff
--- /dev/null
@@ -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 (file)
index 0000000..5ae04e4
--- /dev/null
@@ -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 (file)
index 0000000..40c2034
--- /dev/null
@@ -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 (file)
index 0000000..eb8406f
--- /dev/null
@@ -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 (file)
index 0000000..7bc7875
--- /dev/null
@@ -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 (file)
index 0000000..385dcbc
--- /dev/null
@@ -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 (file)
index 0000000..092c91c
--- /dev/null
@@ -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 (file)
index 0000000..b2bdaf9
--- /dev/null
@@ -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 (file)
index 0000000..f4cd4c5
--- /dev/null
@@ -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 (file)
index 0000000..0aee852
--- /dev/null
@@ -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 (file)
index 0000000..06402e0
--- /dev/null
@@ -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 (file)
index 0000000..5ced6a7
--- /dev/null
@@ -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 (file)
index 0000000..b3c3f70
--- /dev/null
@@ -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 (file)
index 0000000..73ef19a
--- /dev/null
@@ -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 (file)
index 0000000..2abd34a
--- /dev/null
@@ -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 (file)
index 0000000..71141f6
--- /dev/null
@@ -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 (file)
index 0000000..b7ac4e1
--- /dev/null
@@ -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 (file)
index 0000000..286e769
--- /dev/null
@@ -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 (file)
index 0000000..38b230c
--- /dev/null
@@ -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 (file)
index 0000000..c6b7457
--- /dev/null
@@ -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 (file)
index 0000000..778356d
--- /dev/null
@@ -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 (file)
index 0000000..29433ad
--- /dev/null
@@ -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 (file)
index 0000000..4f39203
--- /dev/null
@@ -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 (file)
index 0000000..3d0ddf2
--- /dev/null
@@ -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 (file)
index 0000000..cd9565f
--- /dev/null
@@ -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 (file)
index 0000000..1b5f5d1
--- /dev/null
@@ -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 (file)
index 0000000..91a1cac
--- /dev/null
@@ -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 (file)
index 0000000..5dd95c6
--- /dev/null
@@ -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 (file)
index 0000000..6d8aec3
--- /dev/null
@@ -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 (file)
index 0000000..64771cd
--- /dev/null
@@ -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 (file)
index 0000000..4e168ef
--- /dev/null
@@ -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 (file)
index 0000000..d92eafc
--- /dev/null
@@ -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 (file)
index 0000000..46404e3
--- /dev/null
@@ -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 (file)
index 0000000..a62fe5e
--- /dev/null
@@ -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 (file)
index 0000000..a96c018
--- /dev/null
@@ -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 (file)
index 0000000..114c12f
--- /dev/null
@@ -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 (file)
index 0000000..fb75448
--- /dev/null
@@ -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 (file)
index 0000000..fe612d4
--- /dev/null
@@ -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 (file)
index 0000000..db75455
--- /dev/null
@@ -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 (file)
index 0000000..d73d787
--- /dev/null
@@ -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 (file)
index 0000000..d4b8e9c
--- /dev/null
@@ -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 (file)
index 0000000..dcf167b
--- /dev/null
@@ -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 (file)
index 0000000..0b5a7cf
--- /dev/null
@@ -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 (file)
index 0000000..8165c81
--- /dev/null
@@ -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 (file)
index 0000000..bd89551
--- /dev/null
@@ -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 (file)
index 0000000..e34fc94
--- /dev/null
@@ -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 (file)
index 0000000..6aaf57b
--- /dev/null
@@ -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 (file)
index 0000000..c72e6c6
--- /dev/null
@@ -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 (file)
index 0000000..fa24b7b
--- /dev/null
@@ -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 (file)
index 0000000..31dc87a
--- /dev/null
@@ -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 (file)
index 0000000..8b6f2a1
--- /dev/null
@@ -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 (file)
index 0000000..bdbbd66
--- /dev/null
@@ -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 (file)
index 0000000..d0bb6a2
--- /dev/null
@@ -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 (file)
index 0000000..4b0befa
--- /dev/null
@@ -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 (file)
index 0000000..ee9d115
--- /dev/null
@@ -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 (file)
index 0000000..0b7520e
--- /dev/null
@@ -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 (file)
index 0000000..02d5de0
--- /dev/null
@@ -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 (file)
index 0000000..bd3b4e3
--- /dev/null
@@ -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 (file)
index 0000000..3e21c11
--- /dev/null
@@ -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 (file)
index 0000000..7b23d11
--- /dev/null
@@ -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 (file)
index 0000000..54fcd8b
--- /dev/null
@@ -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 (file)
index 0000000..e86281a
--- /dev/null
@@ -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 (file)
index 0000000..09ebf89
--- /dev/null
@@ -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 (file)
index 0000000..67ae5b5
--- /dev/null
@@ -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 (file)
index 0000000..48bd0a8
--- /dev/null
@@ -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 (file)
index 0000000..9a47437
--- /dev/null
@@ -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 (file)
index 0000000..a28f30b
--- /dev/null
@@ -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 (file)
index 0000000..69581ee
--- /dev/null
@@ -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 (file)
index 0000000..2077834
--- /dev/null
@@ -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 (file)
index 0000000..c0af1f8
--- /dev/null
@@ -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 (file)
index 0000000..e4f20ec
--- /dev/null
@@ -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 (file)
index 0000000..51aba7b
--- /dev/null
@@ -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 (file)
index 0000000..35c0854
--- /dev/null
@@ -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 (file)
index 0000000..5654be0
--- /dev/null
@@ -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 (file)
index 0000000..20a7fb6
--- /dev/null
@@ -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 (file)
index 0000000..b803289
--- /dev/null
@@ -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 (file)
index 0000000..95ce784
--- /dev/null
@@ -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 (file)
index 0000000..1639f26
--- /dev/null
@@ -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 (file)
index 0000000..ad715e1
--- /dev/null
@@ -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 (file)
index 0000000..e412fb4
--- /dev/null
@@ -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 (file)
index 0000000..a29bb11
--- /dev/null
@@ -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 (file)
index 0000000..f33742b
--- /dev/null
@@ -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 (file)
index 0000000..d23f259
--- /dev/null
@@ -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 (file)
index 0000000..1086984
--- /dev/null
@@ -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 (file)
index 0000000..f604d5a
--- /dev/null
@@ -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 (file)
index 0000000..7e68984
--- /dev/null
@@ -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 (file)
index 0000000..4091284
--- /dev/null
@@ -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 (file)
index 0000000..f07c878
--- /dev/null
@@ -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 (file)
index 0000000..28ced34
--- /dev/null
@@ -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 (file)
index 0000000..eb8a2b7
--- /dev/null
@@ -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 (file)
index 0000000..71663bd
--- /dev/null
@@ -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 (file)
index 0000000..c901028
--- /dev/null
@@ -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 (file)
index 0000000..b398353
--- /dev/null
@@ -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 (file)
index 0000000..13f86dd
--- /dev/null
@@ -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 (file)
index 0000000..2f775fd
--- /dev/null
@@ -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 (file)
index 0000000..79476c3
--- /dev/null
@@ -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 (file)
index 0000000..e615f21
--- /dev/null
@@ -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 (file)
index 0000000..ed3d4f1
--- /dev/null
@@ -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 (file)
index 0000000..6c55277
--- /dev/null
@@ -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);
+}