diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 02374d33f82..d3a6a38ed7c 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,15 @@ +2015-08-06 Andrew Sutton + Braden Obrzut + Jason Merrill + + 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 * c-indentation.c (should_warn_for_misleading_indentation): diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index a8e5353749c..f6c5ddd0496 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -491,6 +491,7 @@ const struct c_common_resword c_common_reswords[] = { "__is_literal_type", RID_IS_LITERAL_TYPE, D_CXXONLY }, { "__is_pod", RID_IS_POD, D_CXXONLY }, { "__is_polymorphic", RID_IS_POLYMORPHIC, D_CXXONLY }, + { "__is_same_as", RID_IS_SAME_AS, D_CXXONLY }, { "__is_standard_layout", RID_IS_STD_LAYOUT, D_CXXONLY }, { "__is_trivial", RID_IS_TRIVIAL, D_CXXONLY }, { "__is_trivially_assignable", RID_IS_TRIVIALLY_ASSIGNABLE, D_CXXONLY }, @@ -589,6 +590,11 @@ const struct c_common_resword c_common_reswords[] = { "volatile", RID_VOLATILE, 0 }, { "wchar_t", RID_WCHAR, D_CXXONLY }, { "while", RID_WHILE, 0 }, + + /* Concepts-related keywords */ + { "concept", RID_CONCEPT, D_CXX_CONCEPTS_FLAGS | D_CXXWARN }, + { "requires", RID_REQUIRES, D_CXX_CONCEPTS_FLAGS | D_CXXWARN }, + /* These Objective-C keywords are recognized only immediately after an '@'. */ { "compatibility_alias", RID_AT_ALIAS, D_OBJC }, diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index ff74e53c708..d1f6cba20e3 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -142,6 +142,7 @@ enum rid RID_IS_EMPTY, RID_IS_ENUM, RID_IS_FINAL, RID_IS_LITERAL_TYPE, RID_IS_POD, RID_IS_POLYMORPHIC, + RID_IS_SAME_AS, RID_IS_STD_LAYOUT, RID_IS_TRIVIAL, RID_IS_TRIVIALLY_ASSIGNABLE, RID_IS_TRIVIALLY_CONSTRUCTIBLE, RID_IS_TRIVIALLY_COPYABLE, @@ -150,6 +151,9 @@ enum rid /* C++11 */ RID_CONSTEXPR, RID_DECLTYPE, RID_NOEXCEPT, RID_NULLPTR, RID_STATIC_ASSERT, + /* C++ concepts */ + RID_CONCEPT, RID_REQUIRES, + /* Cilk Plus keywords. */ RID_CILK_SPAWN, RID_CILK_SYNC, RID_CILK_FOR, @@ -386,6 +390,9 @@ extern machine_mode c_default_pointer_mode; #define D_OBJC 0x080 /* In Objective C and neither C nor C++. */ #define D_CXX_OBJC 0x100 /* In Objective C, and C++, but not C. */ #define D_CXXWARN 0x200 /* In C warn with -Wcxx-compat. */ +#define D_CXX_CONCEPTS 0x400 /* In C++, only with concepts. */ + +#define D_CXX_CONCEPTS_FLAGS D_CXXONLY | D_CXX_CONCEPTS /* The reserved keyword table. */ extern const struct c_common_resword c_common_reswords[]; diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c index 1beb2dbfcb7..6e18a77f227 100644 --- a/gcc/c-family/c-cppbuiltin.c +++ b/gcc/c-family/c-cppbuiltin.c @@ -865,6 +865,10 @@ c_cpp_builtins (cpp_reader *pfile) cpp_define (pfile, "__cpp_variable_templates=201304"); cpp_define (pfile, "__cpp_digit_separators=201309"); } + if (flag_concepts) + /* Use a value smaller than the 201507 specified in + the TS, since we don't yet support extended auto. */ + cpp_define (pfile, "__cpp_concepts=201500"); if (flag_sized_deallocation) cpp_define (pfile, "__cpp_sized_deallocation=201309"); } diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c index 73f5db0a664..3239a853546 100644 --- a/gcc/c-family/c-opts.c +++ b/gcc/c-family/c-opts.c @@ -1558,6 +1558,8 @@ set_std_cxx1z (int iso) /* C++11 includes the C99 standard library. */ flag_isoc94 = 1; flag_isoc99 = 1; + /* Enable concepts by default. */ + flag_concepts = 1; flag_isoc11 = 1; cxx_dialect = cxx1z; lang_hooks.name = "GNU C++14"; /* Pretend C++14 till standarization. */ diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 4679038c90b..e86ee12acac 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -1086,6 +1086,10 @@ fcilkplus C ObjC C++ ObjC++ LTO Report Var(flag_cilkplus) Init(0) Enable Cilk Plus +fconcepts +C++ ObjC++ Var(flag_concepts) +Enable support for C++ concepts + fcond-mismatch C ObjC C++ ObjC++ Allow the arguments of the '?' operator to have different types diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 946c2c222d6..2ecb8d93764 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,186 @@ +2015-08-06 Andrew Sutton + Braden Obrzut + Jason Merrill + + 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 PR c++/66533 diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in index a0847be4a2d..a16f228146c 100644 --- a/gcc/cp/Make-lang.in +++ b/gcc/cp/Make-lang.in @@ -78,7 +78,8 @@ CXX_AND_OBJCXX_OBJS = cp/call.o cp/decl.o cp/expr.o cp/pt.o cp/typeck2.o \ cp/mangle.o cp/cp-objcp-common.o cp/name-lookup.o cp/cxx-pretty-print.o \ cp/cp-cilkplus.o \ cp/cp-gimplify.o cp/cp-array-notation.o cp/lambda.o \ - cp/vtable-class-hierarchy.o cp/constexpr.o cp/cp-ubsan.o $(CXX_C_OBJS) + cp/vtable-class-hierarchy.o cp/constexpr.o cp/cp-ubsan.o \ + cp/constraint.o cp/logic.o $(CXX_C_OBJS) # Language-specific object files for C++. CXX_OBJS = cp/cp-lang.o c-family/stub-objc.o $(CXX_AND_OBJCXX_OBJS) @@ -131,7 +132,7 @@ c++.srcinfo: c++.srcextra: c++.tags: force - cd $(srcdir)/cp; etags -o TAGS.sub *.c *.h --language=none \ + cd $(srcdir)/cp; etags -o TAGS.sub *.c *.cc *.h --language=none \ --regex='/DEFTREECODE [(]\([A-Z_]+\)/\1/' cp-tree.def; \ etags --include TAGS.sub --include ../TAGS.sub diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 3b0fd692e9f..4823d37a9c4 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -425,7 +425,8 @@ enum rejection_reason_code { rr_arg_conversion, rr_bad_arg_conversion, rr_template_unification, - rr_invalid_copy + rr_invalid_copy, + rr_constraint_failure }; struct conversion_info { @@ -688,6 +689,27 @@ invalid_copy_with_fn_template_rejection (void) return r; } +// Build a constraint failure record, saving information into the +// template_instantiation field of the rejection. If FN is not a template +// declaration, the TMPL member is the FN declaration and TARGS is empty. + +static struct rejection_reason * +constraint_failure (tree fn) +{ + struct rejection_reason *r = alloc_rejection (rr_constraint_failure); + if (tree ti = DECL_TEMPLATE_INFO (fn)) + { + r->u.template_instantiation.tmpl = TI_TEMPLATE (ti); + r->u.template_instantiation.targs = TI_ARGS (ti); + } + else + { + r->u.template_instantiation.tmpl = fn; + r->u.template_instantiation.targs = NULL_TREE; + } + return r; +} + /* Dynamically allocate a conversion. */ static conversion * @@ -1957,10 +1979,20 @@ add_function_candidate (struct z_candidate **candidates, viable = 0; reason = arity_rejection (first_arg, i + remaining, len); } + + /* Second, for a function to be viable, its constraints must be + satisfied. */ + if (flag_concepts && viable + && !constraints_satisfied_p (fn)) + { + reason = constraint_failure (fn); + viable = false; + } + /* When looking for a function from a subobject from an implicit copy/move constructor/operator=, don't consider anything that takes (a reference to) an unrelated type. See c++/44909 and core 1092. */ - else if (parmlist && (flags & LOOKUP_DEFAULTED)) + if (viable && parmlist && (flags & LOOKUP_DEFAULTED)) { if (DECL_CONSTRUCTOR_P (fn)) i = 1; @@ -1984,7 +2016,7 @@ add_function_candidate (struct z_candidate **candidates, if (! viable) goto out; - /* Second, for F to be a viable function, there shall exist for each + /* Third, for F to be a viable function, there shall exist for each argument an implicit conversion sequence that converts that argument to the corresponding parameter of F. */ @@ -3387,6 +3419,13 @@ print_z_candidate (location_t loc, const char *msgstr, " a constructor taking a single argument of its own " "class type is invalid"); break; + case rr_constraint_failure: + { + tree tmpl = r->u.template_instantiation.tmpl; + tree args = r->u.template_instantiation.targs; + diagnose_constraints (cloc, tmpl, args); + } + break; case rr_none: default: /* This candidate didn't have any issues or we failed to @@ -4044,9 +4083,13 @@ build_new_function_call (tree fn, vec **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 **args, bool koenig_p, through flags so that later we can use it to decide whether to warn about peculiar null pointer conversion. */ if (TREE_CODE (fn) == TEMPLATE_ID_EXPR) - flags |= LOOKUP_EXPLICIT_TMPL_ARGS; + { + /* If overload resolution selects a specialization of a + function concept for non-dependent template arguments, + the expression is true if the constraints are satisfied + and false otherwise. + + NOTE: This is an extension of Concepts Lite TS that + allows constraints to be used in expressions. */ + if (flag_concepts && !processing_template_decl) + { + tree tmpl = DECL_TI_TEMPLATE (cand->fn); + tree targs = DECL_TI_ARGS (cand->fn); + tree decl = DECL_TEMPLATE_RESULT (tmpl); + if (DECL_DECLARED_CONCEPT_P (decl)) + return evaluate_function_concept (decl, targs); + } + + flags |= LOOKUP_EXPLICIT_TMPL_ARGS; + } + result = build_over_call (cand, flags, complain); } @@ -9095,6 +9157,15 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn, return winner; } + // C++ Concepts + // or, if not that, F1 is more constrained than F2. + if (flag_concepts) + { + winner = more_constrained (cand1->fn, cand2->fn); + if (winner) + return winner; + } + /* Check whether we can discard a builtin candidate, either because we have two identical ones or matching builtin and non-builtin candidates. diff --git a/gcc/cp/class.c b/gcc/cp/class.c index f659fd4d3a5..bf7b5c4bab6 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -1138,7 +1138,8 @@ add_method (tree type, tree method, tree using_decl) if (compparms (parms1, parms2) && (!DECL_CONV_FN_P (fn) || same_type_p (TREE_TYPE (fn_type), - TREE_TYPE (method_type)))) + TREE_TYPE (method_type))) + && equivalently_constrained (fn, method)) { /* For function versions, their parms and types match but they are not duplicates. Record function versions @@ -4602,6 +4603,14 @@ build_clone (tree fn, tree name) TREE_TYPE (clone) = TREE_TYPE (result); return clone; } + else + { + // Clone constraints. + if (flag_concepts) + if (tree ci = get_constraints (fn)) + set_constraints (clone, copy_node (ci)); + } + SET_DECL_ASSEMBLER_NAME (clone, NULL_TREE); DECL_CLONED_FUNCTION (clone) = fn; @@ -7281,15 +7290,17 @@ pop_class_stack (void) } /* Returns 1 if the class type currently being defined is either T or - a nested type of T. */ + a nested type of T. Returns the type from the current_class_stack, + which might be equivalent to but not equal to T in case of + constrained partial specializations. */ -bool +tree currently_open_class (tree t) { int i; if (!CLASS_TYPE_P (t)) - return false; + return NULL_TREE; t = TYPE_MAIN_VARIANT (t); @@ -7309,9 +7320,9 @@ currently_open_class (tree t) if (!c) continue; if (same_type_p (c, t)) - return true; + return c; } - return false; + return NULL_TREE; } /* If either current_class_type or one of its enclosing classes are derived @@ -7661,6 +7672,12 @@ resolve_address_of_overloaded_function (tree target_type, /* Instantiation failed. */ continue; + /* Constraints must be satisfied. This is done before + return type deduction since that instantiates the + function. */ + if (flag_concepts && !constraints_satisfied_p (instantiation)) + continue; + /* And now force instantiation to do return type deduction. */ if (undeduced_auto_decl (instantiation)) { diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index fc4a3f75a0d..218faeccf80 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -3525,6 +3525,25 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, non_constant_p, overflow_p, jump_target); break; + case REQUIRES_EXPR: + /* It's possible to get a requires-expression in a constant + expression. For example: + + template concept bool C() { + return requires (T t) { t; }; + } + + template requires !C() void f(T); + + Normalization leaves f with the associated constraint + '!requires (T t) { ... }' which is not transformed into + a constraint. */ + if (!processing_template_decl) + return evaluate_constraint_expression (t, NULL_TREE); + else + *non_constant_p = true; + return t; + default: if (STATEMENT_CODE_P (TREE_CODE (t))) { @@ -3897,6 +3916,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, case PLACEHOLDER_EXPR: case BREAK_STMT: case CONTINUE_STMT: + case REQUIRES_EXPR: return true; case AGGR_INIT_EXPR: diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc new file mode 100644 index 00000000000..cf57cc010a4 --- /dev/null +++ b/gcc/cp/constraint.cc @@ -0,0 +1,2617 @@ +/* Processing rules for constraints. + Copyright (C) 2013-2015 Free Software Foundation, Inc. + Contributed by Andrew Sutton (andrew.n.sutton@gmail.com) + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#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() +// +// 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 %", 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 *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 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 struct S { + void f(T) requires C2; + void g(T) requires T::value; + }; + + S s; // error instantiating S::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 yields C2, 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 = EXPR. */ + tree conv = + perform_direct_initialization_if_possible (type, expr, false, complain); + if (conv == error_mark_node) + return boolean_false_node; + else + return boolean_true_node; +} + +/* Check an argument deduction constraint. */ + +tree +satisfy_argument_deduction_constraint (tree t, tree args, + tsubst_flags_t complain, tree in_decl) +{ + /* Substitute through the expression. */ + tree expr = DEDUCT_CONSTR_EXPR (t); + tree init = tsubst_expr (expr, args, complain, in_decl, false); + if (expr == error_mark_node) + return boolean_false_node; + + /* Perform auto or decltype(auto) deduction to get the result. */ + tree pattern = DEDUCT_CONSTR_PATTERN (t); + tree placeholder = DEDUCT_CONSTR_PLACEHOLDER (t); + tree constr = PLACEHOLDER_TYPE_CONSTRAINTS (placeholder); + PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) + = tsubst_constraint (constr, args, complain|tf_partial, in_decl); + tree type = do_auto_deduction (pattern, init, placeholder, + complain, adc_requirement); + PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) = constr; + if (type == error_mark_node) + return boolean_false_node; + + return boolean_true_node; +} + +/* Check an exception constraint. An exception constraint for an + expression e is satisfied when noexcept(e) is true. */ + +tree +satisfy_exception_constraint (tree t, tree args, + tsubst_flags_t complain, tree in_decl) +{ + tree expr = EXCEPT_CONSTR_EXPR (t); + tree check = tsubst_expr (expr, args, complain, in_decl, false); + if (check == error_mark_node) + return boolean_false_node; + + if (expr_noexcept_p (check, complain)) + return boolean_true_node; + else + return boolean_false_node; +} + +/* Check a parameterized constraint. */ + +tree +satisfy_parameterized_constraint (tree t, tree args, + tsubst_flags_t complain, tree in_decl) +{ + local_specialization_stack stack; + tree parms = PARM_CONSTR_PARMS (t); + tree vars = tsubst_constraint_variables (parms, args, complain, in_decl); + if (vars == error_mark_node) + return boolean_false_node; + tree constr = PARM_CONSTR_OPERAND (t); + return satisfy_constraint_1 (constr, args, complain, in_decl); +} + +/* Check that the conjunction of constraints is satisfied. Note + that if left operand is not satisfied, the right operand + is not checked. + + FIXME: Check that this wouldn't result in a user-defined + operator. Note that this error is partially diagnosed in + satisfy_predicate_constraint. It would be nice to diagnose + the overload, but I don't think it's strictly necessary. */ + +tree +satisfy_conjunction (tree t, tree args, tsubst_flags_t complain, tree in_decl) +{ + tree t0 = satisfy_constraint_1 (TREE_OPERAND (t, 0), args, complain, in_decl); + if (t0 == boolean_false_node) + return t0; + tree t1 = satisfy_constraint_1 (TREE_OPERAND (t, 1), args, complain, in_decl); + if (t1 == boolean_false_node) + return t1; + return boolean_true_node; +} + +/* Check that the disjunction of constraints is satisfied. Note + that if the left operand is satisfied, the right operand is not + checked. */ + +tree +satisfy_disjunction (tree t, tree args, tsubst_flags_t complain, tree in_decl) +{ + tree t0 = satisfy_constraint_1 (TREE_OPERAND (t, 0), args, complain, in_decl); + if (t0 == boolean_true_node) + return boolean_true_node; + tree t1 = satisfy_constraint_1 (TREE_OPERAND (t, 1), args, complain, in_decl); + if (t1 == boolean_true_node) + return boolean_true_node; + return boolean_false_node; +} + +/* Dispatch to an appropriate satisfaction routine depending on the + tree code of T. */ + +tree +satisfy_constraint_1 (tree t, tree args, tsubst_flags_t complain, tree in_decl) +{ + gcc_assert (!processing_template_decl); + + if (!t) + return boolean_false_node; + + if (t == error_mark_node) + return boolean_false_node; + + switch (TREE_CODE (t)) + { + case PRED_CONSTR: + return satisfy_predicate_constraint (t, args, complain, in_decl); + + case EXPR_CONSTR: + return satisfy_expression_constraint (t, args, complain, in_decl); + + case TYPE_CONSTR: + return satisfy_type_constraint (t, args, complain, in_decl); + + case ICONV_CONSTR: + return satisfy_implicit_conversion_constraint (t, args, complain, in_decl); + + case DEDUCT_CONSTR: + return satisfy_argument_deduction_constraint (t, args, complain, in_decl); + + case EXCEPT_CONSTR: + return satisfy_exception_constraint (t, args, complain, in_decl); + + case PARM_CONSTR: + return satisfy_parameterized_constraint (t, args, complain, in_decl); + + case CONJ_CONSTR: + return satisfy_conjunction (t, args, complain, in_decl); + + case DISJ_CONSTR: + return satisfy_disjunction (t, args, complain, in_decl); + + case EXPR_PACK_EXPANSION: + return satisfy_pack_expansion (t, args, complain, in_decl); + + default: + gcc_unreachable (); + } + return boolean_false_node; +} + +/* Check that the constraint is satisfied, according to the rules + for that constraint. Note that each satisfy_* function returns + true or false, depending on whether it is satisfied or not. */ + +tree +satisfy_constraint (tree t, tree args) +{ + /* Turn off template processing. Constraint satisfaction only applies + to non-dependent terms, so we want full checking here. */ + processing_template_decl_sentinel sentinel (true); + /* Avoid early exit in tsubst and tsubst_copy from null args; since earlier + substitution was done with processing_template_decl forced on, there will + be expressions that still need semantic processing, possibly buried in + decltype or a template argument. */ + if (args == NULL_TREE) + args = make_tree_vec (1); + return satisfy_constraint_1 (t, args, tf_none, NULL_TREE); +} + +/* Check the associated constraints in CI against the given + ARGS, returning true when the constraints are satisfied + and false otherwise. */ + +tree +satisfy_associated_constraints (tree ci, tree args) +{ + /* If there are no constraints then this is trivially satisfied. */ + if (!ci) + return boolean_true_node; + + /* If any arguments depend on template parameters, we can't + check constraints. */ + if (args && uses_template_parms (args)) + return boolean_true_node; + + /* Invalid requirements cannot be satisfied. */ + if (!valid_constraints_p (ci)) + return boolean_false_node; + + return satisfy_constraint (CI_NORMALIZED_CONSTRAINTS (ci), args); +} + +} /* namespace */ + +/* Evaluate the given constraint, returning boolean_true_node + if the constraint is satisfied and boolean_false_node + otherwise. */ + +tree +evaluate_constraints (tree constr, tree args) +{ + gcc_assert (constraint_p (constr)); + return satisfy_constraint (normalize_constraint (constr), args); +} + +/* Evaluate the function concept FN by substituting its own args + into its definition and evaluating that as the result. Returns + boolean_true_node if the constraints are satisfied and + boolean_false_node otherwise. */ + +tree +evaluate_function_concept (tree fn, tree args) +{ + ++processing_template_decl; + /* We lift using DECL_TI_ARGS because we want to delay producing + non-dependent expressions until we're doing satisfaction. We can't just + go without any substitution because we need to lower the level of 'auto's + in type deduction constraints. */ + tree constr = transform_expression (lift_function_definition + (fn, DECL_TI_ARGS (fn))); + --processing_template_decl; + return satisfy_constraint (constr, args); +} + +/* Evaluate the variable concept VAR by substituting its own args into + its initializer and checking the resulting constraint. Returns + boolean_true_node if the constraints are satisfied and + boolean_false_node otherwise. */ + +tree +evaluate_variable_concept (tree decl, tree args) +{ + ++processing_template_decl; + tree constr = transform_expression (lift_variable_initializer + (decl, DECL_TI_ARGS (decl))); + --processing_template_decl; + return satisfy_constraint (constr, args); +} + +/* Evaluate the given expression as if it were a predicate + constraint. Returns boolean_true_node if the constraint + is satisfied and boolean_false_node otherwise. */ + +tree +evaluate_constraint_expression (tree expr, tree args) +{ + ++processing_template_decl; + tree constr = transform_expression (lift_expression (expr)); + --processing_template_decl; + return satisfy_constraint (constr, args); +} + +/* Returns true if the DECL's constraints are satisfied. + This is used in cases where a declaration is formed but + before it is used (e.g., overload resolution). */ + +bool +constraints_satisfied_p (tree decl) +{ + /* Get the constraints to check for satisfaction. This depends + on whether we're looking at a template specialization or not. */ + tree ci; + tree args = NULL_TREE; + if (tree ti = DECL_TEMPLATE_INFO (decl)) + { + ci = get_constraints (TI_TEMPLATE (ti)); + args = INNERMOST_TEMPLATE_ARGS (TI_ARGS (ti)); + } + else + { + ci = get_constraints (decl); + } + + tree eval = satisfy_associated_constraints (ci, args); + return eval == boolean_true_node; +} + +/* Returns true if the constraints are satisfied by ARGS. + Here, T can be either a constraint or a constrained + declaration. */ + +bool +constraints_satisfied_p (tree t, tree args) +{ + tree eval; + if (constraint_p (t)) + eval = evaluate_constraints (t, args); + else + eval = satisfy_associated_constraints (get_constraints (t), args); + return eval == boolean_true_node; +} + +namespace +{ + +/* Normalize EXPR and determine if the resulting constraint is + satisfied by ARGS. Returns true if and only if the constraint + is satisfied. This is used extensively by diagnostics to + determine causes for failure. */ + +inline bool +constraint_expression_satisfied_p (tree expr, tree args) +{ + return evaluate_constraint_expression (expr, args) == boolean_true_node; +} + +} /* namespace */ + + +/*--------------------------------------------------------------------------- + Semantic analysis of requires-expressions +---------------------------------------------------------------------------*/ + +/* Finish a requires expression for the given PARMS (possibly + null) and the non-empty sequence of requirements. */ +tree +finish_requires_expr (tree parms, tree reqs) +{ + /* Modify the declared parameters by removing their context + so they don't refer to the enclosing scope and explicitly + indicating that they are constraint variables. */ + for (tree parm = parms; parm; parm = DECL_CHAIN (parm)) + { + DECL_CONTEXT (parm) = NULL_TREE; + CONSTRAINT_VAR_P (parm) = true; + } + + /* Build the node. */ + tree r = build_min (REQUIRES_EXPR, boolean_type_node, parms, reqs); + TREE_SIDE_EFFECTS (r) = false; + TREE_CONSTANT (r) = true; + return r; +} + +/* Construct a requirement for the validity of EXPR. */ +tree +finish_simple_requirement (tree expr) +{ + return build_nt (SIMPLE_REQ, expr); +} + +/* Construct a requirement for the validity of TYPE. */ +tree +finish_type_requirement (tree type) +{ + return build_nt (TYPE_REQ, type); +} + +/* Construct a requirement for the validity of EXPR, along with + its properties. if TYPE is non-null, then it specifies either + an implicit conversion or argument deduction constraint, + depending on whether any placeholders occur in the type name. + NOEXCEPT_P is true iff the noexcept keyword was specified. */ +tree +finish_compound_requirement (tree expr, tree type, bool noexcept_p) +{ + tree req = build_nt (COMPOUND_REQ, expr, type); + COMPOUND_REQ_NOEXCEPT_P (req) = noexcept_p; + return req; +} + +/* Finish a nested requirement. */ +tree +finish_nested_requirement (tree expr) +{ + return build_nt (NESTED_REQ, expr); +} + +// Check that FN satisfies the structural requirements of a +// function concept definition. +tree +check_function_concept (tree fn) +{ + // Check that the function is comprised of only a single + // return statement. + tree body = DECL_SAVED_TREE (fn); + if (TREE_CODE (body) == BIND_EXPR) + body = BIND_EXPR_BODY (body); + + // Sometimes a function call results in the creation of clean up + // points. Allow these to be preserved in the body of the + // constraint, as we might actually need them for some constexpr + // evaluations. + if (TREE_CODE (body) == CLEANUP_POINT_EXPR) + body = TREE_OPERAND (body, 0); + + /* Check that the definition is written correctly. */ + if (TREE_CODE (body) != RETURN_EXPR) + { + location_t loc = DECL_SOURCE_LOCATION (fn); + if (TREE_CODE (body) == STATEMENT_LIST && !STATEMENT_LIST_HEAD (body)) + error_at (loc, "definition of concept %qD is empty", fn); + else + error_at (loc, "definition of concept %qD has multiple statements", fn); + } + + return NULL_TREE; +} + + +// Check that a constrained friend declaration function declaration, +// FN, is admissible. This is the case only when the declaration depends +// on template parameters and does not declare a specialization. +void +check_constrained_friend (tree fn, tree reqs) +{ + if (fn == error_mark_node) + return; + gcc_assert (TREE_CODE (fn) == FUNCTION_DECL); + + // If there are not constraints, this cannot be an error. + if (!reqs) + return; + + // Constrained friend functions that don't depend on template + // arguments are effectively meaningless. + if (!uses_template_parms (TREE_TYPE (fn))) + { + error_at (location_of (fn), + "constrained friend does not depend on template parameters"); + return; + } +} + +/*--------------------------------------------------------------------------- + Equivalence of constraints +---------------------------------------------------------------------------*/ + +/* Returns true when A and B are equivalent constraints. */ +bool +equivalent_constraints (tree a, tree b) +{ + gcc_assert (!a || TREE_CODE (a) == CONSTRAINT_INFO); + gcc_assert (!b || TREE_CODE (b) == CONSTRAINT_INFO); + return cp_tree_equal (a, b); +} + +/* Returns true if the template declarations A and B have equivalent + constraints. This is the case when A's constraints subsume B's and + when B's also constrain A's. */ +bool +equivalently_constrained (tree d1, tree d2) +{ + gcc_assert (TREE_CODE (d1) == TREE_CODE (d2)); + return equivalent_constraints (get_constraints (d1), get_constraints (d2)); +} + +/*--------------------------------------------------------------------------- + Partial ordering of constraints +---------------------------------------------------------------------------*/ + +/* Returns true when the the constraints in A subsume those in B. */ +bool +subsumes_constraints (tree a, tree b) +{ + gcc_assert (!a || TREE_CODE (a) == CONSTRAINT_INFO); + gcc_assert (!b || TREE_CODE (b) == CONSTRAINT_INFO); + return subsumes (a, b); +} + +/* Determines which of the declarations, A or B, is more constrained. + That is, which declaration's constraints subsume but are not subsumed + by the other's? + + Returns 1 if A is more constrained than B, -1 if B is more constrained + than A, and 0 otherwise. */ +int +more_constrained (tree d1, tree d2) +{ + tree c1 = get_constraints (d1); + tree c2 = get_constraints (d2); + int winner = 0; + if (subsumes_constraints (c1, c2)) + ++winner; + if (subsumes_constraints (c2, c1)) + --winner; + return winner; +} + +/* Returns true if D1 is at least as constrained as D2. That is, the + associated constraints of D1 subsume those of D2, or both declarations + are unconstrained. */ +bool +at_least_as_constrained (tree d1, tree d2) +{ + tree c1 = get_constraints (d1); + tree c2 = get_constraints (d2); + return subsumes_constraints (c1, c2); +} + + +/*--------------------------------------------------------------------------- + Constraint diagnostics +---------------------------------------------------------------------------*/ + +/* The diagnosis of constraints performs a combination of + normalization and satisfaction testing. We recursively + walk through the conjunction (or disjunctions) of associated + constraints, testing each sub-expression in turn. + + We currently restrict diagnostics to just the top-level + conjunctions within the associated constraints. A fully + recursive walk is possible, but it can generate a lot + of errors. */ + + +namespace { + +void diagnose_expression (location_t, tree, tree); +void diagnose_constraint (location_t, tree, tree); + +/* Diagnose a conjunction of constraints. */ +void +diagnose_logical_operation (location_t loc, tree t, tree args) +{ + diagnose_expression (loc, TREE_OPERAND (t, 0), args); + diagnose_expression (loc, TREE_OPERAND (t, 0), args); +} + +/* Determine if the trait expression T is satisfied by ARGS. + Emit a precise diagnostic if it is not. */ +void +diagnose_trait_expression (location_t loc, tree t, tree args) +{ + if (constraint_expression_satisfied_p (t, args)) + return; + + /* Rebuild the trait expression so we can diagnose the + specific failure. */ + ++processing_template_decl; + tree expr = tsubst_expr (t, args, tf_none, NULL_TREE, false); + --processing_template_decl; + + tree t1 = TRAIT_EXPR_TYPE1 (expr); + tree t2 = TRAIT_EXPR_TYPE2 (expr); + switch (TRAIT_EXPR_KIND (t)) + { + case CPTK_HAS_NOTHROW_ASSIGN: + inform (loc, " %qT is not nothrow copy assignable", t1); + break; + case CPTK_HAS_NOTHROW_CONSTRUCTOR: + inform (loc, " %qT is not nothrow default constructible", t1); + break; + case CPTK_HAS_NOTHROW_COPY: + inform (loc, " %qT is not nothrow copy constructible", t1); + break; + case CPTK_HAS_TRIVIAL_ASSIGN: + inform (loc, " %qT is not trivially copy assignable", t1); + break; + case CPTK_HAS_TRIVIAL_CONSTRUCTOR: + inform (loc, " %qT is not trivially default constructible", t1); + break; + case CPTK_HAS_TRIVIAL_COPY: + inform (loc, " %qT is not trivially copy constructible", t1); + break; + case CPTK_HAS_TRIVIAL_DESTRUCTOR: + inform (loc, " %qT is not trivially destructible", t1); + break; + case CPTK_HAS_VIRTUAL_DESTRUCTOR: + inform (loc, " %qT does not have a virtual destructor", t1); + break; + case CPTK_IS_ABSTRACT: + inform (loc, " %qT is not an abstract class", t1); + break; + case CPTK_IS_BASE_OF: + inform (loc, " %qT is not a base of %qT", t1, t2); + break; + case CPTK_IS_CLASS: + inform (loc, " %qT is not a class", t1); + break; + case CPTK_IS_EMPTY: + inform (loc, " %qT is not an empty class", t1); + break; + case CPTK_IS_ENUM: + inform (loc, " %qT is not an enum", t1); + break; + case CPTK_IS_FINAL: + inform (loc, " %qT is not a final class", t1); + break; + case CPTK_IS_LITERAL_TYPE: + inform (loc, " %qT is not a literal type", t1); + break; + case CPTK_IS_POD: + inform (loc, " %qT is not a POD type", t1); + break; + case CPTK_IS_POLYMORPHIC: + inform (loc, " %qT is not a polymorphic type", t1); + break; + case CPTK_IS_SAME_AS: + inform (loc, " %qT is not the same as %qT", t1, t2); + break; + case CPTK_IS_STD_LAYOUT: + inform (loc, " %qT is not an standard layout type", t1); + break; + case CPTK_IS_TRIVIAL: + inform (loc, " %qT is not a trivial type", t1); + break; + case CPTK_IS_UNION: + inform (loc, " %qT is not a union", t1); + break; + default: + gcc_unreachable (); + } +} + +/* Determine if the call expression T, when normalized as a constraint, + is satisfied by ARGS. + + TODO: If T is refers to a concept, We could recursively analyze + its definition to identify the exact failure, but that could + emit a *lot* of error messages (defeating the purpose of + improved diagnostics). Consider adding a flag to control the + depth of diagnostics. */ +void +diagnose_call_expression (location_t loc, tree t, tree args) +{ + if (constraint_expression_satisfied_p (t, args)) + return; + + /* Rebuild the expression for the purpose of diagnostics. */ + ++processing_template_decl; + tree expr = tsubst_expr (t, args, tf_none, NULL_TREE, false); + --processing_template_decl; + + /* If the function call is known to be a concept check, then + diagnose it differently (i.e., we may recurse). */ + if (resolve_constraint_check (t)) + inform (loc, " concept %qE was not satisfied", expr); + else + inform (loc, " %qE evaluated to false", expr); +} + +/* Determine if the template-id T, when normalized as a constraint + is satisfied by ARGS. */ +void +diagnose_template_id (location_t loc, tree t, tree args) +{ + /* Check for invalid template-ids. */ + if (!variable_template_p (TREE_OPERAND (t, 0))) + { + inform (loc, " invalid constraint %qE", t); + return; + } + + if (constraint_expression_satisfied_p (t, args)) + return; + + /* Rebuild the expression for the purpose of diagnostics. */ + ++processing_template_decl; + tree expr = tsubst_expr (t, args, tf_none, NULL_TREE, false); + --processing_template_decl; + + tree var = DECL_TEMPLATE_RESULT (TREE_OPERAND (t, 0)); + if (DECL_DECLARED_CONCEPT_P (var)) + inform (loc, " concept %qE was not satisfied", expr); + else + inform (loc, " %qE evaluated to false", expr); +} + +/* Determine if the requires-expression, when normalized as a + constraint is satisfied by ARGS. + + TODO: Build sets of expressions, types, and constraints + based on the requirements in T and emit specific diagnostics + for those. */ +void +diagnose_requires_expression (location_t loc, tree t, tree args) +{ + if (constraint_expression_satisfied_p (t, args)) + return; + inform (loc, "requirements not satisfied"); +} + +void +diagnose_pack_expansion (location_t loc, tree t, tree args) +{ + if (constraint_expression_satisfied_p (t, args)) + return; + + /* Make sure that we don't have naked packs that we don't expect. */ + if (!same_type_p (TREE_TYPE (t), boolean_type_node)) + { + inform (loc, "invalid pack expansion in constraint %qE", t); + return; + } + + inform (loc, " in the expansion of %qE", t); + + /* Get the vector of expanded arguments. Note that n must not + be 0 since this constraint is not satisfied. */ + ++processing_template_decl; + tree exprs = tsubst_pack_expansion (t, args, tf_none, NULL_TREE); + --processing_template_decl; + if (exprs == error_mark_node) + { + /* TODO: This error message could be better. */ + inform (loc, " substitution failure occurred during expansion"); + return; + } + + /* Check each expanded constraint separately. */ + int n = TREE_VEC_LENGTH (exprs); + for (int i = 0; i < n; ++i) + { + tree expr = TREE_VEC_ELT (exprs, i); + if (!constraint_expression_satisfied_p (expr, args)) + inform (loc, " %qE was not satisfied", expr); + } +} + +/* Diagnose an expression that would be characterized as + a predicate constraint. */ +void +diagnose_other_expression (location_t loc, tree t, tree args) +{ + if (constraint_expression_satisfied_p (t, args)) + return; + inform (loc, " %qE evaluated to false", t); +} + +void +diagnose_expression (location_t loc, tree t, tree args) +{ + switch (TREE_CODE (t)) + { + case TRUTH_ANDIF_EXPR: + diagnose_logical_operation (loc, t, args); + break; + + case TRUTH_ORIF_EXPR: + diagnose_logical_operation (loc, t, args); + break; + + case CALL_EXPR: + diagnose_call_expression (loc, t, args); + break; + + case TEMPLATE_ID_EXPR: + diagnose_template_id (loc, t, args); + break; + + case REQUIRES_EXPR: + diagnose_requires_expression (loc, t, args); + break; + + case TRAIT_EXPR: + diagnose_trait_expression (loc, t, args); + break; + + case EXPR_PACK_EXPANSION: + diagnose_pack_expansion (loc, t, args); + break; + + default: + diagnose_other_expression (loc, t, args); + break; + } +} + +inline void +diagnose_predicate_constraint (location_t loc, tree t, tree args) +{ + diagnose_expression (loc, PRED_CONSTR_EXPR (t), args); +} + +inline void +diagnose_conjunction (location_t loc, tree t, tree args) +{ + diagnose_constraint (loc, TREE_OPERAND (t, 0), args); + diagnose_constraint (loc, TREE_OPERAND (t, 1), args); +} + +/* Diagnose the constraint T for the given ARGS. This is only + ever invoked on the associated constraints, so we can + only have conjunctions of predicate constraints. */ +void +diagnose_constraint (location_t loc, tree t, tree args) +{ + switch (TREE_CODE (t)) + { + case CONJ_CONSTR: + diagnose_conjunction (loc, t, args); + break; + + case PRED_CONSTR: + diagnose_predicate_constraint (loc, t, args); + break; + + default: + gcc_unreachable (); + break; + } +} + +/* Diagnose the reason(s) why ARGS do not satisfy the constraints + of declaration DECL. */ + +void +diagnose_declaration_constraints (location_t loc, tree decl, tree args) +{ + inform (loc, " constraints not satisfied"); + + /* Constraints are attached to the template. */ + if (tree ti = DECL_TEMPLATE_INFO (decl)) + { + decl = TI_TEMPLATE (ti); + if (!args) + args = TI_ARGS (ti); + } + + /* Check that the constraints are actually valid. */ + tree ci = get_constraints (decl); + if (!valid_constraints_p (ci)) + { + inform (loc, " invalid constraints"); + return; + } + + /* Recursively diagnose the associated constraints. */ + diagnose_constraint (loc, CI_ASSOCIATED_CONSTRAINTS (ci), args); +} + +} // namespace + +/* Emit diagnostics detailing the failure ARGS to satisfy the + constraints of T. Here, T can be either a constraint + or a declaration. */ + +void +diagnose_constraints (location_t loc, tree t, tree args) +{ + if (constraint_p (t)) + diagnose_constraint (loc, t, args); + else + diagnose_declaration_constraints (loc, t, args); +} diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c index 31f12c8b7a3..2cab89c1552 100644 --- a/gcc/cp/cp-objcp-common.c +++ b/gcc/cp/cp-objcp-common.c @@ -100,6 +100,8 @@ cp_tree_size (enum tree_code code) case TEMPLATE_INFO: return sizeof (struct tree_template_info); + case CONSTRAINT_INFO: return sizeof (struct tree_constraint_info); + case USERDEF_LITERAL: return sizeof (struct tree_userdef_literal); case TEMPLATE_DECL: return sizeof (struct tree_template_decl); @@ -240,6 +242,7 @@ cp_common_init_ts (void) { MARK_TS_DECL_NON_COMMON (USING_DECL); MARK_TS_DECL_COMMON (TEMPLATE_DECL); + MARK_TS_DECL_COMMON (WILDCARD_DECL); MARK_TS_COMMON (TEMPLATE_TEMPLATE_PARM); MARK_TS_COMMON (TEMPLATE_TYPE_PARM); @@ -311,6 +314,7 @@ cp_common_init_ts (void) MARK_TS_TYPED (LAMBDA_EXPR); MARK_TS_TYPED (CTOR_INITIALIZER); MARK_TS_TYPED (ARRAY_NOTATION_REF); + MARK_TS_TYPED (REQUIRES_EXPR); } #include "gt-cp-cp-objcp-common.h" diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def index 6117771d7f0..61acf271885 100644 --- a/gcc/cp/cp-tree.def +++ b/gcc/cp/cp-tree.def @@ -475,6 +475,94 @@ DEFTREECODE (BASES, "bases", tcc_type, 0) instantiation time. */ DEFTREECODE (TEMPLATE_INFO, "template_info", tcc_exceptional, 0) +/* Extensions for Concepts. */ + +/* Used to represent information associated with constrained declarations. */ +DEFTREECODE (CONSTRAINT_INFO, "constraint_info", tcc_exceptional, 0) + +/* A wildcard declaration is a placeholder for a template parameter + used to resolve constrained-type-names in concepts. During + resolution, the matching argument is saved as the TREE_TYPE + of the wildcard. */ +DEFTREECODE (WILDCARD_DECL, "wildcard_decl", tcc_declaration, 0) + +/* A requires-expr is a binary expression. The first operand is + its parameter list (possibly NULL). The second is a list of + requirements, which are denoted by the _REQ* tree codes + below. */ +DEFTREECODE (REQUIRES_EXPR, "requires_expr", tcc_expression, 2) + +/* A requirement for an expression. */ +DEFTREECODE (SIMPLE_REQ, "simple_req", tcc_expression, 1) + +/* A requirement for a type. */ +DEFTREECODE (TYPE_REQ, "type_req", tcc_expression, 1) + +/* A requirement for an expression and its properties. The + first operand is the expression, and the 2nd is its type. + The accessor COMPOUND_REQ_NOEXCEPT determines whether + the noexcept keyword was present. */ +DEFTREECODE (COMPOUND_REQ, "compound_req", tcc_expression, 2) + +/* A requires clause within a requires expression. */ +DEFTREECODE (NESTED_REQ, "nested_req", tcc_expression, 1) + +/* Constraints are modeled as kinds of expressions. + The operands of a constraint can be either types or expressions. + Unlike expressions, constraints do not have a type. */ + +/* A predicate constraint evaluates an expression E. + + PRED_CONSTR_EXPR has the expression to be evaluated. */ +DEFTREECODE (PRED_CONSTR, "pred_constr", tcc_expression, 1) + +/* An expression constraint determines the validity of a expression E. + + EXPR_CONST_EXPR has the expression being validated. */ +DEFTREECODE (EXPR_CONSTR, "expr_constr", tcc_expression, 1) + +/* A type constraint determines the validity of a type T. Note that + + TYPE_CONST_TYPE has the type being validated */ +DEFTREECODE (TYPE_CONSTR, "type_constr", tcc_expression, 1) + +/* An implicit conversion constraint determines if an expression + E is implicitly convertible to a type T. Note that T may + be dependent but does not contain any placeholders. + + ICONV_CONSTR_EXPR has the expression E. + ICONV_CONSTR_TYPE has the type T. + */ +DEFTREECODE (ICONV_CONSTR, "iconv_constr", tcc_expression, 2) + +/* An argument deduction constraint determines if the type of an + expression E can be deduced from a type pattern T. Note that + T must contain at least one place holder. + + DEDUCT_CONSTR_EXPR has the expression E + DEDUCT_CONSTR_PATTERN has the type patter T. + DEDUCT_CONSTR_PLACEHOLDERS has the list of placeholder nodes in T. */ +DEFTREECODE (DEDUCT_CONSTR, "deduct_constr", tcc_expression, 3) + +/* An exception constraint determines if, for an expression E, + noexcept(E) is true. + + EXCEPT_CONSTR_EXPR has the expression E. */ +DEFTREECODE (EXCEPT_CONSTR, "except_constr", tcc_expression, 1) + +/* A parameterized constraint declares constraint variables, which + are used in expression, type, and exception constraints. + + PARM_CONSTR_PARMS has a TREE_LIST of parameter declarations. + PARM_CONSTR_OPERAND has the nested constraint. */ +DEFTREECODE (PARM_CONSTR, "parm_constr", tcc_expression, 2) + +/* The conjunction and disjunction of two constraints, respectively. + Operands are accessed using TREE_OPERAND. */ +DEFTREECODE (CONJ_CONSTR, "conj_constr", tcc_expression, 2) +DEFTREECODE (DISJ_CONSTR, "disj_constr", tcc_expression, 2) + + /* Local variables: mode:c diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index e70dcb45a37..78fd4af0a2f 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -77,6 +77,8 @@ c-common.h, not after. PACK_EXPANSION_LOCAL_P (in *_PACK_EXPANSION) TINFO_HAS_ACCESS_ERRORS (in TEMPLATE_INFO) SIZEOF_EXPR_TYPE_P (in SIZEOF_EXPR) + COMPOUND_REQ_NOEXCEPT_P (in COMPOUND_REQ) + WILDCARD_PACK_P (in WILDCARD_DECL) BLOCK_OUTER_CURLY_BRACE_P (in BLOCK) 1: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE) TI_PENDING_TEMPLATE_FLAG. @@ -154,6 +156,7 @@ c-common.h, not after. LABEL_DECL_CONTINUE (in LABEL_DECL) 2: DECL_THIS_EXTERN (in VAR_DECL or FUNCTION_DECL). DECL_IMPLICIT_TYPEDEF_P (in a TYPE_DECL) + DECL_CONSTRAINT_VAR_P (in a PARM_DECL) TEMPLATE_DECL_COMPLEX_ALIAS_P (in TEMPLATE_DECL) DECL_INSTANTIATING_NSDMI_P (in a FIELD_DECL) 3: DECL_IN_AGGR_P. @@ -660,6 +663,7 @@ typedef enum cp_trait_kind CPTK_IS_LITERAL_TYPE, CPTK_IS_POD, CPTK_IS_POLYMORPHIC, + CPTK_IS_SAME_AS, CPTK_IS_STD_LAYOUT, CPTK_IS_TRIVIAL, CPTK_IS_TRIVIALLY_ASSIGNABLE, @@ -812,6 +816,154 @@ struct GTY(()) tree_template_info { vec *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 +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 *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 *, extern bool perform_deferred_access_checks (tsubst_flags_t); extern bool perform_or_defer_access_check (tree, tree, tree, tsubst_flags_t); + +/* RAII sentinel to ensures that deferred access checks are popped before + a function returns. */ + +struct deferring_access_check_sentinel +{ + deferring_access_check_sentinel () + { + push_deferring_access_checks (dk_deferred); + } + ~deferring_access_check_sentinel () + { + pop_deferring_access_checks (); + } +}; + extern int stmts_are_full_exprs_p (void); extern void init_cp_semantics (void); extern tree do_poplevel (tree); @@ -6392,6 +6645,60 @@ extern bool cxx_omp_privatize_by_reference (const_tree); extern void suggest_alternatives_for (location_t, tree); extern tree strip_using_decl (tree); +/* in constraint.cc */ +extern void init_constraint_processing (); +extern bool constraint_p (tree); +extern tree make_predicate_constraint (tree); +extern tree conjoin_constraints (tree, tree); +extern tree conjoin_constraints (tree); +extern bool valid_constraints_p (tree); +extern tree get_constraints (tree); +extern void set_constraints (tree, tree); +extern void remove_constraints (tree); +extern tree current_template_constraints (void); +extern tree associate_classtype_constraints (tree); +extern tree build_constraints (tree, tree); +extern tree get_shorthand_constraints (tree); +extern tree build_concept_check (tree, tree, tree = NULL_TREE); +extern tree build_constrained_parameter (tree, tree, tree = NULL_TREE); +extern tree make_constrained_auto (tree, tree); +extern bool deduce_constrained_parameter (tree, tree&, tree&); +extern tree resolve_constraint_check (tree); +extern tree check_function_concept (tree); +extern tree finish_template_introduction (tree, tree); +extern bool valid_requirements_p (tree); +extern tree finish_concept_name (tree); +extern tree finish_shorthand_constraint (tree, tree); +extern tree finish_requires_expr (tree, tree); +extern tree finish_simple_requirement (tree); +extern tree finish_type_requirement (tree); +extern tree finish_compound_requirement (tree, tree, bool); +extern tree finish_nested_requirement (tree); +extern void check_constrained_friend (tree, tree); +extern tree tsubst_requires_expr (tree, tree, tsubst_flags_t, tree); +extern tree tsubst_constraint (tree, tree, tsubst_flags_t, tree); +extern tree tsubst_constraint_info (tree, tree, tsubst_flags_t, tree); +extern bool function_concept_check_p (tree); + +extern tree evaluate_constraints (tree, tree); +extern tree evaluate_function_concept (tree, tree); +extern tree evaluate_variable_concept (tree, tree); +extern tree evaluate_constraint_expression (tree, tree); +extern bool constraints_satisfied_p (tree); +extern bool constraints_satisfied_p (tree, tree); + +extern bool equivalent_constraints (tree, tree); +extern bool equivalently_constrained (tree, tree); +extern bool subsumes_constraints (tree, tree); +extern int more_constrained (tree, tree); + +extern void diagnose_constraints (location_t, tree, tree); + +/* in logic.cc */ +extern tree decompose_assumptions (tree); +extern tree decompose_conclusions (tree); +extern bool subsumes (tree, tree); + /* in vtable-class-hierarchy.c */ extern void vtv_compute_class_hierarchy_transitive_closure (void); extern void vtv_generate_init_routine (void); diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c index 60be71ae981..879eb71fc3a 100644 --- a/gcc/cp/cxx-pretty-print.c +++ b/gcc/cp/cxx-pretty-print.c @@ -451,6 +451,10 @@ cxx_pretty_printer::primary_expression (tree t) pp_cxx_offsetof_expression (this, t); break; + case REQUIRES_EXPR: + pp_cxx_requires_expr (this, t); + break; + default: c_pretty_printer::primary_expression (t); break; @@ -1064,6 +1068,7 @@ cxx_pretty_printer::expression (tree t) case TEMPLATE_PARM_INDEX: case TEMPLATE_TEMPLATE_PARM: case STMT_EXPR: + case REQUIRES_EXPR: primary_expression (t); break; @@ -1158,6 +1163,22 @@ cxx_pretty_printer::expression (tree t) pp_cxx_ws_string (this, ""); break; + case TRAIT_EXPR: + pp_cxx_trait_expression (this, t); + break; + + case PRED_CONSTR: + case EXPR_CONSTR: + case TYPE_CONSTR: + case ICONV_CONSTR: + case DEDUCT_CONSTR: + case EXCEPT_CONSTR: + case PARM_CONSTR: + case CONJ_CONSTR: + case DISJ_CONSTR: + pp_cxx_constraint (this, t); + break; + case PAREN_EXPR: pp_cxx_left_paren (this); expression (TREE_OPERAND (t, 0)); @@ -1423,10 +1444,26 @@ pp_cxx_parameter_declaration (cxx_pretty_printer *pp, tree t) static void pp_cxx_parameter_declaration_clause (cxx_pretty_printer *pp, tree t) { - tree args = TYPE_P (t) ? NULL : FUNCTION_FIRST_USER_PARM (t); - tree types = - TYPE_P (t) ? TYPE_ARG_TYPES (t) : FUNCTION_FIRST_USER_PARMTYPE (t); - const bool abstract = args == NULL || pp->flags & pp_c_flag_abstract; + tree args; + tree types; + bool abstract; + + // For a requires clause or the explicit printing of a parameter list + // we expect T to be a chain of PARM_DECLs. Otherwise, the list of + // args and types are taken from the function decl T. + if (TREE_CODE (t) == PARM_DECL) + { + args = t; + types = t; + abstract = false; + } + else + { + bool type_p = TYPE_P (t); + args = type_p ? NULL : FUNCTION_FIRST_USER_PARM (t); + types = type_p ? TYPE_ARG_TYPES (t) : FUNCTION_FIRST_USER_PARMTYPE (t); + abstract = args == NULL || pp->flags & pp_c_flag_abstract; + } bool first = true; /* Skip artificial parameter for nonstatic member functions. */ @@ -1574,6 +1611,12 @@ void cxx_pretty_printer::declarator (tree t) { direct_declarator (t); + + // Print a requires clause. + if (flag_concepts) + if (tree ci = get_constraints (t)) + if (tree reqs = CI_DECLARATOR_REQS (ci)) + pp_cxx_requires_clause (this, reqs); } /* ctor-initializer: @@ -2154,7 +2197,13 @@ pp_cxx_canonical_template_parameter (cxx_pretty_printer *pp, tree parm) /* template-declaration: - export(opt) template < template-parameter-list > declaration */ + export(opt) template < template-parameter-list > declaration + + Concept extensions: + + template-declaration: + export(opt) template < template-parameter-list > + requires-clause(opt) declaration */ static void pp_cxx_template_declaration (cxx_pretty_printer *pp, tree t) @@ -2171,6 +2220,15 @@ pp_cxx_template_declaration (cxx_pretty_printer *pp, tree t) pp_cxx_end_template_argument_list (pp); pp_newline_and_indent (pp, 3); } + + if (flag_concepts) + if (tree ci = get_constraints (t)) + if (tree reqs = CI_TEMPLATE_REQS (ci)) + { + pp_cxx_requires_clause (pp, reqs); + pp_newline_and_indent (pp, 6); + } + if (TREE_CODE (t) == FUNCTION_DECL && DECL_SAVED_TREE (t)) pp_cxx_function_definition (pp, t); else @@ -2387,6 +2445,9 @@ pp_cxx_trait_expression (cxx_pretty_printer *pp, tree t) case CPTK_IS_POLYMORPHIC: pp_cxx_ws_string (pp, "__is_polymorphic"); break; + case CPTK_IS_SAME_AS: + pp_cxx_ws_string (pp, "__is_same_as"); + break; case CPTK_IS_STD_LAYOUT: pp_cxx_ws_string (pp, "__is_std_layout"); break; @@ -2416,7 +2477,7 @@ pp_cxx_trait_expression (cxx_pretty_printer *pp, tree t) pp_cxx_left_paren (pp); pp->type_id (TRAIT_EXPR_TYPE1 (t)); - if (kind == CPTK_IS_BASE_OF) + if (kind == CPTK_IS_BASE_OF || kind == CPTK_IS_SAME_AS) { pp_cxx_separate_with (pp, ','); pp->type_id (TRAIT_EXPR_TYPE2 (t)); @@ -2424,6 +2485,272 @@ pp_cxx_trait_expression (cxx_pretty_printer *pp, tree t) pp_cxx_right_paren (pp); } + +// requires-clause: +// 'requires' logical-or-expression +void +pp_cxx_requires_clause (cxx_pretty_printer *pp, tree t) +{ + if (!t) + return; + pp->padding = pp_before; + pp_cxx_ws_string (pp, "requires"); + pp_space (pp); + pp->expression (t); +} + +/* requirement: + simple-requirement + compound-requirement + type-requirement + nested-requirement */ +static void +pp_cxx_requirement (cxx_pretty_printer *pp, tree t) +{ + switch (TREE_CODE (t)) + { + case SIMPLE_REQ: + pp_cxx_simple_requirement (pp, t); + break; + + case TYPE_REQ: + pp_cxx_type_requirement (pp, t); + break; + + case COMPOUND_REQ: + pp_cxx_compound_requirement (pp, t); + break; + + case NESTED_REQ: + pp_cxx_nested_requirement (pp, t); + break; + + default: + gcc_unreachable (); + } +} + +// requirement-list: +// requirement +// requirement-list ';' requirement[opt] +// +static void +pp_cxx_requirement_list (cxx_pretty_printer *pp, tree t) +{ + for (; t; t = TREE_CHAIN (t)) + pp_cxx_requirement (pp, TREE_VALUE (t)); +} + +// requirement-body: +// '{' requirement-list '}' +static void +pp_cxx_requirement_body (cxx_pretty_printer *pp, tree t) +{ + pp_cxx_left_brace (pp); + pp_cxx_requirement_list (pp, t); + pp_cxx_right_brace (pp); +} + +// requires-expression: +// 'requires' requirement-parameter-list requirement-body +void +pp_cxx_requires_expr (cxx_pretty_printer *pp, tree t) +{ + pp_string (pp, "requires"); + if (tree parms = TREE_OPERAND (t, 0)) + { + pp_cxx_parameter_declaration_clause (pp, parms); + pp_cxx_whitespace (pp); + } + pp_cxx_requirement_body (pp, TREE_OPERAND (t, 1)); +} + +/* simple-requirement: + expression ';' */ +void +pp_cxx_simple_requirement (cxx_pretty_printer *pp, tree t) +{ + pp->expression (TREE_OPERAND (t, 0)); + pp_cxx_semicolon (pp); +} + +/* type-requirement: + typename type-name ';' */ +void +pp_cxx_type_requirement (cxx_pretty_printer *pp, tree t) +{ + pp->type_id (TREE_OPERAND (t, 0)); + pp_cxx_semicolon (pp); +} + +/* compound-requirement: + '{' expression '}' 'noexcept' [opt] trailing-return-type [opt] */ +void +pp_cxx_compound_requirement (cxx_pretty_printer *pp, tree t) +{ + pp_cxx_left_brace (pp); + pp->expression (TREE_OPERAND (t, 0)); + pp_cxx_right_brace (pp); + + if (COMPOUND_REQ_NOEXCEPT_P (t)) + pp_cxx_ws_string (pp, "noexcept"); + + if (tree type = TREE_OPERAND (t, 1)) + { + pp_cxx_ws_string (pp, "->"); + pp->type_id (type); + } +} + +/* nested requirement: + 'requires' constraint-expression */ +void +pp_cxx_nested_requirement (cxx_pretty_printer *pp, tree t) +{ + pp_cxx_ws_string (pp, "requires"); + pp->expression (TREE_OPERAND (t, 0)); + pp_cxx_semicolon (pp); +} + +void +pp_cxx_predicate_constraint (cxx_pretty_printer *pp, tree t) +{ + pp_string (pp, "predicate"); + pp_left_paren (pp); + pp->expression (TREE_OPERAND (t, 0)); + pp_right_paren (pp); +} + +void +pp_cxx_expression_constraint (cxx_pretty_printer *pp, tree t) +{ + pp_string (pp, "valid_expr"); + pp_left_paren (pp); + pp->expression (TREE_OPERAND (t, 0)); + pp_right_paren (pp); +} + +void +pp_cxx_type_constraint (cxx_pretty_printer *pp, tree t) +{ + pp_string (pp, "valid_type"); + pp_left_paren (pp); + pp->type_id (TREE_OPERAND (t, 0)); + pp_right_paren (pp); +} + +void +pp_cxx_implicit_conversion_constraint (cxx_pretty_printer *pp, tree t) +{ + pp_string (pp, "convertible"); + pp_left_paren (pp); + pp->expression (ICONV_CONSTR_EXPR (t)); + pp_cxx_separate_with (pp, ','); + pp->expression (ICONV_CONSTR_TYPE (t)); + pp_right_paren (pp); +} + +void +pp_cxx_argument_deduction_constraint (cxx_pretty_printer *pp, tree t) +{ + pp_string (pp, "deducible"); + pp_left_paren (pp); + pp->expression (DEDUCT_CONSTR_EXPR (t)); + pp_cxx_separate_with (pp, ','); + pp->expression (DEDUCT_CONSTR_PATTERN (t)); + pp_right_paren (pp); +} + +void +pp_cxx_exception_constraint (cxx_pretty_printer *pp, tree t) +{ + pp_cxx_ws_string (pp, "noexcept"); + pp_left_paren (pp); + pp->expression (TREE_OPERAND (t, 0)); + pp_right_paren (pp); +} + +void +pp_cxx_parameterized_constraint (cxx_pretty_printer *pp, tree t) +{ + pp_left_paren (pp); + pp_string (pp, "forall"); + if (tree parms = PARM_CONSTR_PARMS (t)) + { + if (parms) + pp_cxx_parameter_declaration_clause (pp, parms); + pp_cxx_whitespace (pp); + } + pp_cxx_constraint (pp, PARM_CONSTR_OPERAND (t)); + pp_right_paren (pp); +} + +void +pp_cxx_conjunction (cxx_pretty_printer *pp, tree t) +{ + pp_cxx_constraint (pp, TREE_OPERAND (t, 0)); + pp_string (pp, " and "); + pp_cxx_constraint (pp, TREE_OPERAND (t, 1)); +} + +void +pp_cxx_disjunction (cxx_pretty_printer *pp, tree t) +{ + pp_cxx_constraint (pp, TREE_OPERAND (t, 0)); + pp_string (pp, " or "); + pp_cxx_constraint (pp, TREE_OPERAND (t, 1)); +} + +void +pp_cxx_constraint (cxx_pretty_printer *pp, tree t) +{ + if (t == error_mark_node) + return pp->expression (t); + + switch (TREE_CODE (t)) + { + case PRED_CONSTR: + pp_cxx_predicate_constraint (pp, t); + break; + + case EXPR_CONSTR: + pp_cxx_expression_constraint (pp, t); + break; + + case TYPE_CONSTR: + pp_cxx_type_constraint (pp, t); + break; + + case ICONV_CONSTR: + pp_cxx_implicit_conversion_constraint (pp, t); + break; + + case DEDUCT_CONSTR: + pp_cxx_argument_deduction_constraint (pp, t); + break; + + case EXCEPT_CONSTR: + pp_cxx_exception_constraint (pp, t); + break; + + case PARM_CONSTR: + pp_cxx_parameterized_constraint (pp, t); + break; + + case CONJ_CONSTR: + pp_cxx_conjunction (pp, t); + break; + + case DISJ_CONSTR: + pp_cxx_disjunction (pp, t); + break; + + default: + gcc_unreachable (); + } +} + + typedef c_pretty_print_fn pp_fun; diff --git a/gcc/cp/cxx-pretty-print.h b/gcc/cp/cxx-pretty-print.h index 16f3238b707..e5161df0644 100644 --- a/gcc/cp/cxx-pretty-print.h +++ b/gcc/cp/cxx-pretty-print.h @@ -91,6 +91,21 @@ void pp_cxx_trait_expression (cxx_pretty_printer *, tree); void pp_cxx_va_arg_expression (cxx_pretty_printer *, tree); void pp_cxx_offsetof_expression (cxx_pretty_printer *, tree); void pp_cxx_userdef_literal (cxx_pretty_printer *, tree); - +void pp_cxx_requires_clause (cxx_pretty_printer *, tree); +void pp_cxx_requires_expr (cxx_pretty_printer *, tree); +void pp_cxx_simple_requirement (cxx_pretty_printer *, tree); +void pp_cxx_type_requirement (cxx_pretty_printer *, tree); +void pp_cxx_compound_requirement (cxx_pretty_printer *, tree); +void pp_cxx_nested_requirement (cxx_pretty_printer *, tree); +void pp_cxx_predicate_constraint (cxx_pretty_printer *, tree); +void pp_cxx_expression_constraint (cxx_pretty_printer *, tree); +void pp_cxx_type_constraint (cxx_pretty_printer *, tree); +void pp_cxx_implicit_conversion_constraint (cxx_pretty_printer *, tree); +void pp_cxx_argument_deduction_constraint (cxx_pretty_printer *, tree); +void pp_cxx_exception_constraint (cxx_pretty_printer *, tree); +void pp_cxx_parameterized_constraint (cxx_pretty_printer *, tree); +void pp_cxx_conjunction (cxx_pretty_printer *, tree); +void pp_cxx_disjunction (cxx_pretty_printer *, tree); +void pp_cxx_constraint (cxx_pretty_printer *, tree); #endif /* GCC_CXX_PRETTY_PRINT_H */ diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 208173a8dfa..e8ed472293b 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -72,7 +72,6 @@ enum bad_spec_place { BSP_FIELD /* field */ }; -static tree grokparms (tree parmlist, tree *); static const char *redeclaration_error_message (tree, tree); static int decl_jump_unsafe (tree); @@ -1079,8 +1078,10 @@ decls_match (tree newdecl, tree olddecl) } else if (TREE_CODE (newdecl) == TEMPLATE_DECL) { - if (TREE_CODE (DECL_TEMPLATE_RESULT (newdecl)) - != TREE_CODE (DECL_TEMPLATE_RESULT (olddecl))) + tree oldres = DECL_TEMPLATE_RESULT (olddecl); + tree newres = DECL_TEMPLATE_RESULT (newdecl); + + if (TREE_CODE (newres) != TREE_CODE (oldres)) return 0; if (!comp_template_parms (DECL_TEMPLATE_PARMS (newdecl), @@ -1088,11 +1089,12 @@ decls_match (tree newdecl, tree olddecl) return 0; if (TREE_CODE (DECL_TEMPLATE_RESULT (newdecl)) == TYPE_DECL) - types_match = same_type_p (TREE_TYPE (DECL_TEMPLATE_RESULT (olddecl)), - TREE_TYPE (DECL_TEMPLATE_RESULT (newdecl))); + types_match = (same_type_p (TREE_TYPE (oldres), TREE_TYPE (newres)) + && equivalently_constrained (olddecl, newdecl)); else - types_match = decls_match (DECL_TEMPLATE_RESULT (olddecl), - DECL_TEMPLATE_RESULT (newdecl)); + // We don't need to check equivalently_constrained for variable and + // function templates because we check it on the results. + types_match = decls_match (oldres, newres); } else { @@ -1120,6 +1122,11 @@ decls_match (tree newdecl, tree olddecl) COMPARE_REDECLARATION); } + // Normal functions can be constrained, as can variable partial + // specializations. + if (types_match && VAR_OR_FUNCTION_DECL_P (newdecl)) + types_match = equivalently_constrained (newdecl, olddecl); + return types_match; } @@ -1243,6 +1250,36 @@ validate_constexpr_redeclaration (tree old_decl, tree new_decl) return true; } +// If OLDDECL and NEWDECL are concept declarations with the same type +// (i.e., and template parameters), but different requirements, +// emit diagnostics and return true. Otherwise, return false. +static inline bool +check_concept_refinement (tree olddecl, tree newdecl) +{ + if (!DECL_DECLARED_CONCEPT_P (olddecl) || !DECL_DECLARED_CONCEPT_P (newdecl)) + return false; + + tree d1 = DECL_TEMPLATE_RESULT (olddecl); + tree d2 = DECL_TEMPLATE_RESULT (newdecl); + if (TREE_CODE (d1) != TREE_CODE (d2)) + return false; + + tree t1 = TREE_TYPE (d1); + tree t2 = TREE_TYPE (d2); + if (TREE_CODE (d1) == FUNCTION_DECL) + { + if (compparms (TYPE_ARG_TYPES (t1), TYPE_ARG_TYPES (t2)) + && comp_template_parms (DECL_TEMPLATE_PARMS (olddecl), + DECL_TEMPLATE_PARMS (newdecl)) + && !equivalently_constrained (olddecl, newdecl)) + { + error ("cannot specialize concept %q#D", olddecl); + return true; + } + } + return false; +} + /* DECL is a redeclaration of a function or function template. If it does have default arguments issue a diagnostic. Note: this function is used to enforce the requirements in C++11 8.3.6 about @@ -1571,12 +1608,17 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend) /* Template functions can be disambiguated by return type. */ && same_type_p (TREE_TYPE (TREE_TYPE (newdecl)), - TREE_TYPE (TREE_TYPE (olddecl)))) + TREE_TYPE (TREE_TYPE (olddecl))) + // Template functions can also be disambiguated by + // constraints. + && equivalently_constrained (olddecl, newdecl)) { error ("ambiguating new declaration %q+#D", newdecl); inform (DECL_SOURCE_LOCATION (olddecl), "old declaration %q#D", olddecl); } + else if (check_concept_refinement (olddecl, newdecl)) + return error_mark_node; return NULL_TREE; } if (TREE_CODE (newdecl) == FUNCTION_DECL) @@ -1593,8 +1635,11 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend) are not ambiguous. */ else if ((!DECL_FUNCTION_VERSIONED (newdecl) && !DECL_FUNCTION_VERSIONED (olddecl)) + // The functions have the same parameter types. && compparms (TYPE_ARG_TYPES (TREE_TYPE (newdecl)), - TYPE_ARG_TYPES (TREE_TYPE (olddecl)))) + TYPE_ARG_TYPES (TREE_TYPE (olddecl))) + // And the same constraints. + && equivalently_constrained (newdecl, olddecl)) { error ("ambiguating new declaration of %q+#D", newdecl); inform (DECL_SOURCE_LOCATION (olddecl), @@ -2576,6 +2621,12 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend) if (snode) snode->remove (); } + + /* Remove the associated constraints for newdecl, if any, before + reclaiming memory. */ + if (flag_concepts) + remove_constraints (newdecl); + ggc_free (newdecl); return olddecl; @@ -3981,6 +4032,10 @@ cxx_init_decl_processing (void) /* Ensure attribs.c is initialized. */ init_attributes (); + + /* Ensure constraint.cc is initialized. */ + init_constraint_processing (); + extvisattr = build_tree_list (get_identifier ("externally_visible"), NULL_TREE); newattrs = tree_cons (get_identifier ("alloc_size"), @@ -6368,6 +6423,16 @@ value_dependent_init_p (tree init) return false; } +// Returns true if a DECL is VAR_DECL with the concept specifier. +static inline bool +is_concept_var (tree decl) +{ + return (VAR_P (decl) + // Not all variables have DECL_LANG_SPECIFIC. + && DECL_LANG_SPECIFIC (decl) + && DECL_DECLARED_CONCEPT_P (decl)); +} + /* Finish processing of a declaration; install its line number and initial value. If the length of an array type is not known before, @@ -6446,7 +6511,9 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, tf_warning_or_error); d_init = resolve_nondeduced_context (d_init); type = TREE_TYPE (decl) = do_auto_deduction (type, d_init, - auto_node); + auto_node, + tf_warning_or_error, + adc_variable_type); if (type == error_mark_node) return; cp_apply_type_quals_to_decl (cp_type_quals (type), decl); @@ -6544,6 +6611,8 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, init = NULL_TREE; release_tree_vector (cleanups); } + else if (!init && is_concept_var (decl)) + error ("variable concept has no initializer"); else if (!DECL_PRETTY_FUNCTION_P (decl)) { /* Deduce array size even if the initializer is dependent. */ @@ -7606,6 +7675,23 @@ check_static_quals (tree decl, cp_cv_quals quals) decl); } +// Check that FN takes no arguments and returns bool. +static void +check_concept_fn (tree fn) +{ + // A constraint is nullary. + if (DECL_ARGUMENTS (fn)) + error ("concept %q#D declared with function parameters", fn); + + // The declared return type of the concept shall be bool, and + // it shall not be deduced from it definition. + tree type = TREE_TYPE (TREE_TYPE (fn)); + if (is_auto (type)) + error ("concept %q#D declared with a deduced return type", fn); + else if (type != boolean_type_node) + error ("concept %q#D with non-% 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 %"); + 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 %"); + } + 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 ("% cannot appear in a typedef declaration"); + return error_mark_node; + } + if (constexpr_p && typedef_p) { error ("% 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 %"); /* 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_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 %"); + return error_mark_node; + } if (constexpr_p) { error ("a destructor cannot be %"); @@ -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 %"); + 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 %", + 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 %", + unqualified_id); + else if (constexpr_p) { error ("non-static data member %qE declared %", unqualified_id); @@ -11010,10 +11212,12 @@ grokdeclarator (const cp_declarator *declarator, TYPE_HAS_LATE_RETURN_TYPE (type) = 1; decl = grokfndecl (ctype, type, original_name, parms, unqualified_id, - virtualp, flags, memfn_quals, rqual, raises, + reqs, virtualp, flags, memfn_quals, rqual, raises, 1, friendp, - publicp, inlinep | (2 * constexpr_p), - initialized == SD_DELETED, sfk, + publicp, + inlinep | (2 * constexpr_p) | (4 * concept_p), + initialized == SD_DELETED, + sfk, funcdef_flag, template_count, in_namespace, attrlist, declarator->id_loc); @@ -11054,7 +11258,7 @@ grokdeclarator (const cp_declarator *declarator, decl = grokvardecl (type, dname, unqualified_id, declspecs, initialized, - (type_quals & TYPE_QUAL_CONST) != 0, + ((type_quals & TYPE_QUAL_CONST) != 0) | (2 * concept_p), template_count, ctype ? ctype : in_namespace); if (decl == NULL_TREE) @@ -11304,7 +11508,7 @@ type_is_deprecated (tree type) *PARMS is set to the chain of PARM_DECLs created. */ -static tree +tree grokparms (tree parmlist, tree *parms) { tree result = NULL_TREE; @@ -12399,8 +12603,16 @@ xref_tag_1 (enum tag_types tag_code, tree name, { if (template_header_p && MAYBE_CLASS_TYPE_P (t)) { - if (!redeclare_class_template (t, current_template_parms)) - return error_mark_node; + /* Check that we aren't trying to overload a class with different + constraints. */ + tree constr = NULL_TREE; + if (current_template_parms) + { + tree reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms); + constr = build_constraints (reqs, NULL_TREE); + } + if (!redeclare_class_template (t, current_template_parms, constr)) + return error_mark_node; } else if (!processing_template_decl && CLASS_TYPE_P (t) @@ -14252,6 +14464,10 @@ finish_function (int flags) fntype = TREE_TYPE (fndecl); } + // If this is a concept, check that the definition is reasonable. + if (DECL_DECLARED_CONCEPT_P (fndecl)) + check_function_concept (fndecl); + /* Save constexpr function body before it gets munged by the NRV transformation. */ maybe_save_function_definition (fndecl); @@ -14742,6 +14958,7 @@ cp_tree_node_structure (union lang_tree_node * t) case TRAIT_EXPR: return TS_CP_TRAIT_EXPR; case LAMBDA_EXPR: return TS_CP_LAMBDA_EXPR; case TEMPLATE_INFO: return TS_CP_TEMPLATE_INFO; + case CONSTRAINT_INFO: return TS_CP_CONSTRAINT_INFO; case USERDEF_LITERAL: return TS_CP_USERDEF_LITERAL; default: return TS_CP_GENERIC; } diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index a45755ec8de..068d79cd10c 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -714,6 +714,10 @@ check_classfn (tree ctype, tree function, tree template_parms) != type_memfn_rqual (TREE_TYPE (fndecl))) continue; + // Include constraints in the match. + tree c1 = get_constraints (function); + tree c2 = get_constraints (fndecl); + /* While finding a match, same types and params are not enough if the function is versioned. Also check version ("target") attributes. */ @@ -724,6 +728,7 @@ check_classfn (tree ctype, tree function, tree template_parms) && (!is_template || comp_template_parms (template_parms, DECL_TEMPLATE_PARMS (fndecl))) + && equivalent_constraints (c1, c2) && (DECL_TEMPLATE_SPECIALIZATION (function) == DECL_TEMPLATE_SPECIALIZATION (fndecl)) && (!DECL_TEMPLATE_SPECIALIZATION (function) @@ -5081,6 +5086,7 @@ mark_used (tree decl, tsubst_flags_t complain) || (TREE_CODE (decl) == FUNCTION_DECL && DECL_OMP_DECLARE_REDUCTION_P (decl)) || undeduced_auto_decl (decl)) + && !DECL_DECLARED_CONCEPT_P (decl) && !uses_template_parms (DECL_TI_ARGS (decl))) { /* Instantiating a function will result in garbage collection. We @@ -5179,6 +5185,7 @@ mark_used (tree decl, tsubst_flags_t complain) } else if (VAR_OR_FUNCTION_DECL_P (decl) && DECL_TEMPLATE_INFO (decl) + && !DECL_DECLARED_CONCEPT_P (decl) && (!DECL_EXPLICIT_INSTANTIATION (decl) || always_instantiate_p (decl))) /* If this is a function or variable that is an instance of some diff --git a/gcc/cp/error.c b/gcc/cp/error.c index b811df2b9be..4f85751711a 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -1333,6 +1333,15 @@ dump_template_decl (cxx_pretty_printer *pp, tree t, int flags) } } + if (flag_concepts) + if (tree ci = get_constraints (t)) + if (check_constraint_info (ci)) + if (tree reqs = CI_TEMPLATE_REQS (ci)) + { + pp_cxx_requires_clause (pp, reqs); + pp_cxx_whitespace (pp); + } + if (DECL_CLASS_TEMPLATE_P (t)) dump_type (pp, TREE_TYPE (t), ((flags & ~TFF_CLASS_KEY_OR_ENUM) | TFF_TEMPLATE_NAME @@ -1564,6 +1573,11 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags) if (show_return) dump_type_suffix (pp, TREE_TYPE (fntype), flags); + if (flag_concepts) + if (tree ci = get_constraints (t)) + if (tree reqs = CI_DECLARATOR_REQS (ci)) + pp_cxx_requires_clause (pp, reqs); + dump_substitution (pp, t, template_parms, template_args, flags); } else if (template_args) @@ -2689,6 +2703,38 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags) pp_cxx_right_paren (pp); break; + case REQUIRES_EXPR: + pp_cxx_requires_expr (cxx_pp, t); + break; + + case SIMPLE_REQ: + pp_cxx_simple_requirement (cxx_pp, t); + break; + + case TYPE_REQ: + pp_cxx_type_requirement (cxx_pp, t); + break; + + case COMPOUND_REQ: + pp_cxx_compound_requirement (cxx_pp, t); + break; + + case NESTED_REQ: + pp_cxx_nested_requirement (cxx_pp, t); + break; + + case PRED_CONSTR: + case EXPR_CONSTR: + case TYPE_CONSTR: + case ICONV_CONSTR: + case DEDUCT_CONSTR: + case EXCEPT_CONSTR: + case PARM_CONSTR: + case CONJ_CONSTR: + case DISJ_CONSTR: + pp_cxx_constraint (cxx_pp, t); + break; + case PLACEHOLDER_EXPR: pp_string (pp, M_("*this")); break; diff --git a/gcc/cp/lex.c b/gcc/cp/lex.c index a142410a295..f81c17b4a51 100644 --- a/gcc/cp/lex.c +++ b/gcc/cp/lex.c @@ -174,6 +174,8 @@ init_reswords (void) if (cxx_dialect < cxx11) mask |= D_CXX11; + if (!flag_concepts) + mask |= D_CXX_CONCEPTS; if (flag_no_asm) mask |= D_ASM | D_EXT; if (flag_no_gnu_keywords) diff --git a/gcc/cp/logic.cc b/gcc/cp/logic.cc new file mode 100644 index 00000000000..7e016409c30 --- /dev/null +++ b/gcc/cp/logic.cc @@ -0,0 +1,497 @@ +/* Derivation and subsumption rules for constraints. + Copyright (C) 2013-2015 Free Software Foundation, Inc. + Contributed by Andrew Sutton (andrew.n.sutton@gmail.com) + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#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 + +namespace { + +// Helper algorithms + +// Increment iter distance(first, last) times. +template + 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 +{ + 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 (), current (end ()) +{ } + +inline +term_list::term_list (const term_list &x) + : std::list (x) + , current (next_by_distance (begin (), x.begin (), x.current)) +{ } + +inline term_list& +term_list::operator= (const term_list &x) +{ + std::list::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::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::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_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 (1) +{ } + + +/* Branch the current goal by creating a new subgoal, + returning a reference to // the new object. This does + not update the current goal. */ +inline proof_state::iterator +proof_state::branch (iterator i) +{ + gcc_assert (i != end()); + proof_goal& g = *i; + return insert (++i, g); +} + +/*--------------------------------------------------------------------------- + Logical rules +---------------------------------------------------------------------------*/ + +/*These functions modify the current state and goal by decomposing + logical expressions using the logical rules of sequent calculus for + first order logic. + + Note that in each decomposition rule, the term T has been erased + from term list before the specific rule is applied. */ + +/* The left logical rule for conjunction adds both operands + to the current set of constraints. */ +void +left_conjunction (proof_state &, goal_iterator i, tree t) +{ + gcc_assert (TREE_CODE (t) == CONJ_CONSTR); + + /* Insert the operands into the current branch. Note that the + final order of insertion is left-to-right. */ + term_list &l = i->assumptions; + l.insert (TREE_OPERAND (t, 1)); + l.insert (TREE_OPERAND (t, 0)); +} + +/* The left logical rule for disjunction creates a new goal, + adding the first operand to the original set of + constraints and the second operand to the new set + of constraints. */ +void +left_disjunction (proof_state &s, goal_iterator i, tree t) +{ + gcc_assert (TREE_CODE (t) == DISJ_CONSTR); + + /* Branch the current subgoal. */ + goal_iterator j = s.branch (i); + term_list &l1 = i->assumptions; + term_list &l2 = j->assumptions; + + /* Insert operands into the different branches. */ + l1.insert (TREE_OPERAND (t, 0)); + l2.insert (TREE_OPERAND (t, 1)); +} + +/* The left logical rules for parameterized constraints + adds its operand to the current goal. The list of + parameters are effectively discarded. */ +void +left_parameterized_constraint (proof_state &, goal_iterator i, tree t) +{ + gcc_assert (TREE_CODE (t) == PARM_CONSTR); + term_list &l = i->assumptions; + l.insert (PARM_CONSTR_OPERAND (t)); +} + +/*--------------------------------------------------------------------------- + Decomposition +---------------------------------------------------------------------------*/ + +/* The following algorithms decompose expressions into sets of + atomic propositions. In terms of the sequent calculus, these + functions exercise the logical rules only. + + This is equivalent, for the purpose of determining subsumption, + to rewriting a constraint in disjunctive normal form. It also + allows the resulting assumptions to be used as declarations + for the purpose of separate checking. */ + +/* Apply the left logical rules to the proof state. */ +void +decompose_left_term (proof_state &s, goal_iterator i) +{ + term_list &l = i->assumptions; + tree t = l.current_term (); + switch (TREE_CODE (t)) + { + case CONJ_CONSTR: + left_conjunction (s, i, l.erase ()); + break; + case DISJ_CONSTR: + left_disjunction (s, i, l.erase ()); + break; + case PARM_CONSTR: + left_parameterized_constraint (s, i, l.erase ()); + break; + default: + l.next (); + break; + } +} + +/* Apply the left logical rules of the sequent calculus + until the current goal is fully decomposed into atomic + constraints. */ +void +decompose_left_goal (proof_state &s, goal_iterator i) +{ + term_list& l = i->assumptions; + l.start (); + while (!l.done ()) + decompose_left_term (s, i); +} + +/* Apply the left logical rules of the sequent calculus + until the antecedents are fully decomposed into atomic + constraints. */ +void +decompose_left (proof_state& s) +{ + goal_iterator iter = s.begin (); + goal_iterator end = s.end (); + for ( ; iter != end; ++iter) + decompose_left_goal (s, iter); +} + +/* Returns a vector of terms from the term list L. */ +tree +extract_terms (term_list& l) +{ + tree result = make_tree_vec (l.size()); + term_list::iterator iter = l.begin(); + term_list::iterator end = l.end(); + for (int n = 0; iter != end; ++iter, ++n) + TREE_VEC_ELT (result, n) = *iter; + return result; +} + +/* Extract the assumptions from the proof state S + as a vector of vectors of atomic constraints. */ +inline tree +extract_assumptions (proof_state& s) +{ + tree result = make_tree_vec (s.size ()); + goal_iterator iter = s.begin (); + goal_iterator end = s.end (); + for (int n = 0; iter != end; ++iter, ++n) + TREE_VEC_ELT (result, n) = extract_terms (iter->assumptions); + return result; +} + +} // namespace + +/* Decompose the required expression T into a constraint set: a + vector of vectors containing only atomic propositions. If T is + invalid, return an error. */ +tree +decompose_assumptions (tree t) +{ + if (!t || t == error_mark_node) + return t; + + /* Create a proof state, and insert T as the sole assumption. */ + proof_state s; + term_list &l = s.begin ()->assumptions; + l.insert (t); + + /* Decompose the expression into a constraint set, and then + extract the terms for the AST. */ + decompose_left (s); + return extract_assumptions (s); +} + + +/*--------------------------------------------------------------------------- + Subsumption Rules +---------------------------------------------------------------------------*/ + +namespace { + +bool subsumes_constraint (tree, tree); +bool subsumes_conjunction (tree, tree); +bool subsumes_disjunction (tree, tree); +bool subsumes_parameterized_constraint (tree, tree); +bool subsumes_atomic_constraint (tree, tree); + +/* Returns true if the assumption A matches the conclusion C. This + is generally the case when A and C have the same syntax. + + NOTE: There will be specialized matching rules to accommodate + type equivalence, conversion, inheritance, etc. But this is not + in the current concepts draft. */ +inline bool +match_terms (tree a, tree c) +{ + return cp_tree_equal (a, c); +} + +/* Returns true if the list of assumptions AS subsumes the atomic + proposition C. This is the case when we can find a proposition + in AS that entails the conclusion C. */ +bool +subsumes_atomic_constraint (tree as, tree c) +{ + for (int i = 0; i < TREE_VEC_LENGTH (as); ++i) + if (match_terms (TREE_VEC_ELT (as, i), c)) + return true; + return false; +} + +/* Returns true when both operands of C are subsumed by the + assumptions AS. */ +inline bool +subsumes_conjunction (tree as, tree c) +{ + tree l = TREE_OPERAND (c, 0); + tree r = TREE_OPERAND (c, 1); + return subsumes_constraint (as, l) && subsumes_constraint (as, r); +} + +/* Returns true when either operand of C is subsumed by the + assumptions AS. */ +inline bool +subsumes_disjunction (tree as, tree c) +{ + tree l = TREE_OPERAND (c, 0); + tree r = TREE_OPERAND (c, 1); + return subsumes_constraint (as, l) || subsumes_constraint (as, r); +} + +/* Returns true when the operand of C is subsumed by the + assumptions in AS. The parameters are not considered in + the subsumption rules. */ +bool +subsumes_parameterized_constraint (tree as, tree c) +{ + tree t = PARM_CONSTR_OPERAND (c); + return subsumes_constraint (as, t); +} + + +/* Returns true when the list of assumptions AS subsumes the + concluded proposition C. This is a simple recursive descent + on C, matching against propositions in the assumption list AS. */ +bool +subsumes_constraint (tree as, tree c) +{ + switch (TREE_CODE (c)) + { + case CONJ_CONSTR: + return subsumes_conjunction (as, c); + case DISJ_CONSTR: + return subsumes_disjunction (as, c); + case PARM_CONSTR: + return subsumes_parameterized_constraint (as, c); + default: + return subsumes_atomic_constraint (as, c); + } +} + +/* Returns true if the LEFT constraints subsume the RIGHT constraints. + This is done by checking that the RIGHT requirements follow from + each of the LEFT subgoals. */ +bool +subsumes_constraints_nonnull (tree left, tree right) +{ + gcc_assert (check_constraint_info (left)); + gcc_assert (check_constraint_info (right)); + + /* Check that the required expression in RIGHT is subsumed by each + subgoal in the assumptions of LEFT. */ + tree as = CI_ASSUMPTIONS (left); + tree c = CI_NORMALIZED_CONSTRAINTS (right); + for (int i = 0; i < TREE_VEC_LENGTH (as); ++i) + if (!subsumes_constraint (TREE_VEC_ELT (as, i), c)) + return false; + return true; +} + +} /* namespace */ + +/* Returns true if the LEFT constraints subsume the RIGHT + constraints. */ +bool +subsumes (tree left, tree right) +{ + if (left == right) + return true; + if (!left) + return false; + if (!right) + return true; + return subsumes_constraints_nonnull (left, right); +} diff --git a/gcc/cp/method.c b/gcc/cp/method.c index 7d7ec7da187..fdc76043e93 100644 --- a/gcc/cp/method.c +++ b/gcc/cp/method.c @@ -1924,6 +1924,14 @@ implicitly_declare_fn (special_function_kind kind, tree type, rest_of_decl_compilation (fn, toplevel_bindings_p (), at_eof); gcc_assert (!TREE_USED (fn)); + /* Propagate constraints from the inherited constructor. */ + if (flag_concepts && inherited_ctor) + if (tree orig_ci = get_constraints (inherited_ctor)) + { + tree new_ci = copy_node (orig_ci); + set_constraints (fn, new_ci); + } + /* Restore PROCESSING_TEMPLATE_DECL. */ processing_template_decl = saved_processing_template_decl; diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index bfad6089670..a0b249bbf5b 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -1343,7 +1343,7 @@ clear_decl_specs (cp_decl_specifier_seq *decl_specs) VAR_DECLs or FUNCTION_DECLs) should do that directly. */ static cp_declarator *make_call_declarator - (cp_declarator *, tree, cp_cv_quals, cp_virt_specifiers, cp_ref_qualifier, tree, tree); + (cp_declarator *, tree, cp_cv_quals, cp_virt_specifiers, cp_ref_qualifier, tree, tree, tree); static cp_declarator *make_array_declarator (cp_declarator *, tree); static cp_declarator *make_pointer_declarator @@ -1522,7 +1522,8 @@ make_call_declarator (cp_declarator *target, cp_virt_specifiers virt_specifiers, cp_ref_qualifier ref_qualifier, tree exception_specification, - tree late_return_type) + tree late_return_type, + tree requires_clause) { cp_declarator *declarator; @@ -1534,6 +1535,7 @@ make_call_declarator (cp_declarator *target, declarator->u.function.ref_qualifier = ref_qualifier; declarator->u.function.exception_specification = exception_specification; declarator->u.function.late_return_type = late_return_type; + declarator->u.function.requires_clause = requires_clause; if (target) { declarator->id_loc = target->id_loc; @@ -2081,6 +2083,8 @@ static tree cp_parser_type_specifier int *, bool *); static tree cp_parser_simple_type_specifier (cp_parser *, cp_decl_specifier_seq *, cp_parser_flags); +static tree cp_parser_type_name + (cp_parser *, bool); static tree cp_parser_type_name (cp_parser *); static tree cp_parser_nonclass_name @@ -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 *); @@ -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 *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, ¶meter_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,10 +15554,26 @@ 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; @@ -15206,7 +15581,7 @@ cp_parser_type_name (cp_parser* parser) 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 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 %"); @@ -18539,7 +19067,8 @@ static tree cp_parser_template_type_arg (cp_parser *parser) return r; } -static tree cp_parser_trailing_type_id (cp_parser *parser) +static tree +cp_parser_trailing_type_id (cp_parser *parser) { return cp_parser_type_id_1 (parser, false, true); } @@ -19123,6 +19652,7 @@ cp_parser_parameter_declaration (cp_parser *parser, /* If the next token is `=', then process a default argument. */ if (cp_lexer_next_token_is (parser->lexer, CPP_EQ)) { + tree type = decl_specifiers.type; token = cp_lexer_peek_token (parser->lexer); /* If we are defining a class, then the tokens that make up the default argument must be saved and processed later. */ @@ -19130,6 +19660,17 @@ cp_parser_parameter_declaration (cp_parser *parser, && TYPE_BEING_DEFINED (current_class_type) && !LAMBDA_TYPE_P (current_class_type)) default_argument = cp_parser_cache_defarg (parser, /*nsdmi=*/false); + + // A constrained-type-specifier may declare a type template-parameter. + else if (declares_constrained_type_template_parameter (type)) + default_argument + = cp_parser_default_type_template_argument (parser); + + // A constrained-type-specifier may declare a template-template-parameter. + else if (declares_constrained_template_template_parameter (type)) + default_argument + = cp_parser_default_template_template_argument (parser); + /* Outside of a class definition, we can just parse the assignment-expression. */ else @@ -19872,6 +20413,10 @@ cp_parser_class_specifier_1 (cp_parser* parser) = parser->in_unbraced_linkage_specification_p; parser->in_unbraced_linkage_specification_p = false; + // Associate constraints with the type. + if (flag_concepts) + type = associate_classtype_constraints (type); + /* Start the class. */ if (nested_name_specifier_p) { @@ -20770,6 +21315,9 @@ cp_parser_member_declaration (cp_parser* parser) return; } + /* Check for a template introduction. */ + else if (cp_parser_template_declaration_after_export (parser, true)) + return; /* Check for a using-declaration. */ if (cp_lexer_next_token_is_keyword (parser->lexer, RID_USING)) @@ -22764,6 +23312,324 @@ cp_parser_label_declaration (cp_parser* parser) cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); } +// -------------------------------------------------------------------------- // +// Requires Clause + +// Parse a requires clause. +// +// requires-clause: +// 'requires' logical-or-expression +// +// The required logical-or-expression must be a constant expression. Note +// that we don't check that the expression is constepxr here. We defer until +// we analyze constraints and then, we only check atomic constraints. +static tree +cp_parser_requires_clause (cp_parser *parser) +{ + // Parse the requires clause so that it is not automatically folded. + ++processing_template_decl; + tree expr = cp_parser_binary_expression (parser, false, false, + PREC_NOT_OPERATOR, NULL); + --processing_template_decl; + return expr; +} + +// Optionally parse a requires clause: +static tree +cp_parser_requires_clause_opt (cp_parser *parser) +{ + if (!cp_lexer_next_token_is_keyword (parser->lexer, RID_REQUIRES)) + return NULL_TREE; + cp_lexer_consume_token (parser->lexer); + return cp_parser_requires_clause (parser); +} + + +/*--------------------------------------------------------------------------- + Requires expressions +---------------------------------------------------------------------------*/ + +/* Parse a requires expression + + requirement-expression: + 'requires' requirement-parameter-list [opt] requirement-body */ +static tree +cp_parser_requires_expression (cp_parser *parser) +{ + gcc_assert (cp_lexer_next_token_is_keyword (parser->lexer, RID_REQUIRES)); + location_t loc = cp_lexer_consume_token (parser->lexer)->location; + + /* A requires-expression shall appear only within a concept + definition or a requires-clause. + + TODO: Implement this diagnostic correctly. */ + if (!processing_template_decl) + { + error_at (loc, "a requires expression cannot appear outside a template"); + cp_parser_skip_to_end_of_statement (parser); + return error_mark_node; + } + + tree parms, reqs; + { + /* Local parameters are delared as variables within the scope + of the expression. They are not visible past the end of + the expression. Expressions within the requires-expression + are unevaluated. */ + struct scope_sentinel + { + scope_sentinel () + { + ++cp_unevaluated_operand; + begin_scope (sk_block, NULL_TREE); + } + + ~scope_sentinel () + { + pop_bindings_and_leave_scope (); + --cp_unevaluated_operand; + } + } s; + + /* Parse the optional parameter list. */ + if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) + { + parms = cp_parser_requirement_parameter_list (parser); + if (parms == error_mark_node) + return error_mark_node; + } + else + parms = NULL_TREE; + + /* Parse the requirement body. */ + reqs = cp_parser_requirement_body (parser); + if (reqs == error_mark_node) + return error_mark_node; + } + + /* This needs to happen after pop_bindings_and_leave_scope, as it reverses + the parm chain. */ + grokparms (parms, &parms); + return finish_requires_expr (parms, reqs); +} + +/* Parse a parameterized requirement. + + requirement-parameter-list: + '(' parameter-declaration-clause ')' */ +static tree +cp_parser_requirement_parameter_list (cp_parser *parser) +{ + if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN)) + return error_mark_node; + + tree parms = cp_parser_parameter_declaration_clause (parser); + + if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN)) + return error_mark_node; + + return parms; +} + +/* Parse the body of a requirement. + + requirement-body: + '{' requirement-list '}' */ +static tree +cp_parser_requirement_body (cp_parser *parser) +{ + if (!cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE)) + return error_mark_node; + + tree reqs = cp_parser_requirement_list (parser); + + if (!cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE)) + return error_mark_node; + + return reqs; +} + +/* Parse a list of requirements. + + requirement-list: + requirement + requirement-list ';' requirement[opt] */ +static tree +cp_parser_requirement_list (cp_parser *parser) +{ + tree result = NULL_TREE; + while (true) + { + tree req = cp_parser_requirement (parser); + if (req == error_mark_node) + return error_mark_node; + + result = tree_cons (NULL_TREE, req, result); + + /* If we see a semi-colon, consume it. */ + if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) + cp_lexer_consume_token (parser->lexer); + + /* Stop processing at the end of the list. */ + if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE)) + break; + } + + /* Reverse the order of requirements so they are analyzed in + declaration order. */ + return nreverse (result); +} + +/* Parse a syntactic requirement or type requirement. + + requirement: + simple-requirement + compound-requirement + type-requirement + nested-requirement */ +static tree +cp_parser_requirement (cp_parser *parser) +{ + if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) + return cp_parser_compound_requirement (parser); + else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TYPENAME)) + return cp_parser_type_requirement (parser); + else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_REQUIRES)) + return cp_parser_nested_requirement (parser); + else + return cp_parser_simple_requirement (parser); +} + +/* Parse a simple requirement. + + simple-requirement: + expression ';' */ +static tree +cp_parser_simple_requirement (cp_parser *parser) +{ + tree expr = cp_parser_expression (parser, NULL, false, false); + if (!expr || expr == error_mark_node) + return error_mark_node; + + if (!cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON)) + return error_mark_node; + + return finish_simple_requirement (expr); +} + +/* Parse a type requirement + + type-requirement + nested-name-specifier [opt] required-type-name ';' + + required-type-name: + type-name + 'template' [opt] simple-template-id */ +static tree +cp_parser_type_requirement (cp_parser *parser) +{ + cp_lexer_consume_token (parser->lexer); + + // Save the scope before parsing name specifiers. + tree saved_scope = parser->scope; + tree saved_object_scope = parser->object_scope; + tree saved_qualifying_scope = parser->qualifying_scope; + cp_parser_global_scope_opt (parser, /*current_scope_valid_p=*/true); + cp_parser_nested_name_specifier_opt (parser, + /*typename_keyword_p=*/true, + /*check_dependency_p=*/false, + /*type_p=*/true, + /*is_declaration=*/false); + + tree type; + if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TEMPLATE)) + { + cp_lexer_consume_token (parser->lexer); + type = cp_parser_template_id (parser, + /*template_keyword_p=*/true, + /*check_dependency=*/false, + /*tag_type=*/none_type, + /*is_declaration=*/false); + type = make_typename_type (parser->scope, type, typename_type, + /*complain=*/tf_error); + } + else + type = cp_parser_type_name (parser, /*typename_keyword_p=*/true); + + if (TREE_CODE (type) == TYPE_DECL) + type = TREE_TYPE (type); + + parser->scope = saved_scope; + parser->object_scope = saved_object_scope; + parser->qualifying_scope = saved_qualifying_scope; + + if (type == error_mark_node) + cp_parser_skip_to_end_of_statement (parser); + + if (!cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON)) + return error_mark_node; + if (type == error_mark_node) + return error_mark_node; + + return finish_type_requirement (type); +} + +/* Parse a compound requirement + + compound-requirement: + '{' expression '}' 'noexcept' [opt] trailing-return-type [opt] ';' */ +static tree +cp_parser_compound_requirement (cp_parser *parser) +{ + /* Parse an expression enclosed in '{ }'s. */ + if (!cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE)) + return error_mark_node; + + tree expr = cp_parser_expression (parser, NULL, false, false); + if (!expr || expr == error_mark_node) + return error_mark_node; + + if (!cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE)) + return error_mark_node; + + /* Parse the optional noexcept. */ + bool noexcept_p = false; + if (cp_lexer_next_token_is_keyword (parser->lexer, RID_NOEXCEPT)) + { + cp_lexer_consume_token (parser->lexer); + noexcept_p = true; + } + + /* Parse the optional trailing return type. */ + tree type = NULL_TREE; + if (cp_lexer_next_token_is (parser->lexer, CPP_DEREF)) + { + cp_lexer_consume_token (parser->lexer); + bool saved_result_type_constraint_p = parser->in_result_type_constraint_p; + parser->in_result_type_constraint_p = true; + type = cp_parser_trailing_type_id (parser); + parser->in_result_type_constraint_p = saved_result_type_constraint_p; + if (type == error_mark_node) + return error_mark_node; + } + + return finish_compound_requirement (expr, type, noexcept_p); +} + +/* Parse a nested requirement. This is the same as a requires clause. + + nested-requirement: + requires-clause */ +static tree +cp_parser_nested_requirement (cp_parser *parser) +{ + cp_lexer_consume_token (parser->lexer); + tree req = cp_parser_requires_clause (parser); + if (req == error_mark_node) + return error_mark_node; + return finish_nested_requirement (req); +} + /* Support Functions */ /* Looks up NAME in the current scope, as given by PARSER->SCOPE. @@ -23586,87 +24452,29 @@ cp_parser_function_definition_after_declarator (cp_parser* parser, return fn; } -/* Parse a template-declaration, assuming that the `export' (and - `extern') keywords, if present, has already been scanned. MEMBER_P - is as for cp_parser_template_declaration. */ +/* Parse a template-declaration body (following argument list). */ static void -cp_parser_template_declaration_after_export (cp_parser* parser, bool member_p) +cp_parser_template_declaration_after_parameters (cp_parser* parser, + tree parameter_list, + bool member_p) { tree decl = NULL_TREE; - vec *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 *checks = get_deferred_access_checks (); - /* Look for the `>'. */ - cp_parser_skip_to_end_of_template_parameter_list (parser); - /* We just processed one more parameter list. */ - ++parser->num_template_parameter_lists; - /* If the next token is `template', there are more template - parameters. */ - if (cp_lexer_next_token_is_keyword (parser->lexer, - RID_TEMPLATE)) - cp_parser_template_declaration_after_export (parser, member_p); + /* Tentatively parse for a new template parameter list, which can either be + the template keyword or a template introduction. */ + if (cp_parser_template_declaration_after_export (parser, member_p)) + /* OK */; else if (cxx_dialect >= cxx11 && cp_lexer_next_token_is_keyword (parser->lexer, RID_USING)) decl = cp_parser_alias_declaration (parser); @@ -23675,7 +24483,7 @@ cp_parser_template_declaration_after_export (cp_parser* parser, bool member_p) /* There are no access checks when parsing a template, as we do not know if a specialization will be a friend. */ push_deferring_access_checks (dk_no_check); - token = cp_lexer_peek_token (parser->lexer); + cp_token *token = cp_lexer_peek_token (parser->lexer); decl = cp_parser_single_declaration (parser, checks, member_p, @@ -23750,13 +24558,10 @@ cp_parser_template_declaration_after_export (cp_parser* parser, bool member_p) decl); } } + /* Register member declarations. */ if (member_p && !friend_p && decl && !DECL_CLASS_TEMPLATE_P (decl)) finish_member_declaration (decl); - /* For the erroneous case of a template with C linkage, we pushed an - implicit C++ linkage scope; exit that scope now. */ - if (need_lang_pop) - pop_lang_context (); /* If DECL is a function template, we must return to parse it later. (Even though there is no definition, there might be default arguments that need handling.) */ @@ -23765,6 +24570,182 @@ cp_parser_template_declaration_after_export (cp_parser* parser, bool member_p) vec_safe_push (unparsed_funs_with_definitions, decl); } +/* Parse a template introduction header for a template-declaration. Returns + false if tentative parse fails. */ + +static bool +cp_parser_template_introduction (cp_parser* parser, bool member_p) +{ + cp_parser_parse_tentatively (parser); + + tree saved_scope = parser->scope; + tree saved_object_scope = parser->object_scope; + tree saved_qualifying_scope = parser->qualifying_scope; + + /* Look for the optional `::' operator. */ + cp_parser_global_scope_opt (parser, + /*current_scope_valid_p=*/false); + /* Look for the nested-name-specifier. */ + cp_parser_nested_name_specifier_opt (parser, + /*typename_keyword_p=*/false, + /*check_dependency_p=*/true, + /*type_p=*/false, + /*is_declaration=*/false); + + cp_token *token = cp_lexer_peek_token (parser->lexer); + tree concept_name = cp_parser_identifier (parser); + + /* Look up the concept for which we will be matching + template parameters. */ + tree tmpl_decl = cp_parser_lookup_name_simple (parser, concept_name, + token->location); + parser->scope = saved_scope; + parser->object_scope = saved_object_scope; + parser->qualifying_scope = saved_qualifying_scope; + + if (concept_name == error_mark_node) + cp_parser_simulate_error (parser); + + /* Look for opening brace for introduction. */ + cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE); + + if (!cp_parser_parse_definitely (parser)) + return false; + + push_deferring_access_checks (dk_deferred); + + /* Build vector of placeholder parameters and grab + matching identifiers. */ + tree introduction_list = cp_parser_introduction_list (parser); + + /* The introduction-list shall not be empty. */ + int nargs = TREE_VEC_LENGTH (introduction_list); + if (nargs == 0) + { + error ("empty introduction-list"); + return true; + } + + /* Look for closing brace for introduction. */ + if (!cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE)) + return true; + + if (tmpl_decl == error_mark_node) + { + cp_parser_name_lookup_error (parser, concept_name, tmpl_decl, NLE_NULL, + token->location); + return true; + } + + /* Build and associate the constraint. */ + tree parms = finish_template_introduction (tmpl_decl, introduction_list); + if (parms && parms != error_mark_node) + { + cp_parser_template_declaration_after_parameters (parser, parms, + member_p); + return true; + } + + error_at (token->location, "no matching concept for template-introduction"); + return true; +} + +/* Parse a normal template-declaration following the template keyword. */ + +static void +cp_parser_explicit_template_declaration (cp_parser* parser, bool member_p) +{ + tree parameter_list; + bool need_lang_pop; + location_t location = input_location; + + /* Look for the `<' token. */ + if (!cp_parser_require (parser, CPP_LESS, RT_LESS)) + return; + if (at_class_scope_p () && current_function_decl) + { + /* 14.5.2.2 [temp.mem] + + A local class shall not have member templates. */ + error_at (location, + "invalid declaration of member template in local class"); + cp_parser_skip_to_end_of_block_or_statement (parser); + return; + } + /* [temp] + + A template ... shall not have C linkage. */ + if (current_lang_name == lang_name_c) + { + error_at (location, "template with C linkage"); + /* Give it C++ linkage to avoid confusing other parts of the + front end. */ + push_lang_context (lang_name_cplusplus); + need_lang_pop = true; + } + else + need_lang_pop = false; + + /* We cannot perform access checks on the template parameter + declarations until we know what is being declared, just as we + cannot check the decl-specifier list. */ + push_deferring_access_checks (dk_deferred); + + /* If the next token is `>', then we have an invalid + specialization. Rather than complain about an invalid template + parameter, issue an error message here. */ + if (cp_lexer_next_token_is (parser->lexer, CPP_GREATER)) + { + cp_parser_error (parser, "invalid explicit specialization"); + begin_specialization (); + parameter_list = NULL_TREE; + } + else + { + /* Parse the template parameters. */ + parameter_list = cp_parser_template_parameter_list (parser); + } + + /* Look for the `>'. */ + cp_parser_skip_to_end_of_template_parameter_list (parser); + + /* Manage template requirements */ + if (flag_concepts) + { + tree reqs = get_shorthand_constraints (current_template_parms); + if (tree r = cp_parser_requires_clause_opt (parser)) + reqs = conjoin_constraints (reqs, make_predicate_constraint (r)); + TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs; + } + + cp_parser_template_declaration_after_parameters (parser, parameter_list, + member_p); + + /* For the erroneous case of a template with C linkage, we pushed an + implicit C++ linkage scope; exit that scope now. */ + if (need_lang_pop) + pop_lang_context (); +} + +/* Parse a template-declaration, assuming that the `export' (and + `extern') keywords, if present, has already been scanned. MEMBER_P + is as for cp_parser_template_declaration. */ + +static bool +cp_parser_template_declaration_after_export (cp_parser* parser, bool member_p) +{ + if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TEMPLATE)) + { + cp_lexer_consume_token (parser->lexer); + cp_parser_explicit_template_declaration (parser, member_p); + return true; + } + else if (flag_concepts) + return cp_parser_template_introduction (parser, member_p); + + return false; +} + /* Perform the deferred access checks from a template-parameter-list. CHECKS is a TREE_LIST of access checks, as returned by get_deferred_access_checks. */ @@ -23832,6 +24813,12 @@ cp_parser_single_declaration (cp_parser* parser, { if (cp_parser_declares_only_class_p (parser)) { + // If this is a declaration, but not a definition, associate + // any constraints with the type declaration. Constraints + // are associated with definitions in cp_parser_class_specifier. + if (declares_class_or_enum == 1) + associate_classtype_constraints (decl_specifiers.type); + decl = shadow_tag (&decl_specifiers); /* In this case: @@ -33614,15 +34601,57 @@ tree_type_is_auto_or_concept (const_tree t) return TREE_TYPE (t) && is_auto_or_concept (TREE_TYPE (t)); } +/* Returns the template declaration being called or evaluated as + part of the constraint check. Note that T must be a predicate + constraint (it can't be any other kind of constraint). */ +static tree +get_concept_from_constraint (tree t) +{ + gcc_assert (TREE_CODE (t) == PRED_CONSTR); + t = PRED_CONSTR_EXPR (t); + gcc_assert (TREE_CODE (t) == CALL_EXPR + || TREE_CODE (t) == TEMPLATE_ID_EXPR + || VAR_P (t)); + + if (TREE_CODE (t) == TEMPLATE_ID_EXPR) + return DECL_TEMPLATE_RESULT (TREE_OPERAND (t, 0)); + if (VAR_P (t)) + return DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (t)); + else + { + tree fn = CALL_EXPR_FN (t); + tree ovl = TREE_OPERAND (fn, 0); + tree tmpl = OVL_FUNCTION (ovl); + return DECL_TEMPLATE_RESULT (tmpl); + } +} + /* Add an implicit template type parameter to the CURRENT_TEMPLATE_PARMS (creating a new template parameter list if necessary). Returns the newly created template type parm. */ tree -synthesize_implicit_template_parm (cp_parser *parser) +synthesize_implicit_template_parm (cp_parser *parser, tree constr) { gcc_assert (current_binding_level->kind == sk_function_parms); + /* Before committing to modifying any scope, if we're in an + implicit template scope, and we're trying to synthesize a + constrained parameter, try to find a previous parameter with + the same name. This is the same-type rule for abbreviated + function templates. */ + if (parser->implicit_template_scope && constr) + { + tree t = parser->implicit_template_parms; + while (t) + { + tree c = get_concept_from_constraint (TREE_TYPE (t)); + if (c == CONSTRAINED_PARM_CONCEPT (constr)) + return TREE_VALUE (t); + t = TREE_CHAIN (t); + } + } + /* We are either continuing a function template that already contains implicit template parameters, creating a new fully-implicit function template, or extending an existing explicit function template with implicit template @@ -33731,14 +34760,18 @@ synthesize_implicit_template_parm (cp_parser *parser) tree synth_id = make_generic_type_name (); tree synth_tmpl_parm = finish_template_type_parm (class_type_node, synth_id); + + // Attach the constraint to the parm before processing. + tree node = build_tree_list (NULL_TREE, synth_tmpl_parm); + TREE_TYPE (node) = constr; tree new_parm = process_template_parm (parser->implicit_template_parms, input_location, - build_tree_list (NULL_TREE, synth_tmpl_parm), + node, /*non_type=*/false, /*param_pack=*/false); - + // Chain the new parameter to the list of implicit parameters. if (parser->implicit_template_parms) parser->implicit_template_parms = TREE_CHAIN (parser->implicit_template_parms); @@ -33768,6 +34801,15 @@ synthesize_implicit_template_parm (cp_parser *parser) TREE_VEC_ELT (new_parms, new_parm_idx) = parser->implicit_template_parms; } + // If the new parameter was constrained, we need to add that to the + // constraints in the template parameter list. + if (tree req = TEMPLATE_PARM_CONSTRAINTS (tree_last (new_parm))) + { + tree reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms); + reqs = conjoin_constraints (reqs, req); + TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs; + } + current_binding_level = entry_scope; return new_type; diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h index 4381bd16261..c7091c8e8e0 100644 --- a/gcc/cp/parser.h +++ b/gcc/cp/parser.h @@ -397,6 +397,15 @@ typedef struct GTY(()) cp_parser { member definition using a generic type, it is the sk_class scope. */ cp_binding_level* implicit_template_scope; + /* True if parsing a result type in a compound requirement. This permits + constrained-type-specifiers inside what would normally be a trailing + return type. */ + bool in_result_type_constraint_p; + + /* True if a constrained-type-specifier is not allowed in this + context e.g., because they could never be deduced. */ + int prevent_constrained_type_specifiers; + } cp_parser; /* In parser.c */ diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index c3bafd3d37c..1b641746531 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -78,6 +78,23 @@ static GTY(()) tree saved_access_scope; to the EXPR_STMT that is its result. */ static tree cur_stmt_expr; +// -------------------------------------------------------------------------- // +// Local Specialization Stack +// +// Implementation of the RAII helper for creating new local +// specializations. +local_specialization_stack::local_specialization_stack () + : saved (local_specializations) +{ + local_specializations = new hash_map; +} + +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 + // struct S; + // + // template + // struct S; + // + // Here, S 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 + // requires Integral + // struct S; + // + // Here, S refers to the specialization S 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 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' and `C' 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 %"); + 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 struct S {}; */ bool -redeclare_class_template (tree type, tree parms) +redeclare_class_template (tree type, tree parms, tree cons) { tree tmpl; tree tmpl_parms; @@ -5320,6 +5573,15 @@ redeclare_class_template (tree type, tree parms) } } + // Cannot redeclare a class template with a different set of constraints. + if (!equivalent_constraints (get_constraints (tmpl), cons)) + { + error_at (input_location, "redeclaration %q#D with different " + "constraints", tmpl); + inform (DECL_SOURCE_LOCATION (tmpl), + "original declaration appeared here"); + } + return true; } @@ -6622,6 +6884,51 @@ canonicalize_type_argument (tree arg, tsubst_flags_t complain) return canon; } +// A template declaration can be substituted for a constrained +// template template parameter only when the argument is more +// constrained than the parameter. +static bool +is_compatible_template_arg (tree parm, tree arg) +{ + tree parm_cons = get_constraints (parm); + + /* For now, allow constrained template template arguments + and unconstrained template template parameters. */ + if (parm_cons == NULL_TREE) + return true; + + tree arg_cons = get_constraints (arg); + + // If the template parameter is constrained, we need to rewrite its + // constraints in terms of the ARG's template parameters. This ensures + // that all of the template parameter types will have the same depth. + // + // Note that this is only valid when coerce_template_template_parm is + // true for the innermost template parameters of PARM and ARG. In other + // words, because coercion is successful, this conversion will be valid. + if (parm_cons) + { + tree args = template_parms_to_args (DECL_TEMPLATE_PARMS (arg)); + parm_cons = tsubst_constraint_info (parm_cons, + INNERMOST_TEMPLATE_ARGS (args), + tf_none, NULL_TREE); + if (parm_cons == error_mark_node) + return false; + } + + return subsumes (parm_cons, arg_cons); +} + +// Convert a placeholder argument into a binding to the original +// parameter. The original parameter is saved as the TREE_TYPE of +// ARG. +static inline tree +convert_wildcard_argument (tree parm, tree arg) +{ + TREE_TYPE (arg) = parm; + return arg; +} + /* Convert the indicated template ARG as necessary to match the indicated template PARM. Returns the converted ARG, or error_mark_node if the conversion was unsuccessful. Error and @@ -6644,6 +6951,10 @@ convert_template_argument (tree parm, if (parm == error_mark_node) return error_mark_node; + /* Trivially convert placeholders. */ + if (TREE_CODE (arg) == WILDCARD_DECL) + return convert_wildcard_argument (parm, arg); + if (TREE_CODE (arg) == TREE_LIST && TREE_CODE (TREE_VALUE (arg)) == OFFSET_REF) { @@ -6811,6 +7122,22 @@ convert_template_argument (tree parm, val = error_mark_node; } + + // Check that the constraints are compatible before allowing the + // substitution. + if (val != error_mark_node) + if (!is_compatible_template_arg (parm, arg)) + { + if (in_decl && (complain & tf_error)) + { + error ("constraint mismatch at argument %d in " + "template parameter list for %qD", + i + 1, in_decl); + inform (input_location, " expected %qD but got %qD", + parm, arg); + } + val = error_mark_node; + } } } else @@ -6948,6 +7275,16 @@ coerce_template_parameter_pack (tree parms, packed_args = make_tree_vec (TREE_VEC_LENGTH (packed_parms)); } + /* Check if we have a placeholder pack, which indicates we're + in the context of a introduction list. In that case we want + to match this pack to the single placeholder. */ + else if (arg_idx < nargs + && TREE_CODE (TREE_VEC_ELT (inner_args, arg_idx)) == WILDCARD_DECL + && WILDCARD_PACK_P (TREE_VEC_ELT (inner_args, arg_idx))) + { + nargs = arg_idx + 1; + packed_args = make_tree_vec (1); + } else packed_args = make_tree_vec (nargs - arg_idx); @@ -7133,7 +7470,9 @@ coerce_template_parms (tree parms, } /* We can't pass a pack expansion to a non-pack parameter of an alias template (DR 1430). */ - else if (in_decl && DECL_ALIAS_TEMPLATE_P (in_decl) + else if (in_decl + && (DECL_ALIAS_TEMPLATE_P (in_decl) + || concept_template_p (in_decl)) && variadic_args_p && nargs - variadic_args_p < nparms - variadic_p) { @@ -7147,8 +7486,14 @@ coerce_template_parms (tree parms, if (PACK_EXPANSION_P (arg) && !template_parameter_pack_p (parm)) { - error ("pack expansion argument for non-pack parameter " - "%qD of alias template %qD", parm, in_decl); + if (DECL_ALIAS_TEMPLATE_P (in_decl)) + error_at (location_of (arg), + "pack expansion argument for non-pack parameter " + "%qD of alias template %qD", parm, in_decl); + else + error_at (location_of (arg), + "pack expansion argument for non-pack parameter " + "%qD of concept %qD", parm, in_decl); inform (DECL_SOURCE_LOCATION (parm), "declared here"); goto found; } @@ -7318,6 +7663,30 @@ coerce_template_parms (tree parms, return new_inner_args; } +/* Convert all template arguments to their appropriate types, and + return a vector containing the innermost resulting template + arguments. If any error occurs, return error_mark_node. Error and + warning messages are not issued. + + Note that no function argument deduction is performed, and default + arguments are used to fill in unspecified arguments. */ +tree +coerce_template_parms (tree parms, tree args, tree in_decl) +{ + return coerce_template_parms (parms, args, in_decl, tf_none, true, true); +} + +/* Convert all template arguments to their appropriate type, and + instantiate default arguments as needed. This returns a vector + containing the innermost resulting template arguments, or + error_mark_node if unsuccessful. */ +tree +coerce_template_parms (tree parms, tree args, tree in_decl, + tsubst_flags_t complain) +{ + return coerce_template_parms (parms, args, in_decl, complain, true, true); +} + /* Like coerce_template_parms. If PARMS represents all template parameters levels, this function returns a vector of vectors representing all the resulting argument levels. Note that in this @@ -7887,6 +8256,22 @@ lookup_template_class_1 (tree d1, tree arglist, tree in_decl, tree context, if (entry) return entry->spec; + /* If the the template's constraints are not satisfied, + then we cannot form a valid type. + + Note that the check is deferred until after the hash + lookup. This prevents redundant checks on previously + instantiated specializations. */ + if (flag_concepts && !constraints_satisfied_p (gen_tmpl, arglist)) + { + if (complain & tf_error) + { + error ("template constraint failure"); + diagnose_constraints (input_location, gen_tmpl, arglist); + } + return error_mark_node; + } + is_dependent_type = uses_template_parms (arglist); /* If the deduced arguments are invalid, then the binding @@ -8130,6 +8515,7 @@ lookup_template_class_1 (tree d1, tree arglist, tree in_decl, tree context, : CLASSTYPE_TI_TEMPLATE (found); } + // Build template info for the new specialization. SET_TYPE_TEMPLATE_INFO (t, build_template_info (found, arglist)); elt.spec = t; @@ -8184,14 +8570,17 @@ lookup_template_class (tree d1, tree arglist, tree in_decl, tree context, return ret; } -/* Return a TEMPLATE_ID_EXPR for the given variable template and ARGLIST. - The type of the expression is the unknown_type_node since the - template-id could refer to an explicit or partial specialization. */ +/* Return a TEMPLATE_ID_EXPR for the given variable template and ARGLIST. */ tree lookup_template_variable (tree templ, tree arglist) { + /* The type of the expression is NULL_TREE since the template-id could refer + to an explicit or partial specialization. */ tree type = NULL_TREE; + if (flag_concepts && variable_concept_p (templ)) + /* Except that concepts are always bool. */ + type = boolean_type_node; return build2 (TEMPLATE_ID_EXPR, type, templ, arglist); } @@ -8201,8 +8590,14 @@ tree finish_template_variable (tree var, tsubst_flags_t complain) { tree templ = TREE_OPERAND (var, 0); - tree arglist = TREE_OPERAND (var, 1); + + /* We never want to return a VAR_DECL for a variable concept, since they + aren't instantiated. In a template, leave the TEMPLATE_ID_EXPR alone. */ + bool concept_p = flag_concepts && variable_concept_p (templ); + if (concept_p && processing_template_decl) + return var; + tree tmpl_args = DECL_TI_ARGS (DECL_TEMPLATE_RESULT (templ)); arglist = add_outermost_template_args (tmpl_args, arglist); @@ -8211,6 +8606,29 @@ finish_template_variable (tree var, tsubst_flags_t complain) /*req_all*/true, /*use_default*/true); + if (flag_concepts && !constraints_satisfied_p (templ, arglist)) + { + if (complain & tf_error) + { + error ("constraints for %qD not satisfied", templ); + diagnose_constraints (location_of (var), templ, arglist); + } + return error_mark_node; + } + + /* If a template-id refers to a specialization of a variable + concept, then the expression is true if and only if the + concept's constraints are satisfied by the given template + arguments. + + NOTE: This is an extension of Concepts Lite TS that + allows constraints to be used in expressions. */ + if (concept_p) + { + tree decl = DECL_TEMPLATE_RESULT (templ); + return evaluate_variable_concept (decl, arglist); + } + return instantiate_template (templ, arglist, complain); } @@ -9013,7 +9431,8 @@ tsubst_friend_class (tree friend_tmpl, tree args) saved_input_location = input_location; input_location = DECL_SOURCE_LOCATION (friend_tmpl); - redeclare_class_template (TREE_TYPE (tmpl), parms); + tree cons = get_constraints (tmpl); + redeclare_class_template (TREE_TYPE (tmpl), parms, cons); input_location = saved_input_location; } @@ -9831,7 +10250,7 @@ tsubst_template_arg (tree t, tree args, tsubst_flags_t complain, tree in_decl) instantiated from it at *SPEC_P, return a NONTYPE_ARGUMENT_PACK of them and set *SPEC_P to point at the next point in the list. */ -static tree +tree extract_fnparm_pack (tree tmpl_parm, tree *spec_p) { /* Collect all of the extra "packed" parameters into an @@ -10044,6 +10463,16 @@ gen_elem_of_pack_expansion_instantiation (tree pattern, /* Expanding a fixed parameter pack from coerce_template_parameter_pack. */ t = tsubst_decl (pattern, args, complain); + else if (pattern == error_mark_node) + t = error_mark_node; + else if (constraint_p (pattern)) + { + if (processing_template_decl) + t = tsubst_constraint (pattern, args, complain, in_decl); + else + t = (constraints_satisfied_p (pattern, args) + ? boolean_true_node : boolean_false_node); + } else if (!TYPE_P (pattern)) t = tsubst_expr (pattern, args, complain, in_decl, /*integral_constant_expression_p=*/false); @@ -10108,7 +10537,11 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, } if (TREE_CODE (parm_pack) == PARM_DECL) { - if (PACK_EXPANSION_LOCAL_P (t)) + /* We know we have correct local_specializations if this + expansion is at function scope, or if we're dealing with a + local parameter in a requires expression; for the latter, + tsubst_requires_expr set it up appropriately. */ + if (PACK_EXPANSION_LOCAL_P (t) || CONSTRAINT_VAR_P (parm_pack)) arg_pack = retrieve_local_specialization (parm_pack); else { @@ -10788,6 +11221,7 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain) gcc_assert (DECL_LANG_SPECIFIC (r) != 0); DECL_CHAIN (r) = NULL_TREE; + // Build new template info linking to the original template decl. DECL_TEMPLATE_INFO (r) = build_template_info (t, args); if (TREE_CODE (decl) == TYPE_DECL @@ -11041,6 +11475,15 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain) && !grok_op_properties (r, /*complain=*/false)) RETURN (error_mark_node); + /* When instantiating a constrained member, substitute + into the constraints to create a new constraint. */ + if (tree ci = get_constraints (t)) + if (member) + { + ci = tsubst_constraint_info (ci, argvec, complain, NULL_TREE); + set_constraints (r, ci); + } + /* Set up the DECL_TEMPLATE_INFO for R. There's no need to do this in the special friend case mentioned above where GEN_TMPL is NULL. */ @@ -12098,6 +12541,12 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) int levels; tree arg = NULL_TREE; + /* Early in template argument deduction substitution, we don't + want to reduce the level of 'auto', or it will be confused + with a normal template parm in subsequent deduction. */ + if (is_auto (t) && (complain & tf_partial)) + return t; + r = NULL_TREE; gcc_assert (TREE_VEC_LENGTH (args) > 0); @@ -12238,12 +12687,6 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) about the template parameter in question. */ return t; - /* Early in template argument deduction substitution, we don't - want to reduce the level of 'auto', or it will be confused - with a normal template parm in subsequent deduction. */ - if (is_auto (t) && (complain & tf_partial)) - return t; - /* If we get here, we must have been looking at a parm for a more deeply nested template. Make a new version of this template parameter, but with a lower level. */ @@ -12260,6 +12703,14 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) complain | (code == TEMPLATE_TYPE_PARM ? tf_ignore_bad_quals : 0)); } + else if (TREE_CODE (t) == TEMPLATE_TYPE_PARM + && PLACEHOLDER_TYPE_CONSTRAINTS (t) + && (r = (TEMPLATE_PARM_DESCENDANTS + (TEMPLATE_TYPE_PARM_INDEX (t)))) + && (r = TREE_TYPE (r)) + && !PLACEHOLDER_TYPE_CONSTRAINTS (r)) + /* Break infinite recursion when substituting the constraints + of a constrained placeholder. */; else { r = copy_type (t); @@ -12285,6 +12736,12 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) else TYPE_CANONICAL (r) = canonical_type_parameter (r); + /* Propagate constraints on placeholders. */ + if (TREE_CODE (t) == TEMPLATE_TYPE_PARM) + if (tree constr = PLACEHOLDER_TYPE_CONSTRAINTS (t)) + PLACEHOLDER_TYPE_CONSTRAINTS (r) + = tsubst_constraint (constr, args, complain, in_decl); + if (code == BOUND_TEMPLATE_TEMPLATE_PARM) { tree argvec = tsubst (TYPE_TI_ARGS (t), args, @@ -13934,7 +14391,7 @@ tsubst_omp_for_iterator (tree t, int i, tree declv, tree initv, /* Like tsubst_copy for expressions, etc. but also does semantic processing. */ -static tree +tree tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl, bool integral_constant_expression_p) { @@ -15911,6 +16368,9 @@ tsubst_copy_and_build (tree t, complain)); } + case REQUIRES_EXPR: + RETURN (tsubst_requires_expr (t, args, complain, in_decl)); + default: /* Handle Objective-C++ constructs, if appropriate. */ { @@ -19194,6 +19654,16 @@ more_specialized_fn (tree pat1, tree pat2, int len) processing_template_decl--; + /* If both deductions succeed, the partial ordering selects the more + constrained template. */ + if (!lose1 && !lose2) + { + tree c1 = get_constraints (DECL_TEMPLATE_RESULT (pat1)); + tree c2 = get_constraints (DECL_TEMPLATE_RESULT (pat2)); + lose1 = !subsumes_constraints (c1, c2); + lose2 = !subsumes_constraints (c2, c1); + } + /* All things being equal, if the next argument is a pack expansion for one function but not for the other, prefer the non-variadic function. FIXME this is bogus; see c++/41958. */ @@ -19262,6 +19732,11 @@ more_specialized_partial_spec (tree tmpl, tree pat1, tree pat2) } --processing_template_decl; + /* If both deductions succeed, the partial ordering selects the more + constrained template. */ + if (!winner && any_deductions) + return more_constrained (tmpl1, tmpl2); + /* In the case of a tie where at least one of the templates has a parameter pack at the end, the template with the most non-packed parameters wins. */ @@ -19409,6 +19884,34 @@ get_partial_spec_bindings (tree tmpl, tree tparms, tree spec_args, tree args) return deduced_args; } +// Compare two function templates T1 and T2 by deducing bindings +// from one against the other. If both deductions succeed, compare +// constraints to see which is more constrained. +static int +more_specialized_inst (tree t1, tree t2) +{ + int fate = 0; + int count = 0; + + if (get_bindings (t1, DECL_TEMPLATE_RESULT (t2), NULL_TREE, true)) + { + --fate; + ++count; + } + + if (get_bindings (t2, DECL_TEMPLATE_RESULT (t1), NULL_TREE, true)) + { + ++fate; + ++count; + } + + // If both deductions succeed, then one may be more constrained. + if (count == 2 && fate == 0) + fate = more_constrained (t1, t2); + + return fate; +} + /* TEMPLATES is a TREE_LIST. Each TREE_VALUE is a TEMPLATE_DECL. Return the TREE_LIST node with the most specialized template, if any. If there is no most specialized template, the error_mark_node @@ -19430,18 +19933,7 @@ most_specialized_instantiation (tree templates) champ = templates; for (fn = TREE_CHAIN (templates); fn; fn = TREE_CHAIN (fn)) { - int fate = 0; - - if (get_bindings (TREE_VALUE (champ), - DECL_TEMPLATE_RESULT (TREE_VALUE (fn)), - NULL_TREE, /*check_ret=*/true)) - fate--; - - if (get_bindings (TREE_VALUE (fn), - DECL_TEMPLATE_RESULT (TREE_VALUE (champ)), - NULL_TREE, /*check_ret=*/true)) - fate++; - + int fate = more_specialized_inst (TREE_VALUE (champ), TREE_VALUE (fn)); if (fate == -1) champ = fn; else if (!fate) @@ -19458,17 +19950,13 @@ most_specialized_instantiation (tree templates) if (champ) /* Now verify that champ is better than everything earlier in the instantiation list. */ - for (fn = templates; fn != champ; fn = TREE_CHAIN (fn)) - if (get_bindings (TREE_VALUE (champ), - DECL_TEMPLATE_RESULT (TREE_VALUE (fn)), - NULL_TREE, /*check_ret=*/true) - || !get_bindings (TREE_VALUE (fn), - DECL_TEMPLATE_RESULT (TREE_VALUE (champ)), - NULL_TREE, /*check_ret=*/true)) - { - champ = NULL_TREE; - break; - } + for (fn = templates; fn != champ; fn = TREE_CHAIN (fn)) { + if (more_specialized_inst (TREE_VALUE (champ), TREE_VALUE (fn)) != 1) + { + champ = NULL_TREE; + break; + } + } processing_template_decl--; @@ -19629,8 +20117,15 @@ most_specialized_partial_spec (tree target, tsubst_flags_t complain) { if (outer_args) spec_args = add_to_template_args (outer_args, spec_args); - list = tree_cons (spec_args, TREE_VALUE (t), list); - TREE_TYPE (list) = TREE_TYPE (t); + + /* Keep the candidate only if the constraints are satisfied, + or if we're not compiling with concepts. */ + if (!flag_concepts + || constraints_satisfied_p (spec_tmpl, spec_args)) + { + list = tree_cons (spec_args, TREE_VALUE (t), list); + TREE_TYPE (list) = TREE_TYPE (t); + } } } @@ -20308,6 +20803,9 @@ instantiate_decl (tree d, int defer_ok, functions and static member variables. */ gcc_assert (VAR_OR_FUNCTION_DECL_P (d)); + /* A concept is never instantiated. */ + gcc_assert (!DECL_DECLARED_CONCEPT_P (d)); + /* Variables are never deferred; if instantiation is required, they are instantiated right away. That allows for better code in the case that an expression refers to the value of the variable -- @@ -21417,6 +21915,14 @@ value_dependent_expression_p (tree expression) || has_value_dependent_address (op)); } + case REQUIRES_EXPR: + /* Treat all requires-expressions as value-dependent so + we don't try to fold them. */ + return true; + + case TYPE_REQ: + return dependent_type_p (TREE_OPERAND (expression, 0)); + case CALL_EXPR: { tree fn = get_callee_fndecl (expression); @@ -21444,7 +21950,8 @@ value_dependent_expression_p (tree expression) case TEMPLATE_ID_EXPR: /* If a TEMPLATE_ID_EXPR involves a dependent name, it will be type-dependent. */ - return type_dependent_expression_p (expression); + return type_dependent_expression_p (expression) + || variable_concept_p (TREE_OPERAND (expression, 0)); case CONSTRUCTOR: { @@ -21516,7 +22023,9 @@ type_dependent_expression_p (tree expression) return false; /* An unresolved name is always dependent. */ - if (identifier_p (expression) || TREE_CODE (expression) == USING_DECL) + if (identifier_p (expression) + || TREE_CODE (expression) == USING_DECL + || TREE_CODE (expression) == WILDCARD_DECL) return true; /* Some expression forms are never type-dependent. */ @@ -21529,7 +22038,8 @@ type_dependent_expression_p (tree expression) || TREE_CODE (expression) == TYPEID_EXPR || TREE_CODE (expression) == DELETE_EXPR || TREE_CODE (expression) == VEC_DELETE_EXPR - || TREE_CODE (expression) == THROW_EXPR) + || TREE_CODE (expression) == THROW_EXPR + || TREE_CODE (expression) == REQUIRES_EXPR) return false; /* The types of these expressions depends only on the type to which @@ -21779,6 +22289,22 @@ instantiation_dependent_r (tree *tp, int *walk_subtrees, case BIND_EXPR: return *tp; + /* Treat requires-expressions as dependent. */ + case REQUIRES_EXPR: + return *tp; + + case CALL_EXPR: + /* Treat calls to function concepts as dependent. */ + if (function_concept_check_p (*tp)) + return *tp; + break; + + case TEMPLATE_ID_EXPR: + /* And variable concepts. */ + if (variable_concept_p (TREE_OPERAND (*tp, 0))) + return *tp; + break; + default: break; } @@ -22379,6 +22905,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; @@ -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 " - "% requires exactly one element")) - inform (input_location, - "for deduction to %, use copy-" - "list-initialization (i.e. add %<=%> before the %<{%>)"); + if (complain & tf_warning_or_error) + { + if (permerror (input_location, "direct-list-initialization of " + "% requires exactly one element")) + inform (input_location, + "for deduction to %, 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 %", type); + if (complain & tf_error) + error ("%qT as type rather than plain %", 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 '' 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 +{ + 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 *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 (); + *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::create_ggc(37); +} /* Set up the hash tables for template instantiations. */ diff --git a/gcc/cp/ptree.c b/gcc/cp/ptree.c index 8abdb729ced..b0281df19b9 100644 --- a/gcc/cp/ptree.c +++ b/gcc/cp/ptree.c @@ -253,6 +253,19 @@ cxx_print_xnode (FILE *file, tree node, int indent) fprintf (file, "pending_template"); } break; + case CONSTRAINT_INFO: + { + tree_constraint_info *cinfo = (tree_constraint_info *)node; + if (cinfo->template_reqs) + print_node (file, "template_reqs", cinfo->template_reqs, indent+4); + if (cinfo->declarator_reqs) + print_node (file, "declarator_reqs", cinfo->declarator_reqs, + indent+4); + print_node (file, "associated_constr", + cinfo->associated_constr, indent+4); + print_node_brief (file, "assumptions", cinfo->assumptions, indent+4); + break; + } case ARGUMENT_PACK_SELECT: print_node (file, "pack", ARGUMENT_PACK_SELECT_FROM_PACK (node), indent+4); diff --git a/gcc/cp/search.c b/gcc/cp/search.c index 1176b3e6f4e..90cd2438b36 100644 --- a/gcc/cp/search.c +++ b/gcc/cp/search.c @@ -1203,6 +1203,12 @@ lookup_member (tree xbasetype, tree name, int protect, bool want_type, } type = complete_type (type); + + /* Make sure we're looking for a member of the current instantiation in the + right partial specialization. */ + if (flag_concepts && dependent_type_p (type)) + type = currently_open_class (type); + if (!basetype_path) basetype_path = TYPE_BINFO (type); diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 17b0a1457b4..7bbae06f717 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -2710,10 +2710,18 @@ finish_template_template_parm (tree aggr, tree identifier) { tree decl = build_decl (input_location, TYPE_DECL, identifier, NULL_TREE); + tree tmpl = build_lang_decl (TEMPLATE_DECL, identifier, NULL_TREE); DECL_TEMPLATE_PARMS (tmpl) = current_template_parms; DECL_TEMPLATE_RESULT (tmpl) = decl; DECL_ARTIFICIAL (decl) = 1; + + // Associate the constraints with the underlying declaration, + // not the template. + tree reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms); + tree constr = build_constraints (reqs, NULL_TREE); + set_constraints (decl, constr); + end_template_decl (); gcc_assert (DECL_TEMPLATE_PARMS (tmpl)); @@ -2977,6 +2985,72 @@ finish_template_decl (tree parms) end_specialization (); } +// Returns the template type of the class scope being entered. If we're +// entering a constrained class scope. TYPE is the class template +// scope being entered and we may need to match the intended type with +// a constrained specialization. For example: +// +// template +// struct S { void f(); }; #1 +// +// template +// void S::f() { } #2 +// +// We check, in #2, that S 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 that is +// unconstrained, but this is not diagnosed here. +// +// template +// void S::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 +// typename pair::first_type void f(T, U); +// +// Here, it is unlikely that there is a partial specialization of +// pair constrained for for Integral and Floating_point arguments. +// +// The general rule is: if a constrained specialization with matching +// constraints is found return that type. Also note that if TYPE is not a +// class-type (e.g. a typename type), then no fixup is needed. + +static tree +fixup_template_type (tree type) +{ + // Find the template parameter list at the a depth appropriate to + // the scope we're trying to enter. + tree parms = current_template_parms; + int depth = template_class_depth (type); + for (int n = processing_template_decl; n > depth && parms; --n) + parms = TREE_CHAIN (parms); + if (!parms) + return type; + tree cur_reqs = TEMPLATE_PARMS_CONSTRAINTS (parms); + tree cur_constr = build_constraints (cur_reqs, NULL_TREE); + + // Search for a specialization whose type and constraints match. + tree tmpl = CLASSTYPE_TI_TEMPLATE (type); + tree specs = DECL_TEMPLATE_SPECIALIZATIONS (tmpl); + while (specs) + { + tree spec_constr = get_constraints (TREE_VALUE (specs)); + + // If the type and constraints match a specialization, then we + // are entering that type. + if (same_type_p (type, TREE_TYPE (specs)) + && equivalent_constraints (cur_constr, spec_constr)) + return TREE_TYPE (specs); + specs = TREE_CHAIN (specs); + } + + // If no specialization matches, then must return the type + // previously found. + return type; +} + /* Finish processing a template-id (which names a type) of the form NAME < ARGS >. Return the TYPE_DECL for the type named by the template-id. If ENTERING_SCOPE is nonzero we are about to enter @@ -2990,6 +3064,16 @@ finish_template_type (tree name, tree args, int entering_scope) type = lookup_template_class (name, args, NULL_TREE, NULL_TREE, entering_scope, tf_warning_or_error | tf_user); + + /* If we might be entering the scope of a partial specialization, + find the one with the right constraints. */ + if (flag_concepts + && entering_scope + && CLASS_TYPE_P (type) + && dependent_type_p (type) + && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (type))) + type = fixup_template_type (type); + if (type == error_mark_node) return type; else if (CLASS_TYPE_P (type) && !alias_type_or_template_p (type)) @@ -7442,6 +7526,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2) case CPTK_IS_POLYMORPHIC: return (CLASS_TYPE_P (type1) && TYPE_POLYMORPHIC_P (type1)); + case CPTK_IS_SAME_AS: + return same_type_p (type1, type2); + case CPTK_IS_STD_LAYOUT: return (std_layout_type_p (type1)); @@ -7549,8 +7636,9 @@ finish_trait_expr (cp_trait_kind kind, tree type1, tree type2) case CPTK_IS_CLASS: case CPTK_IS_ENUM: case CPTK_IS_UNION: + case CPTK_IS_SAME_AS: break; - + default: gcc_unreachable (); } diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 46818b43e81..987ebe8e2aa 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -2955,16 +2955,19 @@ cp_tree_equal (tree t1, tree t2) up for expressions that involve 'this' in a member function template. */ - if (comparing_specializations) + if (comparing_specializations && !CONSTRAINT_VAR_P (t1)) /* When comparing hash table entries, only an exact match is good enough; we don't want to replace 'this' with the - version from another function. */ + version from another function. But be more flexible + with local parameters in a requires-expression. */ return false; if (same_type_p (TREE_TYPE (t1), TREE_TYPE (t2))) { if (DECL_ARTIFICIAL (t1) ^ DECL_ARTIFICIAL (t2)) return false; + if (CONSTRAINT_VAR_P (t1) ^ CONSTRAINT_VAR_P (t2)) + return false; if (DECL_ARTIFICIAL (t1) || (DECL_PARM_LEVEL (t1) == DECL_PARM_LEVEL (t2) && DECL_PARM_INDEX (t1) == DECL_PARM_INDEX (t2))) @@ -3000,6 +3003,10 @@ cp_tree_equal (tree t1, tree t2) return (cp_tree_equal (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0)) && cp_tree_equal (TREE_OPERAND (t1, 1), TREE_OPERAND (t2, 1))); + case CONSTRAINT_INFO: + return cp_tree_equal (CI_ASSOCIATED_CONSTRAINTS (t1), + CI_ASSOCIATED_CONSTRAINTS (t2)); + case TREE_VEC: { unsigned ix; @@ -3876,6 +3883,14 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func, *walk_subtrees_p = 0; break; + case REQUIRES_EXPR: + // Only recurse through the nested expression. Do not + // walk the parameter list. Doing so causes false + // positives in the pack expansion checker since the + // requires parameters are introduced as pack expansions. + WALK_SUBTREE (TREE_OPERAND (*tp, 1)); + *walk_subtrees_p = 0; + break; default: return NULL_TREE; diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 5d754f1228a..83fd34ca80a 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -3474,6 +3474,25 @@ cp_build_function_call_vec (tree function, vec **params, if (TREE_CODE (function) == FUNCTION_DECL) { + /* If the function is a non-template member function + or a non-template friend, then we need to check the + constraints. + + Note that if overload resolution failed with a single + candidate this function will be used to explicitly diagnose + the failure for the single call expression. The check is + technically redundant since we also would have failed in + add_function_candidate. */ + if (flag_concepts + && (complain & tf_error) + && !constraints_satisfied_p (function)) + { + error ("cannot call function %qD", function); + location_t loc = DECL_SOURCE_LOCATION (function); + diagnose_constraints (loc, function, NULL_TREE); + return error_mark_node; + } + if (!mark_used (function, complain) && !(complain & tf_error)) return error_mark_node; fndecl = function; diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 2a47943574e..64d9a6ab0da 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -19280,7 +19280,8 @@ Predefined Macros,cpp,The GNU C Preprocessor}). * C++ Attributes:: Variable, function, and type attributes for C++ only. * Function Multiversioning:: Declaring multiple function versions. * Namespace Association:: Strong using-directives for namespace association. -* Type Traits:: Compiler support for type traits +* Type Traits:: Compiler support for type traits. +* C++ Concepts:: Improved support for generic programming. * Java Exceptions:: Tweaking exception handling to work with Java. * Deprecated Features:: Things will disappear from G++. * Backwards Compatibility:: Compatibilities with earlier definitions of C++. @@ -20076,6 +20077,52 @@ an enumeration type ([dcl.enum]). @end table + +@node C++ Concepts +@section C++ Concepts + +C++ concepts provide much-improved support for generic programming. In +particular, they allow the specification of constraints on template arguments. +The constraints are used to extend the usual overloading and partial +specialization capabilities of the language, allowing generic data structures +and algorithms to be ``refined'' based on their properties rather than their +type names. + +The following keywords are reserved for concepts. + +@table @code +@item assumes +States an expression as an assumption, and if possible, verifies that the +assumption is valid. For example, @code{assume(n > 0)}. + +@item axiom +Introduces an axiom definition. Axioms introduce requirements on values. + +@item forall +Introduces a universally quantified object in an axiom. For example, +@code{forall (int n) n + 0 == n}). + +@item concept +Introduces a concept definition. Concepts are sets of syntactic and semantic +requirements on types and their values. + +@item requires +Introduces constraints on template arguments or requirements for a member +function of a class template. + +@end table + +The front end also exposes a number of internal mechanism that can be used +to simplify the writing of type traits. Note that some of these traits are +likely to be removed in the future. + +@table @code +@item __is_same (type1, type2) +A binary type trait: true whenever the type arguments are the same. + +@end table + + @node Java Exceptions @section Java Exceptions diff --git a/gcc/testsuite/g++.dg/concepts/alias1.C b/gcc/testsuite/g++.dg/concepts/alias1.C new file mode 100644 index 00000000000..03b3ceae928 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/alias1.C @@ -0,0 +1,15 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +template + requires C() + using X = T*; + +struct S { }; + +int main() +{ + X x1; +} diff --git a/gcc/testsuite/g++.dg/concepts/alias2.C b/gcc/testsuite/g++.dg/concepts/alias2.C new file mode 100644 index 00000000000..d81188ed435 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/alias2.C @@ -0,0 +1,13 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +template using X = T*; + +struct S { }; + +int main() +{ + X x1; +} diff --git a/gcc/testsuite/g++.dg/concepts/alias3.C b/gcc/testsuite/g++.dg/concepts/alias3.C new file mode 100644 index 00000000000..e6ab66976a1 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/alias3.C @@ -0,0 +1,13 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +template + requires C() + using X = T*; + +int main() +{ + X x1; // { dg-error "constraint|invalid" } +} diff --git a/gcc/testsuite/g++.dg/concepts/alias4.C b/gcc/testsuite/g++.dg/concepts/alias4.C new file mode 100644 index 00000000000..4227a44c042 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/alias4.C @@ -0,0 +1,19 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +template + requires C() + using X = T*; + +// BUG: Alias templates are expanded at the point of use, regardless +// of whether or not they are dependent. This causes T* to be substituted +// without acutally checking the constraints. +template + using Y = X; + +int main() +{ + Y y1; // { dg-error "" "" { xfail *-*-* } } +} diff --git a/gcc/testsuite/g++.dg/concepts/class.C b/gcc/testsuite/g++.dg/concepts/class.C new file mode 100644 index 00000000000..ea74a54da6c --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/class.C @@ -0,0 +1,52 @@ +// { dg-options "-std=c++1z" } + +template + concept bool Class() { return __is_class(T); } + +template + concept bool Union() { return __is_union(T); } + + +// Check ordering of specializations +template + concept bool One() { return sizeof(T) >= 4; } + +template + concept bool Two() { return One() && sizeof(T) >= 8; } + +// Check non-overlapping specializations +template + struct S1 { static const int value = 0; }; + +template + struct S1 { static const int value = 1; }; + +template + struct S1 { static const int value = 2; }; + +struct S { }; +union U { }; + +static_assert(S1::value == 0, ""); +static_assert(S1::value == 1, ""); +static_assert(S1::value == 2, ""); + + +// Check ordering of partial specializaitons +template + struct S2 { static const int value = 0; }; + +template + struct S2 { static const int value = 1; }; + +template + struct S2 { static const int value = 2; }; + +struct one_type { char x[4]; }; +struct two_type { char x[8]; }; + +static_assert(S2::value == 0, ""); +static_assert(S2::value == 1, ""); +static_assert(S2::value == 2, ""); + +int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/class1.C b/gcc/testsuite/g++.dg/concepts/class1.C new file mode 100644 index 00000000000..b213cb58b7e --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/class1.C @@ -0,0 +1,14 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +template + requires C() + struct S { }; + +struct X { }; + +S sx; + +int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/class2.C b/gcc/testsuite/g++.dg/concepts/class2.C new file mode 100644 index 00000000000..2c3ea4483ab --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/class2.C @@ -0,0 +1,14 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +template + requires C() + struct S { }; + +struct X { }; + +S sx; // { dg-error "constraint|invalid" } + +int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/class3.C b/gcc/testsuite/g++.dg/concepts/class3.C new file mode 100644 index 00000000000..e3a1d2a949b --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/class3.C @@ -0,0 +1,14 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +// Check class redeclaration with alternative spellings. +template requires C() struct S; +template struct S { }; + +struct X { }; + +// S sx; + +int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/class4.C b/gcc/testsuite/g++.dg/concepts/class4.C new file mode 100644 index 00000000000..7ba82508552 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/class4.C @@ -0,0 +1,21 @@ +// { dg-options "-std=c++1z" } + +template + concept bool Class() { return __is_class(T); } + +template + concept bool Union() { return __is_union(T); } + +// Check non-overlapping specializations +template struct S1 { static const int value = 0; }; +template struct S1 { static const int value = 1; }; +template struct S1 { static const int value = 2; }; + +struct S { }; +union U { }; + +static_assert(S1::value == 0, ""); +static_assert(S1::value == 1, ""); +static_assert(S1::value == 2, ""); + +int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/class5.C b/gcc/testsuite/g++.dg/concepts/class5.C new file mode 100644 index 00000000000..903bf249955 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/class5.C @@ -0,0 +1,26 @@ +// { dg-options "-std=c++1z" } + +template + concept bool One() { return sizeof(T) >= 4; } + +template + concept bool Two() { return One() && sizeof(T) >= 8; } + +// Check ordering of partial specializaitons +template + struct S2 { static const int value = 0; }; + +template + struct S2 { static const int value = 1; }; + +template + struct S2 { static const int value = 2; }; + +struct one_type { char x[4]; }; +struct two_type { char x[8]; }; + +static_assert(S2::value == 0, ""); +static_assert(S2::value == 1, ""); +static_assert(S2::value == 2, ""); + +int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/class6.C b/gcc/testsuite/g++.dg/concepts/class6.C new file mode 100644 index 00000000000..fe6b42d5557 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/class6.C @@ -0,0 +1,18 @@ +// { dg-options "-std=c++1z" } + +template + concept bool One() { return sizeof(T) >= 4; } + +template + concept bool Two() { return One() && sizeof(T) >= 8; } + +// Check that there is no ecsacpe hatch +template struct S4 { }; +template struct S4 { }; // { dg-error "does not specialize" } + +struct one_type { char x[4]; }; + +// Constraints are checked even when decls are not instantiatied. +S4* x4b; // { dg-error "constraint|invalid" } + +int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/constrained-parm.C b/gcc/testsuite/g++.dg/concepts/constrained-parm.C new file mode 100644 index 00000000000..fd21c43a037 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/constrained-parm.C @@ -0,0 +1,13 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +template struct S1 { }; // { dg-error "cv-qualified" } +template struct S2 { }; // { dg-error "cv-qualified" } +template struct S3 { }; // { dg-error "invalid" } +template struct S3a { }; // { dg-error "invalid" } +template struct S3b { }; // { dg-error "invalid" } +template struct S4 { }; // { dg-error "invalid" } +template struct S4 { }; // { dg-error "invalid|expected" } +template struct S5 { }; // { dg-error "invalid" } diff --git a/gcc/testsuite/g++.dg/concepts/decl-diagnose.C b/gcc/testsuite/g++.dg/concepts/decl-diagnose.C new file mode 100644 index 00000000000..67b56f962d0 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/decl-diagnose.C @@ -0,0 +1,32 @@ +// { dg-options "-std=c++1z" } + +typedef concept int CINT; // { dg-error "'concept' cannot appear in a typedef declaration" } + +void f(concept int); // { dg-error "a parameter cannot be declared 'concept'" } + +template +concept int f2() { return 0; } // { dg-error "return type" } +concept bool f3(); // { dg-error "no definition" } + +struct X +{ + template + concept int f4() { return 0; } // { dg-error "return type|member function" } + concept bool f5() { return true; } // { dg-error "member function" } + template + 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 + concept bool X3; // { dg-error "has no initializer" } + +struct S { + template + static concept bool C1 = true; // { dg-error "static data member" } +}; diff --git a/gcc/testsuite/g++.dg/concepts/deduction-constraint1.C b/gcc/testsuite/g++.dg/concepts/deduction-constraint1.C new file mode 100644 index 00000000000..6ff3be94ba9 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/deduction-constraint1.C @@ -0,0 +1,12 @@ +// PR c++/67007 +// { dg-options -std=c++1z } + +template +concept bool A = + requires (U u) { u; }; + +template +concept bool B = + requires (T t) { { t } -> A; }; + +void foo(B); diff --git a/gcc/testsuite/g++.dg/concepts/disjunction1.C b/gcc/testsuite/g++.dg/concepts/disjunction1.C new file mode 100644 index 00000000000..f67fa0beacb --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/disjunction1.C @@ -0,0 +1,59 @@ +// PR c++/66962 +// { dg-options -std=c++1z } + +template struct remove_cv; +template struct is_reference; +template void declval(); +template struct is_constructible; +template struct is_nothrow_constructible; +template using remove_cv_t = typename remove_cv<_Tp>::type; +template struct Trans_NS_extension_apply_list; +template using _t = typename T::type; +template void ImplicitlyConvertibleTo(); +template void Assignable(); +template int ConstructibleObject = requires { T{}; }; +template +concept bool BindableReference = + is_reference::value &&is_constructible::value; +template concept bool Constructible() { + return ConstructibleObject || BindableReference; +} +template concept bool DefaultConstructible() { + return Constructible() && requires { new T[0]; }; +} +template concept bool MoveConstructible() { + return Constructible() && ImplicitlyConvertibleTo; +} +template concept bool Movable() { + return MoveConstructible() && Assignable; +} +template int Swappable_ = requires { 0; }; +template int Swappable(); +template concept bool Dereferencable = requires{{0}}; +template using RvalueReferenceType = decltype(0); +template int IsValueType; +template struct value_type; +template +requires IsValueType< + _t>>> using ValueType = + _t>>; +template concept bool Readable() { + return Movable() && DefaultConstructible() && + Dereferencable && requires{{0}}; +} +template concept bool MoveWritable() { + return Movable() && DefaultConstructible() && + Dereferencable; +} +template concept bool IndirectlyMovable() { + return Readable() && Movable>() && + Constructible>() && + MoveWritable>() && + MoveWritable>(); +} +IndirectlyMovable { In, Out } +int is_nothrow_indirectly_movable_v = + is_nothrow_constructible>::value; +template + requires IndirectlyMovable() && + IndirectlyMovable() void iter_swap2(); diff --git a/gcc/testsuite/g++.dg/concepts/dr1430.C b/gcc/testsuite/g++.dg/concepts/dr1430.C new file mode 100644 index 00000000000..7f857fe4ecc --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/dr1430.C @@ -0,0 +1,34 @@ +// PR c++/66092 +// { dg-options "-std=c++1z" } + +#include + +template +requires (sizeof...(Args) == 0) + constexpr decltype(auto) check() + { + return std::integral_constant(); + } + +template +requires (sizeof...(Args) > 0) + constexpr decltype(auto) check() + { + return std::integral_constant())::value>(); + } + +template + concept bool Same() + { + return decltype(check())::value; + } + +template +requires Same() // { dg-error "concept" } + void foo( Args... args ) {} + +int main() +{ + foo(1, 2, 3); // { dg-error "" } +} diff --git a/gcc/testsuite/g++.dg/concepts/equiv.C b/gcc/testsuite/g++.dg/concepts/equiv.C new file mode 100644 index 00000000000..c2ac7410f01 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/equiv.C @@ -0,0 +1,34 @@ +// { dg-options "-std=c++1z" } + +// Check equivalence of short- and longhand declarations. + +template + concept bool C() { return __is_class(T); } + +template + concept bool D() { return __is_empty(T); } + +struct X { } x; + +void f1(C x); +template void f2(T x); +void f3(C x); +template void f4(T x) requires D(); +template void f5(T x) requires D(); +template void f6(T x) requires D(); + +int main() { + f1(x); + f2(x); + f3(x); + f4(x); + f5(x); + f6(x); +} + +template requires C() void f1(T x) { } +template requires C() void f2(T x) { } +template void f3(T x) { } +template requires C() void f4(T x) requires D() { } +template requires C() and D() void f5(T x) { } +template void f6(T x) requires C() and D() { } diff --git a/gcc/testsuite/g++.dg/concepts/equiv2.C b/gcc/testsuite/g++.dg/concepts/equiv2.C new file mode 100644 index 00000000000..675fe2105f1 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/equiv2.C @@ -0,0 +1,29 @@ +// { dg-do run } +// { dg-options "-std=c++1z" } + + +// template +// concept bool C() { return true; } + + +template +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 +void f2(T, T) { } + +template + requires C +void f3(T, T) { } diff --git a/gcc/testsuite/g++.dg/concepts/explicit-inst1.C b/gcc/testsuite/g++.dg/concepts/explicit-inst1.C new file mode 100644 index 00000000000..3079ca50f64 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/explicit-inst1.C @@ -0,0 +1,21 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +template + concept bool D() { return C() && __is_empty(T); } + +struct X { }; +struct Y { int n; }; + +template void g(T) { } // #1 +template void g(T) { } // #2 +template void g(T) { } // #3 + +// FIXME: How do I test that these generate the right symbols? +template void g(int); // Instantiate #1 +template void g(X); // Instantitae #3 +template void g(Y); // Instantiate #2 + +int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/explicit-inst2.C b/gcc/testsuite/g++.dg/concepts/explicit-inst2.C new file mode 100644 index 00000000000..5e75f4ff631 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/explicit-inst2.C @@ -0,0 +1,24 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +template + concept bool D() { return C() && __is_empty(T); } + +struct X { }; +struct Y { int n; }; + +template struct S { void f1() { } }; // #1 +template struct S { void f2() { } }; // #2 +template struct S { void f3() { } }; // #3 + +template struct S; // Instantiate #1 +template struct S; // Instantiate #2 +template struct S; // Instantiate #2 + +int main() { + S i; i.f1(); + S x; x.f3(); + S y; y.f2(); +} diff --git a/gcc/testsuite/g++.dg/concepts/explicit-inst3.C b/gcc/testsuite/g++.dg/concepts/explicit-inst3.C new file mode 100644 index 00000000000..a471657a750 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/explicit-inst3.C @@ -0,0 +1,27 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +template + concept bool D() { return C() && __is_empty(T); } + +struct X { }; +struct Y { int n; }; + +template + struct S { + void f() { } // #1 + void f() requires C() { } // #2 + + void g() requires C() { } // #1 + void g() requires D() { } // #2 + }; + +template void S::f(); // #1 +template void S::f(); // #2 + +template void S::g(); // #2 +template void S::g(); // #1 + +int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/explicit-inst4.C b/gcc/testsuite/g++.dg/concepts/explicit-inst4.C new file mode 100644 index 00000000000..b075c1009e8 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/explicit-inst4.C @@ -0,0 +1,17 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +template + concept bool D() { return C() && __is_empty(T); } + +template + struct S { + void g() requires C() { } // #1 + void g() requires D() { } // #2 + }; + +template void S::g(); // { dg-error "match" } + +int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec1.C b/gcc/testsuite/g++.dg/concepts/explicit-spec1.C new file mode 100644 index 00000000000..6316410b950 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/explicit-spec1.C @@ -0,0 +1,32 @@ +// { dg-do run } +// { dg-options "-std=c++1z" } + +#include + +template + concept bool C() { return __is_class(T); } + +template + concept bool D() { return C() && __is_empty(T); } + +struct X { } x; +struct Y { int n; } y; + +template void g(T) { } // #1 +template void g(T) { } // #2 +template void g(T) { } // #3 + +int called; + +template<> void g(int) { called = 1; } // Specialization of #1 +template<> void g(X) { called = 2; } // Specialization of #3 +template<> void g(Y) { called = 3; } // Specialization of #2 + +int main() { + g(0); + assert(called == 1); + g(x); + assert(called == 2); + g(y); + assert(called == 3); +} diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec2.C b/gcc/testsuite/g++.dg/concepts/explicit-spec2.C new file mode 100644 index 00000000000..4f196243717 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/explicit-spec2.C @@ -0,0 +1,13 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +struct X { }; + +template struct S; +template<> struct S { void f() { } }; + +int main() { + S x; x.f(); +} diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec3.C b/gcc/testsuite/g++.dg/concepts/explicit-spec3.C new file mode 100644 index 00000000000..29546b3b37c --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/explicit-spec3.C @@ -0,0 +1,13 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +template struct S; + +struct X { }; + +// Not a valid explicit specialization, int does not satisfy C. +template<> struct S { }; // { dg-error "constraint" } + +int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec4.C b/gcc/testsuite/g++.dg/concepts/explicit-spec4.C new file mode 100644 index 00000000000..e9aacd51bbb --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/explicit-spec4.C @@ -0,0 +1,51 @@ +// { dg-do run } +// { dg-options "-std=c++1z" } + +#include + +template + concept bool C() { return __is_class(T); } + +template + concept bool D() { return C() && __is_empty(T); } + +struct X { } x; +struct Y { int n; } y; + +int called = 0; + +template + struct S { + void f() { called = 0; } // #1 + void f() requires C() { called = 0; } // #2 + + void g() requires C() { } // #1 + void g() requires D() { } // #2 + }; + +template<> void S::f() { called = 1; } // Spec of #1 +template<> void S::f() { called = 2; } // Spec of #2 + +template<> void S::g() { called = 3; } // Spec of #2 +template<> void S::g() { called = 4; } // Spec of #1 + +int main() { + S sd; + S si; + S sx; + S sy; + + sd.f(); + assert(called == 0); + si.f(); + assert(called == 1); + sx.f(); + assert(called == 2); + sy.f(); + assert(called == 0); + + sx.g(); + assert(called == 3); + sy.g(); + assert(called == 4); +} diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec5.C b/gcc/testsuite/g++.dg/concepts/explicit-spec5.C new file mode 100644 index 00000000000..8047278aa77 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/explicit-spec5.C @@ -0,0 +1,21 @@ +// { dg-options "-std=c++1z" } + +#include + +template + concept bool C() { return __is_class(T); } + +template + concept bool D() { return C() && __is_empty(T); } + +struct X { } x; +struct Y { int n; } y; + +int called = 0; + +template + struct S { + void f() requires C(); + }; + +template<> void S::f() { called = 1; } // { dg-error "match" } diff --git a/gcc/testsuite/g++.dg/concepts/explicit-spec6.C b/gcc/testsuite/g++.dg/concepts/explicit-spec6.C new file mode 100644 index 00000000000..3eba9ff7bfd --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/explicit-spec6.C @@ -0,0 +1,18 @@ +// { dg-options "-std=c++1z" } + +template +struct A { + template void f1(T, T2); // member template + template void f2(T, T2); // member template +}; + +template<> +template void A::f1(int, X1); + +// Specialization with template-id +template<> +template<> void A::f2(int, char); + +// Specialization with deduction +template<> +template<> void A::f1(int, char); diff --git a/gcc/testsuite/g++.dg/concepts/expression.C b/gcc/testsuite/g++.dg/concepts/expression.C new file mode 100644 index 00000000000..5ae04e43a12 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/expression.C @@ -0,0 +1,20 @@ +// { dg-do run } +// { dg-options "-std=c++1z" } + +#include +#include + +template + concept bool C1 = __is_class(T); + +template + concept bool C2() { return __is_class(T); } + +template + concept bool C3() { return requires (T a) { ++a; }; } + +int main() { + if (C1) assert(false); + if (C2()) assert(false); + if (!C3()) assert(false); +} diff --git a/gcc/testsuite/g++.dg/concepts/expression2.C b/gcc/testsuite/g++.dg/concepts/expression2.C new file mode 100644 index 00000000000..40c2034764a --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/expression2.C @@ -0,0 +1,42 @@ +// { dg-options "-std=c++1z" } + +template +concept bool C1() +{ + return requires (T t) { t.f(); }; +} + +template +concept bool C2() +{ + return requires { typename T::type; }; +} + +template + requires C1() +void f1(T x) { } + +template + requires C2() +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(), ""); // { dg-error "failed" } + static_assert(C2(), ""); // { dg-error "failed" } +} diff --git a/gcc/testsuite/g++.dg/concepts/expression3.C b/gcc/testsuite/g++.dg/concepts/expression3.C new file mode 100644 index 00000000000..eb8406f13b0 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/expression3.C @@ -0,0 +1,23 @@ +// { dg-options "-std=c++1z" } + +template +concept bool C() +{ + return requires (T& t) { t.~T(); }; +} + +class S1 +{ + ~S1() { } +}; + +class S2 +{ + ~S2() = delete; +}; + +int main() +{ + static_assert(C(), ""); // { dg-error "failed" } + static_assert(C(), ""); // { dg-error "failed" } +} diff --git a/gcc/testsuite/g++.dg/concepts/feature-macro.C b/gcc/testsuite/g++.dg/concepts/feature-macro.C new file mode 100644 index 00000000000..7bc787559ec --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/feature-macro.C @@ -0,0 +1,5 @@ +// { dg-options -std=c++1z } + +#ifndef __cpp_concepts +#error __cpp_concepts not defined +#endif diff --git a/gcc/testsuite/g++.dg/concepts/fn-concept1.C b/gcc/testsuite/g++.dg/concepts/fn-concept1.C new file mode 100644 index 00000000000..385dcbc13cf --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/fn-concept1.C @@ -0,0 +1,9 @@ +// { dg-options "-std=c++1z" } + +template + concept bool Tuple() { // { dg-error "multiple statements" } + static_assert(T::value, ""); + return true; + } + + void f(Tuple&); diff --git a/gcc/testsuite/g++.dg/concepts/fn-concept2.C b/gcc/testsuite/g++.dg/concepts/fn-concept2.C new file mode 100644 index 00000000000..092c91c5b84 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/fn-concept2.C @@ -0,0 +1,7 @@ +// { dg-options "-std=c++1z" } + +template + concept auto C1() { return 0; } // { dg-error "deduced return type" } + +template + concept int C2() { return 0; } // { dg-error "return type" } diff --git a/gcc/testsuite/g++.dg/concepts/fn1.C b/gcc/testsuite/g++.dg/concepts/fn1.C new file mode 100644 index 00000000000..b2bdaf91885 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/fn1.C @@ -0,0 +1,25 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +struct S { } s; + +template + requires C() + void f(T x) { } + +// Calls are valid when arguments are dependent, +template + void g(T x) { f(x); } + +// Calls are checked when arguments are non-dependent. +template + void h(T x) { + f(s); + } + +int main() { + f(s); + g(s); +} diff --git a/gcc/testsuite/g++.dg/concepts/fn10.C b/gcc/testsuite/g++.dg/concepts/fn10.C new file mode 100644 index 00000000000..f4cd4c5ce34 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/fn10.C @@ -0,0 +1,92 @@ +// { dg-do compile } +// { dg-options "-std=c++1z" } + +// Test that constraint satisfaction checks work even when +// processing template declarations. + +namespace std +{ + +struct ostream { }; +ostream cout; + +template +auto begin(T& t) -> decltype(t.begin()) { return t.begin(); } + +template +auto begin(T const& t) -> decltype(t.begin()) { return t.begin(); } + +template +auto end(T& t) -> decltype(t.end()) { return t.end(); } + +template +auto end(T const& t) -> decltype(t.end()) { return t.end(); } + +} // namespace std + + +template + concept bool Float() + { + return __is_same_as( T, float ); + } + +template + constexpr decltype(auto) project( T t ) + { + return t; + } + +template + concept bool Concept() + { + return requires( T t ) { + requires Float(); + }; + } + +template + constexpr decltype(auto) operator<<( E&& e, F&& f ) {} + +template + 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 +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 + explicit A( R&& r ) { } +}; + +struct C : A +{ + C() = default; + C( const C& ) = default; +}; + +int main() +{ + C c; // OK + return 0; +} diff --git a/gcc/testsuite/g++.dg/concepts/fn2.C b/gcc/testsuite/g++.dg/concepts/fn2.C new file mode 100644 index 00000000000..0aee852ce38 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/fn2.C @@ -0,0 +1,18 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +template + requires C() + void f(T x) { } + +// Non-dependent args are checked even in dependent scope. +template + void h(T x) { + f(0); // { dg-error "cannot call" } + } + +int main() { + f(0); // { dg-error "cannot call" } +} diff --git a/gcc/testsuite/g++.dg/concepts/fn3.C b/gcc/testsuite/g++.dg/concepts/fn3.C new file mode 100644 index 00000000000..06402e02c3b --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/fn3.C @@ -0,0 +1,25 @@ +// { dg-do run } +// { dg-options "-std=c++1z" } + +#include + +// Check partial ordering during overload resolution. + +template + concept bool C() { return __is_class(T); } + +template + concept bool D() { return C() and __is_empty(T); } + +struct S1 { } s1; +struct S2 { int n; } s2; + +int called = 0; + +template void f1(T x) { called = 1;} +template void f1(T x) { called = 2;} + +int main() { + f1(s1); assert(called == 2); + f1(s2); assert(called == 1); +} diff --git a/gcc/testsuite/g++.dg/concepts/fn4.C b/gcc/testsuite/g++.dg/concepts/fn4.C new file mode 100644 index 00000000000..5ced6a7f390 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/fn4.C @@ -0,0 +1,17 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +template + concept bool D() { return C() and __is_empty(T); } + +struct S1 { } s1; +struct S2 { int n; } s2; + +template void f1(T x) { } +template void f1(T x) { } + +int main() { + f1(0); // { dg-error "matching" } +} diff --git a/gcc/testsuite/g++.dg/concepts/fn5.C b/gcc/testsuite/g++.dg/concepts/fn5.C new file mode 100644 index 00000000000..b3c3f70a878 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/fn5.C @@ -0,0 +1,23 @@ +// { dg-options "-std=c++1z" } + +// Check shorthand notation. + +template + concept bool Type() { return true; } + +template + concept bool Same() { return __is_same_as(T, U); } + +template T> struct S1 { }; +template U> struct S2 { }; + +void f(Same q) { } +void g(Type a, Same b) { } + +int main() { + S1 s1; // { dg-error "constraint|invalid" } + S2 s2; // { dg-error "constraint|invalid" } + + f('a'); // { dg-error "cannot" } + g(0, 'a'); // { dg-error "cannot" } +} diff --git a/gcc/testsuite/g++.dg/concepts/fn6.C b/gcc/testsuite/g++.dg/concepts/fn6.C new file mode 100644 index 00000000000..73ef19a20a7 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/fn6.C @@ -0,0 +1,16 @@ +// { dg-options "-std=c++1z" } + +// Redefinition errors. + +template + concept bool C() { return __is_class(T); } + +template + concept bool D() { return C() and __is_empty(T); } + +template void f(T x) { } +template + requires C() + void f(T x) { } // { dg-error "redefinition" } + +int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/fn7.C b/gcc/testsuite/g++.dg/concepts/fn7.C new file mode 100644 index 00000000000..2abd34a1e78 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/fn7.C @@ -0,0 +1,8 @@ +// { dg-do link } +// { dg-options "-std=c++1z" } + +// FIXME: What is this actually testing? + +void f() requires true { } + +int main() { } diff --git a/gcc/testsuite/g++.dg/concepts/fn8.C b/gcc/testsuite/g++.dg/concepts/fn8.C new file mode 100644 index 00000000000..71141f6ff55 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/fn8.C @@ -0,0 +1,27 @@ +// { dg-options "-std=c++1z" } + +template + concept bool Class() { return __is_class(T); } + +template void f(T) { } + +template void fn(T) { } + +auto p1 = &f; // { dg-error "no matches" } +void (*p2)(int) = &f; // { dg-error "no matches" } +void (*p3)(int) = &f; // { dg-error "no matches" } + +struct S { + template int f(T) { } +}; + +auto p4 = &S::template f; // { dg-error "no matches" } +int (S::*p6)(int) = &S::template f; // { dg-error "no matches" } +int (S::*p7)(int) = &S::f; // { dg-error "no matches" } + +template + void g(T x) { } + +int main () { + g(&f); // { dg-error "no matches" } +} diff --git a/gcc/testsuite/g++.dg/concepts/fn9.C b/gcc/testsuite/g++.dg/concepts/fn9.C new file mode 100644 index 00000000000..b7ac4e10a8d --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/fn9.C @@ -0,0 +1,35 @@ +// { dg-do run } +// { dg-options "-std=c++1z" } + +#include + +template + concept bool Class() { return __is_class(T); } + +template + concept bool Empty() { return Class() and __is_empty(T); } + +template int f(T) { return 1; } +template int f(T) { return 2; } + +struct S { + template int f(T) { return 1; } + template int f(T) { return 2; } +} s; + +struct X { } x; +struct Y { X x; } y; + +int main () { + auto p1 = &f; // Empty f + assert(p1(x) == 2); + + auto p2 = &f; // Class f + assert(p2(y) == 1); + + auto p3 = &S::template f; // Empty f + assert((s.*p3)(x) == 2); + + auto p4 = &S::template f; // Empty f + assert((s.*p4)(y) == 1); +} diff --git a/gcc/testsuite/g++.dg/concepts/friend1.C b/gcc/testsuite/g++.dg/concepts/friend1.C new file mode 100644 index 00000000000..286e7697c73 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/friend1.C @@ -0,0 +1,34 @@ +// { dg-options "-std=c++1z" } + +template + concept bool Eq() { return requires(T t) { t == t; }; } + +struct Nt { + template friend void f(T) { } +} nt; + +template struct S; + +template + void proc(S*); + +template + struct S { + friend bool operator==(S, S) requires Eq() { 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 si; + si == si; // OK + + S sx; + sx == sx; // { dg-error "no match" } +} diff --git a/gcc/testsuite/g++.dg/concepts/friend2.C b/gcc/testsuite/g++.dg/concepts/friend2.C new file mode 100644 index 00000000000..38b230c4ff7 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/friend2.C @@ -0,0 +1,20 @@ +// { dg-options "-std=c++1z" } + +template + concept bool Eq() { return requires(T t) { t == t; }; } + +template struct Foo { }; + +template + struct S { // { dg-error "constraint failure" } + template friend class Bar; + + friend class Foo; + }; + +struct X { }; + +int main() { + S si; // OK + S sx; +} diff --git a/gcc/testsuite/g++.dg/concepts/generic-fn-err.C b/gcc/testsuite/g++.dg/concepts/generic-fn-err.C new file mode 100644 index 00000000000..c6b74571954 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/generic-fn-err.C @@ -0,0 +1,49 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +template + concept bool Int() { return true; } + +template 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 + struct S2 { + void f1(auto x) { } + void f2(C x) { } + + void h1(auto x); + void h2(C x); + + template + void g(T t, U u) { } + }; + +int main() { + S s; + + S1 s1; + s1.f2(0); // { dg-error "matching" } + + S2 s2; + s2.f2(0); // { dg-error "matching" } + s2.h2(0); // { dg-error "matching" } + + s2.g(s, 0); // { dg-error "matching" } + s2.g(0, s); // { dg-error "matching" } +} diff --git a/gcc/testsuite/g++.dg/concepts/generic-fn.C b/gcc/testsuite/g++.dg/concepts/generic-fn.C new file mode 100644 index 00000000000..778356db180 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/generic-fn.C @@ -0,0 +1,157 @@ +// { dg-do run } +// { dg-options "-std=c++1z" } + +#include +#include + +template + concept bool C() { return __is_class(T); } + +template + 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 + 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 + void g1(T t, U u) { called = 1; } + + template + 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::value, "not an lvlaue reference"); +} + +void +fwd_const_lvalue_ref(Type&& x) { + using T = decltype(x); + static_assert(std::is_lvalue_reference::value, "not an lvalue reference"); + using U = typename std::remove_reference::type; + static_assert(std::is_const::value, "not const-qualified"); +} + +void fwd_rvalue_ref(Type&& x) { + using T = decltype(x); + static_assert(std::is_rvalue_reference::value, "not an rvalue reference"); +} + +// Make sure we can use nested names speicifers for concept names. +namespace N { + template + 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 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 + void S2::h1(auto x) { called = 1; } + +template + void S2::h2(C x) { called = 2; } + +template + void S2::h3(auto x) { called = 1; } + +template + void S2::h3(C x) { called = 2; } + +template + template + void S2::g2(T t, U u) { called = 2; } diff --git a/gcc/testsuite/g++.dg/concepts/inherit-ctor1.C b/gcc/testsuite/g++.dg/concepts/inherit-ctor1.C new file mode 100644 index 00000000000..29433ade7b4 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/inherit-ctor1.C @@ -0,0 +1,22 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +template + struct S1 { S1(double) requires C() { } }; + +struct S2 : S1 { + using S1::S1; +}; + +template + struct S3 : S1 { + using S1::S1; + }; + +struct X { }; + +int main() { + S3 s(0.0); +} diff --git a/gcc/testsuite/g++.dg/concepts/inherit-ctor2.C b/gcc/testsuite/g++.dg/concepts/inherit-ctor2.C new file mode 100644 index 00000000000..4f39203db4c --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/inherit-ctor2.C @@ -0,0 +1,18 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +template + struct S1 { + S1(double) requires C() { } + }; + +template + struct S2 : S1 { // { dg-error "matching" } + using S1::S1; + }; + +int main() { + S2 s; // { dg-error "deleted function" } +} diff --git a/gcc/testsuite/g++.dg/concepts/inherit-ctor3.C b/gcc/testsuite/g++.dg/concepts/inherit-ctor3.C new file mode 100644 index 00000000000..3d0ddf210d1 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/inherit-ctor3.C @@ -0,0 +1,21 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +template + struct S1 { + template + S1(U x) { } + }; + +template + struct S2 : S1 { + using S1::S1; + }; + +struct X { } x; + +int main() { + S2 s = x; +} diff --git a/gcc/testsuite/g++.dg/concepts/inherit-ctor4.C b/gcc/testsuite/g++.dg/concepts/inherit-ctor4.C new file mode 100644 index 00000000000..cd9565f660f --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/inherit-ctor4.C @@ -0,0 +1,18 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() { return __is_class(T); } + +template + struct S1 { + template S1(U x) { } + }; + +template + struct S2 : S1 { + using S1::S1; + }; + +int main() { + S2 s(0); // { dg-error "no matching function" } +} diff --git a/gcc/testsuite/g++.dg/concepts/intro1.C b/gcc/testsuite/g++.dg/concepts/intro1.C new file mode 100644 index 00000000000..1b5f5d14b7e --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/intro1.C @@ -0,0 +1,38 @@ +// { dg-options "-std=c++1z" } + +template + 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(); + s.f2(); + S1::f3(); + + return 0; +} + +template + void f1() requires C + { + } + +template + void S1::f2() requires C + { + } + +template + void S1::f3() requires C + { + } diff --git a/gcc/testsuite/g++.dg/concepts/intro2.C b/gcc/testsuite/g++.dg/concepts/intro2.C new file mode 100644 index 00000000000..91a1cacf9c0 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/intro2.C @@ -0,0 +1,27 @@ +// { dg-do run } +// { dg-options "-std=c++1z" } + +#include + +template + concept bool C() { return __is_class(T); } + +template + concept bool P() { return true; } + +C{A} struct S1 +{ + P{B} int f1(); +}; + +struct S2 {}; + +int main() +{ + S1 s; + + assert(s.f1<10>() == sizeof(S2) + 10); + return 0; +} + +C{A} P{B} int S1::f1() { return B + sizeof(A); } diff --git a/gcc/testsuite/g++.dg/concepts/intro3.C b/gcc/testsuite/g++.dg/concepts/intro3.C new file mode 100644 index 00000000000..5dd95c698e1 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/intro3.C @@ -0,0 +1,17 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C1 = true; + +template + concept bool C2 = true; + +C1{...A} void f1() {}; +C2{...A} void f2() {}; + +int main() +{ + f1(); + f2<1, 2, 3>(); + return 0; +} diff --git a/gcc/testsuite/g++.dg/concepts/intro4.C b/gcc/testsuite/g++.dg/concepts/intro4.C new file mode 100644 index 00000000000..6d8aec3ff61 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/intro4.C @@ -0,0 +1,32 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C1 = true; + +template + concept bool C2 = true; + +template + concept bool C3 = __is_class(T); + +template + concept bool C4() { return true; } +template + concept bool C4() { return true; } + +template + concept bool C5() { return __is_class(U); } + +C1{...A, B} void f1() {}; // { dg-error "no matching|wrong number" } +C1{A} void f2() {} // { dg-error "cannot match pack|no matching concept" } +C2{A, B} void f3() {}; // { dg-error "cannot match pack|no matching concept" } +C3{...A} void f4() {}; // { dg-error "cannot match pack|no matching concept" } +C4{A} void f5() {}; // { dg-error "no matching concept" } +C5{A, B} void f6() {}; + +int main() +{ + // Defaults should not transfer + f6(); // { dg-error "no matching" } + return 0; +} diff --git a/gcc/testsuite/g++.dg/concepts/intro5.C b/gcc/testsuite/g++.dg/concepts/intro5.C new file mode 100644 index 00000000000..64771cd6e25 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/intro5.C @@ -0,0 +1,15 @@ +// { dg-options "-std=c++1z" } + +template + concept bool C() + { + return sizeof(U) == sizeof(int); + } + +C{A} void f1() {} + +int main() +{ + f1(); + return 0; +} diff --git a/gcc/testsuite/g++.dg/concepts/intro6.C b/gcc/testsuite/g++.dg/concepts/intro6.C new file mode 100644 index 00000000000..4e168ef3c7d --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/intro6.C @@ -0,0 +1,12 @@ +// PR c++/67003 +// { dg-options "-std=c++1z" } + +namespace X { + template + concept bool C = true; +} + +X::C{T} +void foo() {} + +int main() { foo(); } diff --git a/gcc/testsuite/g++.dg/concepts/intro7.C b/gcc/testsuite/g++.dg/concepts/intro7.C new file mode 100644 index 00000000000..d92eafcfbd5 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/intro7.C @@ -0,0 +1,13 @@ +// PR c++/66985 +// { dg-options "-std=c++1z" } + +template