compiler: change expression importing to use Import_expression

Change expression importing to use a new abstract interface class
Import_expression, so that we can more easily import expressions from
inlinable function bodies.  This is a refactoring with no affect on
compiler behavior.

Change-Id: I9178ad6b2272f9abad540576fcff72a746d1e5bc
Reviewed-on: https://go-review.googlesource.com/c/150065
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
diff --git a/go/expressions.cc b/go/expressions.cc
index 391eb66..9292b5c 100644
--- a/go/expressions.cc
+++ b/go/expressions.cc
@@ -1583,7 +1583,7 @@
   { }
 
   static Expression*
-  do_import(Import*, Location);
+  do_import(Import_expression*, Location);
 
  protected:
   bool
@@ -1649,7 +1649,7 @@
 // Import a boolean constant.
 
 Expression*
-Boolean_expression::do_import(Import* imp, Location loc)
+Boolean_expression::do_import(Import_expression* imp, Location loc)
 {
   if (imp->peek_char() == 't')
     {
@@ -1768,7 +1768,7 @@
 // Import a string expression.
 
 Expression*
-String_expression::do_import(Import* imp, Location loc)
+String_expression::do_import(Import_expression* imp, Location loc)
 {
   imp->require_c_string("\"");
   std::string val;
@@ -1944,7 +1944,7 @@
   { mpz_init_set(this->val_, *val); }
 
   static Expression*
-  do_import(Import*, Location);
+  do_import(Import_expression*, Location);
 
   // Write VAL to string dump.
   static void
@@ -2151,7 +2151,7 @@
 // all these types because they all start with digits.
 
 Expression*
-Integer_expression::do_import(Import* imp, Location loc)
+Integer_expression::do_import(Import_expression* imp, Location loc)
 {
   std::string num = imp->read_identifier();
   imp->require_c_string(" ");
@@ -3133,7 +3133,7 @@
   { }
 
   static Expression*
-  do_import(Import*, Location);
+  do_import(Import_expression*, Location);
 
  protected:
   bool
@@ -3172,7 +3172,7 @@
 // Import a nil expression.
 
 Expression*
-Nil_expression::do_import(Import* imp, Location loc)
+Nil_expression::do_import(Import_expression* imp, Location loc)
 {
   imp->require_c_string("nil");
   return Expression::make_nil(loc);
@@ -3623,7 +3623,7 @@
 // Import a type conversion or a struct construction.
 
 Expression*
-Type_conversion_expression::do_import(Import* imp, Location loc)
+Type_conversion_expression::do_import(Import_expression* imp, Location loc)
 {
   imp->require_c_string("convert(");
   Type* type = imp->read_type();
@@ -4634,7 +4634,7 @@
 // Import a unary expression.
 
 Expression*
-Unary_expression::do_import(Import* imp, Location loc)
+Unary_expression::do_import(Import_expression* imp, Location loc)
 {
   Operator op;
   switch (imp->get_char())
@@ -6403,7 +6403,7 @@
 // Import a binary expression.
 
 Expression*
-Binary_expression::do_import(Import* imp, Location loc)
+Binary_expression::do_import(Import_expression* imp, Location loc)
 {
   imp->require_c_string("(");
 
@@ -16138,7 +16138,7 @@
 // various class definitions.
 
 Expression*
-Expression::import_expression(Import* imp, Location loc)
+Expression::import_expression(Import_expression* imp, Location loc)
 {
   int c = imp->peek_char();
   if (imp->match_c_string("- ")
diff --git a/go/expressions.h b/go/expressions.h
index 3641b03..7061657 100644
--- a/go/expressions.h
+++ b/go/expressions.h
@@ -66,7 +66,7 @@
 class Numeric_constant;
 class Named_object;
 class Export_function_body;
-class Import;
+class Import_expression;
 class Temporary_statement;
 class Label;
 class Ast_dump_context;
@@ -1018,7 +1018,7 @@
   // returned expression.  Errors should be reported using the
   // Import's location method.
   static Expression*
-  import_expression(Import*, Location);
+  import_expression(Import_expression*, Location);
 
   // Return an expression which checks that VAL, of arbitrary integer type,
   // is non-negative and is not more than the maximum integer value.
@@ -1567,7 +1567,7 @@
   { return this->val_; }
 
   static Expression*
-  do_import(Import*, Location);
+  do_import(Import_expression*, Location);
 
  protected:
   bool
@@ -1646,7 +1646,7 @@
 
   // Import a type conversion expression.
   static Expression*
-  do_import(Import*, Location);
+  do_import(Import_expression*, Location);
 
  protected:
   int
@@ -1817,7 +1817,7 @@
 		Location, Numeric_constant* nc, bool *issued_error);
 
   static Expression*
-  do_import(Import*, Location);
+  do_import(Import_expression*, Location);
 
   // Declare that this deref does or does not require an explicit nil check.
   void
@@ -1966,7 +1966,7 @@
 		   bool* result);
 
   static Expression*
-  do_import(Import*, Location);
+  do_import(Import_expression*, Location);
 
   // Report an error if OP can not be applied to TYPE.  Return whether
   // it can.  OTYPE is the type of the other operand.
diff --git a/go/gogo.cc b/go/gogo.cc
index 79d92e5..eb0297b 100644
--- a/go/gogo.cc
+++ b/go/gogo.cc
@@ -527,9 +527,9 @@
       return;
     }
 
-  Import imp(stream, location);
-  imp.register_builtin_types(this);
-  Package* package = imp.import(this, local_name, is_local_name_exported);
+  Import* imp = new Import(stream, location);
+  imp->register_builtin_types(this);
+  Package* package = imp->import(this, local_name, is_local_name_exported);
   if (package != NULL)
     {
       if (package->pkgpath() == this->pkgpath())
@@ -540,7 +540,10 @@
       this->imports_.insert(std::make_pair(filename, package));
     }
 
+  imp->clear_stream();
   delete stream;
+
+  // FIXME: we never delete imp; we may need it for inlinable functions.
 }
 
 Import_init *
@@ -6763,8 +6766,6 @@
   const std::string& body(this->imported_body_);
   go_assert(!body.empty());
 
-  Location orig_loc = no->location();
-
   // Read the "//FILE:LINE" comment starts the export data.
 
   size_t indent = 1;
@@ -6877,7 +6878,7 @@
       no = rtype->add_method(no->name(), fn);
     }
 
-  Import_function_body ifb(gogo, orig_loc, no, body, nl + 1, outer, indent);
+  Import_function_body ifb(gogo, this->imp_, no, body, nl + 1, outer, indent);
 
   if (!Block::import_block(outer, &ifb, start_loc))
     return;
diff --git a/go/gogo.h b/go/gogo.h
index 1c79f6f..8ca567c 100644
--- a/go/gogo.h
+++ b/go/gogo.h
@@ -1691,8 +1691,11 @@
 
   // Record the imported body of this function.
   void
-  set_imported_body(const std::string& imported_body)
-  { this->imported_body_ = imported_body; }
+  set_imported_body(Import* imp, const std::string& imported_body)
+  {
+    this->imp_ = imp;
+    this->imported_body_ = imported_body;
+  }
 
   // Whether this declaration is on the list of inlinable functions.
   bool
@@ -1756,6 +1759,8 @@
   Bfunction* fndecl_;
   // Pragmas for this function.  This is a set of GOPRAGMA bits.
   unsigned int pragmas_;
+  // Importer for function body if imported from a different package.
+  Import* imp_;
   // Export data for function body if imported from a different package.
   std::string imported_body_;
   // Whether this declaration is already on the list of inlinable functions.
diff --git a/go/import.cc b/go/import.cc
index 7ee3cb6..524e739 100644
--- a/go/import.cc
+++ b/go/import.cc
@@ -790,7 +790,7 @@
   if (nointerface)
     no->func_declaration_value()->set_nointerface();
   if (!body.empty() && !no->func_declaration_value()->has_imported_body())
-    no->func_declaration_value()->set_imported_body(body);
+    no->func_declaration_value()->set_imported_body(this, body);
 
   return no;
 }
@@ -886,41 +886,7 @@
   if (c == '>')
     {
       // A reference to a type defined earlier.
-
-      if (index >= 0 && !this->type_data_.empty())
-	{
-	  if (static_cast<size_t>(index) >= this->type_offsets_.size())
-	    {
-	      go_error_at(this->location_,
-			  ("error in import data at %d: "
-			   "bad type index %d >= %d"),
-			  stream->pos(), index,
-			  static_cast<int>(this->type_offsets_.size()));
-	      stream->set_saw_error();
-	      return Type::make_error_type();
-	    }
-
-	  if (this->types_[index] == NULL)
-	    {
-	      if (!this->parse_type(index))
-		return Type::make_error_type();
-	    }
-	}
-
-      if (index < 0
-	  ? (static_cast<size_t>(- index) >= this->builtin_types_.size()
-	     || this->builtin_types_[- index] == NULL)
-	  : (static_cast<size_t>(index) >= this->types_.size()
-	     || this->types_[index] == NULL))
-	{
-	  go_error_at(this->location_,
-		      "error in import data at %d: bad type index %d",
-		      stream->pos(), index);
-	  stream->set_saw_error();
-	  return Type::make_error_type();
-	}
-
-      return index < 0 ? this->builtin_types_[- index] : this->types_[index];
+      return this->type_for_index(index, "import data", stream->pos());
     }
 
   if (this->version_ >= EXPORT_FORMAT_V3)
@@ -1126,6 +1092,47 @@
   return type;
 }
 
+// Return the type given an index.
+
+Type*
+Import::type_for_index(int index, const std::string& input_name,
+		       size_t input_offset)
+{
+  if (index >= 0 && !this->type_data_.empty())
+    {
+      if (static_cast<size_t>(index) >= this->type_offsets_.size())
+	{
+	  go_error_at(this->location_,
+		      "error in %s at %lu: bad type index %d >= %d",
+		      input_name.c_str(),
+		      static_cast<unsigned long>(input_offset),
+		      index, static_cast<int>(this->type_offsets_.size()));
+	  return Type::make_error_type();
+	}
+
+      if (this->types_[index] == NULL)
+	{
+	  if (!this->parse_type(index))
+	    return Type::make_error_type();
+	}
+    }
+
+  if (index < 0
+      ? (static_cast<size_t>(- index) >= this->builtin_types_.size()
+	 || this->builtin_types_[- index] == NULL)
+      : (static_cast<size_t>(index) >= this->types_.size()
+	 || this->types_[index] == NULL))
+    {
+      go_error_at(this->location_,
+		  "error in %s at %lu: bad type index %d",
+		  input_name.c_str(),
+		  static_cast<unsigned long>(input_offset), index);
+      return Type::make_error_type();
+    }
+
+  return index < 0 ? this->builtin_types_[- index] : this->types_[index];
+}
+
 // Read an escape note.
 
 std::string
@@ -1408,3 +1415,88 @@
 {
   return this->named_object_->name();
 }
+
+// Class Import_function_body.
+
+// Require that the next bytes match STR, issuing an error if not.
+// Advance past the string.
+
+void
+Import_function_body::require_c_string(const char* str)
+{
+  if (!this->match_c_string(str))
+    {
+      if (!this->saw_error_)
+	go_error_at(this->location(),
+		    "invalid export data for %qs: expected %qs at %lu",
+		    this->name().c_str(), str,
+		    static_cast<unsigned long>(this->off_));
+      this->saw_error_ = true;
+      return;
+    }
+  this->advance(strlen(str));
+}
+
+// Read an identifier.
+
+std::string
+Import_function_body::read_identifier()
+{
+  size_t start = this->off_;
+  for (size_t i = start; i < this->body_.length(); i++)
+    {
+      int c = static_cast<unsigned char>(this->body_[i]);
+      if (c == ' ' || c == '\n' || c == ';')
+	{
+	  this->off_ = i;
+	  return this->body_.substr(start, i - start);
+	}
+    }
+  this->off_ = this->body_.length();
+  return this->body_.substr(start);
+}
+
+// Read a type.
+
+Type*
+Import_function_body::read_type()
+{
+  this->require_c_string("<type ");
+  size_t start = this->off_;
+  size_t i;
+  int c = '\0';
+  for (i = start; i < this->body_.length(); ++i)
+    {
+      c = static_cast<unsigned char>(this->body_[i]);
+      if (c != '-' && (c < '0' || c > '9'))
+	break;
+    }
+  this->off_ = i + 1;
+
+  char *end;
+  long val = strtol(this->body_.substr(start, i - start).c_str(), &end, 10);
+  if (*end != '\0' || i > 0x7fffffff)
+    {
+      if (!this->saw_error_)
+	go_error_at(this->location(),
+		    "invalid export data for %qs: expected integer at %lu",
+		    this->name().c_str(),
+		    static_cast<unsigned long>(start));
+      this->saw_error_ = true;
+      return Type::make_error_type();
+    }
+
+  if (c != '>')
+    {
+      if (!this->saw_error_)
+	go_error_at(this->location(),
+		    "invalid export data for %qs: expected %<>%> at %lu",
+		    this->name().c_str(),
+		    static_cast<unsigned long>(i));
+      this->saw_error_ = true;
+      return Type::make_error_type();
+    }
+
+  return this->imp_->type_for_index(static_cast<int>(val), this->name(),
+				    static_cast<unsigned long>(start));
+}
diff --git a/go/import.h b/go/import.h
index 3fc05df..c2120c9 100644
--- a/go/import.h
+++ b/go/import.h
@@ -17,10 +17,65 @@
 class Named_object;
 class Named_type;
 class Expression;
+class Import_function_body;
+
+// Expressions can be imported either directly from import data (for
+// simple constant expressions that can appear in a const declaration
+// or as an array length in a type definition) or from an exported
+// function body (for an inlinable function).  These two cases happen
+// at different points in the compilation and have different
+// requirements, so it's not easy to unify them.  Import_expression is
+// an abstract interface that permits the expression import code to
+// work at either point.  When importing expressions that only occur
+// for an inlinable function, the ifb method is available to get the
+// full Import_function_body.
+
+class Import_expression
+{
+ public:
+  // Return the import function body.  This should only be called for
+  // expressions that can not appear outside of an inlinable function
+  // body.
+  virtual Import_function_body*
+  ifb() = 0;
+
+  // The location to report in an error message.
+  virtual Location
+  location() const = 0;
+
+  // Peek at the next character in the input, returning a value from 0
+  // to 0xff.  Returns -1 at end of stream.
+  virtual int
+  peek_char() = 0;
+
+  // Return the next character and advance.
+  virtual int
+  get_char() = 0;
+
+  // Return true if the next bytes match STR.
+  virtual bool
+  match_c_string(const char* str) = 0;
+
+  // Require that the next bytes match STR.
+  virtual void
+  require_c_string(const char* str) = 0;
+
+  // Advance the stream SKIP bytes.
+  virtual void
+  advance(size_t skip) = 0;
+
+  // Read an identifier.
+  virtual std::string
+  read_identifier() = 0;
+
+  // Read a type.
+  virtual Type*
+  read_type() = 0;
+};
 
 // This class manages importing Go declarations.
 
-class Import
+class Import : public Import_expression
 {
  public:
   // The Stream class is an interface used to read the data.  The
@@ -138,6 +193,9 @@
   // Constructor.
   Import(Stream*, Location);
 
+  virtual ~Import()
+  {}
+
   // Register the builtin types.
   void
   register_builtin_types(Gogo*);
@@ -217,10 +275,26 @@
   Type*
   read_type();
 
+  // Return the type for a type index.  INPUT_NAME and INPUT_OFFSET
+  // are only for error reporting.
+  Type*
+  type_for_index(int index, const std::string& input_name,
+		 size_t input_offset);
+
   // Read an escape note.
   std::string
   read_escape();
 
+  // Clear the stream when it is no longer accessible.
+  void
+  clear_stream()
+  { this->stream_ = NULL; }
+
+  // Just so that Import implements Import_expression.
+  Import_function_body*
+  ifb()
+  { return NULL; }
+
  private:
   static Stream*
   try_package_in_directory(const std::string&, Location);
@@ -468,13 +542,13 @@
 // Class to manage importing a function body.  This is passed around
 // to Statements and Expressions.  It parses the function into the IR.
 
-class Import_function_body
+class Import_function_body : public Import_expression
 {
  public:
-  Import_function_body(Gogo* gogo, Location loc, Named_object* named_object,
+  Import_function_body(Gogo* gogo, Import* imp, Named_object* named_object,
 		       const std::string& body, size_t off, Block* block,
 		       int indent)
-    : gogo_(gogo), loc_(loc), named_object_(named_object), body_(body),
+    : gogo_(gogo), imp_(imp), named_object_(named_object), body_(body),
       off_(off), block_(block), indent_(indent)
   { }
 
@@ -486,7 +560,7 @@
   // The location to report in an error message.
   Location
   location() const
-  { return this->loc_; }
+  { return this->imp_->location(); }
 
   // A reference to the body we are reading.
   const std::string&
@@ -503,6 +577,11 @@
   set_off(size_t off)
   { this->off_ = off; }
 
+  // Advance the offset by SKIP bytes.
+  void
+  advance(size_t skip)
+  { this->off_ += skip; }
+
   // The current block.
   Block*
   block()
@@ -517,11 +596,58 @@
   const std::string&
   name() const;
 
+  // Return the next character in the input stream, or -1 at the end.
+  int
+  peek_char()
+  {
+    if (this->body_.length() <= this->off_)
+      return -1;
+    return static_cast<unsigned char>(this->body_[this->off_]);
+  }
+
+  // Return the next character and advance.
+  int
+  get_char()
+  {
+    if (this->body_.length() <= this->off_)
+      return -1;
+    int c = static_cast<unsigned char>(this->body_[this->off_]);
+    this->off_++;
+    return c;
+  }
+
+  // Return whether the C string matches the current body position.
+  bool
+  match_c_string(const char* str)
+  {
+    size_t len = strlen(str);
+    return (this->body_.length() >= this->off_ + len
+	    && this->body_.compare(this->off_, len, str) == 0);
+  }
+
+  // Give an error if the next bytes do not match STR.  Advance the
+  // offset by the length of STR.
+  void
+  require_c_string(const char* str);
+
+  // Read an identifier.
+  std::string
+  read_identifier();
+
+  // Read a type.
+  Type*
+  read_type();
+
+  // Implement Import_expression.
+  Import_function_body*
+  ifb()
+  { return this; }
+
  private:
   // The IR.
   Gogo* gogo_;
-  // The location to report in an error message.
-  Location loc_;
+  // The importer.
+  Import* imp_;
   // The function we are parsing.
   Named_object* named_object_;
   // The exported data we are parsing.  Note that this is a reference;
@@ -533,6 +659,9 @@
   Block* block_;
   // Current expected indentation level.
   int indent_;
+  // Whether we've seen an error.  Used to avoid reporting excess
+  // errors.
+  bool saw_error_;
 };
 
 #endif // !defined(GO_IMPORT_H)