Patchwork [V4,2/2] Support using 'auto' in a function parameter list to introduce an implicit template parameter.

login
register
mail settings
Submitter Adam Butcher
Date Sept. 10, 2013, 2:19 a.m.
Message ID <1378779587-15362-3-git-send-email-adam@jessamine.co.uk>
Download mbox | patch
Permalink /patch/273748/
State New
Headers show

Comments

Adam Butcher - Sept. 10, 2013, 2:19 a.m.
* cp-tree.h (type_uses_auto_or_concept): Declare.
	(is_auto_or_concept): Declare.
	* decl.c (grokdeclarator): Allow 'auto' parameters in lambdas with
	-std=gnu++1y or -std=c++1y or, as a GNU extension, in plain functions.
	* type-utils.h: New header defining ...
	(find_type_usage): ... this new function based on pt.c (type_uses_auto)
	for searching a type tree given a predicate.
	* pt.c (type_uses_auto): Reimplement via type-utils.h (find_type_usage).
	(is_auto_or_concept): New function.
	(type_uses_auto_or_concept): New function.
	* parser.h (struct cp_parser): Add fully_implicit_function_template_p.
	* parser.c (cp_parser_new): Initialize fully_implicit_function_template_p.
	(cp_parser_new): Initialize fully_implicit_function_template_p.
	(cp_parser_lambda_expression): Copy and restore value of
	fully_implicit_function_template_p as per other parser fields.
	(cp_parser_parameter_declaration_list): Count generic
	parameters and call ...
	(add_implicit_template_parms): ... this new function to synthesize them
	with help from type-utils.h (find_type_usage), ...
	(tree_type_is_auto_or_concept): ... this new static function and ...
	(make_generic_type_name): ... this new static function.
	(cp_parser_direct_declarator): Account for implicit template parameters.
	(cp_parser_lambda_declarator_opt): Finish fully implicit template if
	necessary by calling ...
	(finish_fully_implicit_template): ... this new function.
	(cp_parser_member_declaration): Likewise.
	(cp_parser_function_definition_after_declarator): Likewise.
	* Make-lang.in (cp/pt.o): Add dependency on type-utils.h.
	(cp/parser.o): Likewise.
---
 gcc/cp/Make-lang.in |   5 +-
 gcc/cp/cp-tree.h    |   2 +
 gcc/cp/decl.c       |  14 +++-
 gcc/cp/parser.c     | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 gcc/cp/parser.h     |   6 ++
 gcc/cp/pt.c         |  35 ++++++----
 gcc/cp/type-utils.h |  55 +++++++++++++++
 7 files changed, 286 insertions(+), 26 deletions(-)
 create mode 100644 gcc/cp/type-utils.h
Jason Merrill - Sept. 11, 2013, 4:02 p.m.
On 09/09/2013 10:19 PM, Adam Butcher wrote:
> +	  if (current_class_type && LAMBDA_TYPE_P (current_class_type))
> +	    {
> +	      if (cxx_dialect < cxx1y)
> +		pedwarn (location_of (type), 0,
> +			 "use of %<auto%> in lambda parameter declaration "
> +			 "only available with "
> +			 "-std=c++1y or -std=gnu++1y");
> +	    }
> +	  else
> +	    pedwarn (location_of (type), OPT_Wpedantic,
> +		     "ISO C++ forbids use of %<auto%> in parameter "
> +		     "declaration");

I think we want to limit the implicit template extension to C++1y mode 
as well.

OK with that change.

Jason
Adam Butcher - Sept. 12, 2013, 6:17 p.m.
On 11.09.2013 17:02, Jason Merrill wrote:
> On 09/09/2013 10:19 PM, Adam Butcher wrote:
>> +	  if (current_class_type && LAMBDA_TYPE_P (current_class_type))
>> +	    {
>> +	      if (cxx_dialect < cxx1y)
>> +		pedwarn (location_of (type), 0,
>> +			 "use of %<auto%> in lambda parameter declaration "
>> +			 "only available with "
>> +			 "-std=c++1y or -std=gnu++1y");
>> +	    }
>> +	  else
>> +	    pedwarn (location_of (type), OPT_Wpedantic,
>> +		     "ISO C++ forbids use of %<auto%> in parameter "
>> +		     "declaration");
>
> I think we want to limit the implicit template extension to C++1y
> mode as well.
>
OK.  Do you think we should mark 'type' as 'error_mark_node' in the 
pre-C++1y cases to reject the user program or just stick to the pedwarn 
0?

I.e. this:

    if (current_class_type && LAMBDA_TYPE_P (current_class_type))
      {
        if (cxx_dialect < cxx1y)
          {
            pedwarn (location_of (type), 0,
                     "use of %<auto%> in lambda parameter declaration "
                     "only available with "
                     "-std=c++1y or -std=gnu++1y");
            type = error_mark_node;
          }
      }
    else if (cxx_dialect < cxx1y)
      {
        pedwarn (location_of (type), 0,
                 "use of %<auto%> in parameter declaration "
                 "only available with "
                 "-std=c++1y or -std=gnu++1y");
        type = error_mark_node;
      }
    else
      pedwarn (location_of (type), OPT_Wpedantic,
               "ISO C++ forbids use of %<auto%> in parameter "
               "declaration");


or the same but without the  "type = error_mark_node;"  lines?
Jason Merrill - Sept. 12, 2013, 7:59 p.m.
Just the pedwarn, I think.

Jason

Patch

diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in
index 65dfe08..e8d4913 100644
--- a/gcc/cp/Make-lang.in
+++ b/gcc/cp/Make-lang.in
@@ -320,7 +320,7 @@  cp/except.o: cp/except.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) \
 cp/expr.o: cp/expr.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) $(TM_P_H)
 cp/pt.o: cp/pt.c $(CXX_TREE_H) $(TM_H) cp/decl.h cp/cp-objcp-common.h \
   toplev.h $(TREE_INLINE_H) pointer-set.h gt-cp-pt.h intl.h \
-  c-family/c-objc.h
+  c-family/c-objc.h cp/type-utils.h
 cp/error.o: cp/error.c $(CXX_TREE_H) $(TM_H) $(DIAGNOSTIC_H) \
   $(FLAGS_H) $(REAL_H) $(LANGHOOKS_DEF_H) $(CXX_PRETTY_PRINT_H) \
   tree-diagnostic.h tree-pretty-print.h pointer-set.h c-family/c-objc.h
@@ -339,7 +339,8 @@  cp/mangle.o: cp/mangle.c $(CXX_TREE_H) $(TM_H) $(REAL_H) \
   gt-cp-mangle.h $(TARGET_H) $(TM_P_H) $(CGRAPH_H)
 cp/parser.o: cp/parser.c $(CXX_TREE_H) $(TM_H) $(DIAGNOSTIC_CORE_H) \
   gt-cp-parser.h $(TARGET_H) $(PLUGIN_H) intl.h cp/decl.h \
-  c-family/c-objc.h tree-pretty-print.h $(CXX_PARSER_H) $(TIMEVAR_H)
+  c-family/c-objc.h tree-pretty-print.h $(CXX_PARSER_H) $(TIMEVAR_H) \
+  cp/type-utils.h
 cp/cp-gimplify.o: cp/cp-gimplify.c $(CXX_TREE_H) $(C_COMMON_H) \
 	$(TM_H) coretypes.h pointer-set.h tree-iterator.h $(SPLAY_TREE_H)
 cp/vtable-class-hierarchy.o: cp/vtable-class-hierarchy.c \
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 3e4f188..b68562d 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5455,10 +5455,12 @@  extern tree make_auto				(void);
 extern tree make_decltype_auto			(void);
 extern tree do_auto_deduction			(tree, tree, tree);
 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,
 						      location_t);
 extern tree splice_late_return_type		(tree, tree);
 extern bool is_auto				(const_tree);
+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);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index b4223aa..8b67ec8 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -10320,8 +10320,18 @@  grokdeclarator (const cp_declarator *declarator,
 
       if (type_uses_auto (type))
 	{
-	  error ("parameter declared %<auto%>");
-	  type = error_mark_node;
+	  if (current_class_type && LAMBDA_TYPE_P (current_class_type))
+	    {
+	      if (cxx_dialect < cxx1y)
+		pedwarn (location_of (type), 0,
+			 "use of %<auto%> in lambda parameter declaration "
+			 "only available with "
+			 "-std=c++1y or -std=gnu++1y");
+	    }
+	  else
+	    pedwarn (location_of (type), OPT_Wpedantic,
+		     "ISO C++ forbids use of %<auto%> in parameter "
+		     "declaration");
 	}
 
       /* A parameter declared as an array of T is really a pointer to T.
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 1f0c2c2..81a0bdb 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -38,6 +38,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "plugin.h"
 #include "tree-pretty-print.h"
 #include "parser.h"
+#include "type-utils.h"
 
 
 /* The lexer.  */
@@ -2063,6 +2064,11 @@  static vec<constructor_elt, va_gc> *cp_parser_initializer_list
 static bool cp_parser_ctor_initializer_opt_and_function_body
   (cp_parser *, bool);
 
+static tree add_implicit_template_parms
+  (cp_parser *, size_t, tree);
+static tree finish_fully_implicit_template
+  (cp_parser *, tree);
+
 /* Classes [gram.class] */
 
 static tree cp_parser_class_name
@@ -3385,6 +3391,9 @@  cp_parser_new (void)
   /* No template parameters apply.  */
   parser->num_template_parameter_lists = 0;
 
+  /* Not declaring an implicit function template.  */
+  parser->fully_implicit_function_template_p = false;
+
   return parser;
 }
 
@@ -8549,10 +8558,12 @@  cp_parser_lambda_expression (cp_parser* parser)
         = parser->num_template_parameter_lists;
     unsigned char in_statement = parser->in_statement;
     bool in_switch_statement_p = parser->in_switch_statement_p;
+    bool fully_implicit_function_template_p = parser->fully_implicit_function_template_p;
 
     parser->num_template_parameter_lists = 0;
     parser->in_statement = 0;
     parser->in_switch_statement_p = false;
+    parser->fully_implicit_function_template_p = false;
 
     /* By virtue of defining a local class, a lambda expression has access to
        the private variables of enclosing classes.  */
@@ -8576,6 +8587,7 @@  cp_parser_lambda_expression (cp_parser* parser)
     parser->num_template_parameter_lists = saved_num_template_parameter_lists;
     parser->in_statement = in_statement;
     parser->in_switch_statement_p = in_switch_statement_p;
+    parser->fully_implicit_function_template_p = fully_implicit_function_template_p;
   }
 
   pop_deferring_access_checks ();
@@ -8920,6 +8932,8 @@  cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
 	    finish_template_decl (template_param_list);
 	    --parser->num_template_parameter_lists;
 	  }
+	else if (parser->fully_implicit_function_template_p)
+	  fco = finish_fully_implicit_template (parser, fco);
       }
 
     finish_member_declaration (fco);
@@ -16790,8 +16804,10 @@  cp_parser_direct_declarator (cp_parser* parser,
 	      /* Parse the parameter-declaration-clause.  */
 	      params = cp_parser_parameter_declaration_clause (parser);
 
+	      /* Restore saved template parameter lists accounting for implicit
+		 template parameters.  */
 	      parser->num_template_parameter_lists
-		= saved_num_template_parameter_lists;
+		+= saved_num_template_parameter_lists;
 
 	      /* Consume the `)'.  */
 	      cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
@@ -17889,6 +17905,7 @@  cp_parser_parameter_declaration_list (cp_parser* parser, bool *is_error)
   tree *tail = &parameters; 
   bool saved_in_unbraced_linkage_specification_p;
   int index = 0;
+  int implicit_template_parms = 0;
 
   /* Assume all will go well.  */
   *is_error = false;
@@ -17916,11 +17933,18 @@  cp_parser_parameter_declaration_list (cp_parser* parser, bool *is_error)
       deprecated_state = DEPRECATED_SUPPRESS;
 
       if (parameter)
-	decl = grokdeclarator (parameter->declarator,
-			       &parameter->decl_specifiers,
-			       PARM,
-			       parameter->default_argument != NULL_TREE,
-			       &parameter->decl_specifiers.attributes);
+	{
+	  decl = grokdeclarator (parameter->declarator,
+				 &parameter->decl_specifiers,
+				 PARM,
+				 parameter->default_argument != NULL_TREE,
+				 &parameter->decl_specifiers.attributes);
+
+	  if (TREE_TYPE (decl) != error_mark_node
+	      && parameter->decl_specifiers.type
+	      && is_auto_or_concept (parameter->decl_specifiers.type))
+	      ++implicit_template_parms;
+	}
 
       deprecated_state = DEPRECATED_NORMAL;
 
@@ -18008,6 +18032,11 @@  cp_parser_parameter_declaration_list (cp_parser* parser, bool *is_error)
   parser->in_unbraced_linkage_specification_p
     = saved_in_unbraced_linkage_specification_p;
 
+  if (parameters != error_mark_node && implicit_template_parms)
+    parameters = add_implicit_template_parms (parser,
+					      implicit_template_parms,
+					      parameters);
+
   return parameters;
 }
 
@@ -20014,7 +20043,11 @@  cp_parser_member_declaration (cp_parser* parser)
 							      attributes);
 		  /* If the member was not a friend, declare it here.  */
 		  if (!friend_p)
-		    finish_member_declaration (decl);
+		    {
+		      if (parser->fully_implicit_function_template_p)
+			decl = finish_fully_implicit_template (parser, decl);
+		      finish_member_declaration (decl);
+		    }
 		  /* Peek at the next token.  */
 		  token = cp_lexer_peek_token (parser->lexer);
 		  /* If the next token is a semicolon, consume it.  */
@@ -20030,6 +20063,8 @@  cp_parser_member_declaration (cp_parser* parser)
 				  initializer, /*init_const_expr_p=*/true,
 				  asm_specification,
 				  attributes);
+		if (parser->fully_implicit_function_template_p)
+		  decl = finish_fully_implicit_template (parser, decl);
 	    }
 
 	  /* Reset PREFIX_ATTRIBUTES.  */
@@ -22297,6 +22332,9 @@  cp_parser_function_definition_after_declarator (cp_parser* parser,
     = saved_num_template_parameter_lists;
   parser->in_function_body = saved_in_function_body;
 
+  if (parser->fully_implicit_function_template_p)
+    finish_fully_implicit_template (parser, /*member_decl_opt=*/0);
+
   return fn;
 }
 
@@ -28842,4 +28880,147 @@  c_parse_file (void)
   the_parser = NULL;
 }
 
+/* Create an identifier for a generic parameter type (a synthesized
+   template parameter implied by `auto' or a concept identifier). */
+
+static tree
+make_generic_type_name (int i)
+{
+  char buf[32];
+  sprintf (buf, "__GenT%d", i);
+  return get_identifier (buf);
+}
+
+/* Predicate that behaves as is_auto_or_concept but matches the parent
+   node of the generic type rather than the generic type itself.  This
+   allows for type transformation in add_implicit_template_parms.  */
+
+static inline bool
+tree_type_is_auto_or_concept (const_tree t)
+{
+  return TREE_TYPE (t) && is_auto_or_concept (TREE_TYPE (t));
+}
+
+/* Add COUNT implicit template parameters gleaned from the generic
+   type parameters in PARAMETERS to the CURRENT_TEMPLATE_PARMS
+   (creating a new template parameter list if necessary).  Returns
+   PARAMETERS suitably rewritten to reference the newly created types
+   or ERROR_MARK_NODE on failure.  */
+
+tree
+add_implicit_template_parms (cp_parser *parser, size_t count, tree parameters)
+{
+  gcc_assert (current_binding_level->kind == sk_function_parms);
+
+  cp_binding_level *fn_parms_scope = current_binding_level;
+
+  bool become_template =
+    fn_parms_scope->level_chain->kind != sk_template_parms;
+
+  size_t synth_idx = 0;
+
+  /* Roll back a scope level and either introduce a new template parameter list
+     or update an existing one.  The function scope is added back after template
+     parameter synthesis below.  */
+  current_binding_level = fn_parms_scope->level_chain;
+
+  /* TPARMS tracks the function's template parameter list.  This is either a new
+     chain in the case of a fully implicit function template or an extension of
+     the function's explicitly specified template parameter list.  */
+  tree tparms = NULL_TREE;
+
+  if (become_template)
+    {
+      push_deferring_access_checks (dk_deferred);
+      begin_template_parm_list ();
+
+      parser->fully_implicit_function_template_p = true;
+      ++parser->num_template_parameter_lists;
+    }
+  else
+    {
+      /* Roll back the innermost template parameter list such that it may be
+	 extended in the loop below as if it were being explicitly declared.  */
+
+      gcc_assert (current_template_parms);
+
+      /* Pop the innermost template parms into TPARMS.  */
+      tree inner_vec = INNERMOST_TEMPLATE_PARMS (current_template_parms);
+      current_template_parms = TREE_CHAIN (current_template_parms);
+
+      size_t inner_vec_len = TREE_VEC_LENGTH (inner_vec);
+      if (inner_vec_len != 0)
+	{
+	  tree t = tparms = TREE_VEC_ELT (inner_vec, 0);
+	  for (size_t n = 1; n < inner_vec_len; ++n)
+	    t = TREE_CHAIN (t) = TREE_VEC_ELT (inner_vec, n);
+	}
+
+      ++processing_template_parmlist;
+    }
+
+  for (tree p = parameters; p && synth_idx < count; p = TREE_CHAIN (p))
+    {
+      tree generic_type_ptr
+	= find_type_usage (TREE_VALUE (p), tree_type_is_auto_or_concept);
+
+      if (!generic_type_ptr)
+	continue;
+
+      tree synth_id = make_generic_type_name (synth_idx++);
+      tree synth_tmpl_parm = finish_template_type_parm (class_type_node,
+							synth_id);
+      tparms = process_template_parm (tparms, DECL_SOURCE_LOCATION (TREE_VALUE
+								    (p)),
+				      build_tree_list (NULL_TREE,
+						       synth_tmpl_parm),
+				      /*non_type=*/false,
+				      /*param_pack=*/false);
+
+      /* Rewrite the type of P to be the template_parm added above (getdecls is
+         used to retrieve it since it is the most recent declaration in this
+         scope).  Qualifiers need to be preserved also.  */
+
+      tree& cur_type = TREE_TYPE (generic_type_ptr);
+      tree new_type = TREE_TYPE (getdecls ());
+
+      if (TYPE_QUALS (cur_type))
+	cur_type = cp_build_qualified_type (new_type, TYPE_QUALS (cur_type));
+      else
+	cur_type = new_type;
+    }
+
+  gcc_assert (synth_idx == count);
+
+  push_binding_level (fn_parms_scope);
+
+  end_template_parm_list (tparms);
+
+  return parameters;
+}
+
+/* Finish the declaration of a fully implicit function template.  Such a
+   template has no explicit template parameter list so has not been through the
+   normal template head and tail processing.  add_implicit_template_parms tries
+   to do the head; this tries to do the tail.  MEMBER_DECL_OPT should be
+   provided if the declaration is a class member such that its template
+   declaration can be completed.  If MEMBER_DECL_OPT is provided the finished
+   form is returned.  Otherwise NULL_TREE is returned. */
+
+tree
+finish_fully_implicit_template (cp_parser *parser, tree member_decl_opt)
+{
+  gcc_assert (parser->fully_implicit_function_template_p);
+
+  pop_deferring_access_checks ();
+  if (member_decl_opt)
+    member_decl_opt = finish_member_template_decl (member_decl_opt);
+  end_template_decl ();
+
+  parser->fully_implicit_function_template_p = false;
+  --parser->num_template_parameter_lists;
+
+  return member_decl_opt;
+}
+
 #include "gt-cp-parser.h"
diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h
index 3d8bb74..ffdddaf 100644
--- a/gcc/cp/parser.h
+++ b/gcc/cp/parser.h
@@ -341,6 +341,12 @@  typedef struct GTY(()) cp_parser {
   /* The number of template parameter lists that apply directly to the
      current declaration.  */
   unsigned num_template_parameter_lists;
+
+  /* TRUE if the function being declared was made a template due to its
+     parameter list containing generic type specifiers (`auto' or concept
+     identifiers) rather than an explicit template parameter list.  */
+  bool fully_implicit_function_template_p;
+
 } cp_parser;
 
 /* In parser.c  */
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index ba841ca..22087fb 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -41,6 +41,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "toplev.h"
 #include "timevar.h"
 #include "tree-iterator.h"
+#include "type-utils.h"
 
 /* The type of functions taking a tree, and some additional data, and
    returning an int.  */
@@ -21098,31 +21099,35 @@  is_auto (const_tree type)
     return false;
 }
 
-/* Returns true iff TYPE contains a use of 'auto'.  Since auto can only
-   appear as a type-specifier for the declaration in question, we don't
-   have to look through the whole type.  */
+/* Returns the TEMPLATE_TYPE_PARM in TYPE representing `auto' iff TYPE contains
+   a use of `auto'.  Returns NULL_TREE otherwise.  */
 
 tree
 type_uses_auto (tree type)
 {
-  enum tree_code code;
-  if (is_auto (type))
-    return type;
+  return find_type_usage (type, is_auto);
+}
 
-  code = TREE_CODE (type);
+/* Returns true iff TYPE is a TEMPLATE_TYPE_PARM representing 'auto',
+   'decltype(auto)' or a concept.  */
 
-  if (code == POINTER_TYPE || code == REFERENCE_TYPE
-      || code == OFFSET_TYPE || code == FUNCTION_TYPE
-      || code == METHOD_TYPE || code == ARRAY_TYPE)
-    return type_uses_auto (TREE_TYPE (type));
+bool
+is_auto_or_concept (const_tree type)
+{
+  return is_auto (type); // or concept
+}
 
-  if (TYPE_PTRMEMFUNC_P (type))
-    return type_uses_auto (TREE_TYPE (TREE_TYPE
-				   (TYPE_PTRMEMFUNC_FN_TYPE (type))));
+/* Returns the TEMPLATE_TYPE_PARM in TYPE representing a generic type (`auto' or
+   a concept identifier) iff TYPE contains a use of a generic type.  Returns
+   NULL_TREE otherwise.  */
 
-  return NULL_TREE;
+tree
+type_uses_auto_or_concept (tree type)
+{
+  return find_type_usage (type, is_auto_or_concept);
 }
 
+
 /* For a given template T, return the vector of typedefs referenced
    in T for which access check is needed at T instantiation time.
    T is either  a FUNCTION_DECL or a RECORD_TYPE.
diff --git a/gcc/cp/type-utils.h b/gcc/cp/type-utils.h
new file mode 100644
index 0000000..3e82ca4
--- /dev/null
+++ b/gcc/cp/type-utils.h
@@ -0,0 +1,55 @@ 
+/* Utilities for querying and manipulating type trees.
+   Copyright (C) 2013 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_CP_TYPE_UTILS_H
+#define GCC_CP_TYPE_UTILS_H
+
+/* Returns the first tree within T that is directly matched by PRED.  T may be a
+   type or PARM_DECL and is incrementally decomposed toward its type-specifier
+   until a match is found.  NULL_TREE is returned if PRED does not match any
+   part of T.
+
+   This is primarily intended for detecting whether T uses `auto' or a concept
+   identifier.  Since either of these can only appear as a type-specifier for
+   the declaration in question, only top-level qualifications are traversed;
+   find_type_usage does not look through the whole type.  */
+
+inline tree
+find_type_usage (tree t, bool (*pred) (const_tree))
+{
+  enum tree_code code;
+  if (pred (t))
+    return t;
+
+  code = TREE_CODE (t);
+
+  if (code == POINTER_TYPE || code == REFERENCE_TYPE
+      || code == PARM_DECL || code == OFFSET_TYPE
+      || code == FUNCTION_TYPE || code == METHOD_TYPE
+      || code == ARRAY_TYPE)
+    return find_type_usage (TREE_TYPE (t), pred);
+
+  if (TYPE_PTRMEMFUNC_P (t))
+    return find_type_usage
+      (TREE_TYPE (TREE_TYPE (TYPE_PTRMEMFUNC_FN_TYPE (t))), pred);
+
+  return NULL_TREE;
+}
+
+#endif // GCC_CP_TYPE_UTILS_H