Adds support for concept introduction short hand.
Andrew already committed this patch.
2014-09-04 Braden Obrzut <ad...@maniacsvault.net>
* gcc/cp/constraint.cc (deduce_concept_introduction): New.
(build_concept_check): Allow arg to be NULL to skip placeholder.
(process_introduction_parm): New.
(finish_concept_introduction): New.
* gcc/cp/cp-objcp-common.c (cp_common_init_ts): Mark introduced parm.
* gcc/cp/cp-tree.def: New INTRODUCED_PARM_DECL.
* gcc/cp/parser.c (cp_parser_declaration): Tentatively parse for
concept introduction.
(cp_parser_introduction_list): New.
(cp_parser_member_declaration): Tentatively parse for concept
introduction.
(cp_parser_template_introduction): New.
(cp_parser_template_declaration_after_export): Parse concept
introductions.
* gcc/cp/pt.c (convert_template_argument): Treat INTRODUCED_PARM_DECL
as a placeholder.
(coerce_template_parms): If INTRODUCED_PARM_DECL represents a pack
then
match the entire parameter pack of the template.
(type_dependent_expression_p): Treat INTRODUCED_PARM_DECL as a
placeholder.
* gcc/testsuite/g++.dg/concepts/introduction1.C: New.
* gcc/testsuite/g++.dg/concepts/introduction2.C: New.
* gcc/testsuite/g++.dg/concepts/introduction3.C: New.
* gcc/testsuite/g++.dg/concepts/introduction4.C: New.
* gcc/testsuite/g++.dg/concepts/introduction5.C: New.
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index c09d212..664d8f0 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -238,13 +238,43 @@ deduce_constrained_parameter (tree expr, tree& check, tree& proto)
gcc_unreachable ();
}
+// Given a call expression or template-id expression to a concept, EXPR,
+// deduce the concept being checked and return the template parameters.
+// Returns NULL_TREE if deduction fails.
+static tree
+deduce_concept_introduction (tree expr)
+{
+ if (TREE_CODE (expr) == TEMPLATE_ID_EXPR)
+ {
+ // Get the parameters from the template expression.
+ tree decl = TREE_OPERAND (expr, 0);
+ tree args = TREE_OPERAND (expr, 1);
+ tree var = DECL_TEMPLATE_RESULT (decl);
+ tree parms = TREE_VALUE (DECL_TEMPLATE_PARMS (decl));
+
+ parms = coerce_template_parms (parms, args, var);
+ // Check that we are returned a proper set of parameters.
+ if (parms == error_mark_node)
+ return NULL_TREE;
+ return parms;
+ }
+ else if (TREE_CODE (expr) == CALL_EXPR)
+ {
+ // Resolve the constraint check and return parameters.
+ if (tree info = resolve_constraint_check (expr))
+ return TREE_PURPOSE (info);
+ return NULL_TREE;
+ }
+ else
+ gcc_unreachable ();
+}
+
// -------------------------------------------------------------------------- //
-// Requirement Reduction
+// Normalization
//
-// Reduces a template requirement to a logical formula written in terms of
+// Normalize a template requirement to a logical formula written in terms of
// atomic propositions, returing the new expression. If the expression cannot
-// be reduced, a NULL_TREE is returned, indicating failure to reduce the
-// original requirment.
+// be normalized, a NULL_TREE is returned.
namespace {
@@ -1080,7 +1110,8 @@ build_call_check (tree id)
// arguments to the target are given by ARG and REST. If the target is
// a function (overload set or baselink reffering to an overload set),
// then ths builds the call expression TARGET<ARG, REST>(). If REST is
-// NULL_TREE, then the resulting check is just TARGET<ARG>().
+// NULL_TREE, then the resulting check is just TARGET<ARG>(). If ARG is
+// NULL_TREE, then the resulting check is TARGET<REST>().
tree
build_concept_check (tree target, tree arg, tree rest)
{
@@ -1089,19 +1120,26 @@ build_concept_check (tree target, tree arg, tree rest)
// Build a template-id that acts as the call target using TARGET as
// the template and ARG as the only explicit argument.
int n = rest ? TREE_VEC_LENGTH (rest) : 0;
- tree targs = make_tree_vec (n + 1);
- TREE_VEC_ELT (targs, 0) = arg;
- if (rest)
- for (int i = 0; i < n; ++i)
- TREE_VEC_ELT (targs, i + 1) = TREE_VEC_ELT (rest, i);
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, n + 1);
- if (variable_template_p (target))
- return lookup_template_variable (target, targs);
+ tree targs;
+ if (arg)
+ {
+ targs = make_tree_vec (n + 1);
+ TREE_VEC_ELT (targs, 0) = arg;
+ if (rest)
+ for (int i = 0; i < n; ++i)
+ TREE_VEC_ELT (targs, i + 1) = TREE_VEC_ELT (rest, i);
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, n + 1);
+ }
else
{
- tree id = lookup_template_function (target, targs);
- return build_call_check (id);
+ gcc_assert (rest != NULL_TREE);
+ targs = rest;
}
+
+ if (variable_template_p (target))
+ return lookup_template_variable (target, targs);
+ else
+ return build_call_check (lookup_template_function (target, targs));
}
// Returns a TYPE_DECL that contains sufficient information to build
@@ -1185,6 +1223,125 @@ finish_shorthand_constraint (tree decl, tree constr)
return check;
}
+// Returns and chains a new parmater for PARAMETER_LIST which will conform to the
+// prototype given by SRC_PARM. The new parameter will have it's 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 INTRODUCED_PARM_DECL, 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) == INTRODUCED_PARM_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 != INTRODUCED_PACK_P (src_parm))
+ {
+ error_at (parm_loc, "can not 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;
+ current_template_parms = DECL_TEMPLATE_PARMS (src_parm);
+ 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 should be a TREE_VEC of
+// INTRODUCED_PARM_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 some concept, C, this will form a check in
+// the form of C<INTRO_LIST> filling in any extra arguments needed by the
+// defaults deduced.
+//
+// Returns the template parameters as given from end_template_parm_list or
+// NULL_TREE if the process fails.
+tree
+finish_concept_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 ();
+
+ // Produce a parameter for each introduction argument according to the
+ // deduced form.
+ 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);
+
+ // Build a concept check for our constraint.
+ tree check_args = make_tree_vec (TREE_VEC_LENGTH (parms));
+
+ // Start with introduction parameters.
+ 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 form.
+ for (; n < TREE_VEC_LENGTH (parms); ++n)
+ TREE_VEC_ELT (check_args, n) = TREE_VEC_ELT (parms, n);
+
+ // Associate the constraint.
+ tree reqs = build_concept_check (tmpl_decl, NULL_TREE, check_args);
+ current_template_reqs = save_leading_constraints (reqs);
+ TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = current_template_reqs;
+
+ return parm_list;
+}
+
// -------------------------------------------------------------------------- //
// Substitution Rules
//
diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c
index c889332..ffa1c2e 100644
--- a/gcc/cp/cp-objcp-common.c
+++ b/gcc/cp/cp-objcp-common.c
@@ -253,6 +253,7 @@ cp_common_init_ts (void)
{
MARK_TS_DECL_NON_COMMON (USING_DECL);
MARK_TS_DECL_COMMON (TEMPLATE_DECL);
+ MARK_TS_DECL_COMMON (INTRODUCED_PARM_DECL);
MARK_TS_COMMON (TEMPLATE_TEMPLATE_PARM);
MARK_TS_COMMON (TEMPLATE_TYPE_PARM);
diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index ef95815..6aacf85 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -500,6 +500,10 @@ DEFTREECODE (VALIDTYPE_EXPR, "validtype_expr", tcc_expression, 1)
can be evaluated at compile time. */
DEFTREECODE (CONSTEXPR_EXPR, "contexpr_expr", tcc_expression, 1)
+/* Alternative PLACEHOLDER_EXPR which is used for concept introductions. This
+ is a temporary type which is used to produce normal template parameters. */
+DEFTREECODE (INTRODUCED_PARM_DECL, "introduced_parm_decl", tcc_declaration, 0)
+
/*
Local variables:
mode:c
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index a362fb0..05a48ff 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -88,6 +88,7 @@ c-common.h, not after.
PACK_EXPANSION_LOCAL_P (in *_PACK_EXPANSION)
TINFO_RECHECK_ACCESS_P (in TEMPLATE_INFO)
SIZEOF_EXPR_TYPE_P (in SIZEOF_EXPR)
+ INTRODUCED_PACK_P (in INTRODUCED_PARM_DECL)
1: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE)
TI_PENDING_TEMPLATE_FLAG.
TEMPLATE_PARMS_FOR_INLINE.
@@ -3063,6 +3064,14 @@ 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 introudced parm matches a template parameter pack. */
+#define INTRODUCED_PACK_P(NODE) TREE_LANG_FLAG_0 (NODE)
+
+/* True iff this is a introduction representing a pack in an introduction
+ list. */
+#define IS_INTRODUCED_PACK(NODE) \
+ (TREE_CODE (NODE) == INTRODUCED_PARM_DECL && INTRODUCED_PACK_P (NODE))
+
/* Determine if this is an argument pack. */
#define ARGUMENT_PACK_P(NODE) \
(TREE_CODE (NODE) == TYPE_ARGUMENT_PACK \
@@ -6436,6 +6445,7 @@ extern tree build_constrained_parameter (tree, tree, tree = NULL_TREE);
extern bool deduce_constrained_parameter (tree, tree&, tree&);
extern tree resolve_constraint_check (tree);
+extern tree finish_concept_introduction (tree, tree);
extern tree finish_template_constraints (tree);
extern tree save_leading_constraints (tree);
extern tree save_trailing_constraints (tree);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index a31513d..69ebbc6 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -11267,8 +11267,15 @@ cp_parser_declaration (cp_parser* parser)
/* We must have either a block declaration or a function
definition. */
else
- /* Try to parse a block-declaration, or a function-definition. */
- cp_parser_block_declaration (parser, /*statement_p=*/false);
+ {
+ /* At this point we may have a concept introduction. */
+ cp_parser_parse_tentatively (parser);
+ cp_parser_template_declaration (parser, /*member_p=*/false);
+
+ if (!cp_parser_parse_definitely (parser))
+ /* Try to parse a block-declaration, or a function-definition. */
+ cp_parser_block_declaration (parser, /*statement_p=*/false);
+ }
/* Free any declarators allocated. */
obstack_free (&declarator_obstack, p);
@@ -13207,6 +13214,63 @@ 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 INTRODUCED_PARM_DECLs. If the parameter is a pack
+ then the introduced parm will have INTORDUCED_PACK_P set. In addition, the
+ INTRODUCED_PARM_DECL will also have DECL_NAME set and token location in
+ DECL_SOURCE_LOCATION. */
+
+static tree
+cp_parser_introduction_list (cp_parser *parser)
+{
+ tree introduction_list = NULL_TREE;
+
+ 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 (INTRODUCED_PARM_DECL);
+ DECL_SOURCE_LOCATION (parm) = cp_lexer_peek_token (parser->lexer)->location;
+ DECL_NAME (parm) = cp_parser_identifier (parser);
+ INTRODUCED_PACK_P (parm) = is_pack;
+ introduction_list = chainon (introduction_list,
+ build_tree_list (NULL_TREE, 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 TREE_LIST into a TREE_VEC, similar to what is done in
+ // end_template_parm_list.
+ tree introduction_vec = make_tree_vec (list_length (introduction_list));
+ int n = 0;
+ while (introduction_list != NULL_TREE)
+ {
+ tree next = TREE_CHAIN (introduction_list);
+ TREE_VEC_ELT (introduction_vec, n) = TREE_VALUE (introduction_list);
+ TREE_CHAIN (introduction_list) = NULL_TREE;
+
+ introduction_list = next;
+ ++n;
+ }
+
+ return introduction_vec;
+}
+
// Given a declarator, get the declarator-id part, or NULL_TREE if this
// is an abstract declarator.
static inline cp_declarator*
@@ -21108,6 +21172,13 @@ cp_parser_member_declaration (cp_parser* parser)
return;
}
+ /* Tentatively parse for a template since we may have a concept
+ introduction. */
+ cp_parser_parse_tentatively (parser);
+ cp_parser_template_declaration (parser, /*member_p=*/true);
+ if (cp_parser_parse_definitely (parser))
+ return;
+
parser->colon_corrects_to_scope_p = false;
if (cp_parser_using_declaration (parser, /*access_declaration=*/true))
@@ -24176,6 +24247,69 @@ cp_parser_function_definition_after_declarator (cp_parser* parser,
return fn;
}
+/* Parse a concept introduction header for a template-declaration. If
+ successful, returns the template parameters. Otherwise returns
+ error_mark_node. */
+
+static tree
+cp_parser_template_introduction (cp_parser* parser)
+{
+ // Look for the optional `::' operator.
+ cp_parser_global_scope_opt (parser,
+ /*current_scope_valid_p=*/true);
+ // 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);
+ if (concept_name == error_mark_node)
+ return error_mark_node;
+
+ // Look for opening brace for introduction
+ if (!cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE))
+ return error_mark_node;
+
+ // This must be a concept introduction.
+ if (cp_parser_parsing_tentatively (parser))
+ cp_parser_commit_to_tentative_parse (parser);
+
+ // 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 ("an introduction-list shall not be empty");
+ return error_mark_node;
+ }
+
+ // Look for closing brace for introduction
+ if (!cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE))
+ return error_mark_node;
+
+ // 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);
+ if (tmpl_decl == error_mark_node)
+ {
+ cp_parser_name_lookup_error (parser, concept_name, tmpl_decl, NLE_NULL,
+ token->location);
+ return error_mark_node;
+ }
+
+ // Build and associate the constraint.
+ if (tree p = finish_concept_introduction (tmpl_decl, introduction_list))
+ return p;
+
+ error_at (token->location, "no matching concept for introduction-list");
+ return error_mark_node;
+}
+
/* 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. */
@@ -24189,93 +24323,131 @@ cp_parser_template_declaration_after_export (cp_parser* parser, bool member_p)
bool friend_p = false;
bool need_lang_pop;
cp_token *token;
+ tree saved_template_reqs;
/* 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)
+ if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TEMPLATE))
{
- /* 14.5.2.2 [temp.mem]
+ if (cp_parser_parsing_tentatively (parser))
+ cp_parser_commit_to_tentative_parse (parser);
+ cp_lexer_consume_token (parser->lexer);
- 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]
+ /* Look for 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 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;
+ 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]
- /* 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);
+ 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;
- // Save the current template requirements.
- tree saved_template_reqs = release (current_template_reqs);
+ /* 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);
+
+ // Save the current template requirements.
+ saved_template_reqs = release (current_template_reqs);
+
+ /* 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);
+ }
+
+ /* 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 ();
+
+ /* 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, r);
+ current_template_reqs = save_leading_constraints (reqs);
- /* 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))
+ // Attach the constraints to the template parameter list.
+ // This is used to pass template requirements to out-of-class
+ // member definitions.
+ TEMPLATE_PARMS_CONSTRAINTS (current_template_parms)
+ = current_template_reqs;
+ }
+ }
+ else if(flag_concepts)
{
- cp_parser_error (parser, "invalid explicit specialization");
- begin_specialization ();
- parameter_list = NULL_TREE;
+ need_lang_pop = false;
+ checks = NULL;
+ saved_template_reqs = release (current_template_reqs);
+ push_deferring_access_checks (dk_deferred);
+
+ // Scope may be changed by a nested-name-specifier.
+ tree saved_scope = parser->scope;
+ tree saved_qualifying_scope = parser->qualifying_scope;
+ tree saved_object_scope = parser->object_scope;
+
+ parameter_list = cp_parser_template_introduction (parser);
+ if (parameter_list == error_mark_node)
+ {
+ // Restore template requirements before returning.
+ current_template_reqs = saved_template_reqs;
+ return;
+ }
+
+ parser->scope = saved_scope;
+ parser->qualifying_scope = saved_qualifying_scope;
+ parser->object_scope = saved_object_scope;
}
else
{
- /* Parse the template parameters. */
- parameter_list = cp_parser_template_parameter_list (parser);
+ if (!cp_parser_simulate_error (parser))
+ error ("expected template header");
+ return;
}
- /* 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 ();
-
- /* 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;
- // 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, r);
- current_template_reqs = save_leading_constraints (reqs);
-
- // Attach the constraints to the template parameter list.
- // This is used to pass template requirements to out-of-class
- // member definitions.
- TEMPLATE_PARMS_CONSTRAINTS (current_template_parms)
- = current_template_reqs;
- }
-
- /* 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);
+ /* Look for another template. We need to start a tentative parse here as
+ the next header could be a concept introduction. */
+ cp_parser_parse_tentatively (parser);
+ cp_parser_template_declaration_after_export (parser, member_p);
+ if (cp_parser_parse_definitely (parser))
+ /* Found another template. */;
else if (cxx_dialect >= cxx11
&& cp_lexer_next_token_is_keyword (parser->lexer, RID_USING))
decl = cp_parser_alias_declaration (parser);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 553b138..00ddbb6 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -6769,7 +6769,8 @@ convert_template_argument (tree parm,
int is_type, requires_type, is_tmpl_type, requires_tmpl_type;
// Trivially convert placeholders.
- if (TREE_CODE (arg) == PLACEHOLDER_EXPR)
+ if (TREE_CODE (arg) == PLACEHOLDER_EXPR
+ || TREE_CODE (arg) == INTRODUCED_PARM_DECL)
return convert_placeholder_argument (parm, arg);
if (TREE_CODE (arg) == TREE_LIST
@@ -7086,6 +7087,15 @@ 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 simply match this pack to
+ the single placeholder. */
+ else if (arg_idx < nargs
+ && IS_INTRODUCED_PACK (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);
@@ -21590,7 +21600,8 @@ type_dependent_expression_p (tree expression)
/* An unresolved name is always dependent. */
if (identifier_p (expression)
|| TREE_CODE (expression) == USING_DECL
- || TREE_CODE (expression) == PLACEHOLDER_EXPR)
+ || TREE_CODE (expression) == PLACEHOLDER_EXPR
+ || TREE_CODE (expression) == INTRODUCED_PARM_DECL)
return true;
/* Some expression forms are never type-dependent. */
diff --git a/gcc/testsuite/g++.dg/concepts/introduction1.C b/gcc/testsuite/g++.dg/concepts/introduction1.C
new file mode 100644
index 0000000..1b5f5d1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/introduction1.C
@@ -0,0 +1,38 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+ concept bool C = __is_class(T);
+
+C{T} void f1();
+
+struct S1
+{
+ C{T} void f2();
+ C{T} static void f3();
+};
+
+int main()
+{
+ S1 s;
+
+ f1<S1>();
+ s.f2<S1>();
+ S1::f3<S1>();
+
+ return 0;
+}
+
+template<typename T>
+ void f1() requires C<T>
+ {
+ }
+
+template<typename T>
+ void S1::f2() requires C<T>
+ {
+ }
+
+template<typename T>
+ void S1::f3() requires C<T>
+ {
+ }
diff --git a/gcc/testsuite/g++.dg/concepts/introduction2.C b/gcc/testsuite/g++.dg/concepts/introduction2.C
new file mode 100644
index 0000000..5cdcae0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/introduction2.C
@@ -0,0 +1,28 @@
+// { dg-do run }
+// { dg-options "-std=c++1z" }
+
+#include <cassert>
+
+template<typename T>
+ concept bool C() { return __is_class(T); }
+
+template<int N>
+ concept bool P() { return true; }
+
+C{A} struct S1
+{
+ P{B} int f1();
+};
+
+struct S2 {};
+
+int main()
+{
+ S1<S2> s;
+
+ assert(s.f1<10>() == sizeof(S2) + 10);
+ return 0;
+}
+
+C{A} P{B} int S1<A>::f1() { return B + sizeof(A); }
+
diff --git a/gcc/testsuite/g++.dg/concepts/introduction3.C b/gcc/testsuite/g++.dg/concepts/introduction3.C
new file mode 100644
index 0000000..d4486a0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/introduction3.C
@@ -0,0 +1,17 @@
+// { dg-options "-std=c++1z" }
+
+template<typename ... T>
+ concept bool C1 = true;
+
+template<int ... N>
+ concept bool C2 = true;
+
+C1{...A} void f1() {};
+C2{...A} void f2() {};
+
+int main()
+{
+ f1<int, short, char>();
+ f2<1, 2, 3>();
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/concepts/introduction4.C b/gcc/testsuite/g++.dg/concepts/introduction4.C
new file mode 100644
index 0000000..7b32148
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/introduction4.C
@@ -0,0 +1,32 @@
+// { dg-options "-std=c++1z" }
+
+template<typename ... T>
+ concept bool C1 = true;
+
+template<int ... N>
+ concept bool C2 = true;
+
+template<typename T>
+ concept bool C3 = __is_class(T);
+
+template<typename ... T>
+ concept bool C4() { return true; }
+template<int N>
+ concept bool C4() { return true; }
+
+template<typename T, typename U = int>
+ concept bool C5() { return __is_class(U); }
+
+C1{...A, B} void f1() {}; // { dg-error "no matching concept" }
+C1{A} void f2() {} // { dg-error "match pack" }
+C2{A, B} void f3() {}; // { dg-error "match pack" }
+C3{...A} void f4() {}; // { dg-error "match pack" }
+C4{A} void f5() {}; // { dg-error "no matching concept" }
+C5{A, B} void f6() {};
+
+int main()
+{
+ // Defaults should not transfer
+ f6<int>(); // { dg-error "no matching" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/concepts/introduction5.C b/gcc/testsuite/g++.dg/concepts/introduction5.C
new file mode 100644
index 0000000..309928e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/introduction5.C
@@ -0,0 +1,15 @@
+// { dg-options "-std=c++1z" }
+
+template<typename T, typename U = int>
+ concept bool C()
+ {
+ return sizeof(U) == sizeof(int);
+ }
+
+C{A} void f1() {}
+
+int main()
+{
+ f1<char>();
+ return 0;
+}