[PR,c++/84263] GC ICE with decltype

Message ID a134dc6d-4209-b7e6-0acb-4b8883486493@acm.org
State New
Headers show
Series
  • [PR,c++/84263] GC ICE with decltype
Related show

Commit Message

Nathan Sidwell Feb. 8, 2018, 4:29 p.m.
This patch fixes 84263, a GC breakage entirely unconnected with the 
patch of mine that exposed it.  I guess I perturbed the memory layout 
sufficiently to tickle it -- on a 32bit host.

The underlying problem is that decltype parsing has to squirrel away 
some data in the same manner to template-ids.  But it was failing to 
push and pop the deferring access stack.  That meant it stashed its own 
deferred accesses, but they also remained on the stack.  A subsequent 
deferred access could reallocate the vector of accesses there, if a 
subsequent access needed deferring.  That reallocation proceeds by 
gc_alloc/gc_free.  The gc_free frees the original set, which is what we 
had stashed away.  All is well until we try and GC things, with that 
token still live.  Boom!

Fixed by pushing and popping the access stack.  I also reorganized the 
code to use the modern construct of 'if ... else', rather the 
assembler-level use of a goto!

Martin, I've bootstrapped this on an x86_64 native setup, but i686 
native is tricky.  Are you able to give this a spin?  I modified the 
testcase slightly -- replacing an 'int' with '__SIZE_TYPE__'.

nathan

Patch

2018-02-08  Nathan Sidwell  <nathan@acm.org>

	PR c++/84263
	* parser.c (cp_parser_decltype): Push and pop
	deferring_access_checks.  Reorganize to avoid goto.

	* g++.dg/parse/pr84263.C: New.

Index: cp/parser.c
===================================================================
--- cp/parser.c	(revision 257496)
+++ cp/parser.c	(working copy)
@@ -14049,12 +14049,7 @@  cp_parser_decltype_expr (cp_parser *pars
 static tree
 cp_parser_decltype (cp_parser *parser)
 {
-  tree expr;
   bool id_expression_or_member_access_p = false;
-  const char *saved_message;
-  bool saved_integral_constant_expression_p;
-  bool saved_non_integral_constant_expression_p;
-  bool saved_greater_than_is_operator_p;
   cp_token *start_token = cp_lexer_peek_token (parser->lexer);
 
   if (start_token->type == CPP_DECLTYPE)
@@ -14073,77 +14068,83 @@  cp_parser_decltype (cp_parser *parser)
   if (!parens.require_open (parser))
     return error_mark_node;
 
-  /* decltype (auto) */
+  push_deferring_access_checks (dk_deferred);
+
+  tree expr = NULL_TREE;
+  
   if (cxx_dialect >= cxx14
       && cp_lexer_next_token_is_keyword (parser->lexer, RID_AUTO))
+    /* decltype (auto) */
+    cp_lexer_consume_token (parser->lexer);
+  else
     {
-      cp_lexer_consume_token (parser->lexer);
-      if (!parens.require_close (parser))
-	return error_mark_node;
-      expr = make_decltype_auto ();
-      AUTO_IS_DECLTYPE (expr) = true;
-      goto rewrite;
-    }
+      /* decltype (expression)  */
 
-  /* Types cannot be defined in a `decltype' expression.  Save away the
-     old message.  */
-  saved_message = parser->type_definition_forbidden_message;
-
-  /* And create the new one.  */
-  parser->type_definition_forbidden_message
-    = G_("types may not be defined in %<decltype%> expressions");
-
-  /* The restrictions on constant-expressions do not apply inside
-     decltype expressions.  */
-  saved_integral_constant_expression_p
-    = parser->integral_constant_expression_p;
-  saved_non_integral_constant_expression_p
-    = parser->non_integral_constant_expression_p;
-  parser->integral_constant_expression_p = false;
-
-  /* Within a parenthesized expression, a `>' token is always
-     the greater-than operator.  */
-  saved_greater_than_is_operator_p
-    = parser->greater_than_is_operator_p;
-  parser->greater_than_is_operator_p = true;
-
-  /* Do not actually evaluate the expression.  */
-  ++cp_unevaluated_operand;
-
-  /* Do not warn about problems with the expression.  */
-  ++c_inhibit_evaluation_warnings;
-
-  expr = cp_parser_decltype_expr (parser, id_expression_or_member_access_p);
-
-  /* Go back to evaluating expressions.  */
-  --cp_unevaluated_operand;
-  --c_inhibit_evaluation_warnings;
-
-  /* The `>' token might be the end of a template-id or
-     template-parameter-list now.  */
-  parser->greater_than_is_operator_p
-    = saved_greater_than_is_operator_p;
-
-  /* Restore the old message and the integral constant expression
-     flags.  */
-  parser->type_definition_forbidden_message = saved_message;
-  parser->integral_constant_expression_p
-    = saved_integral_constant_expression_p;
-  parser->non_integral_constant_expression_p
-    = saved_non_integral_constant_expression_p;
+      /* Types cannot be defined in a `decltype' expression.  Save away the
+	 old message and set the new one.  */
+      const char *saved_message = parser->type_definition_forbidden_message;
+      parser->type_definition_forbidden_message
+	= G_("types may not be defined in %<decltype%> expressions");
+
+      /* The restrictions on constant-expressions do not apply inside
+	 decltype expressions.  */
+      bool saved_integral_constant_expression_p
+	= parser->integral_constant_expression_p;
+      bool saved_non_integral_constant_expression_p
+	= parser->non_integral_constant_expression_p;
+      parser->integral_constant_expression_p = false;
+
+      /* Within a parenthesized expression, a `>' token is always
+	 the greater-than operator.  */
+      bool saved_greater_than_is_operator_p
+	= parser->greater_than_is_operator_p;
+      parser->greater_than_is_operator_p = true;
+
+      /* Do not actually evaluate the expression.  */
+      ++cp_unevaluated_operand;
+
+      /* Do not warn about problems with the expression.  */
+      ++c_inhibit_evaluation_warnings;
+
+      expr = cp_parser_decltype_expr (parser, id_expression_or_member_access_p);
+
+      /* Go back to evaluating expressions.  */
+      --cp_unevaluated_operand;
+      --c_inhibit_evaluation_warnings;
+
+      /* The `>' token might be the end of a template-id or
+	 template-parameter-list now.  */
+      parser->greater_than_is_operator_p
+	= saved_greater_than_is_operator_p;
+
+      /* Restore the old message and the integral constant expression
+	 flags.  */
+      parser->type_definition_forbidden_message = saved_message;
+      parser->integral_constant_expression_p
+	= saved_integral_constant_expression_p;
+      parser->non_integral_constant_expression_p
+	= saved_non_integral_constant_expression_p;
+    }
 
   /* Parse to the closing `)'.  */
   if (!parens.require_close (parser))
     {
       cp_parser_skip_to_closing_parenthesis (parser, true, false,
 					     /*consume_paren=*/true);
+      pop_deferring_access_checks ();
       return error_mark_node;
     }
 
-  expr = finish_decltype_type (expr, id_expression_or_member_access_p,
-			       tf_warning_or_error);
+  if (!expr)
+    {
+      /* Build auto.  */
+      expr = make_decltype_auto ();
+      AUTO_IS_DECLTYPE (expr) = true;
+    }
+  else
+    expr = finish_decltype_type (expr, id_expression_or_member_access_p,
+				 tf_warning_or_error);
 
- rewrite:
   /* Replace the decltype with a CPP_DECLTYPE so we don't need to parse
      it again.  */
   start_token->type = CPP_DECLTYPE;
@@ -14153,6 +14154,8 @@  cp_parser_decltype (cp_parser *parser)
   start_token->keyword = RID_MAX;
   cp_lexer_purge_tokens_after (parser->lexer, start_token);
 
+  pop_to_parent_deferring_access_checks ();
+  
   return expr;
 }
 
Index: testsuite/g++.dg/parse/pr84263.C
===================================================================
--- testsuite/g++.dg/parse/pr84263.C	(revision 0)
+++ testsuite/g++.dg/parse/pr84263.C	(working copy)
@@ -0,0 +1,36 @@ 
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "--param ggc-min-expand=0 --param ggc-min-heapsize=0" }
+// PR 84263, a GC bug exposed on i686 native compiler (and possibly
+// other 32-bit hosts).  decltype parsing could create a 
+// pointer that would be gc-freed by later actions.
+
+namespace std {
+template <typename a> struct b {
+  int c;
+  a d;
+};
+template <typename> class g;
+template <class> class initializer_list {
+  void *e;
+  __SIZE_TYPE__ f;
+};
+class h;
+class j {
+  typedef b<h> i;
+
+public:
+  j();
+  j(initializer_list<i>);
+};
+template <typename> struct m;
+template <int k> struct m<char[k]> {};
+class h {
+public:
+  template <typename l> h(l &);
+};
+class G {
+  G();
+  j n;
+};
+G::G() { n = decltype(n){{0, ""}, {1, ".unoLineArrowEnd"}}; }
+}