diff mbox

[c++-concepts] concept introductions

Message ID 540B596E.8010006@maniacsvault.net
State New
Headers show

Commit Message

Braden Obrzut Sept. 6, 2014, 6:58 p.m. UTC
Adds support for concept introduction short hand.

Andrew already committed this patch.

2014-09-04  Braden Obrzut  <admin@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 mbox

Patch

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;
+}