Patchwork Go patch committed: Don't crash on recursive consts

login
register
mail settings
Submitter Ian Taylor
Date Dec. 16, 2010, 4:47 a.m.
Message ID <mcrk4jaxr1j.fsf@google.com>
Download mbox | patch
Permalink /patch/75718/
State New
Headers show

Comments

Ian Taylor - Dec. 16, 2010, 4:47 a.m.
In an invalid Go program a const declared at top-level can refer to
itself, as in

const A = A * 2

The gccgo frontend used to go into an infinite loop when it saw this.
This fixes it.  Bootstrapped and tested on x86_64-unknown-linux-gnu.
Committed to mainline.

Ian

Patch

diff -r d92be5fbf69b go/expressions.cc
--- a/go/expressions.cc	Wed Dec 15 18:22:49 2010 -0800
+++ b/go/expressions.cc	Wed Dec 15 20:42:23 2010 -0800
@@ -2292,7 +2292,7 @@ 
  public:
   Const_expression(Named_object* constant, source_location location)
     : Expression(EXPRESSION_CONST_REFERENCE, location),
-      constant_(constant), type_(NULL)
+      constant_(constant), type_(NULL), seen_(false)
   { }
 
   const std::string&
@@ -2350,6 +2350,9 @@ 
   // 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_;
 };
 
 // Lower a constant expression.  This is where we convert the
@@ -2387,6 +2390,9 @@ 
 Const_expression::do_integer_constant_value(bool iota_is_constant, mpz_t val,
 					    Type** ptype) const
 {
+  if (this->seen_)
+    return false;
+
   Type* ctype;
   if (this->type_ != NULL)
     ctype = this->type_;
@@ -2396,9 +2402,14 @@ 
     return false;
 
   Expression* e = this->constant_->const_value()->expr();
+
+  this->seen_ = true;
+
   Type* t;
   bool r = e->integer_constant_value(iota_is_constant, val, &t);
 
+  this->seen_ = false;
+
   if (r
       && ctype != NULL
       && !Integer_expression::check_constant(val, ctype, this->location()))
@@ -2413,6 +2424,9 @@ 
 bool
 Const_expression::do_float_constant_value(mpfr_t val, Type** ptype) const
 {
+  if (this->seen_)
+    return false;
+
   Type* ctype;
   if (this->type_ != NULL)
     ctype = this->type_;
@@ -2421,9 +2435,14 @@ 
   if (ctype != NULL && ctype->float_type() == NULL)
     return false;
 
+  this->seen_ = true;
+
   Type* t;
   bool r = this->constant_->const_value()->expr()->float_constant_value(val,
 									&t);
+
+  this->seen_ = false;
+
   if (r && ctype != NULL)
     {
       if (!Float_expression::check_constant(val, ctype, this->location()))
@@ -2440,6 +2459,9 @@ 
 Const_expression::do_complex_constant_value(mpfr_t real, mpfr_t imag,
 					    Type **ptype) const
 {
+  if (this->seen_)
+    return false;
+
   Type* ctype;
   if (this->type_ != NULL)
     ctype = this->type_;
@@ -2448,10 +2470,15 @@ 
   if (ctype != NULL && ctype->complex_type() == NULL)
     return false;
 
+  this->seen_ = true;
+
   Type *t;
   bool r = this->constant_->const_value()->expr()->complex_constant_value(real,
 									  imag,
 									  &t);
+
+  this->seen_ = false;
+
   if (r && ctype != NULL)
     {
       if (!Complex_expression::check_constant(real, imag, ctype,
@@ -2470,13 +2497,32 @@ 
 {
   if (this->type_ != NULL)
     return this->type_;
+
+  if (this->seen_)
+    {
+      this->report_error(_("constant refers to itself"));
+      this->type_ = Type::make_error_type();
+      return this->type_;
+    }
+
+  this->seen_ = true;
+
   Named_constant* nc = this->constant_->const_value();
   Type* ret = nc->type();
+
   if (ret != NULL)
-    return ret;
+    {
+      this->seen_ = false;
+      return ret;
+    }
+
   // During parsing, a named constant may have a NULL type, but we
   // must not return a NULL type here.
-  return nc->expr()->type();
+  ret = nc->expr()->type();
+
+  this->seen_ = false;
+
+  return ret;
 }
 
 // Set the type of the const reference.
diff -r d92be5fbf69b go/gogo.cc
--- a/go/gogo.cc	Wed Dec 15 18:22:49 2010 -0800
+++ b/go/gogo.cc	Wed Dec 15 20:42:23 2010 -0800
@@ -1472,7 +1472,8 @@ 
       && !ctype->is_boolean_type()
       && !ctype->is_string_type())
     {
-      error_at(constant->location(), "invalid constant type");
+      if (!ctype->is_error_type())
+	error_at(constant->location(), "invalid constant type");
       constant->set_error();
     }
   else if (!constant->expr()->is_constant())