The interface between the Go frontend and the GCC middle-end sets the
return type of a function type that returns a zero-sized value to
void_type_node.  This was done to avoid confusion with libffi, for PR
72814.  However, there are various ways that Go code can correctly use
a zero-sized type returned by a function, and at least one of those
caused a compiler crash (https://golang.org/issue/22305).  This patch
adds some checks for void_type_node to the code that already checks
for a zero-sized type.  Bootstrapped and ran Go testsuite on
x86_64-pc-linux-gnu.  Committed to mainline.

Ian


2018-02-02  Ian Lance Taylor  <i...@golang.org>

* go-gcc.cc (Gcc_backend::type_size): Return 0 for
void_type_node.
(Gcc_backend::convert_expression): Don't convert if the type of
expr_tree is void_type_node.
(Gcc_backend::array_index_expression): Don't index if the type of
the array expression is void_type_node.
(Gcc_backend::init_statement): Don't initialize if the type of the
initializer expression is void_type_node.
(Gcc_backend::assignment_statement): Don't assign if the type of
either the left or right hand side is void_type_node.
(Gcc_backend::temporary_variable): Don't initialize if the type of
the initializer expression is void_type_node.
Index: go-gcc.cc
===================================================================
--- go-gcc.cc   (revision 257319)
+++ go-gcc.cc   (working copy)
@@ -1197,6 +1197,8 @@ Gcc_backend::type_size(Btype* btype)
   tree t = btype->get_tree();
   if (t == error_mark_node)
     return 1;
+  if (t == void_type_node)
+    return 0;
   t = TYPE_SIZE_UNIT(t);
   gcc_assert(tree_fits_uhwi_p (t));
   unsigned HOST_WIDE_INT val_wide = TREE_INT_CST_LOW(t);
@@ -1466,7 +1468,8 @@ Gcc_backend::convert_expression(Btype* t
     return this->error_expression();
 
   tree ret;
-  if (this->type_size(type) == 0)
+  if (this->type_size(type) == 0
+      || TREE_TYPE(expr_tree) == void_type_node)
     {
       // Do not convert zero-sized types.
       ret = expr_tree;
@@ -1894,9 +1897,18 @@ Gcc_backend::array_index_expression(Bexp
       || index_tree == error_mark_node)
     return this->error_expression();
 
-  tree ret = build4_loc(location.gcc_location(), ARRAY_REF,
-                       TREE_TYPE(TREE_TYPE(array_tree)), array_tree,
-                        index_tree, NULL_TREE, NULL_TREE);
+  // A function call that returns a zero sized object will have been
+  // changed to return void.  If we see void here, assume we are
+  // dealing with a zero sized type and just evaluate the operands.
+  tree ret;
+  if (TREE_TYPE(array_tree) != void_type_node)
+    ret = build4_loc(location.gcc_location(), ARRAY_REF,
+                    TREE_TYPE(TREE_TYPE(array_tree)), array_tree,
+                    index_tree, NULL_TREE, NULL_TREE);
+  else
+    ret = fold_build2_loc(location.gcc_location(), COMPOUND_EXPR,
+                         void_type_node, array_tree, index_tree);
+
   return this->make_expression(ret);
 }
 
@@ -2020,6 +2032,7 @@ Gcc_backend::init_statement(Bfunction*,
   // initializer.  Such initializations don't mean anything anyhow.
   if (int_size_in_bytes(TREE_TYPE(var_tree)) != 0
       && init_tree != NULL_TREE
+      && TREE_TYPE(init_tree) != void_type_node
       && int_size_in_bytes(TREE_TYPE(init_tree)) != 0)
     {
       DECL_INITIAL(var_tree) = init_tree;
@@ -2052,7 +2065,9 @@ Gcc_backend::assignment_statement(Bfunct
   // expression; avoid crashes here by avoiding assignments of
   // zero-sized expressions.  Such assignments don't really mean
   // anything anyhow.
-  if (int_size_in_bytes(TREE_TYPE(lhs_tree)) == 0
+  if (TREE_TYPE(lhs_tree) == void_type_node
+      || int_size_in_bytes(TREE_TYPE(lhs_tree)) == 0
+      || TREE_TYPE(rhs_tree) == void_type_node
       || int_size_in_bytes(TREE_TYPE(rhs_tree)) == 0)
     return this->compound_statement(this->expression_statement(bfn, lhs),
                                    this->expression_statement(bfn, rhs));
@@ -2733,7 +2748,9 @@ Gcc_backend::temporary_variable(Bfunctio
       BIND_EXPR_VARS(bind_tree) = BLOCK_VARS(block_tree);
     }
 
-  if (this->type_size(btype) != 0 && init_tree != NULL_TREE)
+  if (this->type_size(btype) != 0
+      && init_tree != NULL_TREE
+      && TREE_TYPE(init_tree) != void_type_node)
     DECL_INITIAL(var) = this->convert_tree(type_tree, init_tree, location);
 
   if (is_address_taken)
@@ -2743,9 +2760,11 @@ Gcc_backend::temporary_variable(Bfunctio
                                                 DECL_EXPR,
                                                void_type_node, var));
 
-  // Don't initialize VAR with BINIT, but still evaluate BINIT for
-  // its side effects.
-  if (this->type_size(btype) == 0 && init_tree != NULL_TREE)
+  // For a zero sized type, don't initialize VAR with BINIT, but still
+  // evaluate BINIT for its side effects.
+  if (init_tree != NULL_TREE
+      && (this->type_size(btype) == 0
+         || TREE_TYPE(init_tree) == void_type_node))
     *pstatement =
       this->compound_statement(this->expression_statement(function, binit),
                               *pstatement);

Reply via email to