compiler: implement Go 1.1 spec of terminating statements.
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 12 Jun 2013 23:49:32 +0000 (23:49 +0000)
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 12 Jun 2013 23:49:32 +0000 (23:49 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@200047 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/go/gofrontend/go.cc
gcc/go/gofrontend/statements.cc
gcc/go/gofrontend/statements.h

index 11692af..c0b3fef 100644 (file)
@@ -44,7 +44,7 @@ go_create_gogo(int int_type_size, int pointer_size, const char *pkgpath,
 GO_EXTERN_C
 void
 go_parse_input_files(const char** filenames, unsigned int filename_count,
-                    bool only_check_syntax, bool require_return_statement)
+                    bool only_check_syntax, bool)
 {
   go_assert(filename_count > 0);
 
@@ -84,6 +84,9 @@ go_parse_input_files(const char** filenames, unsigned int filename_count,
   // Finalize method lists and build stub methods for named types.
   ::gogo->finalize_methods();
 
+  // Check that functions have a terminating statement.
+  ::gogo->check_return_statements();
+
   // Now that we have seen all the names, lower the parse tree into a
   // form which is easier to use.
   ::gogo->lower_parse_tree();
@@ -104,10 +107,6 @@ go_parse_input_files(const char** filenames, unsigned int filename_count,
   if (only_check_syntax)
     return;
 
-  // Check that functions have return statements.
-  if (require_return_statement)
-    ::gogo->check_return_statements();
-
   // Export global identifiers as appropriate.
   ::gogo->do_exports();
 
index 7870dad..c9c62ce 100644 (file)
@@ -1707,8 +1707,8 @@ Expression_statement::do_check_types(Gogo*)
     this->expr_->discarding_value();
 }
 
-// An expression statement may fall through unless it is a call to a
-// function which does not return.
+// An expression statement is only a terminating statement if it is
+// a call to panic.
 
 bool
 Expression_statement::do_may_fall_through() const
@@ -1717,22 +1717,28 @@ Expression_statement::do_may_fall_through() const
   if (call != NULL)
     {
       const Expression* fn = call->fn();
-      const Func_expression* fe = fn->func_expression();
-      if (fe != NULL)
+      // panic is still an unknown named object.
+      const Unknown_expression* ue = fn->unknown_expression();
+      if (ue != NULL)
        {
-         const Named_object* no = fe->named_object();
+         Named_object* no = ue->named_object();
 
-         Function_type* fntype;
-         if (no->is_function())
-           fntype = no->func_value()->type();
-         else if (no->is_function_declaration())
-           fntype = no->func_declaration_value()->type();
-         else
-           fntype = NULL;
-
-         // The builtin function panic does not return.
-         if (fntype != NULL && fntype->is_builtin() && no->name() == "panic")
-           return false;
+          if (no->is_unknown())
+            no = no->unknown_value()->real_named_object();
+          if (no != NULL)
+            {
+              Function_type* fntype;
+              if (no->is_function())
+                fntype = no->func_value()->type();
+              else if (no->is_function_declaration())
+                fntype = no->func_declaration_value()->type();
+              else
+                fntype = NULL;
+
+              // The builtin function panic does not return.
+              if (fntype != NULL && fntype->is_builtin() && no->name() == "panic")
+                return false;
+            }
        }
     }
   return true;
@@ -3700,9 +3706,6 @@ class Constant_switch_statement : public Statement
   void
   do_check_types(Gogo*);
 
-  bool
-  do_may_fall_through() const;
-
   Bstatement*
   do_get_backend(Translate_context*);
 
@@ -3746,22 +3749,6 @@ Constant_switch_statement::do_check_types(Gogo*)
     this->set_is_error();
 }
 
-// Return whether this switch may fall through.
-
-bool
-Constant_switch_statement::do_may_fall_through() const
-{
-  if (this->clauses_ == NULL)
-    return true;
-
-  // If we have a break label, then some case needed it.  That implies
-  // that the switch statement as a whole can fall through.
-  if (this->break_label_ != NULL)
-    return true;
-
-  return this->clauses_->may_fall_through();
-}
-
 // Convert to GENERIC.
 
 Bstatement*
@@ -3911,6 +3898,22 @@ Switch_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const
   ast_dump_context->ostream() << std::endl;
 }
 
+// Return whether this switch may fall through.
+
+bool
+Switch_statement::do_may_fall_through() const
+{
+  if (this->clauses_ == NULL)
+    return true;
+
+  // If we have a break label, then some case needed it.  That implies
+  // that the switch statement as a whole can fall through.
+  if (this->break_label_ != NULL)
+    return true;
+
+  return this->clauses_->may_fall_through();
+}
+
 // Make a switch statement.
 
 Switch_statement*
@@ -4050,6 +4053,17 @@ Type_case_clauses::Type_case_clause::lower(Type* switch_val_type,
     }
 }
 
+// Return true if this type clause may fall through to the statements
+// following the switch.
+
+bool
+Type_case_clauses::Type_case_clause::may_fall_through() const
+{
+  if (this->statements_ == NULL)
+    return true;
+  return this->statements_->may_fall_through();
+}
+
 // Dump the AST representation for a type case clause
 
 void
@@ -4148,6 +4162,25 @@ Type_case_clauses::lower(Type* switch_val_type, Block* b,
                        NULL);
 }
 
+// Return true if these clauses may fall through to the statements
+// following the switch statement.
+
+bool
+Type_case_clauses::may_fall_through() const
+{
+  bool found_default = false;
+  for (Type_clauses::const_iterator p = this->clauses_.begin();
+       p != this->clauses_.end();
+       ++p)
+    {
+      if (p->may_fall_through())
+       return true;
+      if (p->is_default())
+       found_default = true;
+    }
+  return !found_default;
+}
+
 // Dump the AST representation for case clauses (from a switch statement)
 
 void
@@ -4237,6 +4270,22 @@ Type_switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
   return Statement::make_block_statement(b, loc);
 }
 
+// Return whether this switch may fall through.
+
+bool
+Type_switch_statement::do_may_fall_through() const
+{
+  if (this->clauses_ == NULL)
+    return true;
+
+  // If we have a break label, then some case needed it.  That implies
+  // that the switch statement as a whole can fall through.
+  if (this->break_label_ != NULL)
+    return true;
+
+  return this->clauses_->may_fall_through();
+}
+
 // Return the break label for this type switch statement, creating it
 // if necessary.
 
@@ -4954,6 +5003,19 @@ Select_statement::do_lower(Gogo* gogo, Named_object* function,
   return Statement::make_block_statement(b, loc);
 }
 
+// Whether the select statement itself may fall through to the following
+// statement.
+
+bool
+Select_statement::do_may_fall_through() const
+{
+  // A select statement is terminating if no break statement
+  // refers to it and all of its clauses are terminating.
+  if (this->break_label_ != NULL)
+    return true;
+  return this->clauses_->may_fall_through();
+}
+
 // Return the backend representation for a select statement.
 
 Bstatement*
@@ -5114,6 +5176,20 @@ For_statement::set_break_continue_labels(Unnamed_label* break_label,
   this->continue_label_ = continue_label;
 }
 
+// Whether the overall statement may fall through.
+
+bool
+For_statement::do_may_fall_through() const
+{
+  // A for loop is terminating if it has no condition and
+  // no break statement.
+  if(this->cond_ != NULL)
+    return true;
+  if(this->break_label_ != NULL)
+    return true;
+  return false;
+}
+
 // Dump the AST representation for a for statement.
 
 void
index c599571..ee5be87 100644 (file)
@@ -894,8 +894,7 @@ class Select_statement : public Statement
   { this->clauses_->check_types(); }
 
   bool
-  do_may_fall_through() const
-  { return this->clauses_->may_fall_through(); }
+  do_may_fall_through() const;
 
   Bstatement*
   do_get_backend(Translate_context*);
@@ -1086,6 +1085,9 @@ class For_statement : public Statement
   Statement*
   do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
 
+  bool
+  do_may_fall_through() const;
+
   Bstatement*
   do_get_backend(Translate_context*)
   { go_unreachable(); }
@@ -1399,6 +1401,9 @@ class Switch_statement : public Statement
   void
   do_dump_statement(Ast_dump_context*) const;
 
+  bool
+  do_may_fall_through() const;
+
  private:
   // The value to switch on.  This may be NULL.
   Expression* val_;
@@ -1449,6 +1454,11 @@ class Type_case_clauses
   lower(Type*, Block*, Temporary_statement* descriptor_temp,
        Unnamed_label* break_label) const;
 
+  // Return true if these clauses may fall through to the statements
+  // following the switch statement.
+  bool
+  may_fall_through() const;
+
   // Dump the AST representation to a dump context.
   void
   dump_clauses(Ast_dump_context*) const;
@@ -1493,6 +1503,12 @@ class Type_case_clauses
     lower(Type*, Block*, Temporary_statement* descriptor_temp,
          Unnamed_label* break_label, Unnamed_label** stmts_label) const;
 
+    // Return true if this clause may fall through to execute the
+    // statements following the switch statement.  This is not the
+    // same as whether this clause falls through to the next clause.
+    bool
+    may_fall_through() const;
+
     // Dump the AST representation to a dump context.
     void
     dump_clause(Ast_dump_context*) const;
@@ -1556,6 +1572,9 @@ class Type_switch_statement : public Statement
   void
   do_dump_statement(Ast_dump_context*) const;
 
+  bool
+  do_may_fall_through() const;
+
  private:
   // The variable holding the value we are switching on.
   Named_object* var_;