diff mbox series

Go patch committed: Check repeated const expressions in new scop

Message ID CAOyqgcUz0E1kmy=DND3tW3eC5G_CC80SFGm9Q9DJJy4ON0wDvA@mail.gmail.com
State New
Headers show
Series Go patch committed: Check repeated const expressions in new scop | expand

Commit Message

Ian Lance Taylor June 29, 2022, 10:18 p.m. UTC
This patch to the Go frontend checks repeated const expressions in new
scope, in case they refer to a newly defined name.  The test case is
const8.go in https://go.dev/cl/414795.  This fixes
https://go.dev/issue/53585.  Bootstrapped and ran Go testsuite on
x86_64-pc-linux-gnu.  Committed to mainline.

Ian
dabc73c20057d016027fd1af22575e2325cad9ef
diff mbox series

Patch

diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE
index 13cb6ea4046..4fde25af76e 100644
--- a/gcc/go/gofrontend/MERGE
+++ b/gcc/go/gofrontend/MERGE
@@ -1,4 +1,4 @@ 
-c7238f58a26131b7611eff6f555cab02af8a623c
+63782f8a318e9eebfdc983f171a920c7a937c759
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc
index aadca9710e6..00d35a965a9 100644
--- a/gcc/go/gofrontend/expressions.cc
+++ b/gcc/go/gofrontend/expressions.cc
@@ -3352,97 +3352,7 @@  class Find_named_object : public Traverse
   bool found_;
 };
 
-// A reference to a const in an expression.
-
-class Const_expression : public Expression
-{
- public:
-  Const_expression(Named_object* constant, Location location)
-    : Expression(EXPRESSION_CONST_REFERENCE, location),
-      constant_(constant), type_(NULL), seen_(false)
-  { }
-
-  Named_object*
-  named_object()
-  { return this->constant_; }
-
-  const Named_object*
-  named_object() const
-  { return this->constant_; }
-
-  // Check that the initializer does not refer to the constant itself.
-  void
-  check_for_init_loop();
-
- protected:
-  int
-  do_traverse(Traverse*);
-
-  Expression*
-  do_lower(Gogo*, Named_object*, Statement_inserter*, int);
-
-  bool
-  do_is_constant() const
-  { return true; }
-
-  bool
-  do_is_zero_value() const
-  { return this->constant_->const_value()->expr()->is_zero_value(); }
-
-  bool
-  do_is_static_initializer() const
-  { return true; }
-
-  bool
-  do_numeric_constant_value(Numeric_constant* nc) const;
-
-  bool
-  do_string_constant_value(std::string* val) const;
-
-  bool
-  do_boolean_constant_value(bool* val) const;
-
-  Type*
-  do_type();
-
-  // The type of a const is set by the declaration, not the use.
-  void
-  do_determine_type(const Type_context*);
-
-  void
-  do_check_types(Gogo*);
-
-  Expression*
-  do_copy()
-  { return this; }
-
-  Bexpression*
-  do_get_backend(Translate_context* context);
-
-  int
-  do_inlining_cost() const
-  { return 1; }
-
-  // When exporting a reference to a const as part of a const
-  // expression, we export the value.  We ignore the fact that it has
-  // a name.
-  void
-  do_export(Export_function_body* efb) const
-  { this->constant_->const_value()->expr()->export_expression(efb); }
-
-  void
-  do_dump_expression(Ast_dump_context*) const;
-
- private:
-  // The constant.
-  Named_object* constant_;
-  // The type of this reference.  This is used if the constant has an
-  // abstract type.
-  Type* type_;
-  // Used to prevent infinite recursion when a constant incorrectly
-  // refers to itself.
-  mutable bool seen_;
-};
+// Class Const_expression.
 
 // Traversal.
 
@@ -3454,6 +3364,14 @@  Const_expression::do_traverse(Traverse* traverse)
   return TRAVERSE_CONTINUE;
 }
 
+// Whether this is the zero value.
+
+bool
+Const_expression::do_is_zero_value() const
+{
+  return this->constant_->const_value()->expr()->is_zero_value();
+}
+
 // Lower a constant expression.  This is where we convert the
 // predeclared constant iota into an integer value.
 
@@ -3708,6 +3626,16 @@  Const_expression::do_get_backend(Translate_context* context)
   return expr->get_backend(context);
 }
 
+// When exporting a reference to a const as part of a const
+// expression, we export the value.  We ignore the fact that it has
+// a name.
+
+void
+Const_expression::do_export(Export_function_body* efb) const
+{
+  this->constant_->const_value()->expr()->export_expression(efb);
+}
+
 // Dump ast representation for constant expression.
 
 void
diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h
index 707c19336d8..a1e3733aa1d 100644
--- a/gcc/go/gofrontend/expressions.h
+++ b/gcc/go/gofrontend/expressions.h
@@ -28,6 +28,7 @@  class Map_type;
 class Struct_type;
 class Struct_field;
 class Expression_list;
+class Const_expression;
 class Var_expression;
 class Enclosed_var_expression;
 class Temporary_reference_expression;
@@ -626,6 +627,20 @@  class Expression
   is_type_expression() const
   { return this->classification_ == EXPRESSION_TYPE; }
 
+  // If this is a const reference, return the Const_expression
+  // structure.  Otherwise, return NULL.  This is a controlled dynamic
+  // cast.
+  Const_expression*
+  const_expression()
+  { return this->convert<Const_expression, EXPRESSION_CONST_REFERENCE>(); }
+
+  const Const_expression*
+  const_expression() const
+  {
+    return this->convert<const Const_expression,
+			 EXPRESSION_CONST_REFERENCE>();
+  }
+
   // If this is a variable reference, return the Var_expression
   // structure.  Otherwise, return NULL.  This is a controlled dynamic
   // cast.
@@ -1453,6 +1468,96 @@  class Parser_expression : public Expression
   { go_unreachable(); }
 };
 
+// A reference to a const in an expression.
+
+class Const_expression : public Expression
+{
+ public:
+  Const_expression(Named_object* constant, Location location)
+    : Expression(EXPRESSION_CONST_REFERENCE, location),
+      constant_(constant), type_(NULL), seen_(false)
+  { }
+
+  Named_object*
+  named_object()
+  { return this->constant_; }
+
+  const Named_object*
+  named_object() const
+  { return this->constant_; }
+
+  // Check that the initializer does not refer to the constant itself.
+  void
+  check_for_init_loop();
+
+ protected:
+  int
+  do_traverse(Traverse*);
+
+  Expression*
+  do_lower(Gogo*, Named_object*, Statement_inserter*, int);
+
+  bool
+  do_is_constant() const
+  { return true; }
+
+  bool
+  do_is_zero_value() const;
+
+  bool
+  do_is_static_initializer() const
+  { return true; }
+
+  bool
+  do_numeric_constant_value(Numeric_constant* nc) const;
+
+  bool
+  do_string_constant_value(std::string* val) const;
+
+  bool
+  do_boolean_constant_value(bool* val) const;
+
+  Type*
+  do_type();
+
+  // The type of a const is set by the declaration, not the use.
+  void
+  do_determine_type(const Type_context*);
+
+  void
+  do_check_types(Gogo*);
+
+  Expression*
+  do_copy()
+  { return this; }
+
+  Bexpression*
+  do_get_backend(Translate_context* context);
+
+  int
+  do_inlining_cost() const
+  { return 1; }
+
+  // When exporting a reference to a const as part of a const
+  // expression, we export the value.  We ignore the fact that it has
+  // a name.
+  void
+  do_export(Export_function_body* efb) const;
+
+  void
+  do_dump_expression(Ast_dump_context*) const;
+
+ private:
+  // The constant.
+  Named_object* constant_;
+  // The type of this reference.  This is used if the constant has an
+  // abstract type.
+  Type* type_;
+  // Used to prevent infinite recursion when a constant incorrectly
+  // refers to itself.
+  mutable bool seen_;
+};
+
 // An expression which is simply a variable.
 
 class Var_expression : public Expression
diff --git a/gcc/go/gofrontend/parse.cc b/gcc/go/gofrontend/parse.cc
index cc197e5eb35..e388261f494 100644
--- a/gcc/go/gofrontend/parse.cc
+++ b/gcc/go/gofrontend/parse.cc
@@ -1468,6 +1468,7 @@  Parse::const_spec(int iota, Type** last_type, Expression_list** last_expr_list)
 	{
 	  Expression* copy = (*p)->copy();
 	  copy->set_location(loc);
+	  this->update_references(&copy);
 	  expr_list->push_back(copy);
 	}
     }
@@ -1513,6 +1514,94 @@  Parse::const_spec(int iota, Type** last_type, Expression_list** last_expr_list)
   return;
 }
 
+// Update any references to names to refer to the current names,
+// for weird cases like
+//
+// const X = 1
+// func F() {
+// 	const (
+// 		X = X + X
+//		Y
+// 	)
+// }
+//
+// where the X + X for the first X is the outer X, but the X + X
+// copied for Y is the inner X.
+
+class Update_references : public Traverse
+{
+ public:
+  Update_references(Gogo* gogo)
+    : Traverse(traverse_expressions),
+      gogo_(gogo)
+  { }
+
+  int
+  expression(Expression**);
+
+ private:
+  Gogo* gogo_;
+};
+
+int
+Update_references::expression(Expression** pexpr)
+{
+  Named_object* old_no;
+  switch ((*pexpr)->classification())
+    {
+    case Expression::EXPRESSION_CONST_REFERENCE:
+      old_no = (*pexpr)->const_expression()->named_object();
+      break;
+    case Expression::EXPRESSION_VAR_REFERENCE:
+      old_no = (*pexpr)->var_expression()->named_object();
+      break;
+    case Expression::EXPRESSION_ENCLOSED_VAR_REFERENCE:
+      old_no = (*pexpr)->enclosed_var_expression()->variable();
+      break;
+    case Expression::EXPRESSION_FUNC_REFERENCE:
+      old_no = (*pexpr)->func_expression()->named_object();
+      break;
+    case Expression::EXPRESSION_UNKNOWN_REFERENCE:
+      old_no = (*pexpr)->unknown_expression()->named_object();
+      break;
+    default:
+      return TRAVERSE_CONTINUE;
+    }
+
+  if (old_no->package() != NULL)
+    {
+      // This is a qualified reference, so it can't have changed in
+      // scope.  FIXME: This probably doesn't handle dot imports
+      // correctly.
+      return TRAVERSE_CONTINUE;
+    }
+
+  Named_object* in_function;
+  Named_object* new_no = this->gogo_->lookup(old_no->name(), &in_function);
+  if (new_no == old_no)
+    return TRAVERSE_CONTINUE;
+
+  // The new name must be a constant, since that is all we have
+  // introduced into scope.
+  if (!new_no->is_const())
+    {
+      go_assert(saw_errors());
+      return TRAVERSE_CONTINUE;
+    }
+
+  *pexpr = Expression::make_const_reference(new_no, (*pexpr)->location());
+
+  return TRAVERSE_CONTINUE;
+}
+
+void
+Parse::update_references(Expression** pexpr)
+{
+  Update_references ur(this->gogo_);
+  ur.expression(pexpr);
+  (*pexpr)->traverse_subexpressions(&ur);
+}
+
 // TypeDecl = "type" Decl<TypeSpec> .
 
 void
diff --git a/gcc/go/gofrontend/parse.h b/gcc/go/gofrontend/parse.h
index 6e300ef800c..cda0beefebc 100644
--- a/gcc/go/gofrontend/parse.h
+++ b/gcc/go/gofrontend/parse.h
@@ -185,6 +185,7 @@  class Parse
   void list(void (Parse::*)(), bool);
   void const_decl();
   void const_spec(int, Type**, Expression_list**);
+  void update_references(Expression**);
   void type_decl();
   void type_spec();
   void var_decl();