compiler: drop semicolons in export data
authorIan Lance Taylor <ian@gcc.gnu.org>
Thu, 18 Oct 2018 19:35:46 +0000 (19:35 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Thu, 18 Oct 2018 19:35:46 +0000 (19:35 +0000)
    The export data, which is approximately readable and looks something
    like Go, was first implemented back when Go still used semicolons.
    Drop the semicolons, to make it look slightly more Go like and make it
    slightly smaller.

    This updates the compiler and the gccgoimporter package.

    This introduces a new version of the export data.  There are going to
    be more changes to the export data, so this version is still subject
    to change.

    Reviewed-on: https://go-review.googlesource.com/c/143018

From-SVN: r265284

gcc/go/gofrontend/MERGE
gcc/go/gofrontend/export.cc
gcc/go/gofrontend/export.h
gcc/go/gofrontend/gogo.cc
gcc/go/gofrontend/import.cc
gcc/go/gofrontend/import.h
gcc/go/gofrontend/types.cc
libgo/go/go/internal/gccgoimporter/importer.go
libgo/go/go/internal/gccgoimporter/parser.go
libgo/go/go/internal/gccgoimporter/parser_test.go

index 76689a3b50ae063e4c46c1f506367992690913a1..fa708bceae7a35093471d4d44096d76625a9f807 100644 (file)
@@ -1,4 +1,4 @@
-e32e9aaee598eeb43f9616cf6ca1d11acaa9d167
+0494dc5737f0c89ad6f45e04e8313e4161678861
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index 27b76801d5cd9e18d3e255a78167c62353eeb8c2..db57ab58850f5c93ea3e0d918c75a495e0327d09 100644 (file)
@@ -26,14 +26,18 @@ const int Export::magic_len;
 // Current version magic string.
 const char Export::cur_magic[Export::magic_len] =
   {
-    'v', '2', ';', '\n'
+    'v', '3', ';', '\n'
   };
 
-// Magic string for previous version (still supported)
+// Magic strings for previous versions (still supported).
 const char Export::v1_magic[Export::magic_len] =
   {
     'v', '1', ';', '\n'
   };
+const char Export::v2_magic[Export::magic_len] =
+  {
+    'v', '2', ';', '\n'
+  };
 
 const int Export::checksum_len;
 
@@ -147,7 +151,7 @@ Export::export_globals(const std::string& package_name,
   // The package name.
   this->write_c_string("package ");
   this->write_string(package_name);
-  this->write_c_string(";\n");
+  this->write_c_string("\n");
 
   // The prefix or package path, used for all global symbols.
   if (prefix.empty())
@@ -161,7 +165,7 @@ Export::export_globals(const std::string& package_name,
       this->write_c_string("prefix ");
       this->write_string(prefix);
     }
-  this->write_c_string(";\n");
+  this->write_c_string("\n");
 
   this->write_packages(packages);
 
@@ -191,7 +195,7 @@ Export::export_globals(const std::string& package_name,
       dig = c & 0xf;
       s += dig < 10 ? '0' + dig : 'A' + dig - 10;
     }
-  s += ";\n";
+  s += "\n";
   this->stream_->write_checksum(s);
 }
 
@@ -233,7 +237,7 @@ Export::write_packages(const std::map<std::string, Package*>& packages)
       this->write_string((*p)->pkgpath());
       this->write_c_string(" ");
       this->write_string((*p)->pkgpath_symbol());
-      this->write_c_string(";\n");
+      this->write_c_string("\n");
     }
 }
 
@@ -271,7 +275,7 @@ Export::write_imports(const std::map<std::string, Package*>& imports)
       this->write_string(p->second->pkgpath());
       this->write_c_string(" \"");
       this->write_string(p->first);
-      this->write_c_string("\";\n");
+      this->write_c_string("\"\n");
 
       this->packages_.insert(p->second);
     }
@@ -347,7 +351,7 @@ Export::write_imported_init_fns(const std::string& package_name,
 
   if (imported_init_fns.empty())
     {
-      this->write_c_string(";\n");
+      this->write_c_string("\n");
       return;
     }
 
@@ -394,7 +398,7 @@ Export::write_imported_init_fns(const std::string& package_name,
            it->second.push_back(ii->init_name());
        }
     }
-  this->write_c_string(";\n");
+  this->write_c_string("\n");
 
   // Create the init graph. Start by populating the graph with
   // all the edges we inherited from imported packages.
@@ -494,7 +498,7 @@ Export::write_imported_init_fns(const std::string& package_name,
          this->write_unsigned(sink);
        }
     }
-  this->write_c_string(";\n");
+  this->write_c_string("\n");
 }
 
 // Write a name to the export stream.
index 136567725a5148ad3de0d02aa9dfe81f5823540d..b08bf8513182ae99125d8c84fe0f49947ee300c4 100644 (file)
@@ -57,7 +57,8 @@ enum Export_data_version {
   EXPORT_FORMAT_UNKNOWN = 0,
   EXPORT_FORMAT_V1 = 1,
   EXPORT_FORMAT_V2 = 2,
-  EXPORT_FORMAT_CURRENT = EXPORT_FORMAT_V2
+  EXPORT_FORMAT_V3 = 3,
+  EXPORT_FORMAT_CURRENT = EXPORT_FORMAT_V3
 };
 
 // This class manages exporting Go declarations.  It handles the main
@@ -119,9 +120,10 @@ class Export : public String_dump
   // Size of export data magic string (which includes version number).
   static const int magic_len = 4;
 
-  // Magic strings (current version and older v1 version).
+  // Magic strings (current version and older versions).
   static const char cur_magic[magic_len];
   static const char v1_magic[magic_len];
+  static const char v2_magic[magic_len];
 
   // The length of the checksum string.
   static const int checksum_len = 20;
index dd6733fab8bd341b5c6cee659ce9be5f574c9436..d07068d49ac5bf3da594900ebdc1b56f9a01af50 100644 (file)
@@ -5391,7 +5391,7 @@ Function::export_func_with_type(Export* exp, const std::string& name,
          exp->write_c_string(")");
        }
     }
-  exp->write_c_string(";\n");
+  exp->write_c_string("\n");
 }
 
 // Import a function.
@@ -5498,7 +5498,8 @@ Function::import_func(Import* imp, std::string* pname,
          imp->require_c_string(")");
        }
     }
-  imp->require_c_string(";\n");
+  imp->require_semicolon_if_old_version();
+  imp->require_c_string("\n");
   *presults = results;
 }
 
@@ -6885,7 +6886,7 @@ Variable::export_var(Export* exp, const std::string& name) const
   exp->write_string(name);
   exp->write_c_string(" ");
   exp->write_type(this->type());
-  exp->write_c_string(";\n");
+  exp->write_c_string("\n");
 }
 
 // Import a variable.
@@ -6897,7 +6898,8 @@ Variable::import_var(Import* imp, std::string* pname, Type** ptype)
   *pname = imp->read_identifier();
   imp->require_c_string(" ");
   *ptype = imp->read_type();
-  imp->require_c_string(";\n");
+  imp->require_semicolon_if_old_version();
+  imp->require_c_string("\n");
 }
 
 // Convert a variable to the backend representation.
@@ -7089,7 +7091,7 @@ Named_constant::export_const(Export* exp, const std::string& name) const
     }
   exp->write_c_string("= ");
   this->expr()->export_expression(exp);
-  exp->write_c_string(";\n");
+  exp->write_c_string("\n");
 }
 
 // Import a constant.
@@ -7110,7 +7112,8 @@ Named_constant::import_const(Import* imp, std::string* pname, Type** ptype,
     }
   imp->require_c_string("= ");
   *pexpr = Expression::import_expression(imp);
-  imp->require_c_string(";\n");
+  imp->require_semicolon_if_old_version();
+  imp->require_c_string("\n");
 }
 
 // Get the backend representation.
index f9586ac1600d19f43d16a13955a67a991f5e8966..ad30e6ee488e9d7d6093fc2b06d5284c27030afe 100644 (file)
@@ -241,8 +241,9 @@ Import::find_export_data(const std::string& filename, int fd, Location location)
     return NULL;
 
   // Check for a file containing nothing but Go export data.
-  if (memcmp(buf, Export::cur_magic, Export::magic_len) == 0 ||
-      memcmp(buf, Export::v1_magic, Export::magic_len) == 0)
+  if (memcmp(buf, Export::cur_magic, Export::magic_len) == 0
+      || memcmp(buf, Export::v1_magic, Export::magic_len) == 0
+      || memcmp(buf, Export::v2_magic, Export::magic_len) == 0)
     return new Stream_from_file(fd);
 
   // See if we can read this as an archive.
@@ -325,6 +326,12 @@ Import::import(Gogo* gogo, const std::string& local_name,
                                Export::magic_len);
          this->version_ = EXPORT_FORMAT_V1;
        }
+      else if (stream->match_bytes(Export::v2_magic, Export::magic_len))
+       {
+         stream->require_bytes(this->location_, Export::v2_magic,
+                               Export::magic_len);
+         this->version_ = EXPORT_FORMAT_V2;
+       }
       else
        {
          go_error_at(this->location_,
@@ -335,7 +342,8 @@ Import::import(Gogo* gogo, const std::string& local_name,
 
       this->require_c_string("package ");
       std::string package_name = this->read_identifier();
-      this->require_c_string(";\n");
+      this->require_semicolon_if_old_version();
+      this->require_c_string("\n");
 
       std::string pkgpath;
       std::string pkgpath_symbol;
@@ -343,7 +351,8 @@ Import::import(Gogo* gogo, const std::string& local_name,
        {
          this->advance(7);
          std::string unique_prefix = this->read_identifier();
-         this->require_c_string(";\n");
+         this->require_semicolon_if_old_version();
+         this->require_c_string("\n");
          pkgpath = unique_prefix + '.' + package_name;
          pkgpath_symbol = (Gogo::pkgpath_for_symbol(unique_prefix) + '.'
                            + Gogo::pkgpath_for_symbol(package_name));
@@ -352,10 +361,14 @@ Import::import(Gogo* gogo, const std::string& local_name,
        {
          this->require_c_string("pkgpath ");
          pkgpath = this->read_identifier();
-         this->require_c_string(";\n");
+         this->require_semicolon_if_old_version();
+         this->require_c_string("\n");
          pkgpath_symbol = Gogo::pkgpath_for_symbol(pkgpath);
        }
 
+      if (stream->saw_error())
+       return NULL;
+
       this->package_ = gogo->add_imported_package(package_name, local_name,
                                                  is_local_name_exported,
                                                  pkgpath, pkgpath_symbol,
@@ -418,7 +431,8 @@ Import::import(Gogo* gogo, const std::string& local_name,
       // load time.
       this->require_c_string("checksum ");
       stream->advance(Export::checksum_len * 2);
-      this->require_c_string(";\n");
+      this->require_semicolon_if_old_version();
+      this->require_c_string("\n");
     }
 
   return this->package_;
@@ -436,7 +450,8 @@ Import::read_one_package()
   std::string pkgpath = this->read_identifier();
   this->require_c_string(" ");
   std::string pkgpath_symbol = this->read_identifier();
-  this->require_c_string(";\n");
+  this->require_semicolon_if_old_version();
+  this->require_c_string("\n");
 
   Package* p = this->gogo_->register_package(pkgpath, pkgpath_symbol,
                                             Linemap::unknown_location());
@@ -456,7 +471,9 @@ Import::read_one_import()
   Stream* stream = this->stream_;
   while (stream->peek_char() != '"')
     stream->advance(1);
-  this->require_c_string("\";\n");
+  this->require_c_string("\"");
+  this->require_semicolon_if_old_version();
+  this->require_c_string("\n");
 
   Package* p = this->gogo_->register_package(pkgpath, "",
                                             Linemap::unknown_location());
@@ -474,7 +491,7 @@ Import::read_import_init_fns(Gogo* gogo)
   // to read the init_graph section.
   std::map<std::string, unsigned> init_idx;
 
-  while (!this->match_c_string(";"))
+  while (!this->match_c_string("\n") && !this->match_c_string(";"))
     {
       int priority = -1;
 
@@ -499,7 +516,8 @@ Import::read_import_init_fns(Gogo* gogo)
       unsigned idx = init_idx.size();
       init_idx[init_name] = idx;
     }
-  this->require_c_string(";\n");
+  this->require_semicolon_if_old_version();
+  this->require_c_string("\n");
 
   if (this->match_c_string("init_graph"))
     {
@@ -524,7 +542,7 @@ Import::read_import_init_fns(Gogo* gogo)
       //
       // where src + sink are init functions indices.
 
-      while (!this->match_c_string(";"))
+      while (!this->match_c_string("\n") && !this->match_c_string(";"))
        {
          this->require_c_string(" ");
          std::string src_string = this->read_identifier();
@@ -543,7 +561,8 @@ Import::read_import_init_fns(Gogo* gogo)
 
          ii_src->record_precursor_fcn(ii_sink->init_name());
        }
-      this->require_c_string(";\n");
+      this->require_semicolon_if_old_version();
+      this->require_c_string("\n");
     }
 }
 
@@ -967,7 +986,7 @@ Import::read_identifier()
   while (true)
     {
       c = stream->peek_char();
-      if (c == -1 || c == ' ' || c == ';')
+      if (c == -1 || c == ' ' || c == '\n' || c == ';')
        break;
       ret += c;
       stream->advance(1);
index aab4b899ca2d821c4c7e1965adf1767f32b7ce52..84f5fc7eb86ef255e71aca44030568cc05db9896 100644 (file)
@@ -184,6 +184,15 @@ class Import
   advance(size_t skip)
   { this->stream_->advance(skip); }
 
+  // Skip a semicolon if using an older version.
+  void
+  require_semicolon_if_old_version()
+  {
+    if (this->version_ == EXPORT_FORMAT_V1
+       || this->version_ == EXPORT_FORMAT_V2)
+      this->require_c_string(";");
+  }
+
   // Read an identifier.
   std::string
   read_identifier();
index 3768719a03ac3599de0743c34ce05bfa97e782ca..573011a25658da72e110850c022ac2d746dfbe35 100644 (file)
@@ -10892,7 +10892,7 @@ Named_type::export_named_type(Export* exp, const std::string&) const
   // be written by Export::write_type anyhow.
   exp->write_c_string("type ");
   exp->write_type(this);
-  exp->write_c_string(";\n");
+  exp->write_c_string("\n");
 }
 
 // Import a named type.
@@ -10904,7 +10904,8 @@ Named_type::import_named_type(Import* imp, Named_type** ptype)
   Type *type = imp->read_type();
   *ptype = type->named_type();
   go_assert(*ptype != NULL);
-  imp->require_c_string(";\n");
+  imp->require_semicolon_if_old_version();
+  imp->require_c_string("\n");
 }
 
 // Export the type when it is referenced by another type.  In this
index 7bc2afc4d78a5f668772869bd0ea0103417f803a..d3ce10cb0ca64f95e5ebcc44e603d9072deff7a1 100644 (file)
@@ -64,6 +64,7 @@ func findExportFile(searchpaths []string, pkgpath string) (string, error) {
 const (
        gccgov1Magic    = "v1;\n"
        gccgov2Magic    = "v2;\n"
+       gccgov3Magic    = "v3;\n"
        goimporterMagic = "\n$$ "
        archiveMagic    = "!<ar"
        aixbigafMagic   = "<big"
@@ -93,7 +94,7 @@ func openExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err e
 
        var objreader io.ReaderAt
        switch string(magic[:]) {
-       case gccgov1Magic, gccgov2Magic, goimporterMagic:
+       case gccgov1Magic, gccgov2Magic, gccgov3Magic, goimporterMagic:
                // Raw export data.
                reader = f
                return
@@ -208,7 +209,7 @@ func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) Impo
                }
 
                switch magics {
-               case gccgov1Magic, gccgov2Magic:
+               case gccgov1Magic, gccgov2Magic, gccgov3Magic:
                        var p parser
                        p.init(fpath, reader, imports)
                        pkg = p.parsePackage()
index 9f8c19b63887e7a3d0786d126998a4692ef4f4e0..5988c5d36738f117319cfb76eb84ab9c762f7466 100644 (file)
@@ -34,7 +34,7 @@ func (p *parser) init(filename string, src io.Reader, imports map[string]*types.
        p.scanner.Init(src)
        p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
        p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments
-       p.scanner.Whitespace = 1<<'\t' | 1<<'\n' | 1<<' '
+       p.scanner.Whitespace = 1<<'\t' | 1<<' '
        p.scanner.Filename = filename // for good error messages
        p.next()
        p.imports = imports
@@ -71,6 +71,13 @@ func (p *parser) expect(tok rune) string {
        return lit
 }
 
+func (p *parser) expectEOL() {
+       if p.version == "v1" || p.version == "v2" {
+               p.expect(';')
+       }
+       p.expect('\n')
+}
+
 func (p *parser) expectKeyword(keyword string) {
        lit := p.expect(scanner.Ident)
        if lit != keyword {
@@ -96,7 +103,7 @@ func (p *parser) parseUnquotedString() string {
        buf.WriteString(p.scanner.TokenText())
        // This loop needs to examine each character before deciding whether to consume it. If we see a semicolon,
        // we need to let it be consumed by p.next().
-       for ch := p.scanner.Peek(); ch != ';' && ch != scanner.EOF && p.scanner.Whitespace&(1<<uint(ch)) == 0; ch = p.scanner.Peek() {
+       for ch := p.scanner.Peek(); ch != '\n' && ch != ';' && ch != scanner.EOF && p.scanner.Whitespace&(1<<uint(ch)) == 0; ch = p.scanner.Peek() {
                buf.WriteRune(ch)
                p.scanner.Next()
        }
@@ -431,19 +438,22 @@ func (p *parser) parseNamedType(n int) types.Type {
                nt.SetUnderlying(underlying.Underlying())
        }
 
-       // collect associated methods
-       for p.tok == scanner.Ident {
-               p.expectKeyword("func")
-               p.expect('(')
-               receiver, _ := p.parseParam(pkg)
-               p.expect(')')
-               name := p.parseName()
-               params, isVariadic := p.parseParamList(pkg)
-               results := p.parseResultList(pkg)
-               p.expect(';')
-
-               sig := types.NewSignature(receiver, params, results, isVariadic)
-               nt.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig))
+       if p.tok == '\n' {
+               p.next()
+               // collect associated methods
+               for p.tok == scanner.Ident {
+                       p.expectKeyword("func")
+                       p.expect('(')
+                       receiver, _ := p.parseParam(pkg)
+                       p.expect(')')
+                       name := p.parseName()
+                       params, isVariadic := p.parseParamList(pkg)
+                       results := p.parseResultList(pkg)
+                       p.expectEOL()
+
+                       sig := types.NewSignature(receiver, params, results, isVariadic)
+                       nt.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig))
+               }
        }
 
        return nt
@@ -740,11 +750,12 @@ func (p *parser) parsePackageInit() PackageInit {
        return PackageInit{Name: name, InitFunc: initfunc, Priority: priority}
 }
 
-// Throw away tokens until we see a ';'. If we see a '<', attempt to parse as a type.
+// Throw away tokens until we see a newline or ';'.
+// If we see a '<', attempt to parse as a type.
 func (p *parser) discardDirectiveWhileParsingTypes(pkg *types.Package) {
        for {
                switch p.tok {
-               case ';':
+               case '\n', ';':
                        return
                case '<':
                        p.parseType(pkg)
@@ -763,7 +774,7 @@ func (p *parser) maybeCreatePackage() {
        }
 }
 
-// InitDataDirective = ( "v1" | "v2" ) ";" |
+// InitDataDirective = ( "v1" | "v2" | "v3" ) ";" |
 //                     "priority" int ";" |
 //                     "init" { PackageInit } ";" |
 //                     "checksum" unquotedString ";" .
@@ -774,31 +785,32 @@ func (p *parser) parseInitDataDirective() {
        }
 
        switch p.lit {
-       case "v1", "v2":
+       case "v1", "v2", "v3":
                p.version = p.lit
                p.next()
                p.expect(';')
+               p.expect('\n')
 
        case "priority":
                p.next()
                p.initdata.Priority = int(p.parseInt())
-               p.expect(';')
+               p.expectEOL()
 
        case "init":
                p.next()
-               for p.tok != ';' && p.tok != scanner.EOF {
+               for p.tok != '\n' && p.tok != ';' && p.tok != scanner.EOF {
                        p.initdata.Inits = append(p.initdata.Inits, p.parsePackageInit())
                }
-               p.expect(';')
+               p.expectEOL()
 
        case "init_graph":
                p.next()
                // The graph data is thrown away for now.
-               for p.tok != ';' && p.tok != scanner.EOF {
+               for p.tok != '\n' && p.tok != ';' && p.tok != scanner.EOF {
                        p.parseInt()
                        p.parseInt()
                }
-               p.expect(';')
+               p.expectEOL()
 
        case "checksum":
                // Don't let the scanner try to parse the checksum as a number.
@@ -808,7 +820,7 @@ func (p *parser) parseInitDataDirective() {
                p.scanner.Mode &^= scanner.ScanInts | scanner.ScanFloats
                p.next()
                p.parseUnquotedString()
-               p.expect(';')
+               p.expectEOL()
 
        default:
                p.errorf("unexpected identifier: %q", p.lit)
@@ -831,29 +843,29 @@ func (p *parser) parseDirective() {
        }
 
        switch p.lit {
-       case "v1", "v2", "priority", "init", "init_graph", "checksum":
+       case "v1", "v2", "v3", "priority", "init", "init_graph", "checksum":
                p.parseInitDataDirective()
 
        case "package":
                p.next()
                p.pkgname = p.parseUnquotedString()
                p.maybeCreatePackage()
-               if p.version == "v2" && p.tok != ';' {
+               if p.version != "v1" && p.tok != '\n' && p.tok != ';' {
                        p.parseUnquotedString()
                        p.parseUnquotedString()
                }
-               p.expect(';')
+               p.expectEOL()
 
        case "pkgpath":
                p.next()
                p.pkgpath = p.parseUnquotedString()
                p.maybeCreatePackage()
-               p.expect(';')
+               p.expectEOL()
 
        case "prefix":
                p.next()
                p.pkgpath = p.parseUnquotedString()
-               p.expect(';')
+               p.expectEOL()
 
        case "import":
                p.next()
@@ -861,7 +873,7 @@ func (p *parser) parseDirective() {
                pkgpath := p.parseUnquotedString()
                p.getPkg(pkgpath, pkgname)
                p.parseString()
-               p.expect(';')
+               p.expectEOL()
 
        case "func":
                p.next()
@@ -869,24 +881,24 @@ func (p *parser) parseDirective() {
                if fun != nil {
                        p.pkg.Scope().Insert(fun)
                }
-               p.expect(';')
+               p.expectEOL()
 
        case "type":
                p.next()
                p.parseType(p.pkg)
-               p.expect(';')
+               p.expectEOL()
 
        case "var":
                p.next()
                v := p.parseVar(p.pkg)
                p.pkg.Scope().Insert(v)
-               p.expect(';')
+               p.expectEOL()
 
        case "const":
                p.next()
                c := p.parseConst(p.pkg)
                p.pkg.Scope().Insert(c)
-               p.expect(';')
+               p.expectEOL()
 
        default:
                p.errorf("unexpected identifier: %q", p.lit)
index 4a103dc462af8e830b971bff75caa4b0d8359a7c..4d3568afc3df3d9c15d2b13cae60b989c2a8531c 100644 (file)
@@ -19,7 +19,7 @@ var typeParserTests = []struct {
        {id: "foo", typ: "<type 1 *<type -19>>", want: "*error"},
        {id: "foo", typ: "<type 1 *any>", want: "unsafe.Pointer"},
        {id: "foo", typ: "<type 1 \"Bar\" <type 2 *<type 1>>>", want: "foo.Bar", underlying: "*foo.Bar"},
-       {id: "foo", typ: "<type 1 \"bar.Foo\" \"bar\" <type -1> func (? <type 1>) M (); >", want: "bar.Foo", underlying: "int8", methods: "func (bar.Foo).M()"},
+       {id: "foo", typ: "<type 1 \"bar.Foo\" \"bar\" <type -1>\n func (? <type 1>) M ()\n>", want: "bar.Foo", underlying: "int8", methods: "func (bar.Foo).M()"},
        {id: "foo", typ: "<type 1 \".bar.foo\" \"bar\" <type -1>>", want: "bar.foo", underlying: "int8"},
        {id: "foo", typ: "<type 1 []<type -1>>", want: "[]int8"},
        {id: "foo", typ: "<type 1 [42]<type -1>>", want: "[42]int8"},