compiler: Fix order of initialization bug with global var a, b = f().
authorIan Lance Taylor <ian@gcc.gnu.org>
Thu, 3 May 2012 16:09:25 +0000 (16:09 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Thu, 3 May 2012 16:09:25 +0000 (16:09 +0000)
From-SVN: r187103

gcc/go/gofrontend/gogo-tree.cc
gcc/go/gofrontend/gogo.cc
gcc/go/gofrontend/gogo.h
gcc/go/gofrontend/parse.cc

index 7f7323869599a8094d1a3db46d80fdeaf4e72114..9dea885985c05d121525e7a933d6ee71bca6f914 100644 (file)
@@ -590,10 +590,11 @@ Find_var::expression(Expression** pexpr)
   return TRAVERSE_CONTINUE;
 }
 
-// Return true if EXPR refers to VAR.
+// Return true if EXPR, PREINIT, or DEP refers to VAR.
 
 static bool
-expression_requires(Expression* expr, Block* preinit, Named_object* var)
+expression_requires(Expression* expr, Block* preinit, Named_object* dep,
+                   Named_object* var)
 {
   Find_var::Seen_objects seen_objects;
   Find_var find_var(var, &seen_objects);
@@ -601,7 +602,15 @@ expression_requires(Expression* expr, Block* preinit, Named_object* var)
     Expression::traverse(&expr, &find_var);
   if (preinit != NULL)
     preinit->traverse(&find_var);
-  
+  if (dep != NULL)
+    {
+      Expression* init = dep->var_value()->init();
+      if (init != NULL)
+       Expression::traverse(&init, &find_var);
+      if (dep->var_value()->has_pre_init())
+       dep->var_value()->preinit()->traverse(&find_var);
+    }
+
   return find_var.found();
 }
 
@@ -658,7 +667,7 @@ typedef std::list<Var_init> Var_inits;
 // variable V2 then we initialize V1 after V2.
 
 static void
-sort_var_inits(Var_inits* var_inits)
+sort_var_inits(Gogo* gogo, Var_inits* var_inits)
 {
   Var_inits ready;
   while (!var_inits->empty())
@@ -667,6 +676,7 @@ sort_var_inits(Var_inits* var_inits)
       Named_object* var = p1->var();
       Expression* init = var->var_value()->init();
       Block* preinit = var->var_value()->preinit();
+      Named_object* dep = gogo->var_depends_on(var->var_value());
 
       // Start walking through the list to see which variables VAR
       // needs to wait for.  We can skip P1->WAITING variables--that
@@ -678,20 +688,22 @@ sort_var_inits(Var_inits* var_inits)
 
       for (; p2 != var_inits->end(); ++p2)
        {
-         if (expression_requires(init, preinit, p2->var()))
+         Named_object* p2var = p2->var();
+         if (expression_requires(init, preinit, dep, p2var))
            {
              // Check for cycles.
-             if (expression_requires(p2->var()->var_value()->init(),
-                                     p2->var()->var_value()->preinit(),
+             if (expression_requires(p2var->var_value()->init(),
+                                     p2var->var_value()->preinit(),
+                                     gogo->var_depends_on(p2var->var_value()),
                                      var))
                {
                  error_at(var->location(),
                           ("initialization expressions for %qs and "
                            "%qs depend upon each other"),
                           var->message_name().c_str(),
-                          p2->var()->message_name().c_str());
+                          p2var->message_name().c_str());
                  inform(p2->var()->location(), "%qs defined here",
-                        p2->var()->message_name().c_str());
+                        p2var->message_name().c_str());
                  p2 = var_inits->end();
                }
              else
@@ -714,9 +726,11 @@ sort_var_inits(Var_inits* var_inits)
          // VAR does not depends upon any other initialization expressions.
 
          // Check for a loop of VAR on itself.  We only do this if
-         // INIT is not NULL; when INIT is NULL, it means that
-         // PREINIT sets VAR, which we will interpret as a loop.
-         if (init != NULL && expression_requires(init, preinit, var))
+         // INIT is not NULL and there is no dependency; when INIT is
+         // NULL, it means that PREINIT sets VAR, which we will
+         // interpret as a loop.
+         if (init != NULL && dep == NULL
+             && expression_requires(init, preinit, NULL, var))
            error_at(var->location(),
                     "initialization expression for %qs depends upon itself",
                     var->message_name().c_str());
@@ -783,7 +797,7 @@ Gogo::write_globals()
        }
 
       // There is nothing useful we can output for constants which
-      // have ideal or non-integeral type.
+      // have ideal or non-integral type.
       if (no->is_const())
        {
          Type* type = no->const_value()->type();
@@ -834,7 +848,9 @@ Gogo::write_globals()
                ;
              else if (TREE_CONSTANT(init))
                {
-                 if (expression_requires(no->var_value()->init(), NULL, no))
+                 if (expression_requires(no->var_value()->init(), NULL,
+                                         this->var_depends_on(no->var_value()),
+                                         no))
                    error_at(no->location(),
                             "initialization expression for %qs depends "
                             "upon itself",
@@ -879,6 +895,14 @@ Gogo::write_globals()
              else
                var_inits.push_back(Var_init(no, var_init_tree));
            }
+         else if (this->var_depends_on(no->var_value()) != NULL)
+           {
+             // This variable is initialized from something that is
+             // not in its init or preinit.  This variable needs to
+             // participate in dependency analysis sorting, in case
+             // some other variable depends on this one.
+             var_inits.push_back(Var_init(no, integer_zero_node));
+           }
 
          if (!is_sink && no->var_value()->type()->has_pointer())
            var_gc.push_back(no);
@@ -896,7 +920,7 @@ Gogo::write_globals()
   // workable order.
   if (!var_inits.empty())
     {
-      sort_var_inits(&var_inits);
+      sort_var_inits(this, &var_inits);
       for (Var_inits::const_iterator p = var_inits.begin();
           p != var_inits.end();
           ++p)
index 7bc0b557c9f2f7bcb56f5474ac8069eaac241714..15c814bd12bd460f007b40e164ad70f56f1ae12c 100644 (file)
@@ -32,6 +32,7 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int int_type_size,
     imported_unsafe_(false),
     packages_(),
     init_functions_(),
+    var_deps_(),
     need_init_fn_(false),
     init_fn_name_(),
     imported_init_fns_(),
@@ -3820,6 +3821,10 @@ void
 Variable::lower_init_expression(Gogo* gogo, Named_object* function,
                                Statement_inserter* inserter)
 {
+  Named_object* dep = gogo->var_depends_on(this);
+  if (dep != NULL && dep->is_variable())
+    dep->var_value()->lower_init_expression(gogo, function, inserter);
+
   if (this->init_ != NULL && !this->init_is_lowered_)
     {
       if (this->seen_)
index 9c5f8cb4a6bebe5c92b068e5b0140efeee73342c..4990bf26dc753c74e6a2881951e1543a928e1b84 100644 (file)
@@ -384,6 +384,23 @@ class Gogo
   void
   clear_file_scope();
 
+  // Record that VAR1 must be initialized after VAR2.  This is used
+  // when VAR2 does not appear in VAR1's INIT or PREINIT.
+  void
+  record_var_depends_on(Variable* var1, Named_object* var2)
+  {
+    go_assert(this->var_deps_.find(var1) == this->var_deps_.end());
+    this->var_deps_[var1] = var2;
+  }
+
+  // Return the variable that VAR depends on, or NULL if none.
+  Named_object*
+  var_depends_on(Variable* var) const
+  {
+    Var_deps::const_iterator p = this->var_deps_.find(var);
+    return p != this->var_deps_.end() ? p->second : NULL;
+  }
+
   // Queue up a type-specific function to be written out.  This is
   // used when a type-specific function is needed when not at the top
   // level.
@@ -639,8 +656,9 @@ class Gogo
   // Type used to map package names to packages.
   typedef std::map<std::string, Package*> Packages;
 
-  // Type used to map special names in the sys package.
-  typedef std::map<std::string, std::string> Sys_names;
+  // Type used to map variables to the function calls that set them.
+  // This is used for initialization dependency analysis.
+  typedef std::map<Variable*, Named_object*> Var_deps;
 
   // Type used to queue writing a type specific function.
   struct Specific_type_function
@@ -683,6 +701,10 @@ class Gogo
   Packages packages_;
   // The functions named "init", if there are any.
   std::vector<Named_object*> init_functions_;
+  // A mapping from variables to the function calls that initialize
+  // them, if it is not stored in the variable's init or preinit.
+  // This is used for dependency analysis.
+  Var_deps var_deps_;
   // Whether we need a magic initialization function.
   bool need_init_fn_;
   // The name of the magic initialization function.
index 7a567a1cd14065c3cd64b7d3ea16297024b66a43..6567a42b36f58ba6a5399b2a329c2d3c0f4a4d7b 100644 (file)
@@ -1667,6 +1667,7 @@ Parse::init_vars_from_call(const Typed_identifier_list* vars, Type* type,
   // the right number of values, but it might.  Declare the variables,
   // and then assign the results of the call to them.
 
+  Named_object* first_var = NULL;
   unsigned int index = 0;
   bool any_new = false;
   for (Typed_identifier_list::const_iterator pv = vars->begin();
@@ -1674,7 +1675,22 @@ Parse::init_vars_from_call(const Typed_identifier_list* vars, Type* type,
        ++pv, ++index)
     {
       Expression* init = Expression::make_call_result(call, index);
-      this->init_var(*pv, type, init, is_coloneq, false, &any_new);
+      Named_object* no = this->init_var(*pv, type, init, is_coloneq, false,
+                                       &any_new);
+
+      if (this->gogo_->in_global_scope() && no->is_variable())
+       {
+         if (first_var == NULL)
+           first_var = no;
+         else
+           {
+             // The subsequent vars have an implicit dependency on
+             // the first one, so that everything gets initialized in
+             // the right order and so that we detect cycles
+             // correctly.
+             this->gogo_->record_var_depends_on(no->var_value(), first_var);
+           }
+       }
     }
 
   if (is_coloneq && !any_new)