compiler: Add -fgo-relative-import-path.
[gcc.git] / gcc / go / gofrontend / import.cc
index 0f0da89a018168e363ed26058fd9f1a0c2d51497..4913100b5fddd17ba0db2a83bfe2cb1c2f52b0ac 100644 (file)
@@ -11,6 +11,7 @@
 
 #include "go-c.h"
 #include "gogo.h"
+#include "lex.h"
 #include "types.h"
 #include "export.h"
 #include "import.h"
@@ -33,17 +34,15 @@ go_add_search_path(const char* path)
   search_path.push_back(std::string(path));
 }
 
-// The name used for parameters, receivers, and results in imported
-// function types.
-
-const char* const Import::import_marker = "*imported*";
-
 // Find import data.  This searches the file system for FILENAME and
 // returns a pointer to a Stream object to read the data that it
 // exports.  If the file is not found, it returns NULL.
 
-// When FILENAME is not an absolute path, we use the search path
-// provided by -I and -L options.
+// When FILENAME is not an absolute path and does not start with ./ or
+// ../, we use the search path provided by -I and -L options.
+
+// When FILENAME does start with ./ or ../, we use
+// RELATIVE_IMPORT_PATH as a prefix.
 
 // When FILENAME does not exist, we try modifying FILENAME to find the
 // file.  We use the first of these which exists:
@@ -59,9 +58,36 @@ const char* const Import::import_marker = "*imported*";
 // later in the search path.
 
 Import::Stream*
-Import::open_package(const std::string& filename, source_location location)
+Import::open_package(const std::string& filename, Location location,
+                    const std::string& relative_import_path)
 {
-  if (!IS_ABSOLUTE_PATH(filename))
+  bool is_local;
+  if (IS_ABSOLUTE_PATH(filename))
+    is_local = true;
+  else if (filename[0] == '.'
+          && (filename[1] == '\0' || IS_DIR_SEPARATOR(filename[1])))
+    is_local = true;
+  else if (filename[0] == '.'
+          && filename[1] == '.'
+          && (filename[2] == '\0' || IS_DIR_SEPARATOR(filename[2])))
+    is_local = true;
+  else
+    is_local = false;
+
+  std::string fn = filename;
+  if (is_local && !IS_ABSOLUTE_PATH(filename) && !relative_import_path.empty())
+    {
+      if (fn == ".")
+       {
+         // A special case.
+         fn = relative_import_path;
+       }
+      else
+       fn = relative_import_path + '/' + fn;
+      is_local = false;
+    }
+
+  if (!is_local)
     {
       for (std::vector<std::string>::const_iterator p = search_path.begin();
           p != search_path.end();
@@ -70,14 +96,14 @@ Import::open_package(const std::string& filename, source_location location)
          std::string indir = *p;
          if (!indir.empty() && indir[indir.size() - 1] != '/')
            indir += '/';
-         indir += filename;
+         indir += fn;
          Stream* s = Import::try_package_in_directory(indir, location);
          if (s != NULL)
            return s;
        }
     }
 
-  Stream* s = Import::try_package_in_directory(filename, location);
+  Stream* s = Import::try_package_in_directory(fn, location);
   if (s != NULL)
     return s;
 
@@ -88,7 +114,7 @@ Import::open_package(const std::string& filename, source_location location)
 
 Import::Stream*
 Import::try_package_in_directory(const std::string& filename,
-                                source_location location)
+                                Location location)
 {
   std::string found_filename = filename;
   int fd = open(found_filename.c_str(), O_RDONLY | O_BINARY);
@@ -175,7 +201,7 @@ Import::try_suffixes(std::string* pfilename)
 
 Import::Stream*
 Import::find_export_data(const std::string& filename, int fd,
-                        source_location location)
+                        Location location)
 {
   // See if we can read this as an object file.
   Import::Stream* stream = Import::find_object_export_data(filename, fd, 0,
@@ -213,46 +239,26 @@ Import::Stream*
 Import::find_object_export_data(const std::string& filename,
                                int fd,
                                off_t offset,
-                               source_location location)
+                               Location location)
 {
-  const char* errmsg;
+  char *buf;
+  size_t len;
   int err;
-  simple_object_read* sobj = simple_object_start_read(fd, offset,
-                                                     "__GNU_GO",
-                                                     &errmsg, &err);
-  if (sobj == NULL)
-    return NULL;
-
-  off_t sec_offset;
-  off_t sec_length;
-  int found = simple_object_find_section(sobj, ".go_export", &sec_offset,
-                                        &sec_length, &errmsg, &err);
-
-  simple_object_release_read(sobj);
-
-  if (!found)
-    return NULL;
-
-  if (lseek(fd, offset + sec_offset, SEEK_SET) < 0)
+  const char *errmsg = go_read_export_data(fd, offset, &buf, &len, &err);
+  if (errmsg != NULL)
     {
-      error_at(location, "lseek %s failed: %m", filename.c_str());
+      if (err == 0)
+       error_at(location, "%s: %s", filename.c_str(), errmsg);
+      else
+       error_at(location, "%s: %s: %s", filename.c_str(), errmsg,
+                xstrerror(err));
       return NULL;
     }
 
-  char* buf = new char[sec_length];
-  ssize_t c = read(fd, buf, sec_length);
-  if (c < 0)
-    {
-      error_at(location, "read %s failed: %m", filename.c_str());
-      return NULL;
-    }
-  if (c < sec_length)
-    {
-      error_at(location, "%s: short read", filename.c_str());
-      return NULL;
-    }
+  if (buf == NULL)
+    return NULL;
 
-  return new Stream_from_buffer(buf, sec_length);
+  return new Stream_from_buffer(buf, len);
 }
 
 // Class Import.
@@ -260,7 +266,7 @@ Import::find_object_export_data(const std::string& filename,
 // Construct an Import object.  We make the builtin_types_ vector
 // large enough to hold all the builtin types.
 
-Import::Import(Stream* stream, source_location location)
+Import::Import(Stream* stream, Location location)
   : gogo_(NULL), stream_(stream), location_(location), package_(NULL),
     add_to_globals_(false),
     builtin_types_((- SMALLEST_BUILTIN_CODE) + 1),
@@ -294,13 +300,24 @@ Import::import(Gogo* gogo, const std::string& local_name,
       std::string package_name = this->read_identifier();
       this->require_c_string(";\n");
 
-      this->require_c_string("prefix ");
-      std::string unique_prefix = this->read_identifier();
-      this->require_c_string(";\n");
+      std::string pkgpath;
+      if (this->match_c_string("prefix "))
+       {
+         this->advance(7);
+         std::string unique_prefix = this->read_identifier();
+         this->require_c_string(";\n");
+         pkgpath = unique_prefix + '.' + package_name;
+       }
+      else
+       {
+         this->require_c_string("pkgpath ");
+         pkgpath = this->read_identifier();
+         this->require_c_string(";\n");
+       }
 
       this->package_ = gogo->add_imported_package(package_name, local_name,
                                                  is_local_name_exported,
-                                                 unique_prefix,
+                                                 pkgpath,
                                                  this->location_,
                                                  &this->add_to_globals_);
       if (this->package_ == NULL)
@@ -317,7 +334,10 @@ Import::import(Gogo* gogo, const std::string& local_name,
       this->package_->set_priority(prio);
       this->require_c_string(";\n");
 
-      if (stream->match_c_string("import "))
+      while (stream->match_c_string("import"))
+       this->read_one_import();
+
+      if (stream->match_c_string("init"))
        this->read_import_init_fns(gogo);
 
       // Loop over all the input data for this package.
@@ -357,12 +377,32 @@ Import::import(Gogo* gogo, const std::string& local_name,
   return this->package_;
 }
 
+// Read an import line.  We don't actually care about these.
+
+void
+Import::read_one_import()
+{
+  this->require_c_string("import ");
+  std::string package_name = this->read_identifier();
+  this->require_c_string(" ");
+  std::string pkgpath = this->read_identifier();
+  this->require_c_string(" \"");
+  Stream* stream = this->stream_;
+  while (stream->peek_char() != '"')
+    stream->advance(1);
+  this->require_c_string("\";\n");
+
+  Package* p = this->gogo_->register_package(pkgpath,
+                                            Linemap::unknown_location());
+  p->set_package_name(package_name, this->location());
+}
+
 // Read the list of import control functions.
 
 void
 Import::read_import_init_fns(Gogo* gogo)
 {
-  this->require_c_string("import");
+  this->require_c_string("init");
   while (!this->match_c_string(";"))
     {
       this->require_c_string(" ");
@@ -446,16 +486,33 @@ Import::import_func(Package* package)
   if (is_varargs)
     fntype->set_is_varargs();
 
-  source_location loc = this->location_;
+  Location loc = this->location_;
   Named_object* no;
   if (fntype->is_method())
     {
-      Type* rtype = receiver->type()->deref();
+      Type* rtype = receiver->type();
+
+      // We may still be reading the definition of RTYPE, so we have
+      // to be careful to avoid calling base or convert.  If RTYPE is
+      // a named type or a forward declaration, then we know that it
+      // is not a pointer, because we are reading a method on RTYPE
+      // and named pointers can't have methods.
+
+      if (rtype->classification() == Type::TYPE_POINTER)
+       rtype = rtype->points_to();
+
       if (rtype->is_error_type())
        return NULL;
-      Named_type* named_rtype = rtype->named_type();
-      gcc_assert(named_rtype != NULL);
-      no = named_rtype->add_method_declaration(name, package, fntype, loc);
+      else if (rtype->named_type() != NULL)
+       no = rtype->named_type()->add_method_declaration(name, package, fntype,
+                                                        loc);
+      else if (rtype->forward_declaration_type() != NULL)
+       no = rtype->forward_declaration_type()->add_method_declaration(name,
+                                                                      package,
+                                                                      fntype,
+                                                                      loc);
+      else
+       go_unreachable();
     }
   else
     {
@@ -553,55 +610,50 @@ Import::read_type()
   while ((c = stream->get_char()) != '"')
     type_name += c;
 
-  // If this type is in the current package, the name will be
-  // .PREFIX.PACKAGE.NAME or simply NAME with no dots.  Otherwise, a
-  // non-hidden symbol will be PREFIX.PACKAGE.NAME and a hidden symbol
-  // will be .PREFIX.PACKAGE.NAME.
-  std::string package_name;
-  std::string unique_prefix;
+  // If this type is in the package we are currently importing, the
+  // name will be .PKGPATH.NAME or simply NAME with no dots.
+  // Otherwise, a non-hidden symbol will be PKGPATH.NAME and a hidden
+  // symbol will be .PKGPATH.NAME.
+  std::string pkgpath;
   if (type_name.find('.') != std::string::npos)
     {
-      bool is_hidden = false;
       size_t start = 0;
       if (type_name[0] == '.')
-       {
-         ++start;
-         is_hidden = true;
-       }
-      size_t dot1 = type_name.find('.', start);
-      size_t dot2;
-      if (dot1 == std::string::npos)
-       dot2 = std::string::npos;
-      else
-       dot2 = type_name.find('.', dot1 + 1);
-      if (dot1 == std::string::npos || dot2 == std::string::npos)
-       {
-         error_at(this->location_,
-                  ("error at import data at %d: missing dot in type name"),
-                  stream->pos());
-         stream->set_saw_error();
-       }
-      else
-       {
-         unique_prefix = type_name.substr(start, dot1 - start);
-         package_name = type_name.substr(dot1 + 1, dot2 - (dot1 + 1));
-       }
-      if (!is_hidden)
-       type_name.erase(0, dot2 + 1);
+       start = 1;
+      size_t dot = type_name.rfind('.');
+      pkgpath = type_name.substr(start, dot - start);
+      if (type_name[0] != '.')
+       type_name.erase(0, dot + 1);
     }
 
   this->require_c_string(" ");
 
+  // The package name may follow.  This is the name of the package in
+  // the package clause of that package.  The type name will include
+  // the pkgpath, which may be different.
+  std::string package_name;
+  if (stream->peek_char() == '"')
+    {
+      stream->advance(1);
+      while ((c = stream->get_char()) != '"')
+       package_name += c;
+      this->require_c_string(" ");
+    }
+
   // Declare the type in the appropriate package.  If we haven't seen
   // it before, mark it as invisible.  We declare it before we read
   // the actual definition of the type, since the definition may refer
   // to the type itself.
   Package* package;
-  if (package_name.empty())
+  if (pkgpath.empty() || pkgpath == this->gogo_->pkgpath())
     package = this->package_;
   else
-    package = this->gogo_->register_package(package_name, unique_prefix,
-                                           UNKNOWN_LOCATION);
+    {
+      package = this->gogo_->register_package(pkgpath,
+                                             Linemap::unknown_location());
+      if (!package_name.empty())
+       package->set_package_name(package_name, this->location());
+    }
 
   Named_object* no = package->bindings()->lookup(type_name);
   if (no == NULL)
@@ -609,13 +661,12 @@ Import::read_type()
   else if (!no->is_type_declaration() && !no->is_type())
     {
       error_at(this->location_, "imported %<%s.%s%> both type and non-type",
-              Gogo::message_name(package->name()).c_str(),
-              Gogo::message_name(type_name).c_str());
+              pkgpath.c_str(), Gogo::message_name(type_name).c_str());
       stream->set_saw_error();
       return Type::make_error_type();
     }
   else
-    gcc_assert(no->package() == package);
+    go_assert(no->package() == package);
 
   if (this->types_[index] == NULL)
     {
@@ -626,7 +677,7 @@ Import::read_type()
        }
       else
        {
-         gcc_assert(no->is_type());
+         go_assert(no->is_type());
          this->types_[index] = no->type_value();
        }
     }
@@ -650,14 +701,17 @@ Import::read_type()
          // This type has not yet been imported.
          ntype->clear_is_visible();
 
+         if (!type->is_undefined() && type->interface_type() != NULL)
+           this->gogo_->record_interface_type(type->interface_type());
+
          type = ntype;
        }
       else if (no->is_type())
        {
          // We have seen this type before.  FIXME: it would be a good
          // idea to check that the two imported types are identical,
-         // but we have not finalized the methds yet, which means
-         // that we can nt reliably compare interface types.
+         // but we have not finalized the methods yet, which means
+         // that we can not reliably compare interface types.
          type = no->type_value();
 
          // Don't change the visibility of the existing type.
@@ -702,10 +756,11 @@ Import::register_builtin_types(Gogo* gogo)
   this->register_builtin_type(gogo, "int", BUILTIN_INT);
   this->register_builtin_type(gogo, "uint", BUILTIN_UINT);
   this->register_builtin_type(gogo, "uintptr", BUILTIN_UINTPTR);
-  this->register_builtin_type(gogo, "float", BUILTIN_FLOAT);
-  this->register_builtin_type(gogo, "complex", BUILTIN_COMPLEX);
   this->register_builtin_type(gogo, "bool", BUILTIN_BOOL);
   this->register_builtin_type(gogo, "string", BUILTIN_STRING);
+  this->register_builtin_type(gogo, "error", BUILTIN_ERROR);
+  this->register_builtin_type(gogo, "byte", BUILTIN_BYTE);
+  this->register_builtin_type(gogo, "rune", BUILTIN_RUNE);
 }
 
 // Register a single builtin type.
@@ -714,9 +769,9 @@ void
 Import::register_builtin_type(Gogo* gogo, const char* name, Builtin_code code)
 {
   Named_object* named_object = gogo->lookup_global(name);
-  gcc_assert(named_object != NULL && named_object->is_type());
+  go_assert(named_object != NULL && named_object->is_type());
   int index = - static_cast<int>(code);
-  gcc_assert(index > 0
+  go_assert(index > 0
             && static_cast<size_t>(index) < this->builtin_types_.size());
   this->builtin_types_[index] = named_object->type_value();
 }
@@ -740,6 +795,19 @@ Import::read_identifier()
   return ret;
 }
 
+// Read a name from the stream.
+
+std::string
+Import::read_name()
+{
+  std::string ret = this->read_identifier();
+  if (ret == "?")
+    ret.clear();
+  else if (!Lex::is_exported_name(ret))
+    ret = '.' + this->package_->pkgpath() + '.' + ret;
+  return ret;
+}
+
 // Turn a string into a integer with appropriate error handling.
 
 bool
@@ -798,7 +866,7 @@ Import::Stream::match_bytes(const char* bytes, size_t length)
 // Require that the next LENGTH bytes from the stream match BYTES.
 
 void
-Import::Stream::require_bytes(source_location location, const char* bytes,
+Import::Stream::require_bytes(Location location, const char* bytes,
                              size_t length)
 {
   const char* read;
@@ -842,7 +910,7 @@ Stream_from_file::do_peek(size_t length, const char** bytes)
       return true;
     }
   // Don't bother to handle the general case, since we don't need it.
-  gcc_assert(length < 64);
+  go_assert(length < 64);
   char buf[64];
   ssize_t got = read(this->fd_, buf, length);