diff --git a/cmd/gotype/gotype.go b/cmd/gotype/gotype.go index 789d8223..45808087 100644 --- a/cmd/gotype/gotype.go +++ b/cmd/gotype/gotype.go @@ -18,6 +18,7 @@ import ( "runtime" "time" + _ "code.google.com/p/go.tools/go/gcimporter" "code.google.com/p/go.tools/go/types" ) diff --git a/cmd/vet/main.go b/cmd/vet/main.go index 362162fa..3763cc8d 100644 --- a/cmd/vet/main.go +++ b/cmd/vet/main.go @@ -22,6 +22,7 @@ import ( "strings" "code.google.com/p/go.tools/go/exact" + _ "code.google.com/p/go.tools/go/gcimporter" "code.google.com/p/go.tools/go/types" ) diff --git a/go/types/exportdata.go b/go/gcimporter/exportdata.go similarity index 93% rename from go/types/exportdata.go rename to go/gcimporter/exportdata.go index 1f6a3c72..c53d18cb 100644 --- a/go/types/exportdata.go +++ b/go/gcimporter/exportdata.go @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file implements FindGcExportData. +// This file implements FindExportData. -package types +package gcimporter import ( "bufio" @@ -36,12 +36,12 @@ func readGopackHeader(r *bufio.Reader) (name string, size int, err error) { return } -// FindGcExportData positions the reader r at the beginning of the +// FindExportData positions the reader r at the beginning of the // export data section of an underlying GC-created object/archive // file by reading from it. The reader must be positioned at the // start of the file before calling this function. // -func FindGcExportData(r *bufio.Reader) (err error) { +func FindExportData(r *bufio.Reader) (err error) { // Read first line to make sure this is an object file. line, err := r.ReadSlice('\n') if err != nil { diff --git a/go/types/gcimporter.go b/go/gcimporter/gcimporter.go similarity index 70% rename from go/types/gcimporter.go rename to go/gcimporter/gcimporter.go index 065b8a1b..86aba4fc 100644 --- a/go/types/gcimporter.go +++ b/go/gcimporter/gcimporter.go @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file implements an Importer for gc-generated object files. - -package types +// Package gcimporter implements Import for gc-generated object files. +// Importing this package installs Import as go/types.DefaultImport. +package gcimporter import ( "bufio" @@ -16,14 +16,21 @@ import ( "io" "os" "path/filepath" - "sort" "strconv" "strings" "text/scanner" "code.google.com/p/go.tools/go/exact" + "code.google.com/p/go.tools/go/types" ) +// debugging/development support +const debug = false + +func init() { + types.DefaultImport = Import +} + var pkgExts = [...]string{".a", ".5", ".6", ".8"} // FindPkg returns the filename and unique package id for an import @@ -72,7 +79,7 @@ func FindPkg(path, srcDir string) (filename, id string) { return } -// GcImportData imports a package by reading the gc-generated export data, +// ImportData imports a package by reading the gc-generated export data, // adds the corresponding package object to the imports map indexed by id, // and returns the object. // @@ -84,8 +91,8 @@ func FindPkg(path, srcDir string) (filename, id string) { // can be used directly, and there is no need to call this function (but // there is also no harm but for extra time used). // -func GcImportData(imports map[string]*Package, filename, id string, data *bufio.Reader) (pkg *Package, err error) { - // support for gcParser error handling +func ImportData(imports map[string]*types.Package, filename, id string, data *bufio.Reader) (pkg *types.Package, err error) { + // support for parser error handling defer func() { switch r := recover().(type) { case nil: @@ -97,21 +104,21 @@ func GcImportData(imports map[string]*Package, filename, id string, data *bufio. } }() - var p gcParser + var p parser p.init(filename, id, data, imports) pkg = p.parseExport() return } -// GcImport imports a gc-generated package given its import path, adds the +// Import imports a gc-generated package given its import path, adds the // corresponding package object to the imports map, and returns the object. // Local import paths are interpreted relative to the current working directory. // The imports map must contains all packages already imported. // -func GcImport(imports map[string]*Package, path string) (pkg *Package, err error) { +func Import(imports map[string]*types.Package, path string) (pkg *types.Package, err error) { if path == "unsafe" { - return Unsafe, nil + return types.Unsafe, nil } srcDir := "." @@ -129,7 +136,7 @@ func GcImport(imports map[string]*Package, path string) (pkg *Package, err error } // no need to re-import if the package was imported completely before - if pkg = imports[id]; pkg != nil && pkg.complete { + if pkg = imports[id]; pkg != nil && pkg.Complete() { return } @@ -147,17 +154,17 @@ func GcImport(imports map[string]*Package, path string) (pkg *Package, err error }() buf := bufio.NewReader(f) - if err = FindGcExportData(buf); err != nil { + if err = FindExportData(buf); err != nil { return } - pkg, err = GcImportData(imports, filename, id, buf) + pkg, err = ImportData(imports, filename, id, buf) return } // ---------------------------------------------------------------------------- -// gcParser +// Parser // TODO(gri) Imported objects don't have position information. // Ideally use the debug table line info; alternatively @@ -165,17 +172,17 @@ func GcImport(imports map[string]*Package, path string) (pkg *Package, err error // import). That way error messages referring to imported // objects can print meaningful information. -// gcParser parses the exports inside a gc compiler-produced +// parser parses the exports inside a gc compiler-produced // object/archive file and populates its scope with the results. -type gcParser struct { +type parser struct { scanner scanner.Scanner - tok rune // current token - lit string // literal string; only valid for Ident, Int, String tokens - id string // package id of imported package - imports map[string]*Package // package id -> package object + tok rune // current token + lit string // literal string; only valid for Ident, Int, String tokens + id string // package id of imported package + imports map[string]*types.Package // package id -> package object } -func (p *gcParser) init(filename, id string, src io.Reader, imports map[string]*Package) { +func (p *parser) init(filename, id string, src io.Reader, imports map[string]*types.Package) { p.scanner.Init(src) p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) } p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanChars | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments @@ -184,18 +191,17 @@ func (p *gcParser) init(filename, id string, src io.Reader, imports map[string]* p.next() p.id = id p.imports = imports - // leave for debugging - if false { + if debug { // check consistency of imports map for _, pkg := range imports { - if pkg.name == "" { - fmt.Printf("no package name for %s\n", pkg.path) + if pkg.Name() == "" { + fmt.Printf("no package name for %s\n", pkg.Path()) } } } } -func (p *gcParser) next() { +func (p *parser) next() { p.tok = p.scanner.Scan() switch p.tok { case scanner.Ident, scanner.Int, scanner.Char, scanner.String, '·': @@ -203,54 +209,20 @@ func (p *gcParser) next() { default: p.lit = "" } - // leave for debugging - if false { + if debug { fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit) } } -func declConst(pkg *Package, name string) *Const { - // the constant may have been imported before - if it exists - // already in the respective scope, return that constant - scope := pkg.scope +func declTypeName(pkg *types.Package, name string) *types.TypeName { + scope := pkg.Scope() if obj := scope.Lookup(name); obj != nil { - return obj.(*Const) + return obj.(*types.TypeName) } - // otherwise create a new constant and insert it into the scope - obj := NewConst(token.NoPos, pkg, name, nil, nil) - scope.Insert(obj) - return obj -} - -func declTypeName(pkg *Package, name string) *TypeName { - scope := pkg.scope - if obj := scope.Lookup(name); obj != nil { - return obj.(*TypeName) - } - obj := NewTypeName(token.NoPos, pkg, name, nil) + obj := types.NewTypeName(token.NoPos, pkg, name, nil) // a named type may be referred to before the underlying type // is known - set it up - obj.typ = &Named{obj: obj} - scope.Insert(obj) - return obj -} - -func declVar(pkg *Package, name string) *Var { - scope := pkg.scope - if obj := scope.Lookup(name); obj != nil { - return obj.(*Var) - } - obj := NewVar(token.NoPos, pkg, name, nil) - scope.Insert(obj) - return obj -} - -func declFunc(pkg *Package, name string) *Func { - scope := pkg.scope - if obj := scope.Lookup(name); obj != nil { - return obj.(*Func) - } - obj := NewFunc(token.NoPos, pkg, name, nil) + types.NewNamed(obj, nil, nil) scope.Insert(obj) return obj } @@ -268,7 +240,7 @@ func (e importError) Error() string { return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err) } -func (p *gcParser) error(err interface{}) { +func (p *parser) error(err interface{}) { if s, ok := err.(string); ok { err = errors.New(s) } @@ -276,11 +248,11 @@ func (p *gcParser) error(err interface{}) { panic(importError{p.scanner.Pos(), err.(error)}) } -func (p *gcParser) errorf(format string, args ...interface{}) { +func (p *parser) errorf(format string, args ...interface{}) { p.error(fmt.Sprintf(format, args...)) } -func (p *gcParser) expect(tok rune) string { +func (p *parser) expect(tok rune) string { lit := p.lit if p.tok != tok { p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit) @@ -289,7 +261,7 @@ func (p *gcParser) expect(tok rune) string { return lit } -func (p *gcParser) expectSpecial(tok string) { +func (p *parser) expectSpecial(tok string) { sep := 'x' // not white space i := 0 for i < len(tok) && p.tok == rune(tok[i]) && sep > ' ' { @@ -302,7 +274,7 @@ func (p *gcParser) expectSpecial(tok string) { } } -func (p *gcParser) expectKeyword(keyword string) { +func (p *parser) expectKeyword(keyword string) { lit := p.expect(scanner.Ident) if lit != keyword { p.errorf("expected keyword %s, got %q", keyword, lit) @@ -314,7 +286,7 @@ func (p *gcParser) expectKeyword(keyword string) { // PackageId = string_lit . // -func (p *gcParser) parsePackageId() string { +func (p *parser) parsePackageId() string { id, err := strconv.Unquote(p.expect(scanner.String)) if err != nil { p.error(err) @@ -329,12 +301,12 @@ func (p *gcParser) parsePackageId() string { // PackageName = ident . // -func (p *gcParser) parsePackageName() string { +func (p *parser) parsePackageName() string { return p.expect(scanner.Ident) } // dotIdentifier = ( ident | '·' ) { ident | int | '·' } . -func (p *gcParser) parseDotIdent() string { +func (p *parser) parseDotIdent() string { ident := "" if p.tok != scanner.Int { sep := 'x' // not white space @@ -352,7 +324,7 @@ func (p *gcParser) parseDotIdent() string { // QualifiedName = "@" PackageId "." ( "?" | dotIdentifier ) . // -func (p *gcParser) parseQualifiedName() (id, name string) { +func (p *parser) parseQualifiedName() (id, name string) { p.expect('@') id = p.parsePackageId() p.expect('.') @@ -369,23 +341,23 @@ func (p *gcParser) parseQualifiedName() (id, name string) { // 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 { +func (p *parser) getPkg(id, name string) *types.Package { // package unsafe is not in the imports map - handle explicitly if id == "unsafe" { - return Unsafe + return types.Unsafe } pkg := p.imports[id] if pkg == nil && name != "" { - pkg = NewPackage(id, name, NewScope(nil)) + pkg = types.NewPackage(id, name, types.NewScope(nil)) p.imports[id] = pkg } return pkg } // parseExportedName is like parseQualifiedName, but -// the package id is resolved to an imported *Package. +// the package id is resolved to an imported *types.Package. // -func (p *gcParser) parseExportedName() (pkg *Package, name string) { +func (p *parser) parseExportedName() (pkg *types.Package, name string) { id, name := p.parseQualifiedName() pkg = p.getPkg(id, "") if pkg == nil { @@ -399,11 +371,11 @@ func (p *gcParser) parseExportedName() (pkg *Package, name string) { // BasicType = identifier . // -func (p *gcParser) parseBasicType() Type { +func (p *parser) parseBasicType() types.Type { id := p.expect(scanner.Ident) - obj := Universe.Lookup(id) - if obj, ok := obj.(*TypeName); ok { - return obj.typ + obj := types.Universe.Lookup(id) + if obj, ok := obj.(*types.TypeName); ok { + return obj.Type() } p.errorf("not a basic type: %s", id) return nil @@ -411,7 +383,7 @@ func (p *gcParser) parseBasicType() Type { // ArrayType = "[" int_lit "]" Type . // -func (p *gcParser) parseArrayType() Type { +func (p *parser) parseArrayType() types.Type { // "[" already consumed and lookahead known not to be "]" lit := p.expect(scanner.Int) p.expect(']') @@ -420,18 +392,18 @@ func (p *gcParser) parseArrayType() Type { if err != nil { p.error(err) } - return &Array{len: n, elem: elem} + return types.NewArray(elem, n) } // MapType = "map" "[" Type "]" Type . // -func (p *gcParser) parseMapType() Type { +func (p *parser) parseMapType() types.Type { p.expectKeyword("map") p.expect('[') key := p.parseType() p.expect(']') elem := p.parseType() - return &Map{key: key, elem: elem} + return types.NewMap(key, elem) } // Name = identifier | "?" | QualifiedName . @@ -444,7 +416,7 @@ func (p *gcParser) parseMapType() Type { // we cannot create a real package because we don't have a package name. // For non-qualified names, the returned package is the imported package. // -func (p *gcParser) parseName(materializePkg bool) (pkg *Package, name string) { +func (p *parser) parseName(materializePkg bool) (pkg *types.Package, name string) { switch p.tok { case scanner.Ident: pkg = p.imports[p.id] @@ -463,7 +435,7 @@ func (p *gcParser) parseName(materializePkg bool) (pkg *Package, name string) { // doesn't exist yet, create a fake package instead pkg = p.getPkg(id, "") if pkg == nil { - pkg = &Package{path: id} + pkg = types.NewPackage(id, "", nil) } } default: @@ -472,21 +444,29 @@ func (p *gcParser) parseName(materializePkg bool) (pkg *Package, name string) { return } +func deref(typ types.Type) types.Type { + if p, _ := typ.(*types.Pointer); p != nil { + return p.Elem() + } + return typ +} + // Field = Name Type [ string_lit ] . // -func (p *gcParser) parseField() (*Var, string) { +func (p *parser) parseField() (*types.Var, string) { pkg, name := p.parseName(true) typ := p.parseType() anonymous := false if name == "" { // anonymous field - typ must be T or *T and T must be a type name - switch typ, _ := deref(typ); typ := typ.(type) { - case *Basic: // basic types are named types + switch typ := deref(typ).(type) { + case *types.Basic: // basic types are named types pkg = nil - name = typ.name - case *Named: - pkg = typ.obj.pkg // TODO(gri) is this still correct? - name = typ.obj.name + name = typ.Name() + case *types.Named: + obj := typ.Obj() + pkg = obj.Pkg() // TODO(gri) is this still correct? + name = obj.Name() default: p.errorf("anonymous field expected") } @@ -496,19 +476,18 @@ func (p *gcParser) parseField() (*Var, string) { if p.tok == scanner.String { tag = p.expect(scanner.String) } - return NewField(token.NoPos, pkg, name, typ, anonymous), tag + return types.NewField(token.NoPos, pkg, name, typ, anonymous), tag } // StructType = "struct" "{" [ FieldList ] "}" . // FieldList = Field { ";" Field } . // -func (p *gcParser) parseStructType() Type { - var fields []*Var +func (p *parser) parseStructType() types.Type { + var fields []*types.Var var tags []string p.expectKeyword("struct") p.expect('{') - var fset objset for i := 0; p.tok != '}'; i++ { if i > 0 { p.expect(';') @@ -520,26 +499,16 @@ func (p *gcParser) parseStructType() Type { if tags != nil { tags = append(tags, tag) } - if fld.name != "_" { - if alt := fset.insert(fld); alt != nil { - pname := "" - if pkg := alt.Pkg(); pkg != nil { - pname = pkg.name - } - p.errorf("multiple fields named %s.%s", pname, alt.Name()) - continue - } - } fields = append(fields, fld) } p.expect('}') - return &Struct{fields: fields, tags: tags} + return types.NewStruct(fields, tags) } // Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] . // -func (p *gcParser) parseParameter() (par *Var, isVariadic bool) { +func (p *parser) parseParameter() (par *types.Var, isVariadic bool) { _, name := p.parseName(false) if name == "" { name = "_" // cannot access unnamed identifiers @@ -550,21 +519,21 @@ func (p *gcParser) parseParameter() (par *Var, isVariadic bool) { } typ := p.parseType() if isVariadic { - typ = &Slice{elem: typ} + typ = types.NewSlice(typ) } // ignore argument tag (e.g. "noescape") if p.tok == scanner.String { p.next() } // TODO(gri) should we provide a package? - par = NewVar(token.NoPos, nil, name, typ) + par = types.NewVar(token.NoPos, nil, name, typ) return } // Parameters = "(" [ ParameterList ] ")" . // ParameterList = { Parameter "," } Parameter . // -func (p *gcParser) parseParameters() (list []*Var, isVariadic bool) { +func (p *parser) parseParameters() (list []*types.Var, isVariadic bool) { p.expect('(') for p.tok != ')' { if len(list) > 0 { @@ -587,11 +556,11 @@ func (p *gcParser) parseParameters() (list []*Var, isVariadic bool) { // Signature = Parameters [ Result ] . // Result = Type | Parameters . // -func (p *gcParser) parseSignature() *Signature { +func (p *parser) parseSignature(recv *types.Var) *types.Signature { params, isVariadic := p.parseParameters() // optional result type - var results []*Var + var results []*types.Var if p.tok == '(' { var variadic bool results, variadic = p.parseParameters() @@ -600,7 +569,7 @@ func (p *gcParser) parseSignature() *Signature { } } - return &Signature{params: NewTuple(params...), results: NewTuple(results...), isVariadic: isVariadic} + return types.NewSignature(nil, recv, types.NewTuple(params...), types.NewTuple(results...), isVariadic) } // InterfaceType = "interface" "{" [ MethodList ] "}" . @@ -611,40 +580,27 @@ func (p *gcParser) parseSignature() *Signature { // by the compiler and thus embedded interfaces are never // visible in the export data. // -func (p *gcParser) parseInterfaceType() Type { - typ := new(Interface) - var methods []*Func +func (p *parser) parseInterfaceType() types.Type { + var methods []*types.Func p.expectKeyword("interface") p.expect('{') - var mset objset for i := 0; p.tok != '}'; i++ { if i > 0 { p.expect(';') } pkg, name := p.parseName(true) - sig := p.parseSignature() - // TODO(gri) Ideally, we should use a named type here instead of - // typ, for less verbose printing of interface method signatures. - sig.recv = NewVar(token.NoPos, pkg, "", typ) - m := NewFunc(token.NoPos, pkg, name, sig) - if alt := mset.insert(m); alt != nil { - p.errorf("multiple methods named %s.%s", alt.Pkg().name, alt.Name()) - continue - } - methods = append(methods, m) + sig := p.parseSignature(nil) + methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig)) } p.expect('}') - sort.Sort(byUniqueMethodName(methods)) - typ.methods = methods - typ.allMethods = methods // ok to share underlying array since we are not changing methods - return typ + return types.NewInterface(methods, nil) } // ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type . // -func (p *gcParser) parseChanType() Type { +func (p *parser) parseChanType() types.Type { dir := ast.SEND | ast.RECV if p.tok == scanner.Ident { p.expectKeyword("chan") @@ -658,7 +614,7 @@ func (p *gcParser) parseChanType() Type { dir = ast.RECV } elem := p.parseType() - return &Chan{dir: dir, elem: elem} + return types.NewChan(dir, elem) } // Type = @@ -672,7 +628,7 @@ func (p *gcParser) parseChanType() Type { // PointerType = "*" Type . // FuncType = "func" Signature . // -func (p *gcParser) parseType() Type { +func (p *parser) parseType() types.Type { switch p.tok { case scanner.Ident: switch p.lit { @@ -683,7 +639,7 @@ func (p *gcParser) parseType() Type { case "func": // FuncType p.next() - return p.parseSignature() + return p.parseSignature(nil) case "interface": return p.parseInterfaceType() case "map": @@ -694,19 +650,19 @@ func (p *gcParser) parseType() Type { case '@': // TypeName pkg, name := p.parseExportedName() - return declTypeName(pkg, name).typ + return declTypeName(pkg, name).Type() case '[': p.next() // look ahead if p.tok == ']' { // SliceType p.next() - return &Slice{elem: p.parseType()} + return types.NewSlice(p.parseType()) } return p.parseArrayType() case '*': // PointerType p.next() - return &Pointer{base: p.parseType()} + return types.NewPointer(p.parseType()) case '<': return p.parseChanType() case '(': @@ -725,7 +681,7 @@ func (p *gcParser) parseType() Type { // ImportDecl = "import" PackageName PackageId . // -func (p *gcParser) parseImportDecl() { +func (p *parser) parseImportDecl() { p.expectKeyword("import") name := p.parsePackageName() p.getPkg(p.parsePackageId(), name) @@ -733,7 +689,7 @@ func (p *gcParser) parseImportDecl() { // int_lit = [ "+" | "-" ] { "0" ... "9" } . // -func (p *gcParser) parseInt() string { +func (p *parser) parseInt() string { s := "" switch p.tok { case '-': @@ -747,12 +703,12 @@ func (p *gcParser) parseInt() string { // number = int_lit [ "p" int_lit ] . // -func (p *gcParser) parseNumber() (x operand) { - x.mode = constant - +func (p *parser) parseNumber() (typ *types.Basic, val exact.Value) { // mantissa mant := exact.MakeFromLiteral(p.parseInt(), token.INT) - assert(mant != nil) + if mant == nil { + panic("invalid mantissa") + } if p.lit == "p" { // exponent (base 2) @@ -764,20 +720,20 @@ func (p *gcParser) parseNumber() (x operand) { if exp < 0 { denom := exact.MakeInt64(1) denom = exact.Shift(denom, token.SHL, uint(-exp)) - x.typ = Typ[UntypedFloat] - x.val = exact.BinaryOp(mant, token.QUO, denom) + typ = types.Typ[types.UntypedFloat] + val = exact.BinaryOp(mant, token.QUO, denom) return } if exp > 0 { mant = exact.Shift(mant, token.SHL, uint(exp)) } - x.typ = Typ[UntypedFloat] - x.val = mant + typ = types.Typ[types.UntypedFloat] + val = mant return } - x.typ = Typ[UntypedInt] - x.val = mant + typ = types.Typ[types.UntypedInt] + val = mant return } @@ -788,28 +744,31 @@ func (p *gcParser) parseNumber() (x operand) { // rune_lit = "(" int_lit "+" int_lit ")" . // string_lit = `"` { unicode_char } `"` . // -func (p *gcParser) parseConstDecl() { +func (p *parser) parseConstDecl() { p.expectKeyword("const") pkg, name := p.parseExportedName() - obj := declConst(pkg, name) - var x operand + + var typ0 types.Type if p.tok != '=' { - obj.typ = p.parseType() + typ0 = p.parseType() } + p.expect('=') + var typ types.Type + var val exact.Value switch p.tok { case scanner.Ident: // bool_lit if p.lit != "true" && p.lit != "false" { p.error("expected true or false") } - x.typ = Typ[UntypedBool] - x.val = exact.MakeBool(p.lit == "true") + typ = types.Typ[types.UntypedBool] + val = exact.MakeBool(p.lit == "true") p.next() case '-', scanner.Int: // int_lit - x = p.parseNumber() + typ, val = p.parseNumber() case '(': // complex_lit or rune_lit @@ -817,44 +776,45 @@ func (p *gcParser) parseConstDecl() { if p.tok == scanner.Char { p.next() p.expect('+') - x = p.parseNumber() - x.typ = Typ[UntypedRune] + typ = types.Typ[types.UntypedRune] + _, val = p.parseNumber() p.expect(')') break } - re := p.parseNumber() + _, re := p.parseNumber() p.expect('+') - im := p.parseNumber() + _, im := p.parseNumber() p.expectKeyword("i") p.expect(')') - x.typ = Typ[UntypedComplex] - // TODO(gri) fix this - _, _ = re, im - x.val = exact.MakeInt64(0) + typ = types.Typ[types.UntypedComplex] + val = exact.BinaryOp(re, token.ADD, exact.MakeImag(im)) case scanner.Char: // rune_lit - x.setConst(token.CHAR, p.lit) + typ = types.Typ[types.UntypedRune] + val = exact.MakeFromLiteral(p.lit, token.CHAR) p.next() case scanner.String: // string_lit - x.setConst(token.STRING, p.lit) + typ = types.Typ[types.UntypedString] + val = exact.MakeFromLiteral(p.lit, token.STRING) p.next() default: p.errorf("expected literal got %s", scanner.TokenString(p.tok)) } - if obj.typ == nil { - obj.typ = x.typ + + if typ0 == nil { + typ0 = typ } - assert(x.val != nil) - obj.val = x.val + + pkg.Scope().Insert(types.NewConst(token.NoPos, pkg, name, typ0, val)) } // TypeDecl = "type" ExportedName Type . // -func (p *gcParser) parseTypeDecl() { +func (p *parser) parseTypeDecl() { p.expectKeyword("type") pkg, name := p.parseExportedName() obj := declTypeName(pkg, name) @@ -866,26 +826,25 @@ func (p *gcParser) parseTypeDecl() { // a given type declaration. typ := p.parseType() - if name := obj.typ.(*Named); name.underlying == nil { - name.underlying = typ - name.complete = true + if name := obj.Type().(*types.Named); name.Underlying() == nil { + name.SetUnderlying(typ) } } // VarDecl = "var" ExportedName Type . // -func (p *gcParser) parseVarDecl() { +func (p *parser) parseVarDecl() { p.expectKeyword("var") pkg, name := p.parseExportedName() - obj := declVar(pkg, name) - obj.typ = p.parseType() + typ := p.parseType() + pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, typ)) } // Func = Signature [ Body ] . // Body = "{" ... "}" . // -func (p *gcParser) parseFunc() *Signature { - sig := p.parseSignature() +func (p *parser) parseFunc(recv *types.Var) *types.Signature { + sig := p.parseSignature(recv) if p.tok == '{' { p.next() for i := 1; i > 0; p.next() { @@ -903,44 +862,37 @@ func (p *gcParser) parseFunc() *Signature { // MethodDecl = "func" Receiver Name Func . // Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" . // -func (p *gcParser) parseMethodDecl() { +func (p *parser) parseMethodDecl() { // "func" already consumed p.expect('(') recv, _ := p.parseParameter() // receiver p.expect(')') // determine receiver base type object - typ := recv.typ - if ptr, ok := typ.(*Pointer); ok { - typ = ptr.base - } - base := typ.(*Named) + base := deref(recv.Type()).(*types.Named) // parse method name, signature, and possibly inlined body pkg, name := p.parseName(true) - sig := p.parseFunc() - sig.recv = recv + sig := p.parseFunc(recv) // add method to type unless type was imported before // and method exists already - // TODO(gri) This is a quadratic algorithm - ok for now because method counts are small. - if _, m := lookupMethod(base.methods, pkg, name); m == nil { - base.methods = append(base.methods, NewFunc(token.NoPos, pkg, name, sig)) - } + // TODO(gri) This leads to a quadratic algorithm - ok for now because method counts are small. + base.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig)) } // FuncDecl = "func" ExportedName Func . // -func (p *gcParser) parseFuncDecl() { +func (p *parser) parseFuncDecl() { // "func" already consumed pkg, name := p.parseExportedName() - typ := p.parseFunc() - declFunc(pkg, name).typ = typ + typ := p.parseFunc(nil) + pkg.Scope().Insert(types.NewFunc(token.NoPos, pkg, name, typ)) } // Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" . // -func (p *gcParser) parseDecl() { +func (p *parser) parseDecl() { switch p.lit { case "import": p.parseImportDecl() @@ -967,7 +919,7 @@ func (p *gcParser) parseDecl() { // Export = "PackageClause { Decl } "$$" . // PackageClause = "package" PackageName [ "safe" ] "\n" . // -func (p *gcParser) parseExport() *Package { +func (p *parser) parseExport() *types.Package { p.expectKeyword("package") name := p.parsePackageName() if p.tok != '\n' { @@ -995,7 +947,7 @@ func (p *gcParser) parseExport() *Package { } // package was imported completely and without errors - pkg.complete = true + pkg.MarkComplete() return pkg } diff --git a/go/types/gcimporter_test.go b/go/gcimporter/gcimporter_test.go similarity index 76% rename from go/types/gcimporter_test.go rename to go/gcimporter/gcimporter_test.go index 67f5dc14..11a53c51 100644 --- a/go/types/gcimporter_test.go +++ b/go/gcimporter/gcimporter_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package types +package gcimporter import ( "go/build" @@ -14,6 +14,8 @@ import ( "strings" "testing" "time" + + "code.google.com/p/go.tools/go/types" ) var gcPath string // Go compiler path @@ -50,11 +52,11 @@ func compile(t *testing.T, dirname, filename string) string { // Use the same global imports map for all tests. The effect is // as if all tested packages were imported into a single package. -var imports = make(map[string]*Package) +var imports = make(map[string]*types.Package) func testPath(t *testing.T, path string) bool { t0 := time.Now() - _, err := GcImport(imports, path) + _, err := Import(imports, path) if err != nil { t.Errorf("testPath(%s): %s", path, err) return false @@ -94,7 +96,12 @@ func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) { return } -func TestGcImport(t *testing.T) { +func TestImport(t *testing.T) { + // This package does not handle gccgo export data. + if runtime.Compiler == "gccgo" { + return + } + // On cross-compile builds, the path will not exist. // Need to use GOHOSTOS, which is not available. if _, err := os.Stat(gcPath); err != nil { @@ -125,8 +132,8 @@ var importedObjectTests = []struct { // TODO(gri) add more tests } -func TestGcImportedTypes(t *testing.T) { - // This package does not yet know how to read gccgo export data. +func TestImportedTypes(t *testing.T) { + // This package does not handle gccgo export data. if runtime.Compiler == "gccgo" { return } @@ -138,13 +145,13 @@ func TestGcImportedTypes(t *testing.T) { importPath := s[0] objName := s[1] - pkg, err := GcImport(imports, importPath) + pkg, err := Import(imports, importPath) if err != nil { t.Error(err) continue } - obj := pkg.scope.Lookup(objName) + obj := pkg.Scope().Lookup(objName) if obj == nil { t.Errorf("%s: object not found", test.name) continue @@ -156,3 +163,32 @@ func TestGcImportedTypes(t *testing.T) { } } } + +func TestIssue5815(t *testing.T) { + // This package does not handle gccgo export data. + if runtime.Compiler == "gccgo" { + return + } + + pkg, err := Import(make(map[string]*types.Package), "strings") + if err != nil { + t.Fatal(err) + } + + scope := pkg.Scope() + for _, name := range scope.Names() { + obj := scope.Lookup(name) + if obj.Pkg() == nil { + t.Errorf("no pkg for %s", obj) + } + if tname, _ := obj.(*types.TypeName); tname != nil { + named := tname.Type().(*types.Named) + for i := 0; i < named.NumMethods(); i++ { + m := named.Method(i) + if m.Pkg() == nil { + t.Errorf("no pkg for %s", m) + } + } + } + } +} diff --git a/go/types/testdata/exports.go b/go/gcimporter/testdata/exports.go similarity index 100% rename from go/types/testdata/exports.go rename to go/gcimporter/testdata/exports.go diff --git a/go/types/api.go b/go/types/api.go index dbf145ec..6eb10340 100644 --- a/go/types/api.go +++ b/go/types/api.go @@ -59,6 +59,19 @@ func (err Error) Error() string { return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Msg) } +// An importer resolves import paths to Packages. +// The imports map records packages already known, +// indexed by package path. The type-checker +// will invoke Import with Config.Packages. +// An importer must determine the canonical package path and +// check imports to see if it is already present in the map. +// If so, the Importer can return the map entry. Otherwise, +// the importer must load the package data for the given path +// into a new *Package, record it in imports map, and return +// the package. +// TODO(gri) Need to be clearer about requirements of completeness. +type Importer func(map[string]*Package, string) (*Package, error) + // A Config specifies the configuration for type checking. // The zero value for Config is a ready-to-use default configuration. type Config struct { @@ -85,24 +98,22 @@ type Config struct { Error func(err error) // If Import != nil, it is called for each imported package. - // Otherwise, GcImporter is called. - // An importer resolves import paths to Packages. - // The imports map records packages already known, - // indexed by canonical package path. The type-checker will - // invoke Import with Config.Packages. - // An importer must determine the canonical package path and - // check imports to see if it is already present in the map. - // If so, the Importer can return the map entry. Otherwise, - // the importer must load the package data for the given path - // into a new *Package, record it in imports map, and return - // the package. - Import func(imports map[string]*Package, path string) (pkg *Package, err error) + // Otherwise, DefaultImport is called. + Import Importer // If Sizes != nil, it provides the sizing functions for package unsafe. // Otherwise &StdSize{WordSize: 8, MaxAlign: 8} is used instead. Sizes Sizes } +// DefaultImport is the default importer invoked if Config.Import == nil. +// The declaration: +// +// import _ "code.google.com/p/go.tools/go/gcimporter" +// +// in a client of go/types will initialize DefaultImport to gcimporter.Import. +var DefaultImport Importer + // Info holds result type information for a type-checked package. // Only the information for which a map is provided is collected. // If the package has type errors, the collected information may diff --git a/go/types/api_test.go b/go/types/api_test.go index 8c74d6b0..f7fb33e6 100644 --- a/go/types/api_test.go +++ b/go/types/api_test.go @@ -4,7 +4,7 @@ // TODO(gri) This file needs to be expanded significantly. -package types +package types_test import ( "bytes" @@ -13,6 +13,9 @@ import ( "go/token" "strings" "testing" + + _ "code.google.com/p/go.tools/go/gcimporter" + . "code.google.com/p/go.tools/go/types" ) func pkgFor(path, source string, info *Info) (*Package, error) { @@ -70,7 +73,7 @@ func TestCommaOkTypes(t *testing.T) { // look for comma-ok expression type var typ Type for e, t := range info.Types { - if exprString(e) == test.expr { + if ExprString(e) == test.expr { typ = t break } @@ -214,10 +217,10 @@ func initString(init *Initializer) string { if i > 0 { buf.WriteString(", ") } - buf.WriteString(lhs.name) + buf.WriteString(lhs.Name()) } buf.WriteString(" = ") - writeExpr(&buf, init.Rhs) + WriteExpr(&buf, init.Rhs) return buf.String() } diff --git a/go/types/builtins_test.go b/go/types/builtins_test.go index af1ad9e6..63295226 100644 --- a/go/types/builtins_test.go +++ b/go/types/builtins_test.go @@ -2,123 +2,129 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package types +package types_test import ( "fmt" "go/ast" "go/parser" "testing" + + _ "code.google.com/p/go.tools/go/gcimporter" + . "code.google.com/p/go.tools/go/types" ) var builtinCalls = []struct { - id builtinId - src string - sig string + name, src, sig string }{ - {_Append, `var s []int; _ = append(s)`, `func([]int, ...int) []int`}, - {_Append, `var s []int; _ = append(s, 0)`, `func([]int, ...int) []int`}, - {_Append, `var s []int; _ = (append)(s, 0)`, `func([]int, ...int) []int`}, - {_Append, `var s []byte; _ = ((append))(s, 0)`, `func([]byte, ...byte) []byte`}, + {"append", `var s []int; _ = append(s)`, `func([]int, ...int) []int`}, + {"append", `var s []int; _ = append(s, 0)`, `func([]int, ...int) []int`}, + {"append", `var s []int; _ = (append)(s, 0)`, `func([]int, ...int) []int`}, + {"append", `var s []byte; _ = ((append))(s, 0)`, `func([]byte, ...byte) []byte`}, // Note that ...uint8 (instead of ..byte) appears below because that is the type // that corresponds to Typ[byte] (an alias) - in the other cases, the type name // is chosen by the source. Either way, byte and uint8 denote identical types. - {_Append, `var s []byte; _ = append(s, "foo"...)`, `func([]byte, ...uint8) []byte`}, - {_Append, `type T []byte; var s T; _ = append(s, "foo"...)`, `func(p.T, ...uint8) p.T`}, + {"append", `var s []byte; _ = append(s, "foo"...)`, `func([]byte, ...uint8) []byte`}, + {"append", `type T []byte; var s T; _ = append(s, "foo"...)`, `func(p.T, ...uint8) p.T`}, - {_Cap, `var s [10]int; _ = cap(s)`, `invalid type`}, // constant - {_Cap, `var s [10]int; _ = cap(&s)`, `invalid type`}, // constant - {_Cap, `var s []int64; _ = cap(s)`, `func([]int64) int`}, - {_Cap, `var c chan<-bool; _ = cap(c)`, `func(chan<- bool) int`}, + {"cap", `var s [10]int; _ = cap(s)`, `invalid type`}, // constant + {"cap", `var s [10]int; _ = cap(&s)`, `invalid type`}, // constant + {"cap", `var s []int64; _ = cap(s)`, `func([]int64) int`}, + {"cap", `var c chan<-bool; _ = cap(c)`, `func(chan<- bool) int`}, - {_Len, `_ = len("foo")`, `invalid type`}, // constant - {_Len, `var s string; _ = len(s)`, `func(string) int`}, - {_Len, `var s [10]int; _ = len(s)`, `invalid type`}, // constant - {_Len, `var s [10]int; _ = len(&s)`, `invalid type`}, // constant - {_Len, `var s []int64; _ = len(s)`, `func([]int64) int`}, - {_Len, `var c chan<-bool; _ = len(c)`, `func(chan<- bool) int`}, - {_Len, `var m map[string]float32; _ = len(m)`, `func(map[string]float32) int`}, + {"len", `_ = len("foo")`, `invalid type`}, // constant + {"len", `var s string; _ = len(s)`, `func(string) int`}, + {"len", `var s [10]int; _ = len(s)`, `invalid type`}, // constant + {"len", `var s [10]int; _ = len(&s)`, `invalid type`}, // constant + {"len", `var s []int64; _ = len(s)`, `func([]int64) int`}, + {"len", `var c chan<-bool; _ = len(c)`, `func(chan<- bool) int`}, + {"len", `var m map[string]float32; _ = len(m)`, `func(map[string]float32) int`}, - {_Close, `var c chan int; close(c)`, `func(chan int)`}, - {_Close, `var c chan<- chan string; close(c)`, `func(chan<- chan string)`}, + {"close", `var c chan int; close(c)`, `func(chan int)`}, + {"close", `var c chan<- chan string; close(c)`, `func(chan<- chan string)`}, - {_Complex, `_ = complex(1, 0)`, `invalid type`}, // constant - {_Complex, `var re float32; _ = complex(re, 1.0)`, `func(float32, float32) complex64`}, - {_Complex, `var im float64; _ = complex(1, im)`, `func(float64, float64) complex128`}, - {_Complex, `type F32 float32; var re, im F32; _ = complex(re, im)`, `func(p.F32, p.F32) complex64`}, - {_Complex, `type F64 float64; var re, im F64; _ = complex(re, im)`, `func(p.F64, p.F64) complex128`}, + {"complex", `_ = complex(1, 0)`, `invalid type`}, // constant + {"complex", `var re float32; _ = complex(re, 1.0)`, `func(float32, float32) complex64`}, + {"complex", `var im float64; _ = complex(1, im)`, `func(float64, float64) complex128`}, + {"complex", `type F32 float32; var re, im F32; _ = complex(re, im)`, `func(p.F32, p.F32) complex64`}, + {"complex", `type F64 float64; var re, im F64; _ = complex(re, im)`, `func(p.F64, p.F64) complex128`}, - {_Copy, `var src, dst []byte; copy(dst, src)`, `func([]byte, []byte) int`}, - {_Copy, `type T [][]int; var src, dst T; _ = copy(dst, src)`, `func([][]int, [][]int) int`}, + {"copy", `var src, dst []byte; copy(dst, src)`, `func([]byte, []byte) int`}, + {"copy", `type T [][]int; var src, dst T; _ = copy(dst, src)`, `func([][]int, [][]int) int`}, - {_Delete, `var m map[string]bool; delete(m, "foo")`, `func(map[string]bool, string)`}, - {_Delete, `type (K string; V int); var m map[K]V; delete(m, "foo")`, `func(map[p.K]p.V, p.K)`}, + {"delete", `var m map[string]bool; delete(m, "foo")`, `func(map[string]bool, string)`}, + {"delete", `type (K string; V int); var m map[K]V; delete(m, "foo")`, `func(map[p.K]p.V, p.K)`}, - {_Imag, `_ = imag(1i)`, `invalid type`}, // constant - {_Imag, `var c complex64; _ = imag(c)`, `func(complex64) float32`}, - {_Imag, `var c complex128; _ = imag(c)`, `func(complex128) float64`}, - {_Imag, `type C64 complex64; var c C64; _ = imag(c)`, `func(p.C64) float32`}, - {_Imag, `type C128 complex128; var c C128; _ = imag(c)`, `func(p.C128) float64`}, + {"imag", `_ = imag(1i)`, `invalid type`}, // constant + {"imag", `var c complex64; _ = imag(c)`, `func(complex64) float32`}, + {"imag", `var c complex128; _ = imag(c)`, `func(complex128) float64`}, + {"imag", `type C64 complex64; var c C64; _ = imag(c)`, `func(p.C64) float32`}, + {"imag", `type C128 complex128; var c C128; _ = imag(c)`, `func(p.C128) float64`}, - {_Real, `_ = real(1i)`, `invalid type`}, // constant - {_Real, `var c complex64; _ = real(c)`, `func(complex64) float32`}, - {_Real, `var c complex128; _ = real(c)`, `func(complex128) float64`}, - {_Real, `type C64 complex64; var c C64; _ = real(c)`, `func(p.C64) float32`}, - {_Real, `type C128 complex128; var c C128; _ = real(c)`, `func(p.C128) float64`}, + {"real", `_ = real(1i)`, `invalid type`}, // constant + {"real", `var c complex64; _ = real(c)`, `func(complex64) float32`}, + {"real", `var c complex128; _ = real(c)`, `func(complex128) float64`}, + {"real", `type C64 complex64; var c C64; _ = real(c)`, `func(p.C64) float32`}, + {"real", `type C128 complex128; var c C128; _ = real(c)`, `func(p.C128) float64`}, - {_Make, `_ = make([]int, 10)`, `func([]int, int) []int`}, - {_Make, `type T []byte; _ = make(T, 10, 20)`, `func(p.T, int, int) p.T`}, + {"make", `_ = make([]int, 10)`, `func([]int, int) []int`}, + {"make", `type T []byte; _ = make(T, 10, 20)`, `func(p.T, int, int) p.T`}, - {_New, `_ = new(int)`, `func(int) *int`}, - {_New, `type T struct{}; _ = new(T)`, `func(p.T) *p.T`}, + {"new", `_ = new(int)`, `func(int) *int`}, + {"new", `type T struct{}; _ = new(T)`, `func(p.T) *p.T`}, - {_Panic, `panic(0)`, `func(interface{})`}, - {_Panic, `panic("foo")`, `func(interface{})`}, + {"panic", `panic(0)`, `func(interface{})`}, + {"panic", `panic("foo")`, `func(interface{})`}, - {_Print, `print()`, `func()`}, - {_Print, `print(0)`, `func(int)`}, - {_Print, `print(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`}, + {"print", `print()`, `func()`}, + {"print", `print(0)`, `func(int)`}, + {"print", `print(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`}, - {_Println, `println()`, `func()`}, - {_Println, `println(0)`, `func(int)`}, - {_Println, `println(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`}, + {"println", `println()`, `func()`}, + {"println", `println(0)`, `func(int)`}, + {"println", `println(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`}, - {_Recover, `recover()`, `func() interface{}`}, - {_Recover, `_ = recover()`, `func() interface{}`}, + {"recover", `recover()`, `func() interface{}`}, + {"recover", `_ = recover()`, `func() interface{}`}, - {_Alignof, `_ = unsafe.Alignof(0)`, `invalid type`}, // constant - {_Alignof, `var x struct{}; _ = unsafe.Alignof(x)`, `invalid type`}, // constant + {"Alignof", `_ = unsafe.Alignof(0)`, `invalid type`}, // constant + {"Alignof", `var x struct{}; _ = unsafe.Alignof(x)`, `invalid type`}, // constant - {_Offsetof, `var x struct{f bool}; _ = unsafe.Offsetof(x.f)`, `invalid type`}, // constant - {_Offsetof, `var x struct{_ int; f bool}; _ = unsafe.Offsetof((&x).f)`, `invalid type`}, // constant + {"Offsetof", `var x struct{f bool}; _ = unsafe.Offsetof(x.f)`, `invalid type`}, // constant + {"Offsetof", `var x struct{_ int; f bool}; _ = unsafe.Offsetof((&x).f)`, `invalid type`}, // constant - {_Sizeof, `_ = unsafe.Sizeof(0)`, `invalid type`}, // constant - {_Sizeof, `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant + {"Sizeof", `_ = unsafe.Sizeof(0)`, `invalid type`}, // constant + {"Sizeof", `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant - {_Assert, `assert(true)`, `invalid type`}, // constant - {_Assert, `type B bool; const pred B = 1 < 2; assert(pred)`, `invalid type`}, // constant + {"assert", `assert(true)`, `invalid type`}, // constant + {"assert", `type B bool; const pred B = 1 < 2; assert(pred)`, `invalid type`}, // constant // no tests for trace since it produces output as a side-effect } func TestBuiltinSignatures(t *testing.T) { - defPredeclaredTestFuncs() + DefPredeclaredTestFuncs() - seen := map[builtinId]bool{_Trace: true} // no test for _Trace; add it manually + seen := map[string]bool{"trace": true} // no test for trace built-in; add it manually for _, call := range builtinCalls { - testBuiltinSignature(t, call.id, call.src, call.sig) - seen[call.id] = true + testBuiltinSignature(t, call.name, call.src, call.sig) + seen[call.name] = true } // make sure we didn't miss one - for i := range predeclaredFuncs { - if id := builtinId(i); !seen[id] { - t.Errorf("missing test for %s", predeclaredFuncs[id].name) + for _, name := range Universe.Names() { + if _, ok := Universe.Lookup(name).(*Builtin); ok && !seen[name] { + t.Errorf("missing test for %s", name) + } + } + for _, name := range Unsafe.Scope().Names() { + if _, ok := Unsafe.Scope().Lookup(name).(*Builtin); ok && !seen[name] { + t.Errorf("missing test for unsafe.%s", name) } } } -func testBuiltinSignature(t *testing.T, id builtinId, src0, want string) { +func testBuiltinSignature(t *testing.T, name, src0, want string) { src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _() { %s }`, src0) f, err := parser.ParseFile(fset, "", src, 0) if err != nil { @@ -154,7 +160,7 @@ func testBuiltinSignature(t *testing.T, id builtinId, src0, want string) { // the recorded type for the built-in must match the wanted signature typ := types[fun] if typ == nil { - t.Errorf("%s: no type recorded for %s", src0, exprString(fun)) + t.Errorf("%s: no type recorded for %s", src0, ExprString(fun)) return } if got := typ.String(); got != want { @@ -176,8 +182,8 @@ func testBuiltinSignature(t *testing.T, id builtinId, src0, want string) { t.Errorf("%s: %s does not denote a built-in", src0, p) return } - if bin.id != id { - t.Errorf("%s: got built-in %s; want %s", src0, bin.name, predeclaredFuncs[id].name) + if bin.Name() != name { + t.Errorf("%s: got built-in %s; want %s", src0, bin.Name(), name) return } return // we're done diff --git a/go/types/check_test.go b/go/types/check_test.go index 70e8de26..3c5f6967 100644 --- a/go/types/check_test.go +++ b/go/types/check_test.go @@ -20,7 +20,7 @@ // _ = x /* ERROR "not declared" */ + 1 // } -package types +package types_test import ( "flag" @@ -32,6 +32,9 @@ import ( "regexp" "strings" "testing" + + _ "code.google.com/p/go.tools/go/gcimporter" + . "code.google.com/p/go.tools/go/types" ) var ( @@ -254,7 +257,7 @@ func checkFiles(t *testing.T, testfiles []string) { func TestCheck(t *testing.T) { // Declare builtins for testing. - defPredeclaredTestFuncs() + DefPredeclaredTestFuncs() // If explicit test files are specified, only check those. if files := *testFiles; files != "" { diff --git a/go/types/errors.go b/go/types/errors.go index 7c63653d..346effdb 100644 --- a/go/types/errors.go +++ b/go/types/errors.go @@ -34,7 +34,7 @@ func (check *checker) sprintf(format string, args ...interface{}) string { case token.Pos: args[i] = check.fset.Position(a).String() case ast.Expr: - args[i] = exprString(a) + args[i] = ExprString(a) } } return fmt.Sprintf(format, args...) @@ -82,14 +82,14 @@ func (check *checker) invalidOp(pos token.Pos, format string, args ...interface{ } // exprString returns a (simplified) string representation for an expression. -func exprString(expr ast.Expr) string { +func ExprString(expr ast.Expr) string { var buf bytes.Buffer - writeExpr(&buf, expr) + WriteExpr(&buf, expr) return buf.String() } // TODO(gri) Need to merge with typeString since some expressions are types (try: ([]int)(a)) -func writeExpr(buf *bytes.Buffer, expr ast.Expr) { +func WriteExpr(buf *bytes.Buffer, expr ast.Expr) { switch x := expr.(type) { case *ast.Ident: buf.WriteString(x.Name) @@ -105,66 +105,66 @@ func writeExpr(buf *bytes.Buffer, expr ast.Expr) { case *ast.ParenExpr: buf.WriteByte('(') - writeExpr(buf, x.X) + WriteExpr(buf, x.X) buf.WriteByte(')') case *ast.SelectorExpr: - writeExpr(buf, x.X) + WriteExpr(buf, x.X) buf.WriteByte('.') buf.WriteString(x.Sel.Name) case *ast.IndexExpr: - writeExpr(buf, x.X) + WriteExpr(buf, x.X) buf.WriteByte('[') - writeExpr(buf, x.Index) + WriteExpr(buf, x.Index) buf.WriteByte(']') case *ast.SliceExpr: - writeExpr(buf, x.X) + WriteExpr(buf, x.X) buf.WriteByte('[') if x.Low != nil { - writeExpr(buf, x.Low) + WriteExpr(buf, x.Low) } buf.WriteByte(':') if x.High != nil { - writeExpr(buf, x.High) + WriteExpr(buf, x.High) } buf.WriteByte(']') case *ast.TypeAssertExpr: - writeExpr(buf, x.X) + WriteExpr(buf, x.X) buf.WriteString(".(") - // TODO(gri) expand writeExpr so that types are not handled by default case - writeExpr(buf, x.Type) + // TODO(gri) expand WriteExpr so that types are not handled by default case + WriteExpr(buf, x.Type) buf.WriteByte(')') case *ast.CallExpr: - writeExpr(buf, x.Fun) + WriteExpr(buf, x.Fun) buf.WriteByte('(') for i, arg := range x.Args { if i > 0 { buf.WriteString(", ") } - writeExpr(buf, arg) + WriteExpr(buf, arg) } buf.WriteByte(')') case *ast.StarExpr: buf.WriteByte('*') - writeExpr(buf, x.X) + WriteExpr(buf, x.X) case *ast.UnaryExpr: buf.WriteString(x.Op.String()) - writeExpr(buf, x.X) + WriteExpr(buf, x.X) case *ast.BinaryExpr: // The AST preserves source-level parentheses so there is // no need to introduce parentheses here for correctness. - writeExpr(buf, x.X) + WriteExpr(buf, x.X) buf.WriteByte(' ') buf.WriteString(x.Op.String()) buf.WriteByte(' ') - writeExpr(buf, x.Y) + WriteExpr(buf, x.Y) default: // TODO(gri) Consider just calling x.String(). May cause @@ -228,7 +228,7 @@ func writeType(buf *bytes.Buffer, typ Type) { buf.WriteString("") case *Basic: - if t.Kind() == UnsafePointer { + if t.kind == UnsafePointer { buf.WriteString("unsafe.") } buf.WriteString(t.name) @@ -269,9 +269,6 @@ func writeType(buf *bytes.Buffer, typ Type) { buf.WriteString("func") writeSignature(buf, t) - case *Builtin: - fmt.Fprintf(buf, "", t.name) - case *Interface: // We write the source-level methods and embedded types rather // than the actual method set since resolved method signatures diff --git a/go/types/eval_test.go b/go/types/eval_test.go index 41663185..097cc0b5 100644 --- a/go/types/eval_test.go +++ b/go/types/eval_test.go @@ -4,7 +4,7 @@ // This file contains tests for Eval. -package types +package types_test import ( "go/ast" @@ -12,6 +12,9 @@ import ( "go/token" "strings" "testing" + + _ "code.google.com/p/go.tools/go/gcimporter" + . "code.google.com/p/go.tools/go/types" ) func testEval(t *testing.T, pkg *Package, scope *Scope, str string, typ Type, typStr, valStr string) { @@ -53,7 +56,7 @@ func testEval(t *testing.T, pkg *Package, scope *Scope, str string, typ Type, ty func TestEvalBasic(t *testing.T) { for _, typ := range Typ[Bool : String+1] { - testEval(t, nil, nil, typ.name, typ, "", "") + testEval(t, nil, nil, typ.Name(), typ, "", "") } } @@ -106,7 +109,7 @@ func f(a int, s string) float64 { t.Fatal(err) } - pkgScope := pkg.scope + pkgScope := pkg.Scope() if n := pkgScope.NumChildren(); n != 1 { t.Fatalf("got %d file scopes, want 1", n) } diff --git a/go/types/issues_test.go b/go/types/issues_test.go index 7d8126b9..3cfa2018 100644 --- a/go/types/issues_test.go +++ b/go/types/issues_test.go @@ -4,13 +4,16 @@ // This file implements tests for various issues. -package types +package types_test import ( "go/ast" "go/parser" "strings" "testing" + + _ "code.google.com/p/go.tools/go/gcimporter" + . "code.google.com/p/go.tools/go/types" ) func TestIssue5770(t *testing.T) { @@ -78,28 +81,6 @@ var ( } } -func TestIssue5815(t *testing.T) { - pkg, err := GcImport(make(map[string]*Package), "strings") - if err != nil { - t.Fatal(err) - } - - for _, obj := range pkg.scope.elems { - if obj.Pkg() == nil { - t.Errorf("no pkg for %s", obj) - } - if tname, _ := obj.(*TypeName); tname != nil { - if named, _ := tname.typ.(*Named); named != nil { - for _, m := range named.methods { - if m.pkg == nil { - t.Errorf("no pkg for %s", m) - } - } - } - } - } -} - func TestIssue6413(t *testing.T) { src := ` package p diff --git a/go/types/operand.go b/go/types/operand.go index 57fe32f9..85c67922 100644 --- a/go/types/operand.go +++ b/go/types/operand.go @@ -99,7 +99,7 @@ func (x *operand) String() string { var expr string if x.expr != nil { - expr = exprString(x.expr) + expr = ExprString(x.expr) } else { switch x.mode { case builtin: diff --git a/go/types/package.go b/go/types/package.go index 06803e13..dc6c78ea 100644 --- a/go/types/package.go +++ b/go/types/package.go @@ -4,6 +4,8 @@ package types +import "fmt" + // A Package describes a Go package. type Package struct { path string @@ -39,3 +41,10 @@ func (pkg *Package) Complete() bool { return pkg.complete } // Imports returns the list of packages explicitly imported by // pkg; the list is in source order. Package unsafe is excluded. func (pkg *Package) Imports() []*Package { return pkg.imports } + +// MarkComplete marks a package as complete. +func (pkg *Package) MarkComplete() { pkg.complete = true } + +func (pkg *Package) String() string { + return fmt.Sprintf("package %s (%s)", pkg.name, pkg.path) +} diff --git a/go/types/resolver.go b/go/types/resolver.go index 26e69bfa..daddb8b1 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -146,7 +146,10 @@ func (check *checker) resolveFiles(files []*ast.File) { importer := check.conf.Import if importer == nil { - importer = GcImport + if DefaultImport == nil { + panic("no Config.Import and no DefaultImport") + } + importer = DefaultImport } var ( diff --git a/go/types/resolver_test.go b/go/types/resolver_test.go index 91ead4b7..5c8a97e9 100644 --- a/go/types/resolver_test.go +++ b/go/types/resolver_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package types +package types_test import ( "fmt" @@ -10,6 +10,9 @@ import ( "go/parser" "go/token" "testing" + + _ "code.google.com/p/go.tools/go/gcimporter" + . "code.google.com/p/go.tools/go/types" ) var sources = []string{ diff --git a/go/types/self_test.go b/go/types/self_test.go index befc46bc..08fb1edf 100644 --- a/go/types/self_test.go +++ b/go/types/self_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package types +package types_test import ( "fmt" @@ -11,6 +11,9 @@ import ( "go/token" "testing" "time" + + _ "code.google.com/p/go.tools/go/gcimporter" + . "code.google.com/p/go.tools/go/types" ) func TestSelf(t *testing.T) { diff --git a/go/types/stdlib_test.go b/go/types/stdlib_test.go index 32f66523..1816df4a 100644 --- a/go/types/stdlib_test.go +++ b/go/types/stdlib_test.go @@ -5,7 +5,7 @@ // This file tests types.Check by using it to // typecheck the standard library and tests. -package types +package types_test import ( "flag" @@ -22,6 +22,9 @@ import ( "strings" "testing" "time" + + _ "code.google.com/p/go.tools/go/gcimporter" + . "code.google.com/p/go.tools/go/types" ) var verbose = flag.Bool("types.v", false, "verbose mode") diff --git a/go/types/types.go b/go/types/types.go index 91c4f632..209288c9 100644 --- a/go/types/types.go +++ b/go/types/types.go @@ -139,6 +139,15 @@ type Struct struct { // only as long as required to hold the tag with the largest index i. Consequently, // if no field has a tag, tags may be nil. func NewStruct(fields []*Var, tags []string) *Struct { + var fset objset + for _, f := range fields { + if f.name != "_" && fset.insert(f) != nil { + panic("multiple fields with the same name") + } + } + if len(tags) > len(fields) { + panic("more tags than fields") + } return &Struct{fields: fields, tags: tags} } @@ -249,9 +258,25 @@ type Interface struct { } // NewInterface returns a new interface for the given methods. -func NewInterface(methods []*Func) *Interface { - // TODO(gri) should provide receiver to all methods +func NewInterface(methods []*Func, types []*Named) *Interface { + typ := new(Interface) + + var mset objset + for _, m := range methods { + if mset.insert(m) != nil { + panic("multiple methods with the same name") + } + // set receiver + // TODO(gri) Ideally, we should use a named type here instead of + // typ, for less verbose printing of interface method signatures. + m.typ.(*Signature).recv = NewVar(m.pos, m.pkg, "", typ) + } sort.Sort(byUniqueMethodName(methods)) + + if types != nil { + panic("unimplemented") + } + return &Interface{methods: methods, allMethods: methods} } @@ -304,13 +329,12 @@ type Named struct { } // NewNamed returns a new named type for the given type name, underlying type, and associated methods. -// The underlying type must exist and not be a *Named, and the methods scope entries must be *Func -// objects if the scope is not empty. +// The underlying type must not be a *Named. func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { if _, ok := underlying.(*Named); ok { panic("types.NewNamed: underlying type must not be *Named") } - typ := &Named{obj: obj, underlying: underlying, complete: true, methods: methods} + typ := &Named{obj: obj, underlying: underlying, complete: underlying != nil, methods: methods} if obj.typ == nil { obj.typ = typ } @@ -326,6 +350,27 @@ func (t *Named) NumMethods() int { return len(t.methods) } // Method returns the i'th method of named type t for 0 <= i < t.NumMethods(). func (t *Named) Method(i int) *Func { return t.methods[i] } +// SetUnderlying sets the underlying type and marks t as complete. +// TODO(gri) determine if there's a better solution rather than providing this function +func (t *Named) SetUnderlying(underlying Type) { + if underlying == nil { + panic("types.Named.SetUnderlying: underlying type must not be nil") + } + if _, ok := underlying.(*Named); ok { + panic("types.Named.SetUnderlying: underlying type must not be *Named") + } + t.underlying = underlying + t.complete = true +} + +// AddMethod adds method m unless it is already in the method list. +// TODO(gri) find a better solution instead of providing this function +func (t *Named) AddMethod(m *Func) { + if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 { + t.methods = append(t.methods, m) + } +} + // Implementations for Type methods. func (t *Basic) Underlying() Type { return t } @@ -335,7 +380,6 @@ func (t *Struct) Underlying() Type { return t } func (t *Pointer) Underlying() Type { return t } func (t *Tuple) Underlying() Type { return t } func (t *Signature) Underlying() Type { return t } -func (t *Builtin) Underlying() Type { return t } func (t *Interface) Underlying() Type { return t } func (t *Map) Underlying() Type { return t } func (t *Chan) Underlying() Type { return t } @@ -355,7 +399,6 @@ func (t *Pointer) MethodSet() *MethodSet { } func (t *Tuple) MethodSet() *MethodSet { return &emptyMethodSet } func (t *Signature) MethodSet() *MethodSet { return &emptyMethodSet } -func (t *Builtin) MethodSet() *MethodSet { return &emptyMethodSet } func (t *Interface) MethodSet() *MethodSet { return t.mset.of(t) } func (t *Map) MethodSet() *MethodSet { return &emptyMethodSet } func (t *Chan) MethodSet() *MethodSet { return &emptyMethodSet } @@ -368,7 +411,6 @@ func (t *Struct) String() string { return typeString(t) } func (t *Pointer) String() string { return typeString(t) } func (t *Tuple) String() string { return typeString(t) } func (t *Signature) String() string { return typeString(t) } -func (t *Builtin) String() string { return typeString(t) } func (t *Interface) String() string { return typeString(t) } func (t *Map) String() string { return typeString(t) } func (t *Chan) String() string { return typeString(t) } diff --git a/go/types/types_test.go b/go/types/types_test.go index af4fdb7d..31d1a744 100644 --- a/go/types/types_test.go +++ b/go/types/types_test.go @@ -5,12 +5,15 @@ // This file contains tests verifying the types associated with an AST after // type checking. -package types +package types_test import ( "go/ast" "go/parser" "testing" + + _ "code.google.com/p/go.tools/go/gcimporter" + . "code.google.com/p/go.tools/go/types" ) const filename = "" @@ -122,8 +125,8 @@ func TestTypes(t *testing.T) { t.Errorf("%s: %s", src, err) continue } - typ := pkg.scope.Lookup("T").Type().Underlying() - str := typeString(typ) + typ := pkg.Scope().Lookup("T").Type().Underlying() + str := typ.String() if str != test.str { t.Errorf("%s: got %s, want %s", test.src, str, test.str) } @@ -171,7 +174,7 @@ func TestExprs(t *testing.T) { t.Errorf("%s: %s", src, err) continue } - str := exprString(file.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec).Values[0]) + str := ExprString(file.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec).Values[0]) if str != test.str { t.Errorf("%s: got %s, want %s", test.src, str, test.str) } diff --git a/go/types/universe.go b/go/types/universe.go index 63a6be11..2e57b880 100644 --- a/go/types/universe.go +++ b/go/types/universe.go @@ -67,7 +67,7 @@ func defPredeclaredTypes() { res := NewVar(token.NoPos, nil, "", Typ[String]) sig := &Signature{results: NewTuple(res)} err := NewFunc(token.NoPos, nil, "Error", sig) - typ := &Named{underlying: NewInterface([]*Func{err}), complete: true} + typ := &Named{underlying: NewInterface([]*Func{err}, nil), complete: true} sig.recv = NewVar(token.NoPos, nil, "", typ) def(NewTypeName(token.NoPos, nil, "error", typ)) } @@ -163,7 +163,10 @@ func defPredeclaredFuncs() { } } -func defPredeclaredTestFuncs() { +// DefPredeclaredTestFuncs defines the assert and trace built-ins. +// These built-ins are intended for debugging and testing of this +// package only. +func DefPredeclaredTestFuncs() { if Universe.Lookup("assert") != nil { return // already defined } diff --git a/importer/importer.go b/importer/importer.go index 70be1da7..c0ec0ecc 100644 --- a/importer/importer.go +++ b/importer/importer.go @@ -53,17 +53,15 @@ import ( "sync" "code.google.com/p/go.tools/go/exact" + "code.google.com/p/go.tools/go/gcimporter" "code.google.com/p/go.tools/go/types" ) -// Alias for type of types.Config.Import function. -type importfn func(map[string]*types.Package, string) (*types.Package, error) - // An Importer's exported methods are not thread-safe. type Importer struct { Fset *token.FileSet // position info for all files seen config Config // the client configuration, modified by us - importfn importfn // client's type import function + importfn types.Importer // client's type import function augment map[string]bool // packages to be augmented by TestFiles when imported allPackagesMu sync.Mutex // guards 'allPackages' during internal concurrency allPackages []*PackageInfo // all packages, including non-importable ones @@ -103,7 +101,7 @@ type Config struct { func New(config *Config) *Importer { importfn := config.TypeChecker.Import if importfn == nil { - importfn = types.GcImport + importfn = gcimporter.Import } imp := &Importer{ diff --git a/oracle/describe.go b/oracle/describe.go index 3426dcea..a6cb702f 100644 --- a/oracle/describe.go +++ b/oracle/describe.go @@ -196,7 +196,7 @@ func findInterestingNode(pkginfo *importer.PackageInfo, path []ast.Node) ([]ast. continue case *ast.Ident: - switch obj := pkginfo.ObjectOf(n).(type) { + switch pkginfo.ObjectOf(n).(type) { case *types.PkgName: return path, actionPackage @@ -227,15 +227,12 @@ func findInterestingNode(pkginfo *importer.PackageInfo, path []ast.Node) ([]ast. return path[3:], actionType } } - - // For reference to built-in function, return enclosing call. - if _, ok := obj.Type().(*types.Builtin); ok { - // Ascend to enclosing function call. - path = path[1:] - continue - } - return path, actionExpr + + case *types.Builtin: + // For reference to built-in function, return enclosing call. + path = path[1:] // ascend to enclosing function call + continue } // No object. diff --git a/pointer/gen.go b/pointer/gen.go index 89143c93..297ad633 100644 --- a/pointer/gen.go +++ b/pointer/gen.go @@ -20,7 +20,7 @@ import ( ) var ( - tEface = types.NewInterface(nil) + tEface = types.NewInterface(nil, nil) tInvalid = types.Typ[types.Invalid] tUnsafePtr = types.Typ[types.UnsafePointer] ) diff --git a/pointer/util.go b/pointer/util.go index aa30f585..10350169 100644 --- a/pointer/util.go +++ b/pointer/util.go @@ -161,9 +161,6 @@ func (a *analysis) flatten(t types.Type) []*fieldInfo { } } - case *types.Builtin: - panic("flatten(*types.Builtin)") // not the type of any value - default: panic(t) }