From 8d8f7c4b504df982a81d4ed95961a256d22f8367 Mon Sep 17 00:00:00 2001
From: Adam Butcher <ajb@localhost.(none)>
Date: Mon, 10 Aug 2009 23:45:06 +0100
Subject: [PATCH] Second version of typename inference from auto parameters in lambda call operator.

Still quite hacky -- though better than previous.  No longer loses
qualifiers on replaced auto parameters so is functionally closer to
what's really needed.

 - This is just a behavioural proof to find out how things work.
 - Need to shuffle some stuff into pt.c and do away with code dup.
 - Not sure how to free tree_vec's and tidy up the counts and sizes
   (looks like they're only intended to grow.)
 - Added `type_decays_to (non_reference (finish_decltype_type' as
   suggested by Jason.  Currently doesn't remove cv-quals from
   non-class types though.  Need to treat implicit return type
   differently for dependent types -- should defer and mark that it
   needs to be computed later.
---
 gcc/cp/parser.c    |  225 +++++++++++++++++++++++++++++++++++++++++++++++++---
 gcc/cp/pt.c        |    8 ++
 gcc/cp/semantics.c |    6 +-
 3 files changed, 226 insertions(+), 13 deletions(-)

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 36762ef..1461309 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -7032,7 +7032,7 @@ cp_parser_lambda_introducer (cp_parser* parser,
   /* Eat the leading `['.  */
   cp_parser_require (parser, CPP_OPEN_SQUARE, "%<[%>");
 
-  /* Record default capture mode.  "[&" "[=" "[&," "[=,"  */
+  /* Record default capture mode.  `[&' `[=' `[&,' `[=,'  */
   if (cp_lexer_next_token_is (parser->lexer, CPP_AND)
       && cp_lexer_peek_nth_token (parser->lexer, 2)->type != CPP_NAME)
     LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) = CPLD_REFERENCE;
@@ -7157,6 +7157,189 @@ cp_parser_lambda_introducer (cp_parser* parser,
 
 }
 
+
+/* The struct auto_parm_handler_t defines an interface for customizing
+   the behaviour when a C++0x `auto' type is found as the primary type
+   specifier of a function parameter declaration.
+   If auto_parm_handler is set whilst parsing a function parameter
+   list, the function auto_parm_handler->hook will be called for each
+   parameter having `auto' as its primary type; in each case the
+   result of the hook will be used to replace `auto' as the primary
+   type.  */
+typedef struct auto_parm_handler_t auto_parm_handler_t;
+typedef tree (*auto_parm_hook_t) (auto_parm_handler_t*);
+struct auto_parm_handler_t
+{
+   auto_parm_hook_t hook;
+};
+/* Set to a structure that provides the above interface to be called
+   if a type containing `auto' is found during
+   cp_parser_parameter_declaration_list.  */
+auto_parm_handler_t* auto_parm_handler = 0;
+
+/* Handler state for processing `auto' found in lambda function call
+   parameter list.  Supports implicit polymorphic lambdas.  */
+typedef struct lambda_auto_handler_t
+{
+   auto_parm_hook_t hook;
+   tree* template_param_list;
+   VEC (deferred_access_check,gc)* checks;
+   cp_parser* parser;
+   int i;
+}
+lambda_auto_handler_t;
+
+/* FIXME: Much of this would appear to fit better in pt.c.  */
+
+/* FIXME: It would also mean the copied function
+          build_template_parm_index and rudely extern'd
+          x_canonical_type_parameter would be unnecessary.  */
+
+tree lambda_parameter_make_auto_type_name
+  (lambda_auto_handler_t*);
+tree lambda_auto_handler
+  (lambda_auto_handler_t*);
+
+tree
+lambda_parameter_make_auto_type_name (lambda_auto_handler_t* handler)
+{
+  char buf[32];
+  sprintf (buf, "__AutoT%d", ++handler->i);
+  return get_identifier (buf);
+}
+
+/* Return a new TEMPLATE_PARM_INDEX with the indicated INDEX, LEVEL,
+   ORIG_LEVEL, DECL, and TYPE.  
+   FIXME: Remove this copy from here; i.e. probably move rest into
+          pt.c.  */
+
+static tree
+build_template_parm_index (int index,
+			   int level,
+			   int orig_level,
+			   tree decl,
+			   tree type)
+{
+  tree t = make_node (TEMPLATE_PARM_INDEX);
+  TEMPLATE_PARM_IDX (t) = index;
+  TEMPLATE_PARM_LEVEL (t) = level;
+  TEMPLATE_PARM_ORIG_LEVEL (t) = orig_level;
+  TEMPLATE_PARM_DECL (t) = decl;
+  TREE_TYPE (t) = type;
+  TREE_CONSTANT (t) = TREE_CONSTANT (decl);
+  TREE_READONLY (t) = TREE_READONLY (decl);
+
+  return t;
+}
+
+tree
+lambda_auto_handler (lambda_auto_handler_t* handler)
+{
+  struct cp_binding_level* scope = current_binding_level;
+  location_t param_loc = cp_lexer_peek_token (handler->parser->lexer)->location; /* XXX: Any way to get current location? */
+
+  /* First auto parameter may need to start a template parameter list.  */
+  bool become_template = *handler->template_param_list == NULL_TREE;
+
+  tree synth_id = lambda_parameter_make_auto_type_name (handler);
+  tree synth_tmpl_parm = finish_template_type_parm (class_type_node, synth_id);
+  synth_tmpl_parm = build_tree_list (NULL_TREE, synth_tmpl_parm);
+
+  if (become_template)
+    {
+      /* do something rude and pretend that the template parameter
+	 scope surrounds the function definition.  XXX: can we
+	 guarantee that the immediate-outer scope /is/ the fco? */
+      current_binding_level = current_binding_level->level_chain;
+
+      /*if (ENABLE_SCOPE_CHECKING)
+	--binding_depth;*/
+
+      push_deferring_access_checks (dk_deferred);
+      begin_template_parm_list ();
+    }
+
+  synth_tmpl_parm = process_template_parm (0,
+				       param_loc,
+				       synth_tmpl_parm,
+				       /*non_type=*/false,
+				       /*param_pack=*/false);
+
+  /* Re-index based on last existing parameter.  */
+  if (!become_template)
+    {
+      tree old = *handler->template_param_list;
+      size_t len = TREE_VEC_LENGTH (old);
+      size_t idx;
+      extern tree x_canonical_type_parameter (tree);
+
+      tree p = TREE_VALUE (TREE_VEC_ELT (old, --len));
+      if (TREE_CODE (p) == TYPE_DECL || TREE_CODE (p) == TEMPLATE_DECL)
+	idx = TEMPLATE_TYPE_IDX (TREE_TYPE (p));
+      else
+	idx = TEMPLATE_PARM_IDX (DECL_INITIAL (p));
+
+      ++idx;
+
+      TEMPLATE_TYPE_PARM_INDEX (TREE_TYPE (synth_id))
+	= build_template_parm_index (idx, processing_template_decl,
+				     processing_template_decl,
+				     TYPE_NAME (TREE_TYPE (synth_id)),
+				     TREE_TYPE (synth_id));
+      TYPE_CANONICAL (TREE_TYPE (synth_id)) = x_canonical_type_parameter (TREE_TYPE (synth_id));
+    }
+
+  if (become_template)
+    {
+      /* Initial template parameter, create new list.  */
+      *handler->template_param_list = end_template_parm_list (synth_tmpl_parm);
+      ++handler->parser->num_template_parameter_lists;
+      push_deferring_access_checks (dk_no_check);
+      push_binding_level (scope);
+    }
+  else /* Add to existing template parameter list.  */
+    {
+      tree old = *handler->template_param_list;
+      tree new_vec;
+      size_t len;
+
+      gcc_assert (TREE_CODE (old) == TREE_VEC);
+
+      len = TREE_VEC_LENGTH (old);
+
+      /* XXX: Maybe do doubling and store real length in handler
+	 state, there doesn't seem to be any way to free these; or
+	 rather no way to free them that tidies up the
+	 tree_node_counts and sizes.  Ideally just want a
+	 realloc_tree_vec or some such thing that would do realloc and
+	 housekeeping.  Maybe there's some reason why this
+         would be a bad thing. (?)  In fact it would be better
+         to keep building a tree list and only flatten into
+         a vector after parsing the parameter list. */
+      new_vec = make_tree_vec (len+1);
+      {
+	size_t n;
+	for (n = 0; n != len; ++n)
+	  TREE_VEC_ELT (new_vec, n) = TREE_VEC_ELT (old, n);
+      }
+      TREE_VEC_ELT (new_vec, len) = synth_tmpl_parm;
+     /* memcpy (new_vec, old, sizeof (struct tree_vec) + sizeof (tree) * (len-1));
+      TREE_VEC_LENGTH (new_vec) = ++len;
+      */
+
+      /* XXX: free old one - how?  maybe ggc_free? but how to tidy up
+       * the node counts and sizes? */
+      ggc_free (old);
+
+      *handler->template_param_list = new_vec;
+
+      TREE_VALUE (current_template_parms) = new_vec;
+    }
+
+  /* Return synthesized type as a substitute for `auto'. */
+  return TREE_TYPE (synth_id);
+}
+
 /* Parse the (optional) middle of a lambda expression.
 
    lambda-parameter-declaration:
@@ -7176,11 +7359,10 @@ cp_parser_lambda_parameter_declaration_opt (cp_parser* parser,
   tree param_list = NULL_TREE;
   tree exception_spec = NULL_TREE;
   tree template_param_list = NULL_TREE;
+  VEC (deferred_access_check,gc) *checks = 0;
 
   if (cp_lexer_next_token_is (parser->lexer, CPP_LESS))
     {
-      VEC (deferred_access_check,gc) *checks;
-    
       cp_parser_require (parser, CPP_LESS, "%<<%>");
 
       push_deferring_access_checks (dk_deferred);
@@ -7208,10 +7390,23 @@ cp_parser_lambda_parameter_declaration_opt (cp_parser* parser,
       if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN))
         {
           bool is_error = false;
+
+	  /* Set up handler for auto being used in function parameter list.  */
+	  lambda_auto_handler_t auto_handler;
+	  auto_handler.hook = (auto_parm_hook_t) lambda_auto_handler;
+	  auto_handler.template_param_list = &template_param_list;
+	  auto_handler.checks = 0;
+	  auto_handler.parser = parser;
+	  auto_handler.i = 0;
+	  auto_parm_handler = (auto_parm_handler_t*) &auto_handler;
+
           param_list = cp_parser_parameter_declaration_list (parser, &is_error);
           /* TODO: better way to handle this error?  */
           if (is_error)
             param_list = NULL_TREE;
+
+	  /* TODO: copy auto_handler.checks out */
+	  auto_parm_handler = 0;
         }
 
       cp_parser_require (parser, CPP_CLOSE_PAREN, "%<)%>");
@@ -7307,6 +7502,7 @@ cp_parser_lambda_parameter_declaration_opt (cp_parser* parser,
       {
         tree saved_current_function_decl = current_function_decl;
         pop_deferring_access_checks ();
+	/* TODO: do checks */
 
         /* Clear current function decl to allow check_member_template
            to pass.  Currently it rejects templates inside functions.
@@ -7316,16 +7512,14 @@ cp_parser_lambda_parameter_declaration_opt (cp_parser* parser,
         fco = finish_member_template_decl (fco);
         current_function_decl = saved_current_function_decl;
 
-        --parser->num_template_parameter_lists;
+	--parser->num_template_parameter_lists;
         pop_deferring_access_checks ();
         finish_template_decl (template_param_list);
       }
     finish_member_declaration (fco);
 
     LAMBDA_EXPR_FUNCTION (lambda_expr) = fco;
-
   }
-
 }
 
 /* Parse the body of a lambda expression, which is simply
@@ -14656,11 +14850,20 @@ 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);
+	{
+	  /* If there is a custom `auto' handler and the primary type
+	     of this parameter is `auto', then invoke the hook and
+	     replace `auto' with the result. */
+	  if (auto_parm_handler && is_auto (parameter->decl_specifiers.type))
+	    {
+	      parameter->decl_specifiers.type = auto_parm_handler->hook (auto_parm_handler);
+	    }
+	  decl = grokdeclarator (parameter->declarator,
+				 &parameter->decl_specifiers,
+				 PARM,
+				 parameter->default_argument != NULL_TREE,
+				 &parameter->decl_specifiers.attributes);
+	}
 
       deprecated_state = DEPRECATED_NORMAL;
 
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index ed45324..2d8380c 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -3150,6 +3150,13 @@ canonical_type_parameter (tree type)
     }
 }
 
+/* FIXME: Cleanup this mess */
+tree x_canonical_type_parameter (tree type);
+tree x_canonical_type_parameter (tree type)
+{
+  return canonical_type_parameter (type);
+}
+
 /* Return a TEMPLATE_PARM_INDEX, similar to INDEX, but whose
    TEMPLATE_PARM_LEVEL has been decreased by LEVELS.  If such a
    TEMPLATE_PARM_INDEX already exists, it is returned; otherwise, a
@@ -17480,6 +17487,7 @@ is_auto (const_tree type)
 tree
 type_uses_auto (tree type)
 {
+  /* XXX: Maybe to loop rather than recurse here? */
   enum tree_code code;
   if (is_auto (type))
     return type;
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 5f1aab0..482887c 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -5276,8 +5276,10 @@ deduce_lambda_return_type (tree lambda, tree expr)
     fco = DECL_TEMPLATE_RESULT (fco);
 
   if (type_dependent_expression_p (expr))
-    return_type = finish_decltype_type
-		   (expr, /*id_expression_or_member_access_p=*/false);
+    /* TODO: Should defer this until instantiation rather than using
+             decltype.  */
+    return_type = type_decays_to (non_reference (finish_decltype_type
+		   (expr, /*id_expression_or_member_access_p=*/false)));
   else
     return_type = type_decays_to (unlowered_expr_type (expr));
 
-- 
1.5.6.GIT

