Commit f6fe3271 authored by Robert Griesemer's avatar Robert Griesemer

go/types: adjust gcimporter to actual gc export data

Unexported field and method names that appear in the
export data (as part of some exported type) are fully
qualified with a package id (path). In some cases, a
package with that id was never exported for any other
use (i.e. only the path is of interest).

We must not create a "real" package in those cases
because we don't have a package name. Entering an
unnamed package into the map of imported packages
makes that package accessible for other imports.
Such a subsequent import may find the unnamed
package in the map, and reuse it. That reused and
imported package is then entered into the importing
file scope, still w/o a name. References to that
package cannot resolved after that. Was bug.

R=adonovan
CC=golang-dev
https://golang.org/cl/7307112
parent 4a524311
...@@ -82,7 +82,7 @@ var tests = []string{ ...@@ -82,7 +82,7 @@ var tests = []string{
"crypto/md5", "crypto/md5",
// "crypto/rand", // "crypto/rand",
"crypto/rc4", "crypto/rc4",
// "crypto/rsa", // intermittent failure: /home/gri/go2/src/pkg/crypto/rsa/pkcs1v15.go:21:27: undeclared name: io "crypto/rsa",
"crypto/sha1", "crypto/sha1",
"crypto/sha256", "crypto/sha256",
"crypto/sha512", "crypto/sha512",
......
...@@ -131,7 +131,7 @@ func GcImport(imports map[string]*Package, path string) (pkg *Package, err error ...@@ -131,7 +131,7 @@ func GcImport(imports map[string]*Package, path string) (pkg *Package, err error
defer func() { defer func() {
f.Close() f.Close()
if err != nil { if err != nil {
// Add file name to error. // add file name to error
err = fmt.Errorf("reading export data: %s: %v", filename, err) err = fmt.Errorf("reading export data: %s: %v", filename, err)
} }
}() }()
...@@ -168,6 +168,15 @@ func (p *gcParser) init(filename, id string, src io.Reader, imports map[string]* ...@@ -168,6 +168,15 @@ func (p *gcParser) init(filename, id string, src io.Reader, imports map[string]*
p.next() p.next()
p.id = id p.id = id
p.imports = imports p.imports = imports
// leave for debugging
if false {
// check consistency of imports map
for _, pkg := range imports {
if pkg.Name == "" {
fmt.Printf("no package name for %s\n", pkg.Path)
}
}
}
} }
func (p *gcParser) next() { func (p *gcParser) next() {
...@@ -281,33 +290,27 @@ func (p *gcParser) expectKeyword(keyword string) { ...@@ -281,33 +290,27 @@ func (p *gcParser) expectKeyword(keyword string) {
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Import declarations // Qualified and unqualified names
// ImportPath = string_lit . // PackageId = string_lit .
// //
func (p *gcParser) parsePkgId() *Package { func (p *gcParser) parsePackageId() string {
id, err := strconv.Unquote(p.expect(scanner.String)) id, err := strconv.Unquote(p.expect(scanner.String))
if err != nil { if err != nil {
p.error(err) p.error(err)
} }
// id == "" stands for the imported package id
switch id { // (only known at time of package installation)
case "": if id == "" {
// id == "" stands for the imported package id
// (only known at time of package installation)
id = p.id id = p.id
case "unsafe":
// package unsafe is not in the imports map - handle explicitly
return Unsafe
}
pkg := p.imports[id]
if pkg == nil {
pkg = &Package{Scope: new(Scope)}
p.imports[id] = pkg
} }
return id
}
return pkg // PackageName = ident .
//
func (p *gcParser) parsePackageName() string {
return p.expect(scanner.Ident)
} }
// dotIdentifier = ( ident | '·' ) { ident | int | '·' } . // dotIdentifier = ( ident | '·' ) { ident | int | '·' } .
...@@ -327,14 +330,43 @@ func (p *gcParser) parseDotIdent() string { ...@@ -327,14 +330,43 @@ func (p *gcParser) parseDotIdent() string {
return ident return ident
} }
// ExportedName = "@" ImportPath "." dotIdentifier . // QualifiedName = "@" PackageId "." dotIdentifier .
// //
func (p *gcParser) parseExportedName() (*Package, string) { func (p *gcParser) parseQualifiedName() (id, name string) {
p.expect('@') p.expect('@')
pkg := p.parsePkgId() id = p.parsePackageId()
p.expect('.') p.expect('.')
name := p.parseDotIdent() name = p.parseDotIdent()
return pkg, name return
}
// getPkg returns the package for a given id. If the package is
// not found but we have a package name, create the package and
// add it to the p.imports map.
//
func (p *gcParser) getPkg(id, name string) *Package {
// package unsafe is not in the imports map - handle explicitly
if id == "unsafe" {
return Unsafe
}
pkg := p.imports[id]
if pkg == nil && name != "" {
pkg = &Package{Name: name, Path: id, Scope: new(Scope)}
p.imports[id] = pkg
}
return pkg
}
// parseExportedName is like parseQualifiedName, but
// the package id is resolved to an imported *Package.
//
func (p *gcParser) parseExportedName() (pkg *Package, name string) {
id, name := p.parseQualifiedName()
pkg = p.getPkg(id, "")
if pkg == nil {
p.errorf("%s package not found", id)
}
return
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
...@@ -377,9 +409,19 @@ func (p *gcParser) parseMapType() Type { ...@@ -377,9 +409,19 @@ func (p *gcParser) parseMapType() Type {
return &Map{Key: key, Elt: elt} return &Map{Key: key, Elt: elt}
} }
// Name = identifier | "?" | ExportedName . // Name = identifier | "?" | QualifiedName .
//
// If materializePkg is set, a package is returned for fully qualified names.
// That package may be a fake package (without name, scope, and not in the
// p.imports map), created for the sole purpose of providing a package path
// for QualifiedNames. Fake packages are created when the package id is not
// found in the p.imports map; we cannot create a real package in that case
// because we don't have a package name.
//
// TODO(gri): consider changing QualifiedIdents to (path, name) pairs to
// simplify this code.
// //
func (p *gcParser) parseName() (pkg *Package, name string) { func (p *gcParser) parseName(materializePkg bool) (pkg *Package, name string) {
switch p.tok { switch p.tok {
case scanner.Ident: case scanner.Ident:
name = p.lit name = p.lit
...@@ -389,7 +431,16 @@ func (p *gcParser) parseName() (pkg *Package, name string) { ...@@ -389,7 +431,16 @@ func (p *gcParser) parseName() (pkg *Package, name string) {
p.next() p.next()
case '@': case '@':
// exported name prefixed with package path // exported name prefixed with package path
pkg, name = p.parseExportedName() var id string
id, name = p.parseQualifiedName()
if materializePkg {
// we don't have a package name - if the package
// doesn't exist yet, create a fake package instead
pkg = p.getPkg(id, "")
if pkg == nil {
pkg = &Package{Path: id}
}
}
default: default:
p.error("name expected") p.error("name expected")
} }
...@@ -400,7 +451,7 @@ func (p *gcParser) parseName() (pkg *Package, name string) { ...@@ -400,7 +451,7 @@ func (p *gcParser) parseName() (pkg *Package, name string) {
// //
func (p *gcParser) parseField() *Field { func (p *gcParser) parseField() *Field {
var f Field var f Field
f.Pkg, f.Name = p.parseName() f.Pkg, f.Name = p.parseName(true)
f.Type = p.parseType() f.Type = p.parseType()
if p.tok == scanner.String { if p.tok == scanner.String {
f.Tag = p.expect(scanner.String) f.Tag = p.expect(scanner.String)
...@@ -439,7 +490,7 @@ func (p *gcParser) parseStructType() Type { ...@@ -439,7 +490,7 @@ func (p *gcParser) parseStructType() Type {
// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] . // Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] .
// //
func (p *gcParser) parseParameter() (par *Var, isVariadic bool) { func (p *gcParser) parseParameter() (par *Var, isVariadic bool) {
_, name := p.parseName() _, name := p.parseName(false)
if name == "" { if name == "" {
name = "_" // cannot access unnamed identifiers name = "_" // cannot access unnamed identifiers
} }
...@@ -489,6 +540,7 @@ func (p *gcParser) parseSignature() *Signature { ...@@ -489,6 +540,7 @@ func (p *gcParser) parseSignature() *Signature {
var results []*Var var results []*Var
switch p.tok { switch p.tok {
case scanner.Ident, '[', '*', '<', '@': case scanner.Ident, '[', '*', '<', '@':
// TODO(gri) does this ever happen?
// single, unnamed result // single, unnamed result
results = []*Var{{Type: p.parseType()}} results = []*Var{{Type: p.parseType()}}
case '(': case '(':
...@@ -520,7 +572,7 @@ func (p *gcParser) parseInterfaceType() Type { ...@@ -520,7 +572,7 @@ func (p *gcParser) parseInterfaceType() Type {
if len(methods) > 0 { if len(methods) > 0 {
p.expect(';') p.expect(';')
} }
pkg, name := p.parseName() pkg, name := p.parseName(true)
typ := p.parseSignature() typ := p.parseSignature()
methods = append(methods, &Method{QualifiedName{pkg, name}, typ}) methods = append(methods, &Method{QualifiedName{pkg, name}, typ})
} }
...@@ -610,17 +662,12 @@ func (p *gcParser) parseType() Type { ...@@ -610,17 +662,12 @@ func (p *gcParser) parseType() Type {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Declarations // Declarations
// ImportDecl = "import" identifier string_lit . // ImportDecl = "import" PackageName PackageId .
// //
func (p *gcParser) parseImportDecl() { func (p *gcParser) parseImportDecl() {
p.expectKeyword("import") p.expectKeyword("import")
// The identifier has no semantic meaning in the import data. name := p.parsePackageName()
// It exists so that error messages can print the real package p.getPkg(p.parsePackageId(), name)
// name: binary.ByteOrder instead of "encoding/binary".ByteOrder.
name := p.expect(scanner.Ident)
pkg := p.parsePkgId()
assert(pkg.Name == "" || pkg.Name == name)
pkg.Name = name
} }
// int_lit = [ "+" | "-" ] { "0" ... "9" } . // int_lit = [ "+" | "-" ] { "0" ... "9" } .
...@@ -814,7 +861,7 @@ func (p *gcParser) parseMethodDecl() { ...@@ -814,7 +861,7 @@ func (p *gcParser) parseMethodDecl() {
base := typ.(*NamedType) base := typ.(*NamedType)
// parse method name, signature, and possibly inlined body // parse method name, signature, and possibly inlined body
pkg, name := p.parseName() // unexported method names in imports are qualified with their package. pkg, name := p.parseName(true) // unexported method names in imports are qualified with their package.
sig := p.parseFunc() sig := p.parseFunc()
sig.Recv = recv sig.Recv = recv
...@@ -865,11 +912,11 @@ func (p *gcParser) parseDecl() { ...@@ -865,11 +912,11 @@ func (p *gcParser) parseDecl() {
// Export // Export
// Export = "PackageClause { Decl } "$$" . // Export = "PackageClause { Decl } "$$" .
// PackageClause = "package" identifier [ "safe" ] "\n" . // PackageClause = "package" PackageName [ "safe" ] "\n" .
// //
func (p *gcParser) parseExport() *Package { func (p *gcParser) parseExport() *Package {
p.expectKeyword("package") p.expectKeyword("package")
name := p.expect(scanner.Ident) name := p.parsePackageName()
if p.tok != '\n' { if p.tok != '\n' {
// A package is safe if it was compiled with the -u flag, // A package is safe if it was compiled with the -u flag,
// which disables the unsafe package. // which disables the unsafe package.
...@@ -878,11 +925,7 @@ func (p *gcParser) parseExport() *Package { ...@@ -878,11 +925,7 @@ func (p *gcParser) parseExport() *Package {
} }
p.expect('\n') p.expect('\n')
pkg := p.imports[p.id] pkg := p.getPkg(p.id, name)
if pkg == nil {
pkg = &Package{Name: name, Scope: new(Scope)}
p.imports[p.id] = pkg
}
for p.tok != '$' && p.tok != scanner.EOF { for p.tok != '$' && p.tok != scanner.EOF {
p.parseDecl() p.parseDecl()
......
...@@ -4,6 +4,11 @@ ...@@ -4,6 +4,11 @@
package types package types
import (
"bytes"
"fmt"
)
// A Scope maintains the set of named language entities declared // A Scope maintains the set of named language entities declared
// in the scope and a link to the immediately surrounding (outer) // in the scope and a link to the immediately surrounding (outer)
// scope. // scope.
...@@ -57,3 +62,17 @@ func (s *Scope) Insert(obj Object) Object { ...@@ -57,3 +62,17 @@ func (s *Scope) Insert(obj Object) Object {
return nil return nil
} }
// Debugging support
func (s *Scope) String() string {
var buf bytes.Buffer
fmt.Fprintf(&buf, "scope %p {", s)
if s != nil && len(s.Entries) > 0 {
fmt.Fprintln(&buf)
for _, obj := range s.Entries {
fmt.Fprintf(&buf, "\t%s\t%T\n", obj.GetName(), obj)
}
}
fmt.Fprintf(&buf, "}\n")
return buf.String()
}
...@@ -90,8 +90,11 @@ type Slice struct { ...@@ -90,8 +90,11 @@ type Slice struct {
} }
// A QualifiedName is a name qualified with the package that declared the name. // A QualifiedName is a name qualified with the package that declared the name.
// Note: Pkg may be a fake package (no name, no scope) because the GC compiler's
// export information doesn't provide full information in some cases.
// TODO(gri): Should change Pkg to PkgPath since it's the only thing we care about.
type QualifiedName struct { type QualifiedName struct {
Pkg *Package // nil only for predeclared error.Error Pkg *Package // nil only for predeclared error.Error (exported)
Name string // unqualified type name for anonymous fields Name string // unqualified type name for anonymous fields
} }
...@@ -105,7 +108,7 @@ func (p QualifiedName) IsSame(q QualifiedName) bool { ...@@ -105,7 +108,7 @@ func (p QualifiedName) IsSame(q QualifiedName) bool {
return false return false
} }
// p.Name == q.Name // p.Name == q.Name
return ast.IsExported(p.Name) || p.Pkg == q.Pkg return ast.IsExported(p.Name) || p.Pkg.Path == q.Pkg.Path
} }
// A Field represents a field of a struct. // A Field represents a field of a struct.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment