diff --git a/go/types/api.go b/go/types/api.go index 70920c0397..1dd46d43aa 100644 --- a/go/types/api.go +++ b/go/types/api.go @@ -29,10 +29,7 @@ package types // The API is still slightly in flux and the following changes are considered: // -// API(gri): Provide accessors uniformly to all fields and do not export fields directly. -// API(gri): Provide scope information for all objects. // API(gri): Provide position information for all objects. -// API(gri): The semantics of QualifiedIdent needs to be revisited. // API(gri): The GcImporter should probably be in its own package - it is only one of possible importers. import ( @@ -111,12 +108,12 @@ type Importer func(imports map[string]*Package, path string) (pkg *Package, err // entire package is checked. If there are errors, the package may be // only partially type-checked, and the resulting package may be incomplete // (missing objects, imports, etc.). -func (ctxt *Context) Check(fset *token.FileSet, files []*ast.File) (*Package, error) { - return check(ctxt, fset, files) +func (ctxt *Context) Check(path string, fset *token.FileSet, files ...*ast.File) (*Package, error) { + return check(ctxt, path, fset, files...) } // Check is shorthand for ctxt.Check where ctxt is a default (empty) context. -func Check(fset *token.FileSet, files []*ast.File) (*Package, error) { +func Check(path string, fset *token.FileSet, files ...*ast.File) (*Package, error) { var ctxt Context - return ctxt.Check(fset, files) + return ctxt.Check(path, fset, files...) } diff --git a/go/types/builtins.go b/go/types/builtins.go index 5ede421910..4bd7d338a9 100644 --- a/go/types/builtins.go +++ b/go/types/builtins.go @@ -21,7 +21,7 @@ import ( // of the call is returned via x. If the call has type errors, the returned x is marked // as invalid (x.mode == invalid). // -func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota int) { +func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota int) { args := call.Args id := bin.id @@ -59,7 +59,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota switch id { case _Append: - if _, ok := underlying(x.typ).(*Slice); !ok { + if _, ok := x.typ.Underlying().(*Slice); !ok { check.invalidArg(x.pos(), "%s is not a typed slice", x) goto Error } @@ -77,7 +77,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota case _Cap, _Len: mode := invalid var val exact.Value - switch typ := implicitArrayDeref(underlying(x.typ)).(type) { + switch typ := implicitArrayDeref(x.typ.Underlying()).(type) { case *Basic: if isString(typ) && id == _Len { if x.mode == constant { @@ -96,7 +96,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota // function calls; in this case s is not evaluated." if !check.containsCallsOrReceives(arg0) { mode = constant - val = exact.MakeInt64(typ.Len) + val = exact.MakeInt64(typ.len) } case *Slice, *Chan: @@ -117,12 +117,12 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota x.val = val case _Close: - ch, ok := underlying(x.typ).(*Chan) + ch, ok := x.typ.Underlying().(*Chan) if !ok { check.invalidArg(x.pos(), "%s is not a channel", x) goto Error } - if ch.Dir&ast.SEND == 0 { + if ch.dir&ast.SEND == 0 { check.invalidArg(x.pos(), "%s must not be a receive-only channel", x) goto Error } @@ -156,14 +156,14 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota goto Error } - typ := underlying(x.typ).(*Basic) + typ := x.typ.Underlying().(*Basic) if x.mode == constant && y.mode == constant { x.val = exact.BinaryOp(x.val, token.ADD, exact.MakeImag(y.val)) } else { x.mode = value } - switch typ.Kind { + switch typ.kind { case Float32: x.typ = Typ[Complex64] case Float64: @@ -202,16 +202,16 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota } var dst, src Type - if t, ok := underlying(x.typ).(*Slice); ok { - dst = t.Elt + if t, ok := x.typ.Underlying().(*Slice); ok { + dst = t.elt } - switch t := underlying(y.typ).(type) { + switch t := y.typ.Underlying().(type) { case *Basic: if isString(y.typ) { src = Typ[Byte] } case *Slice: - src = t.Elt + src = t.elt } if dst == nil || src == nil { @@ -228,7 +228,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota x.typ = Typ[Int] case _Delete: - m, ok := underlying(x.typ).(*Map) + m, ok := x.typ.Underlying().(*Map) if !ok { check.invalidArg(x.pos(), "%s is not a map", x) goto Error @@ -237,8 +237,8 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota if x.mode == invalid { goto Error } - if !x.isAssignable(check.ctxt, m.Key) { - check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.Key) + if !x.isAssignable(check.ctxt, m.key) { + check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key) goto Error } x.mode = novalue @@ -258,7 +258,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota x.mode = value } k := Invalid - switch underlying(x.typ).(*Basic).Kind { + switch x.typ.Underlying().(*Basic).kind { case Complex64: k = Float32 case Complex128: @@ -276,7 +276,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota goto Error } var min int // minimum number of arguments - switch underlying(resultTyp).(type) { + switch resultTyp.Underlying().(type) { case *Slice: min = 2 case *Map, *Chan: @@ -308,7 +308,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota goto Error } x.mode = variable - x.typ = &Pointer{Base: resultTyp} + x.typ = &Pointer{base: resultTyp} case _Panic: x.mode = novalue @@ -342,12 +342,12 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota goto Error } sel := arg.Sel.Name - res := lookupField(x.typ, QualifiedName{check.pkg, arg.Sel.Name}) + res := lookupField(x.typ, check.pkg, arg.Sel.Name) if res.index == nil { check.invalidArg(x.pos(), "%s has no single field %s", x, sel) goto Error } - offs := check.ctxt.offsetof(deref(x.typ), res.index) + offs := check.ctxt.offsetof(x.typ.Deref(), res.index) if offs < 0 { check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, x) goto Error @@ -415,7 +415,7 @@ Error: // func implicitArrayDeref(typ Type) Type { if p, ok := typ.(*Pointer); ok { - if a, ok := underlying(p.Base).(*Array); ok { + if a, ok := p.base.Underlying().(*Array); ok { return a } } @@ -454,8 +454,8 @@ func unparen(x ast.Expr) ast.Expr { } func (check *checker) complexArg(x *operand) bool { - t, _ := underlying(x.typ).(*Basic) - if t != nil && (t.Info&IsFloat != 0 || t.Kind == UntypedInt || t.Kind == UntypedRune) { + t, _ := x.typ.Underlying().(*Basic) + if t != nil && (t.info&IsFloat != 0 || t.kind == UntypedInt || t.kind == UntypedRune) { return true } check.invalidArg(x.pos(), "%s must be a float32, float64, or an untyped non-complex numeric constant", x) diff --git a/go/types/check.go b/go/types/check.go index 5a8cf5fb24..4b41195407 100644 --- a/go/types/check.go +++ b/go/types/check.go @@ -117,7 +117,7 @@ func (check *checker) declareIdent(scope *Scope, ident *ast.Ident, obj Object) { if ident.Name != "_" { if alt := scope.Insert(obj); alt != nil { prevDecl := "" - if pos := alt.GetPos(); pos.IsValid() { + if pos := alt.Pos(); pos.IsValid() { prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", check.fset.Position(pos)) } check.errorf(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl)) @@ -153,9 +153,9 @@ func (check *checker) valueSpec(pos token.Pos, obj Object, lhs []*ast.Ident, spe assert(l != nil) switch obj := obj.(type) { case *Const: - obj.Type = typ + obj.typ = typ case *Var: - obj.Type = typ + obj.typ = typ default: unreachable() } @@ -174,9 +174,9 @@ func (check *checker) valueSpec(pos token.Pos, obj Object, lhs []*ast.Ident, spe for _, name := range lhs { switch obj := check.lookup(name).(type) { case *Const: - obj.Type = typ + obj.typ = typ case *Var: - obj.Type = typ + obj.typ = typ default: unreachable() } @@ -202,7 +202,7 @@ func (check *checker) object(obj Object, cycleOk bool) { // nothing to do case *Const: - if obj.Type != nil { + if obj.typ != nil { return // already checked } // The obj.Val field for constants is initialized to its respective @@ -214,15 +214,15 @@ func (check *checker) object(obj Object, cycleOk bool) { // know that x is a constant and has type float32, but we don't // have a value due to the error in the conversion). if obj.visited { - check.errorf(obj.GetPos(), "illegal cycle in initialization of constant %s", obj.Name) - obj.Type = Typ[Invalid] + check.errorf(obj.Pos(), "illegal cycle in initialization of constant %s", obj.Name) + obj.typ = Typ[Invalid] return } obj.visited = true spec := obj.spec - iota, ok := exact.Int64Val(obj.Val) + iota, ok := exact.Int64Val(obj.val) assert(ok) - obj.Val = exact.MakeUnknown() + obj.val = exact.MakeUnknown() // determine spec for type and initialization expressions init := spec if len(init.Values) == 0 { @@ -231,12 +231,12 @@ func (check *checker) object(obj Object, cycleOk bool) { check.valueSpec(spec.Pos(), obj, spec.Names, init, int(iota)) case *Var: - if obj.Type != nil { + if obj.typ != nil { return // already checked } if obj.visited { - check.errorf(obj.GetPos(), "illegal cycle in initialization of variable %s", obj.Name) - obj.Type = Typ[Invalid] + check.errorf(obj.Pos(), "illegal cycle in initialization of variable %s", obj.Name) + obj.typ = Typ[Invalid] return } obj.visited = true @@ -252,20 +252,20 @@ func (check *checker) object(obj Object, cycleOk bool) { } case *TypeName: - if obj.Type != nil { + if obj.typ != nil { return // already checked } - typ := &NamedType{Obj: obj} - obj.Type = typ // "mark" object so recursion terminates - typ.Underlying = underlying(check.typ(obj.spec.Type, cycleOk)) + typ := &Named{obj: obj} + obj.typ = typ // "mark" object so recursion terminates + typ.underlying = check.typ(obj.spec.Type, cycleOk).Underlying() // typecheck associated method signatures if scope := check.methods[obj]; scope != nil { - switch t := typ.Underlying.(type) { + switch t := typ.underlying.(type) { case *Struct: // struct fields must not conflict with methods - for _, f := range t.Fields { + for _, f := range t.fields { if m := scope.Lookup(f.Name); m != nil { - check.errorf(m.GetPos(), "type %s has both field and method named %s", obj.Name, f.Name) + check.errorf(m.Pos(), "type %s has both field and method named %s", obj.Name, f.Name) // ok to continue } } @@ -278,33 +278,33 @@ func (check *checker) object(obj Object, cycleOk bool) { } } // typecheck method signatures - var methods []*Method + var methods ObjSet for _, obj := range scope.Entries { m := obj.(*Func) sig := check.typ(m.decl.Type, cycleOk).(*Signature) params, _ := check.collectParams(m.decl.Recv, false) - sig.Recv = params[0] // the parser/assocMethod ensure there is exactly one parameter - m.Type = sig - methods = append(methods, &Method{QualifiedName{check.pkg, m.Name}, sig}) + sig.recv = params[0] // the parser/assocMethod ensure there is exactly one parameter + m.typ = sig + assert(methods.Insert(obj) == nil) check.later(m, sig, m.decl.Body) } - typ.Methods = methods + typ.methods = methods delete(check.methods, obj) // we don't need this scope anymore } case *Func: - if obj.Type != nil { + if obj.typ != nil { return // already checked } fdecl := obj.decl // methods are typechecked when their receivers are typechecked if fdecl.Recv == nil { sig := check.typ(fdecl.Type, cycleOk).(*Signature) - if obj.Name == "init" && (len(sig.Params) != 0 || len(sig.Results) != 0) { + if obj.name == "init" && (sig.params.Len() > 0 || sig.results.Len() > 0) { check.errorf(fdecl.Pos(), "func init must have no arguments and no return values") // ok to continue } - obj.Type = sig + obj.typ = sig check.later(obj, sig, fdecl.Body) } @@ -374,7 +374,7 @@ func (check *checker) assocMethod(meth *ast.FuncDecl) { scope = new(Scope) check.methods[tname] = scope } - check.declareIdent(scope, meth.Name, &Func{Pkg: check.pkg, Name: meth.Name.Name, decl: meth}) + check.declareIdent(scope, meth.Name, &Func{pkg: check.pkg, name: meth.Name.Name, decl: meth}) } func (check *checker) decl(decl ast.Decl) { @@ -406,7 +406,7 @@ func (check *checker) decl(decl ast.Decl) { // since they are not in any scope. Create a dummy object for them. if d.Name.Name == "init" { assert(obj == nil) // all other functions should have an object - obj = &Func{Pkg: check.pkg, Name: d.Name.Name, decl: d} + obj = &Func{pkg: check.pkg, name: d.Name.Name, decl: d} check.register(d.Name, obj) } check.object(obj, false) @@ -418,12 +418,13 @@ func (check *checker) decl(decl ast.Decl) { // A bailout panic is raised to indicate early termination. type bailout struct{} -func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (pkg *Package, err error) { +func check(ctxt *Context, path string, fset *token.FileSet, files ...*ast.File) (pkg *Package, err error) { // initialize checker check := checker{ ctxt: ctxt, fset: fset, files: files, + pkg: &Package{path: path, scope: &Scope{Outer: Universe}, imports: make(map[string]*Package)}, idents: make(map[*ast.Ident]Object), objects: make(map[*ast.Object]Object), initspecs: make(map[*ast.ValueSpec]*ast.ValueSpec), @@ -476,13 +477,13 @@ func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (pkg *Package, if trace { s := "" if f.obj != nil { - s = f.obj.Name + s = f.obj.name } fmt.Println("---", s) } check.funcsig = f.sig check.stmtList(f.body.List) - if len(f.sig.Results) > 0 && f.body != nil && !check.isTerminating(f.body, "") { + if f.sig.results.Len() > 0 && f.body != nil && !check.isTerminating(f.body, "") { check.errorf(f.body.Rbrace, "missing return") } } diff --git a/go/types/check_test.go b/go/types/check_test.go index 2ee7f6eef8..28d9804299 100644 --- a/go/types/check_test.go +++ b/go/types/check_test.go @@ -121,7 +121,7 @@ func errMap(t *testing.T, testname string, files []*ast.File) map[string][]strin } var s scanner.Scanner - s.Init(fset.AddFile(filename, fset.Base(), len(src)), src, nil, scanner.ScanComments) + s.Init(fset.AddFile(filename, -1, len(src)), src, nil, scanner.ScanComments) var prev string // position string of last non-comment, non-semicolon token scanFile: @@ -189,7 +189,7 @@ func checkFiles(t *testing.T, testname string, testfiles []string) { // typecheck and collect typechecker errors var ctxt Context ctxt.Error = func(err error) { errlist = append(errlist, err) } - ctxt.Check(fset, files) + ctxt.Check(testname, fset, files...) if *listErrors { t.Errorf("--- %s: %d errors found:", testname, len(errlist)) @@ -224,8 +224,8 @@ func TestCheck(t *testing.T) { if !testBuiltinsDeclared { testBuiltinsDeclared = true // Pkg == nil for Universe objects - def(&Func{Name: "assert", Type: &builtin{_Assert, "assert", 1, false, true}}) - def(&Func{Name: "trace", Type: &builtin{_Trace, "trace", 0, true, true}}) + def(&Func{name: "assert", typ: &Builtin{_Assert, "assert", 1, false, true}}) + def(&Func{name: "trace", typ: &Builtin{_Trace, "trace", 0, true, true}}) } // For easy debugging w/o changing the testing code, diff --git a/go/types/conversions.go b/go/types/conversions.go index c8433b425c..3fa8f5a067 100644 --- a/go/types/conversions.go +++ b/go/types/conversions.go @@ -32,11 +32,11 @@ func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota if x.mode == constant && isConstType(typ) { // constant conversion - typ := underlying(typ).(*Basic) + typ := typ.Underlying().(*Basic) // For now just implement string(x) where x is an integer, // as a temporary work-around for issue 4982, which is a // common issue. - if typ.Kind == String { + if typ.kind == String { switch { case x.isInteger(): codepoint := int64(-1) @@ -86,8 +86,8 @@ func (x *operand) isConvertible(ctxt *Context, T Type) bool { // "x's type and T have identical underlying types" V := x.typ - Vu := underlying(V) - Tu := underlying(T) + Vu := V.Underlying() + Tu := T.Underlying() if IsIdentical(Vu, Tu) { return true } @@ -95,7 +95,7 @@ func (x *operand) isConvertible(ctxt *Context, T Type) bool { // "x's type and T are unnamed pointer types and their pointer base types have identical underlying types" if V, ok := V.(*Pointer); ok { if T, ok := T.(*Pointer); ok { - if IsIdentical(underlying(V.Base), underlying(T.Base)) { + if IsIdentical(V.base.Underlying(), T.base.Underlying()) { return true } } @@ -136,12 +136,12 @@ func (x *operand) isConvertible(ctxt *Context, T Type) bool { func isUintptr(typ Type) bool { t, ok := typ.(*Basic) - return ok && t.Kind == Uintptr + return ok && t.kind == Uintptr } func isUnsafePointer(typ Type) bool { t, ok := typ.(*Basic) - return ok && t.Kind == UnsafePointer + return ok && t.kind == UnsafePointer } func isPointer(typ Type) bool { @@ -151,8 +151,8 @@ func isPointer(typ Type) bool { func isBytesOrRunes(typ Type) bool { if s, ok := typ.(*Slice); ok { - t, ok := underlying(s.Elt).(*Basic) - return ok && (t.Kind == Byte || t.Kind == Rune) + t, ok := s.elt.Underlying().(*Basic) + return ok && (t.kind == Byte || t.kind == Rune) } return false } diff --git a/go/types/errors.go b/go/types/errors.go index 62ee547917..88d099104f 100644 --- a/go/types/errors.go +++ b/go/types/errors.go @@ -53,14 +53,16 @@ func (check *checker) untrace(format string, args ...interface{}) { func (check *checker) formatMsg(format string, args []interface{}) string { for i, arg := range args { switch a := arg.(type) { + case operand: + panic("internal error: should always pass *operand") + case *operand: + args[i] = a.String() case token.Pos: args[i] = check.fset.Position(a).String() case ast.Expr: args[i] = exprString(a) case Type: args[i] = typeString(a) - case operand: - panic("internal error: should always pass *operand") } } return fmt.Sprintf(format, args...) @@ -181,6 +183,8 @@ func writeExpr(buf *bytes.Buffer, expr ast.Expr) { writeExpr(buf, x.Y) default: + // TODO(gri) Consider just calling x.String(). May cause + // infinite recursion if we missed a local type. fmt.Fprintf(buf, "", x) } } @@ -192,40 +196,44 @@ func typeString(typ Type) string { return buf.String() } -func writeParams(buf *bytes.Buffer, params []*Var, isVariadic bool) { +func writeTuple(buf *bytes.Buffer, tup *Tuple, isVariadic bool) { buf.WriteByte('(') - for i, par := range params { - if i > 0 { - buf.WriteString(", ") + if tup != nil { + for i, v := range tup.vars { + if i > 0 { + buf.WriteString(", ") + } + if v.name != "" { + buf.WriteString(v.name) + buf.WriteByte(' ') + } + if isVariadic && i == len(tup.vars)-1 { + buf.WriteString("...") + } + writeType(buf, v.typ) } - if par.Name != "" { - buf.WriteString(par.Name) - buf.WriteByte(' ') - } - if isVariadic && i == len(params)-1 { - buf.WriteString("...") - } - writeType(buf, par.Type) } buf.WriteByte(')') } func writeSignature(buf *bytes.Buffer, sig *Signature) { - writeParams(buf, sig.Params, sig.IsVariadic) - if len(sig.Results) == 0 { + writeTuple(buf, sig.params, sig.isVariadic) + + n := sig.results.Len() + if n == 0 { // no result return } buf.WriteByte(' ') - if len(sig.Results) == 1 && sig.Results[0].Name == "" { + if n == 1 && sig.results.vars[0].name == "" { // single unnamed result - writeType(buf, sig.Results[0].Type.(Type)) + writeType(buf, sig.results.vars[0].typ) return } // multiple or named result(s) - writeParams(buf, sig.Results, false) + writeTuple(buf, sig.results, false) } func writeType(buf *bytes.Buffer, typ Type) { @@ -234,19 +242,19 @@ func writeType(buf *bytes.Buffer, typ Type) { buf.WriteString("") case *Basic: - buf.WriteString(t.Name) + buf.WriteString(t.name) case *Array: - fmt.Fprintf(buf, "[%d]", t.Len) - writeType(buf, t.Elt) + fmt.Fprintf(buf, "[%d]", t.len) + writeType(buf, t.elt) case *Slice: buf.WriteString("[]") - writeType(buf, t.Elt) + writeType(buf, t.elt) case *Struct: buf.WriteString("struct{") - for i, f := range t.Fields { + for i, f := range t.fields { if i > 0 { buf.WriteString("; ") } @@ -255,46 +263,47 @@ func writeType(buf *bytes.Buffer, typ Type) { buf.WriteByte(' ') } writeType(buf, f.Type) - if f.Tag != "" { - fmt.Fprintf(buf, " %q", f.Tag) + if tag := t.Tag(i); tag != "" { + fmt.Fprintf(buf, " %q", tag) } } buf.WriteByte('}') case *Pointer: buf.WriteByte('*') - writeType(buf, t.Base) + writeType(buf, t.base) - case *Result: - writeParams(buf, t.Values, false) + case *Tuple: + writeTuple(buf, t, false) case *Signature: buf.WriteString("func") writeSignature(buf, t) - case *builtin: + case *Builtin: fmt.Fprintf(buf, "", t.name) case *Interface: buf.WriteString("interface{") - for i, m := range t.Methods { + for i, obj := range t.methods.entries { if i > 0 { buf.WriteString("; ") } - buf.WriteString(m.Name) - writeSignature(buf, m.Type) + m := obj.(*Func) + buf.WriteString(m.name) + writeSignature(buf, m.typ.(*Signature)) } buf.WriteByte('}') case *Map: buf.WriteString("map[") - writeType(buf, t.Key) + writeType(buf, t.key) buf.WriteByte(']') - writeType(buf, t.Elt) + writeType(buf, t.elt) case *Chan: var s string - switch t.Dir { + switch t.dir { case ast.SEND: s = "chan<- " case ast.RECV: @@ -303,33 +312,21 @@ func writeType(buf *bytes.Buffer, typ Type) { s = "chan " } buf.WriteString(s) - writeType(buf, t.Elt) + writeType(buf, t.elt) - case *NamedType: - s := "" - if obj := t.Obj; obj != nil { - if obj.Pkg != nil && obj.Pkg.Path != "" { - buf.WriteString(obj.Pkg.Path) + case *Named: + s := "" + if obj := t.obj; obj != nil { + if obj.pkg != nil && obj.pkg.path != "" { + buf.WriteString(obj.pkg.path) buf.WriteString(".") } - s = t.Obj.GetName() + s = t.obj.name } buf.WriteString(s) default: - fmt.Fprintf(buf, "", t) + // For externally defined implementations of Type. + buf.WriteString(t.String()) } } - -func (t *Array) String() string { return typeString(t) } -func (t *Basic) String() string { return typeString(t) } -func (t *Chan) String() string { return typeString(t) } -func (t *Interface) String() string { return typeString(t) } -func (t *Map) String() string { return typeString(t) } -func (t *NamedType) String() string { return typeString(t) } -func (t *Pointer) String() string { return typeString(t) } -func (t *Result) String() string { return typeString(t) } -func (t *Signature) String() string { return typeString(t) } -func (t *Slice) String() string { return typeString(t) } -func (t *Struct) String() string { return typeString(t) } -func (t *builtin) String() string { return typeString(t) } diff --git a/go/types/expr.go b/go/types/expr.go index 0af67a53e4..b759599168 100644 --- a/go/types/expr.go +++ b/go/types/expr.go @@ -93,14 +93,14 @@ func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (param // named parameter for _, name := range field.Names { par := check.lookup(name).(*Var) - par.Type = typ + par.typ = typ last = par copy := *par params = append(params, ©) } } else { // anonymous parameter - par := &Var{Type: typ} + par := &Var{typ: typ} last = nil // not accessible inside function params = append(params, par) } @@ -109,12 +109,12 @@ func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (param // from T to []T (this is the type used inside the function), but // keep the params list unchanged (this is the externally visible type). if isVariadic && last != nil { - last.Type = &Slice{Elt: last.Type} + last.typ = &Slice{elt: last.typ} } return } -func (check *checker) collectMethods(list *ast.FieldList) (methods []*Method) { +func (check *checker) collectMethods(list *ast.FieldList) (methods ObjSet) { if list == nil { return } @@ -131,34 +131,27 @@ func (check *checker) collectMethods(list *ast.FieldList) (methods []*Method) { continue } for _, name := range f.Names { - methods = append(methods, &Method{QualifiedName{check.pkg, name.Name}, sig}) + // TODO(gri) provide correct declaration info + obj := &Func{check.pkg, name.Name, sig, nil} + if alt := methods.Insert(obj); alt != nil { + check.errorf(list.Pos(), "multiple methods named %s", name.Name) + } } } else { // embedded interface - utyp := underlying(typ) + utyp := typ.Underlying() if ityp, ok := utyp.(*Interface); ok { - methods = append(methods, ityp.Methods...) + for _, obj := range ityp.methods.entries { + if alt := methods.Insert(obj); alt != nil { + check.errorf(list.Pos(), "multiple methods named %s", obj.Name()) + } + } } else if utyp != Typ[Invalid] { // if utyp is invalid, don't complain (the root cause was reported before) check.errorf(f.Type.Pos(), "%s is not an interface type", typ) } } } - // Check for double declarations. - // The parser inserts methods into an interface-local scope, so local - // double declarations are reported by the parser already. We need to - // check again for conflicts due to embedded interfaces. This will lead - // to a 2nd error message if the double declaration was reported before - // by the parser. - // TODO(gri) clean this up a bit - seen := make(map[string]bool) - for _, m := range methods { - if seen[m.Name] { - check.errorf(list.Pos(), "multiple methods named %s", m.Name) - return // keep multiple entries, lookup will only return the first entry - } - seen[m.Name] = true - } return } @@ -174,7 +167,7 @@ func (check *checker) tag(t *ast.BasicLit) string { return "" } -func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*Field) { +func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*Field, tags []string) { if list == nil { return } @@ -182,7 +175,14 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [ var typ Type // current field typ var tag string // current field tag add := func(name string, isAnonymous bool) { - fields = append(fields, &Field{QualifiedName{check.pkg, name}, typ, tag, isAnonymous}) + // TODO(gri): rethink this - at the moment we allocate only a prefix + if tag != "" && tags == nil { + tags = make([]string, len(fields)) + } + if tags != nil { + tags = append(tags, tag) + } + fields = append(fields, &Field{check.pkg, name, typ, isAnonymous}) } for _, f := range list.List { @@ -195,11 +195,11 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [ } } else { // anonymous field - switch t := deref(typ).(type) { + switch t := typ.Deref().(type) { case *Basic: - add(t.Name, true) - case *NamedType: - add(t.Obj.GetName(), true) + add(t.name, true) + case *Named: + add(t.obj.name, true) default: if typ != Typ[Invalid] { check.invalidAST(f.Type.Pos(), "anonymous field type %s must be named", typ) @@ -245,21 +245,21 @@ func (check *checker) unary(x *operand, op token.Token) { check.invalidOp(x.pos(), "cannot take address of %s", x) goto Error } - x.typ = &Pointer{Base: x.typ} + x.typ = &Pointer{base: x.typ} return case token.ARROW: - typ, ok := underlying(x.typ).(*Chan) + typ, ok := x.typ.Underlying().(*Chan) if !ok { check.invalidOp(x.pos(), "cannot receive from non-channel %s", x) goto Error } - if typ.Dir&ast.RECV == 0 { + if typ.dir&ast.RECV == 0 { check.invalidOp(x.pos(), "cannot receive from send-only channel %s", x) goto Error } x.mode = valueok - x.typ = typ.Elt + x.typ = typ.elt return } @@ -268,7 +268,7 @@ func (check *checker) unary(x *operand, op token.Token) { } if x.mode == constant { - typ := underlying(x.typ).(*Basic) + typ := x.typ.Underlying().(*Basic) size := -1 if isUnsigned(typ) { size = int(check.ctxt.sizeof(typ)) @@ -417,7 +417,7 @@ func (check *checker) isRepresentable(x *operand, typ *Basic) { return } - if !isRepresentableConst(x.val, check.ctxt, typ.Kind) { + if !isRepresentableConst(x.val, check.ctxt, typ.kind) { var msg string if isNumeric(x.typ) && isNumeric(typ) { msg = "%s overflows (or cannot be accurately represented as) %s" @@ -517,7 +517,7 @@ func (check *checker) updateExprType(x ast.Expr, typ Type, final bool) { // If the new type is not final and still untyped, just // update the recorded type. if !final && isUntyped(typ) { - old.typ = underlying(typ).(*Basic) + old.typ = typ.Underlying().(*Basic) check.untyped[x] = old return } @@ -551,8 +551,8 @@ func (check *checker) convertUntyped(x *operand, target Type) { if isUntyped(target) { // both x and target are untyped - xkind := x.typ.(*Basic).Kind - tkind := target.(*Basic).Kind + xkind := x.typ.(*Basic).kind + tkind := target.(*Basic).kind if isNumeric(x.typ) && isNumeric(target) { if xkind < tkind { x.typ = target @@ -565,7 +565,7 @@ func (check *checker) convertUntyped(x *operand, target Type) { } // typed target - switch t := underlying(target).(type) { + switch t := target.Underlying().(type) { case nil: // We may reach here due to previous type errors. // Be conservative and don't crash. @@ -577,7 +577,7 @@ func (check *checker) convertUntyped(x *operand, target Type) { return // error already reported } case *Interface: - if !x.isNil() && len(t.Methods) > 0 /* empty interfaces are ok */ { + if !x.isNil() && !t.IsEmpty() /* empty interfaces are ok */ { goto Error } // Update operand types to the default type rather then @@ -590,7 +590,7 @@ func (check *checker) convertUntyped(x *operand, target Type) { target = Typ[UntypedNil] } else { // cannot assign untyped values to non-empty interfaces - if len(t.Methods) > 0 { + if !t.IsEmpty() { goto Error } target = defaultType(x.typ) @@ -814,7 +814,7 @@ func (check *checker) binary(x *operand, lhs, rhs ast.Expr, op token.Token, iota } if x.mode == constant && y.mode == constant { - typ := underlying(x.typ).(*Basic) + typ := x.typ.Underlying().(*Basic) // force integer division of integer operands if op == token.QUO && isInteger(typ) { op = token.QUO_ASSIGN @@ -871,7 +871,7 @@ func (check *checker) index(arg ast.Expr, length int64, iota int) (i int64, ok b // For details, see comment in go/parser/parser.go, method parseElement. func (check *checker) compositeLitKey(key ast.Expr) { if ident, ok := key.(*ast.Ident); ok && ident.Obj == nil { - if obj := check.pkg.Scope.Lookup(ident.Name); obj != nil { + if obj := check.pkg.scope.Lookup(ident.Name); obj != nil { check.register(ident, obj) } else if obj := Universe.Lookup(ident.Name); obj != nil { check.register(ident, obj) @@ -937,13 +937,22 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64, iota func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand, passSlice bool) { // determine parameter var par *Var - n := len(sig.Params) + n := sig.params.Len() if i < n { - par = sig.Params[i] - } else if sig.IsVariadic { - par = sig.Params[n-1] + par = sig.params.vars[i] + } else if sig.isVariadic { + par = sig.params.vars[n-1] } else { - check.errorf(arg.Pos(), "too many arguments") + var pos token.Pos + switch { + case arg != nil: + pos = arg.Pos() + case x != nil: + pos = x.pos() + default: + // TODO(gri) what position to use? + } + check.errorf(pos, "too many arguments") return } @@ -951,7 +960,7 @@ func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand, var z operand z.mode = variable z.expr = nil // TODO(gri) can we do better here? (for good error messages) - z.typ = par.Type + z.typ = par.typ if arg != nil { check.expr(x, arg, z.typ, -1) @@ -969,7 +978,7 @@ func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand, // spec: "If the final argument is assignable to a slice type []T, // it may be passed unchanged as the value for a ...T parameter if // the argument is followed by ..." - z.typ = &Slice{Elt: z.typ} // change final parameter type to []T + z.typ = &Slice{elt: z.typ} // change final parameter type to []T } if !check.assignment(x, z.typ) && x.mode != invalid { @@ -977,8 +986,6 @@ func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand, } } -var emptyResult Result - func (check *checker) callExpr(x *operand) { // convert x into a user-friendly set of values var typ Type @@ -987,7 +994,7 @@ func (check *checker) callExpr(x *operand) { case invalid: return // nothing to do case novalue: - typ = &emptyResult + typ = (*Tuple)(nil) case constant: typ = x.typ val = x.val @@ -1050,7 +1057,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle check.errorf(e.Pos(), "use of package %s not in selector", obj.Name) goto Error case *Const: - if obj.Type == Typ[Invalid] { + if obj.typ == Typ[Invalid] { goto Error } x.mode = constant @@ -1061,11 +1068,11 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle } x.val = exact.MakeInt64(int64(iota)) } else { - x.val = obj.Val // may be nil if we don't know the constant value + x.val = obj.val // may be nil if we don't know the constant value } case *TypeName: x.mode = typexpr - if !cycleOk && underlying(obj.Type) == nil { + if !cycleOk && obj.typ.Underlying() == nil { check.errorf(obj.spec.Pos(), "illegal cycle in declaration of %s", obj.Name) x.expr = e x.typ = Typ[Invalid] @@ -1078,7 +1085,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle default: unreachable() } - x.typ = obj.GetType() + x.typ = obj.Type() case *ast.Ellipsis: // ellipses are handled explicitly where they are legal @@ -1115,7 +1122,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle // We have an "open" [...]T array type. // Create a new ArrayType with unknown length (-1) // and finish setting it up after analyzing the literal. - typ = &Array{Len: -1, Elt: check.typ(atyp.Elt, cycleOk)} + typ = &Array{len: -1, elt: check.typ(atyp.Elt, cycleOk)} openArray = true } } @@ -1128,12 +1135,12 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle goto Error } - switch utyp := underlying(deref(typ)).(type) { + switch utyp := typ.Deref().Underlying().(type) { case *Struct: if len(e.Elts) == 0 { break } - fields := utyp.Fields + fields := utyp.fields if _, ok := e.Elts[0].(*ast.KeyValueExpr); ok { // all elements must have keys visited := make([]bool, len(fields)) @@ -1148,7 +1155,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key) continue } - i := utyp.fieldIndex(QualifiedName{check.pkg, key.Name}) + i := utyp.fieldIndex(check.pkg, key.Name) if i < 0 { check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name) continue @@ -1196,14 +1203,14 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle } case *Array: - n := check.indexedElts(e.Elts, utyp.Elt, utyp.Len, iota) + n := check.indexedElts(e.Elts, utyp.elt, utyp.len, iota) // if we have an "open" [...]T array, set the length now that we know it if openArray { - utyp.Len = n + utyp.len = n } case *Slice: - check.indexedElts(e.Elts, utyp.Elt, -1, iota) + check.indexedElts(e.Elts, utyp.elt, -1, iota) case *Map: visited := make(map[interface{}]bool, len(e.Elts)) @@ -1215,9 +1222,9 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle } check.compositeLitKey(kv.Key) check.expr(x, kv.Key, nil, iota) - if !check.assignment(x, utyp.Key) { + if !check.assignment(x, utyp.key) { if x.mode != invalid { - check.errorf(x.pos(), "cannot use %s as %s key in map literal", x, utyp.Key) + check.errorf(x.pos(), "cannot use %s as %s key in map literal", x, utyp.key) } continue } @@ -1228,10 +1235,10 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle } visited[x.val] = true } - check.expr(x, kv.Value, utyp.Elt, iota) - if !check.assignment(x, utyp.Elt) { + check.expr(x, kv.Value, utyp.elt, iota) + if !check.assignment(x, utyp.elt) { if x.mode != invalid { - check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.Elt) + check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.elt) } continue } @@ -1256,11 +1263,11 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle // selector expressions. if ident, ok := e.X.(*ast.Ident); ok { if pkg, ok := check.lookup(ident).(*Package); ok { - exp := pkg.Scope.Lookup(sel) + exp := pkg.scope.Lookup(sel) if exp == nil { check.errorf(e.Pos(), "%s not declared by package %s", sel, ident) goto Error - } else if !ast.IsExported(exp.GetName()) { + } else if !ast.IsExported(exp.Name()) { // gcimported package scopes contain non-exported // objects such as types used in partially exported // objects - do not accept them @@ -1275,17 +1282,17 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle case *Const: assert(exp.Val != nil) x.mode = constant - x.typ = exp.Type - x.val = exp.Val + x.typ = exp.typ + x.val = exp.val case *TypeName: x.mode = typexpr - x.typ = exp.Type + x.typ = exp.typ case *Var: x.mode = variable - x.typ = exp.Type + x.typ = exp.typ case *Func: x.mode = value - x.typ = exp.Type + x.typ = exp.typ default: unreachable() } @@ -1298,7 +1305,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle if x.mode == invalid { goto Error } - res := lookupField(x.typ, QualifiedName{check.pkg, sel}) + res := lookupField(x.typ, check.pkg, sel) if res.mode == invalid { check.invalidOp(e.Pos(), "%s has no single field or method %s", x, sel) goto Error @@ -1314,11 +1321,15 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle // argument of the method expression's function type // TODO(gri) at the moment, method sets don't correctly track // pointer vs non-pointer receivers => typechecker is too lenient + var params []*Var + if sig.params != nil { + params = sig.params.vars + } x.mode = value x.typ = &Signature{ - Params: append([]*Var{{Type: x.typ}}, sig.Params...), - Results: sig.Results, - IsVariadic: sig.IsVariadic, + params: NewTuple(append([]*Var{{typ: x.typ}}, params...)...), + results: sig.results, + isVariadic: sig.isVariadic, } } else { // regular selector @@ -1334,7 +1345,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle valid := false length := int64(-1) // valid if >= 0 - switch typ := underlying(x.typ).(type) { + switch typ := x.typ.Underlying().(type) { case *Basic: if isString(typ) { valid = true @@ -1350,36 +1361,36 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle case *Array: valid = true - length = typ.Len + length = typ.len if x.mode != variable { x.mode = value } - x.typ = typ.Elt + x.typ = typ.elt case *Pointer: - if typ, _ := underlying(typ.Base).(*Array); typ != nil { + if typ, _ := typ.base.Underlying().(*Array); typ != nil { valid = true - length = typ.Len + length = typ.len x.mode = variable - x.typ = typ.Elt + x.typ = typ.elt } case *Slice: valid = true x.mode = variable - x.typ = typ.Elt + x.typ = typ.elt case *Map: var key operand check.expr(&key, e.Index, nil, iota) - if !check.assignment(&key, typ.Key) { + if !check.assignment(&key, typ.key) { if key.mode != invalid { - check.invalidOp(key.pos(), "cannot use %s as map index of type %s", &key, typ.Key) + check.invalidOp(key.pos(), "cannot use %s as map index of type %s", &key, typ.key) } goto Error } x.mode = valueok - x.typ = typ.Elt + x.typ = typ.elt x.expr = e return } @@ -1405,7 +1416,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle valid := false length := int64(-1) // valid if >= 0 - switch typ := underlying(x.typ).(type) { + switch typ := x.typ.Underlying().(type) { case *Basic: if isString(typ) { valid = true @@ -1419,26 +1430,26 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle x.mode = value // x.typ doesn't change, but if it is an untyped // string it becomes string (see also issue 4913). - if typ.Kind == UntypedString { + if typ.kind == UntypedString { x.typ = Typ[String] } } case *Array: valid = true - length = typ.Len + 1 // +1 for slice + length = typ.len + 1 // +1 for slice if x.mode != variable { check.invalidOp(x.pos(), "cannot slice %s (value not addressable)", x) goto Error } - x.typ = &Slice{Elt: typ.Elt} + x.typ = &Slice{elt: typ.elt} case *Pointer: - if typ, _ := underlying(typ.Base).(*Array); typ != nil { + if typ, _ := typ.base.Underlying().(*Array); typ != nil { valid = true - length = typ.Len + 1 // +1 for slice + length = typ.len + 1 // +1 for slice x.mode = variable - x.typ = &Slice{Elt: typ.Elt} + x.typ = &Slice{elt: typ.elt} } case *Slice: @@ -1479,7 +1490,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle goto Error } var T *Interface - if T, _ = underlying(x.typ).(*Interface); T == nil { + if T, _ = x.typ.Underlying().(*Interface); T == nil { check.invalidOp(x.pos(), "%s is not an interface", x) goto Error } @@ -1499,7 +1510,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle } else { msg = "%s cannot have dynamic type %s (missing method %s)" } - check.errorf(e.Type.Pos(), msg, x, typ, method.Name) + check.errorf(e.Type.Pos(), msg, x, typ, method.name) // ok to continue } x.mode = valueok @@ -1512,7 +1523,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle goto Error } else if x.mode == typexpr { check.conversion(x, e, x.typ, iota) - } else if sig, ok := underlying(x.typ).(*Signature); ok { + } else if sig, ok := x.typ.Underlying().(*Signature); ok { // check parameters // If we have a trailing ... at the end of the parameter @@ -1520,7 +1531,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle // []T of a variadic function parameter x ...T. passSlice := false if e.Ellipsis.IsValid() { - if sig.IsVariadic { + if sig.isVariadic { passSlice = true } else { check.errorf(e.Ellipsis, "cannot use ... in call to %s", e.Fun) @@ -1543,13 +1554,14 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle if x.mode == invalid { goto Error // TODO(gri): we can do better } - if t, _ := x.typ.(*Result); t != nil { + if t, ok := x.typ.(*Tuple); ok { // multiple result values - n = len(t.Values) - for i, obj := range t.Values { + n = t.Len() + for i := 0; i < n; i++ { + obj := t.At(i) x.mode = value x.expr = nil // TODO(gri) can we do better here? (for good error messages) - x.typ = obj.Type + x.typ = obj.typ check.argument(sig, i, nil, x, passSlice && i+1 == n) } } else { @@ -1567,29 +1579,29 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle } // determine if we have enough arguments - if sig.IsVariadic { + if sig.isVariadic { // a variadic function accepts an "empty" // last argument: count one extra n++ } - if n < len(sig.Params) { + if n < sig.params.Len() { check.errorf(e.Fun.Pos(), "too few arguments in call to %s", e.Fun) // ok to continue } // determine result - switch len(sig.Results) { + switch sig.results.Len() { case 0: x.mode = novalue case 1: x.mode = value - x.typ = sig.Results[0].Type + x.typ = sig.results.vars[0].typ default: x.mode = value - x.typ = &Result{Values: sig.Results} + x.typ = sig.results } - } else if bin, ok := x.typ.(*builtin); ok { + } else if bin, ok := x.typ.(*Builtin); ok { check.builtin(x, e, bin, iota) } else { @@ -1603,11 +1615,11 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle case invalid: goto Error case typexpr: - x.typ = &Pointer{Base: x.typ} + x.typ = &Pointer{base: x.typ} default: - if typ, ok := underlying(x.typ).(*Pointer); ok { + if typ, ok := x.typ.Underlying().(*Pointer); ok { x.mode = variable - x.typ = typ.Base + x.typ = typ.base } else { check.invalidOp(x.pos(), "cannot indirect %s", x) goto Error @@ -1656,33 +1668,34 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle check.errorf(x.pos(), "invalid array length %s", x) goto Error } - x.typ = &Array{Len: n, Elt: check.typ(e.Elt, cycleOk)} + x.typ = &Array{len: n, elt: check.typ(e.Elt, cycleOk)} } else { - x.typ = &Slice{Elt: check.typ(e.Elt, true)} + x.typ = &Slice{elt: check.typ(e.Elt, true)} } x.mode = typexpr case *ast.StructType: x.mode = typexpr - x.typ = &Struct{Fields: check.collectFields(e.Fields, cycleOk)} + fields, tags := check.collectFields(e.Fields, cycleOk) + x.typ = &Struct{fields: fields, tags: tags} case *ast.FuncType: params, isVariadic := check.collectParams(e.Params, true) results, _ := check.collectParams(e.Results, false) x.mode = typexpr - x.typ = &Signature{Recv: nil, Params: params, Results: results, IsVariadic: isVariadic} + x.typ = &Signature{recv: nil, params: NewTuple(params...), results: NewTuple(results...), isVariadic: isVariadic} case *ast.InterfaceType: x.mode = typexpr - x.typ = &Interface{Methods: check.collectMethods(e.Methods)} + x.typ = &Interface{methods: check.collectMethods(e.Methods)} case *ast.MapType: x.mode = typexpr - x.typ = &Map{Key: check.typ(e.Key, true), Elt: check.typ(e.Value, true)} + x.typ = &Map{key: check.typ(e.Key, true), elt: check.typ(e.Value, true)} case *ast.ChanType: x.mode = typexpr - x.typ = &Chan{Dir: e.Dir, Elt: check.typ(e.Value, true)} + x.typ = &Chan{dir: e.Dir, elt: check.typ(e.Value, true)} default: if debug { diff --git a/go/types/gcimporter.go b/go/types/gcimporter.go index ed9b856e86..d417263012 100644 --- a/go/types/gcimporter.go +++ b/go/types/gcimporter.go @@ -124,7 +124,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 } @@ -177,7 +177,7 @@ func (p *gcParser) init(filename, id string, src io.Reader, imports map[string]* if false { // check consistency of imports map for _, pkg := range imports { - if pkg.Name == "" { + if pkg.name == "" { fmt.Printf("no package name for %s\n", pkg.Path) } } @@ -201,45 +201,45 @@ func (p *gcParser) next() { 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 + scope := pkg.scope if obj := scope.Lookup(name); obj != nil { return obj.(*Const) } // otherwise create a new constant and insert it into the scope - obj := &Const{Pkg: pkg, Name: name} + obj := &Const{pkg: pkg, name: name} scope.Insert(obj) return obj } func declTypeName(pkg *Package, name string) *TypeName { - scope := pkg.Scope + scope := pkg.scope if obj := scope.Lookup(name); obj != nil { return obj.(*TypeName) } - obj := &TypeName{Pkg: pkg, Name: name} + obj := &TypeName{pkg: pkg, name: name} // a named type may be referred to before the underlying type // is known - set it up - obj.Type = &NamedType{Obj: obj} + obj.typ = &Named{obj: obj} scope.Insert(obj) return obj } func declVar(pkg *Package, name string) *Var { - scope := pkg.Scope + scope := pkg.scope if obj := scope.Lookup(name); obj != nil { return obj.(*Var) } - obj := &Var{Pkg: pkg, Name: name} + obj := &Var{pkg: pkg, name: name} scope.Insert(obj) return obj } func declFunc(pkg *Package, name string) *Func { - scope := pkg.Scope + scope := pkg.scope if obj := scope.Lookup(name); obj != nil { return obj.(*Func) } - obj := &Func{Pkg: pkg, Name: name} + obj := &Func{pkg: pkg, name: name} scope.Insert(obj) return obj } @@ -360,7 +360,7 @@ func (p *gcParser) getPkg(id, name string) *Package { } pkg := p.imports[id] if pkg == nil && name != "" { - pkg = &Package{Name: name, Path: id, Scope: new(Scope)} + pkg = &Package{name: name, path: id, scope: new(Scope)} p.imports[id] = pkg } return pkg @@ -387,7 +387,7 @@ func (p *gcParser) parseBasicType() Type { id := p.expect(scanner.Ident) obj := Universe.Lookup(id) if obj, ok := obj.(*TypeName); ok { - return obj.Type + return obj.typ } p.errorf("not a basic type: %s", id) return nil @@ -404,7 +404,7 @@ func (p *gcParser) parseArrayType() Type { if err != nil { p.error(err) } - return &Array{Len: n, Elt: elt} + return &Array{len: n, elt: elt} } // MapType = "map" "[" Type "]" Type . @@ -415,7 +415,7 @@ func (p *gcParser) parseMapType() Type { key := p.parseType() p.expect(']') elt := p.parseType() - return &Map{Key: key, Elt: elt} + return &Map{key: key, elt: elt} } // Name = identifier | "?" | QualifiedName . @@ -447,7 +447,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 = &Package{path: id} } } default: @@ -458,23 +458,27 @@ func (p *gcParser) parseName(materializePkg bool) (pkg *Package, name string) { // Field = Name Type [ string_lit ] . // -func (p *gcParser) parseField() *Field { +func (p *gcParser) parseField() (*Field, string) { var f Field f.Pkg, f.Name = p.parseName(true) f.Type = p.parseType() + tag := "" if p.tok == scanner.String { - f.Tag = p.expect(scanner.String) + tag = p.expect(scanner.String) } if f.Name == "" { // anonymous field - typ must be T or *T and T must be a type name - if typ, ok := deref(f.Type).(*NamedType); ok && typ.Obj != nil { - f.Name = typ.Obj.GetName() - f.IsAnonymous = true - } else { + switch typ := f.Type.Deref().(type) { + case *Basic: // basic types are named types + f.Name = typ.name + case *Named: + f.Name = typ.obj.name + default: p.errorf("anonymous field expected") } + f.IsAnonymous = true } - return &f + return &f, tag } // StructType = "struct" "{" [ FieldList ] "}" . @@ -482,6 +486,7 @@ func (p *gcParser) parseField() *Field { // func (p *gcParser) parseStructType() Type { var fields []*Field + var tags []string p.expectKeyword("struct") p.expect('{') @@ -489,11 +494,19 @@ func (p *gcParser) parseStructType() Type { if len(fields) > 0 { p.expect(';') } - fields = append(fields, p.parseField()) + fld, tag := p.parseField() + // TODO(gri) same code in collectFields (expr.go) - factor? + if tag != "" && tags == nil { + tags = make([]string, len(fields)) + } + if tags != nil { + tags = append(tags, tag) + } + fields = append(fields, fld) } p.expect('}') - return &Struct{Fields: fields} + return &Struct{fields: fields, tags: tags} } // Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] . @@ -512,7 +525,7 @@ func (p *gcParser) parseParameter() (par *Var, isVariadic bool) { if p.tok == scanner.String { p.next() } - par = &Var{Name: name, Type: typ} // Pkg == nil + par = &Var{name: name, typ: typ} // Pkg == nil return } @@ -555,7 +568,7 @@ func (p *gcParser) parseSignature() *Signature { } } - return &Signature{Params: params, Results: results, IsVariadic: isVariadic} + return &Signature{params: NewTuple(params...), results: NewTuple(results...), isVariadic: isVariadic} } // InterfaceType = "interface" "{" [ MethodList ] "}" . @@ -567,21 +580,23 @@ func (p *gcParser) parseSignature() *Signature { // visible in the export data. // func (p *gcParser) parseInterfaceType() Type { - var methods []*Method + var methods ObjSet p.expectKeyword("interface") p.expect('{') - for p.tok != '}' { - if len(methods) > 0 { + for i := 0; p.tok != '}'; i++ { + if i > 0 { p.expect(';') } pkg, name := p.parseName(true) - typ := p.parseSignature() - methods = append(methods, &Method{QualifiedName{pkg, name}, typ}) + sig := p.parseSignature() + if alt := methods.Insert(&Func{pkg, name, sig, nil}); alt != nil { + p.errorf("multiple methods named %s.%s", alt.Pkg().name, alt.Name()) + } } p.expect('}') - return &Interface{Methods: methods} + return &Interface{methods: methods} } // ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type . @@ -600,7 +615,7 @@ func (p *gcParser) parseChanType() Type { dir = ast.RECV } elt := p.parseType() - return &Chan{Dir: dir, Elt: elt} + return &Chan{dir: dir, elt: elt} } // Type = @@ -636,19 +651,19 @@ func (p *gcParser) parseType() Type { case '@': // TypeName pkg, name := p.parseExportedName() - return declTypeName(pkg, name).Type + return declTypeName(pkg, name).typ case '[': p.next() // look ahead if p.tok == ']' { // SliceType p.next() - return &Slice{Elt: p.parseType()} + return &Slice{elt: p.parseType()} } return p.parseArrayType() case '*': // PointerType p.next() - return &Pointer{Base: p.parseType()} + return &Pointer{base: p.parseType()} case '<': return p.parseChanType() case '(': @@ -736,7 +751,7 @@ func (p *gcParser) parseConstDecl() { obj := declConst(pkg, name) var x operand if p.tok != '=' { - obj.Type = p.parseType() + obj.typ = p.parseType() } p.expect('=') switch p.tok { @@ -787,11 +802,11 @@ func (p *gcParser) parseConstDecl() { default: p.errorf("expected literal got %s", scanner.TokenString(p.tok)) } - if obj.Type == nil { - obj.Type = x.typ + if obj.typ == nil { + obj.typ = x.typ } assert(x.val != nil) - obj.Val = x.val + obj.val = x.val } // TypeDecl = "type" ExportedName Type . @@ -808,8 +823,8 @@ func (p *gcParser) parseTypeDecl() { // a given type declaration. typ := p.parseType() - if name := obj.Type.(*NamedType); name.Underlying == nil { - name.Underlying = typ + if name := obj.typ.(*Named); name.underlying == nil { + name.underlying = typ } } @@ -819,7 +834,7 @@ func (p *gcParser) parseVarDecl() { p.expectKeyword("var") pkg, name := p.parseExportedName() obj := declVar(pkg, name) - obj.Type = p.parseType() + obj.typ = p.parseType() } // Func = Signature [ Body ] . @@ -851,26 +866,20 @@ func (p *gcParser) parseMethodDecl() { p.expect(')') // determine receiver base type object - typ := recv.Type + typ := recv.typ if ptr, ok := typ.(*Pointer); ok { - typ = ptr.Base + typ = ptr.base } - base := typ.(*NamedType) + base := typ.(*Named) // parse method name, signature, and possibly inlined body pkg, name := p.parseName(true) // unexported method names in imports are qualified with their package. sig := p.parseFunc() - sig.Recv = recv + sig.recv = recv // add method to type unless type was imported before // and method exists already - // TODO(gri) investigate if this can be avoided - for _, m := range base.Methods { - if m.Name == name { - return // method was added before - } - } - base.Methods = append(base.Methods, &Method{QualifiedName{pkg, name}, sig}) + base.methods.Insert(&Func{pkg, name, sig, nil}) } // FuncDecl = "func" ExportedName Func . @@ -879,7 +888,7 @@ func (p *gcParser) parseFuncDecl() { // "func" already consumed pkg, name := p.parseExportedName() typ := p.parseFunc() - declFunc(pkg, name).Type = typ + declFunc(pkg, name).typ = typ } // Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" . @@ -939,7 +948,7 @@ func (p *gcParser) parseExport() *Package { } // package was imported completely and without errors - pkg.Complete = true + pkg.complete = true return pkg } diff --git a/go/types/gcimporter_test.go b/go/types/gcimporter_test.go index b793eb4cb3..bc0378a816 100644 --- a/go/types/gcimporter_test.go +++ b/go/types/gcimporter_test.go @@ -146,7 +146,7 @@ func TestGcImportedTypes(t *testing.T) { continue } - obj := pkg.Scope.Lookup(objName) + obj := pkg.scope.Lookup(objName) // TODO(gri) should define an accessor on Object var kind ast.ObjKind @@ -154,16 +154,16 @@ func TestGcImportedTypes(t *testing.T) { switch obj := obj.(type) { case *Const: kind = ast.Con - typ = obj.Type + typ = obj.typ case *TypeName: kind = ast.Typ - typ = obj.Type + typ = obj.typ case *Var: kind = ast.Var - typ = obj.Type + typ = obj.typ case *Func: kind = ast.Fun - typ = obj.Type + typ = obj.typ default: unreachable() } @@ -172,7 +172,7 @@ func TestGcImportedTypes(t *testing.T) { t.Errorf("%s: got kind = %q; want %q", test.name, kind, test.kind) } - str := typeString(underlying(typ)) + str := typeString(typ.Underlying()) if str != test.typ { t.Errorf("%s: got type = %q; want %q", test.name, typ, test.typ) } diff --git a/go/types/objects.go b/go/types/objects.go index e91f818b3a..96cb8c1d77 100644 --- a/go/types/objects.go +++ b/go/types/objects.go @@ -16,143 +16,157 @@ import ( // All objects implement the Object interface. // type Object interface { - GetPkg() *Package - GetName() string - GetType() Type - GetPos() token.Pos - - anObject() + Pkg() *Package // nil for objects in the Universe scope + Scope() *Scope + Name() string + Type() Type + Pos() token.Pos + // TODO(gri) provide String method! } // A Package represents the contents (objects) of a Go package. type Package struct { - Name string - Path string // import path, "" for current (non-imported) package - Scope *Scope // package-level scope - Imports map[string]*Package // map of import paths to imported packages - Complete bool // if set, this package was imported completely + name string + path string // import path, "" for current (non-imported) package + scope *Scope // package-level scope + imports map[string]*Package // map of import paths to imported packages + complete bool // if set, this package was imported completely spec *ast.ImportSpec } +func NewPackage(path, name string) *Package { + return &Package{name: name, path: path, complete: true} +} + +func (obj *Package) Pkg() *Package { return obj } +func (obj *Package) Scope() *Scope { return obj.scope } +func (obj *Package) Name() string { return obj.name } +func (obj *Package) Type() Type { return Typ[Invalid] } +func (obj *Package) Pos() token.Pos { + if obj.spec == nil { + return token.NoPos + } + return obj.spec.Pos() +} +func (obj *Package) Path() string { return obj.path } +func (obj *Package) Imports() map[string]*Package { return obj.imports } +func (obj *Package) Complete() bool { return obj.complete } + // A Const represents a declared constant. type Const struct { - Pkg *Package - Name string - Type Type - Val exact.Value + pkg *Package + name string + typ Type + val exact.Value visited bool // for initialization cycle detection spec *ast.ValueSpec } -// A TypeName represents a declared type. -type TypeName struct { - Pkg *Package - Name string - Type Type // *NamedType or *Basic - - spec *ast.TypeSpec -} - -// A Variable represents a declared variable (including function parameters and results). -type Var struct { - Pkg *Package // nil for parameters - Name string - Type Type - - visited bool // for initialization cycle detection - decl interface{} -} - -// A Func represents a declared function. -type Func struct { - Pkg *Package - Name string - Type Type // *Signature or *Builtin - - decl *ast.FuncDecl -} - -func (obj *Package) GetPkg() *Package { return obj } -func (obj *Const) GetPkg() *Package { return obj.Pkg } -func (obj *TypeName) GetPkg() *Package { return obj.Pkg } -func (obj *Var) GetPkg() *Package { return obj.Pkg } -func (obj *Func) GetPkg() *Package { return obj.Pkg } - -func (obj *Package) GetName() string { return obj.Name } -func (obj *Const) GetName() string { return obj.Name } -func (obj *TypeName) GetName() string { return obj.Name } -func (obj *Var) GetName() string { return obj.Name } -func (obj *Func) GetName() string { return obj.Name } - -func (obj *Package) GetType() Type { return Typ[Invalid] } -func (obj *Const) GetType() Type { return obj.Type } -func (obj *TypeName) GetType() Type { return obj.Type } -func (obj *Var) GetType() Type { return obj.Type } -func (obj *Func) GetType() Type { return obj.Type } - -func (obj *Package) GetPos() token.Pos { - if obj.spec == nil { - return token.NoPos - } - return obj.spec.Pos() -} - -func (obj *Const) GetPos() token.Pos { +func (obj *Const) Pkg() *Package { return obj.pkg } +func (obj *Const) Scope() *Scope { panic("unimplemented") } +func (obj *Const) Name() string { return obj.name } +func (obj *Const) Type() Type { return obj.typ } +func (obj *Const) Pos() token.Pos { if obj.spec == nil { return token.NoPos } for _, n := range obj.spec.Names { - if n.Name == obj.Name { + if n.Name == obj.name { return n.Pos() } } return token.NoPos } -func (obj *TypeName) GetPos() token.Pos { +func (obj *Const) Val() exact.Value { return obj.val } + +// A TypeName represents a declared type. +type TypeName struct { + pkg *Package + name string + typ Type // *Named or *Basic + + spec *ast.TypeSpec +} + +func NewTypeName(pkg *Package, name string, typ Type) *TypeName { + return &TypeName{pkg, name, typ, nil} +} + +func (obj *TypeName) Pkg() *Package { return obj.pkg } +func (obj *TypeName) Scope() *Scope { panic("unimplemented") } +func (obj *TypeName) Name() string { return obj.name } +func (obj *TypeName) Type() Type { return obj.typ } +func (obj *TypeName) Pos() token.Pos { if obj.spec == nil { return token.NoPos } return obj.spec.Pos() } -func (obj *Var) GetPos() token.Pos { +// A Variable represents a declared variable (including function parameters and results). +type Var struct { + pkg *Package // nil for parameters + name string + typ Type + + visited bool // for initialization cycle detection + decl interface{} +} + +func NewVar(pkg *Package, name string, typ Type) *Var { + return &Var{pkg, name, typ, false, nil} +} + +func (obj *Var) Pkg() *Package { return obj.pkg } +func (obj *Var) Scope() *Scope { panic("unimplemented") } +func (obj *Var) Name() string { return obj.name } +func (obj *Var) Type() Type { return obj.typ } +func (obj *Var) Pos() token.Pos { switch d := obj.decl.(type) { case *ast.Field: for _, n := range d.Names { - if n.Name == obj.Name { + if n.Name == obj.name { return n.Pos() } } case *ast.ValueSpec: for _, n := range d.Names { - if n.Name == obj.Name { + if n.Name == obj.name { return n.Pos() } } case *ast.AssignStmt: for _, x := range d.Lhs { - if ident, isIdent := x.(*ast.Ident); isIdent && ident.Name == obj.Name { + if ident, isIdent := x.(*ast.Ident); isIdent && ident.Name == obj.name { return ident.Pos() } } } return token.NoPos } -func (obj *Func) GetPos() token.Pos { + +// A Func represents a declared function. +type Func struct { + pkg *Package + name string + typ Type // *Signature or *Builtin + + decl *ast.FuncDecl +} + +func (obj *Func) Pkg() *Package { return obj.pkg } +func (obj *Func) Scope() *Scope { panic("unimplemented") } +func (obj *Func) Name() string { return obj.name } +func (obj *Func) Type() Type { return obj.typ } +func (obj *Func) Pos() token.Pos { if obj.decl != nil && obj.decl.Name != nil { return obj.decl.Name.Pos() } return token.NoPos } -func (*Package) anObject() {} -func (*Const) anObject() {} -func (*TypeName) anObject() {} -func (*Var) anObject() {} -func (*Func) anObject() {} - // newObj returns a new Object for a given *ast.Object. // It does not canonicalize them (it always returns a new one). // For canonicalization, see check.lookup. @@ -171,9 +185,9 @@ func newObj(pkg *Package, astObj *ast.Object) Object { unreachable() case ast.Con: iota := astObj.Data.(int) - return &Const{Pkg: pkg, Name: name, Type: typ, Val: exact.MakeInt64(int64(iota)), spec: astObj.Decl.(*ast.ValueSpec)} + return &Const{pkg: pkg, name: name, typ: typ, val: exact.MakeInt64(int64(iota)), spec: astObj.Decl.(*ast.ValueSpec)} case ast.Typ: - return &TypeName{Pkg: pkg, Name: name, Type: typ, spec: astObj.Decl.(*ast.TypeSpec)} + return &TypeName{pkg: pkg, name: name, typ: typ, spec: astObj.Decl.(*ast.TypeSpec)} case ast.Var: switch astObj.Decl.(type) { case *ast.Field: // function parameters @@ -182,9 +196,9 @@ func newObj(pkg *Package, astObj *ast.Object) Object { default: unreachable() // everything else is not ok } - return &Var{Pkg: pkg, Name: name, Type: typ, decl: astObj.Decl} + return &Var{pkg: pkg, name: name, typ: typ, decl: astObj.Decl} case ast.Fun: - return &Func{Pkg: pkg, Name: name, Type: typ, decl: astObj.Decl.(*ast.FuncDecl)} + return &Func{pkg: pkg, name: name, typ: typ, decl: astObj.Decl.(*ast.FuncDecl)} case ast.Lbl: unreachable() // for now } diff --git a/go/types/objset.go b/go/types/objset.go new file mode 100644 index 0000000000..bd3ea01dd9 --- /dev/null +++ b/go/types/objset.go @@ -0,0 +1,64 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +import ( + "bytes" + "fmt" + "go/ast" +) + +// An ObjSet maintains a set of objects identified by +// their name and package that declares them. +// +type ObjSet struct { + entries []Object // set entries in insertion order +} + +// Lookup returns the object with the given package and name +// if it is found in ObjSet s, otherwise it returns nil. +// +func (s *ObjSet) Lookup(pkg *Package, name string) Object { + for _, obj := range s.entries { + // spec: + // "Two identifiers are different if they are spelled differently, + // or if they appear in different packages and are not exported. + // Otherwise, they are the same." + if obj.Name() == name && (ast.IsExported(name) || obj.Pkg().path == pkg.path) { + return obj + } + } + return nil +} + +// Insert attempts to insert an object obj into ObjSet s. +// If s already contains an object from the same package +// with the same name, Insert leaves s unchanged and returns +// that object. Otherwise it inserts obj and returns nil. +// +func (s *ObjSet) Insert(obj Object) Object { + pkg := obj.Pkg() + name := obj.Name() + assert(obj.Type() != nil) + if alt := s.Lookup(pkg, name); alt != nil { + return alt + } + s.entries = append(s.entries, obj) + return nil +} + +// Debugging support +func (s *ObjSet) String() string { + var buf bytes.Buffer + fmt.Fprintf(&buf, "ObjSet %p {", s) + if s != nil && len(s.entries) > 0 { + fmt.Fprintln(&buf) + for _, obj := range s.entries { + fmt.Fprintf(&buf, "\t%s.%s\t%T\n", obj.Pkg().path, obj.Name(), obj) + } + } + fmt.Fprintf(&buf, "}\n") + return buf.String() +} diff --git a/go/types/operand.go b/go/types/operand.go index ce84cbb35c..e2e27869d0 100644 --- a/go/types/operand.go +++ b/go/types/operand.go @@ -136,8 +136,8 @@ func (x *operand) isAssignable(ctxt *Context, T Type) bool { return true } - Vu := underlying(V) - Tu := underlying(T) + Vu := V.Underlying() + Tu := T.Underlying() // x's type V and T have identical underlying types // and at least one of V or T is not a named type @@ -155,8 +155,8 @@ func (x *operand) isAssignable(ctxt *Context, T Type) bool { // x is a bidirectional channel value, T is a channel // type, x's type V and T have identical element types, // and at least one of V or T is not a named type - if Vc, ok := Vu.(*Chan); ok && Vc.Dir == ast.SEND|ast.RECV { - if Tc, ok := Tu.(*Chan); ok && IsIdentical(Vc.Elt, Tc.Elt) { + if Vc, ok := Vu.(*Chan); ok && Vc.dir == ast.SEND|ast.RECV { + if Tc, ok := Tu.(*Chan); ok && IsIdentical(Vc.elt, Tc.elt) { return !isNamed(V) || !isNamed(T) } } @@ -166,7 +166,7 @@ func (x *operand) isAssignable(ctxt *Context, T Type) bool { if x.isNil() { switch t := Tu.(type) { case *Basic: - if t.Kind == UnsafePointer { + if t.kind == UnsafePointer { return true } case *Pointer, *Signature, *Slice, *Map, *Chan, *Interface: @@ -182,15 +182,15 @@ func (x *operand) isAssignable(ctxt *Context, T Type) bool { switch t := Tu.(type) { case *Basic: if x.mode == constant { - return isRepresentableConst(x.val, ctxt, t.Kind) + return isRepresentableConst(x.val, ctxt, t.kind) } // The result of a comparison is an untyped boolean, // but may not be a constant. if Vb, _ := Vu.(*Basic); Vb != nil { - return Vb.Kind == UntypedBool && isBoolean(Tu) + return Vb.kind == UntypedBool && isBoolean(Tu) } case *Interface: - return x.isNil() || len(t.Methods) == 0 + return x.isNil() || t.IsEmpty() case *Pointer, *Signature, *Slice, *Map, *Chan: return x.isNil() } @@ -214,7 +214,7 @@ type lookupResult struct { } type embeddedType struct { - typ *NamedType + typ *Named index []int // field index sequence multiples bool // if set, typ is embedded multiple times at the same level } @@ -224,9 +224,9 @@ type embeddedType struct { // the result describes the field mode and type; otherwise the result mode is invalid. // (This function is similar in structure to FieldByNameFunc in reflect/type.go) // -func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res lookupResult) { +func lookupFieldBreadthFirst(list []embeddedType, pkg *Package, name string) (res lookupResult) { // visited records the types that have been searched already. - visited := make(map[*NamedType]bool) + visited := make(map[*Named]bool) // embedded types of the next lower level var next []embeddedType @@ -264,20 +264,19 @@ func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res looku visited[typ] = true // look for a matching attached method - for _, m := range typ.Methods { - if name.IsSame(m.QualifiedName) { - assert(m.Type != nil) - if !potentialMatch(e.multiples, value, m.Type) { - return // name collision - } + if obj := typ.methods.Lookup(pkg, name); obj != nil { + m := obj.(*Func) + assert(m.typ != nil) + if !potentialMatch(e.multiples, value, m.typ) { + return // name collision } } - switch t := typ.Underlying.(type) { + switch t := typ.underlying.(type) { case *Struct: // look for a matching field and collect embedded types - for i, f := range t.Fields { - if name.IsSame(f.QualifiedName) { + for i, f := range t.fields { + if f.isMatch(pkg, name) { assert(f.Type != nil) if !potentialMatch(e.multiples, variable, f.Type) { return // name collision @@ -300,7 +299,7 @@ func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res looku if f.IsAnonymous && res.mode == invalid { // Ignore embedded basic types - only user-defined // named types can have methods or have struct fields. - if t, _ := deref(f.Type).(*NamedType); t != nil { + if t, _ := f.Type.Deref().(*Named); t != nil { var index []int index = append(index, e.index...) // copy e.index index = append(index, i) @@ -311,12 +310,11 @@ func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res looku case *Interface: // look for a matching method - for _, m := range t.Methods { - if name.IsSame(m.QualifiedName) { - assert(m.Type != nil) - if !potentialMatch(e.multiples, value, m.Type) { - return // name collision - } + if obj := t.methods.Lookup(pkg, name); obj != nil { + m := obj.(*Func) + assert(m.typ != nil) + if !potentialMatch(e.multiples, value, m.typ) { + return // name collision } } } @@ -349,7 +347,7 @@ func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res looku return } -func findType(list []embeddedType, typ *NamedType) *embeddedType { +func findType(list []embeddedType, typ *Named) *embeddedType { for i := range list { if p := &list[i]; p.typ == typ { return p @@ -358,24 +356,23 @@ func findType(list []embeddedType, typ *NamedType) *embeddedType { return nil } -func lookupField(typ Type, name QualifiedName) lookupResult { - typ = deref(typ) +func lookupField(typ Type, pkg *Package, name string) lookupResult { + typ = typ.Deref() - if t, ok := typ.(*NamedType); ok { - for _, m := range t.Methods { - if name.IsSame(m.QualifiedName) { - assert(m.Type != nil) - return lookupResult{value, m.Type, nil} - } + if t, ok := typ.(*Named); ok { + if obj := t.methods.Lookup(pkg, name); obj != nil { + m := obj.(*Func) + assert(m.typ != nil) + return lookupResult{value, m.typ, nil} } - typ = t.Underlying + typ = t.underlying } switch t := typ.(type) { case *Struct: var next []embeddedType - for i, f := range t.Fields { - if name.IsSame(f.QualifiedName) { + for i, f := range t.fields { + if f.isMatch(pkg, name) { return lookupResult{variable, f.Type, []int{i}} } if f.IsAnonymous { @@ -384,20 +381,20 @@ func lookupField(typ Type, name QualifiedName) lookupResult { // ignore it. // Ignore embedded basic types - only user-defined // named types can have methods or have struct fields. - if t, _ := deref(f.Type).(*NamedType); t != nil { + if t, _ := f.Type.Deref().(*Named); t != nil { next = append(next, embeddedType{t, []int{i}, false}) } } } if len(next) > 0 { - return lookupFieldBreadthFirst(next, name) + return lookupFieldBreadthFirst(next, pkg, name) } case *Interface: - for _, m := range t.Methods { - if name.IsSame(m.QualifiedName) { - return lookupResult{value, m.Type, nil} - } + if obj := t.methods.Lookup(pkg, name); obj != nil { + m := obj.(*Func) + assert(m.typ != nil) + return lookupResult{value, m.typ, nil} } } diff --git a/go/types/predicates.go b/go/types/predicates.go index a99c91a4ef..521cd54433 100644 --- a/go/types/predicates.go +++ b/go/types/predicates.go @@ -10,82 +10,82 @@ func isNamed(typ Type) bool { if _, ok := typ.(*Basic); ok { return ok } - _, ok := typ.(*NamedType) + _, ok := typ.(*Named) return ok } func isBoolean(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsBoolean != 0 + t, ok := typ.Underlying().(*Basic) + return ok && t.info&IsBoolean != 0 } func isInteger(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsInteger != 0 + t, ok := typ.Underlying().(*Basic) + return ok && t.info&IsInteger != 0 } func isUnsigned(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsUnsigned != 0 + t, ok := typ.Underlying().(*Basic) + return ok && t.info&IsUnsigned != 0 } func isFloat(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsFloat != 0 + t, ok := typ.Underlying().(*Basic) + return ok && t.info&IsFloat != 0 } func isComplex(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsComplex != 0 + t, ok := typ.Underlying().(*Basic) + return ok && t.info&IsComplex != 0 } func isNumeric(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsNumeric != 0 + t, ok := typ.Underlying().(*Basic) + return ok && t.info&IsNumeric != 0 } func isString(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsString != 0 + t, ok := typ.Underlying().(*Basic) + return ok && t.info&IsString != 0 } func isUntyped(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsUntyped != 0 + t, ok := typ.Underlying().(*Basic) + return ok && t.info&IsUntyped != 0 } func isOrdered(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsOrdered != 0 + t, ok := typ.Underlying().(*Basic) + return ok && t.info&IsOrdered != 0 } func isConstType(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsConstType != 0 + t, ok := typ.Underlying().(*Basic) + return ok && t.info&IsConstType != 0 } func isComparable(typ Type) bool { - switch t := underlying(typ).(type) { + switch t := typ.Underlying().(type) { case *Basic: - return t.Kind != Invalid && t.Kind != UntypedNil + return t.kind != Invalid && t.kind != UntypedNil case *Pointer, *Interface, *Chan: // assumes types are equal for pointers and channels return true case *Struct: - for _, f := range t.Fields { + for _, f := range t.fields { if !isComparable(f.Type) { return false } } return true case *Array: - return isComparable(t.Elt) + return isComparable(t.elt) } return false } func hasNil(typ Type) bool { - switch underlying(typ).(type) { + switch typ.Underlying().(type) { case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan: return true } @@ -104,20 +104,20 @@ func IsIdentical(x, y Type) bool { // aliases, thus we cannot solely rely on the x == y check // above. if y, ok := y.(*Basic); ok { - return x.Kind == y.Kind + return x.kind == y.kind } case *Array: // Two array types are identical if they have identical element types // and the same array length. if y, ok := y.(*Array); ok { - return x.Len == y.Len && IsIdentical(x.Elt, y.Elt) + return x.len == y.len && IsIdentical(x.elt, y.elt) } case *Slice: // Two slice types are identical if they have identical element types. if y, ok := y.(*Slice); ok { - return IsIdentical(x.Elt, y.Elt) + return IsIdentical(x.elt, y.elt) } case *Struct: @@ -126,13 +126,13 @@ func IsIdentical(x, y Type) bool { // and identical tags. Two anonymous fields are considered to have the same // name. Lower-case field names from different packages are always different. if y, ok := y.(*Struct); ok { - if len(x.Fields) == len(y.Fields) { - for i, f := range x.Fields { - g := y.Fields[i] - if !f.QualifiedName.IsSame(g.QualifiedName) || - !IsIdentical(f.Type, g.Type) || - f.Tag != g.Tag || - f.IsAnonymous != g.IsAnonymous { + if len(x.fields) == len(y.fields) { + for i, f := range x.fields { + g := y.fields[i] + if f.IsAnonymous != g.IsAnonymous || + x.Tag(i) != y.Tag(i) || + !f.isMatch(g.Pkg, g.Name) || + !IsIdentical(f.Type, g.Type) { return false } } @@ -143,7 +143,7 @@ func IsIdentical(x, y Type) bool { case *Pointer: // Two pointer types are identical if they have identical base types. if y, ok := y.(*Pointer); ok { - return IsIdentical(x.Base, y.Base) + return IsIdentical(x.base, y.base) } case *Signature: @@ -152,9 +152,9 @@ func IsIdentical(x, y Type) bool { // and either both functions are variadic or neither is. Parameter and result // names are not required to match. if y, ok := y.(*Signature); ok { - return identicalTypes(x.Params, y.Params) && - identicalTypes(x.Results, y.Results) && - x.IsVariadic == y.IsVariadic + return x.isVariadic == y.isVariadic && + identicalTypes(x.params, y.params) && + identicalTypes(x.results, y.results) } case *Interface: @@ -162,27 +162,27 @@ func IsIdentical(x, y Type) bool { // the same names and identical function types. Lower-case method names from // different packages are always different. The order of the methods is irrelevant. if y, ok := y.(*Interface); ok { - return identicalMethods(x.Methods, y.Methods) // methods are sorted + return identicalMethods(x.methods, y.methods) // methods are sorted } case *Map: // Two map types are identical if they have identical key and value types. if y, ok := y.(*Map); ok { - return IsIdentical(x.Key, y.Key) && IsIdentical(x.Elt, y.Elt) + return IsIdentical(x.key, y.key) && IsIdentical(x.elt, y.elt) } case *Chan: // Two channel types are identical if they have identical value types // and the same direction. if y, ok := y.(*Chan); ok { - return x.Dir == y.Dir && IsIdentical(x.Elt, y.Elt) + return x.dir == y.dir && IsIdentical(x.elt, y.elt) } - case *NamedType: + case *Named: // Two named types are identical if their type names originate // in the same type declaration. - if y, ok := y.(*NamedType); ok { - return x.Obj == y.Obj + if y, ok := y.(*Named); ok { + return x.obj == y.obj } } @@ -191,56 +191,45 @@ func IsIdentical(x, y Type) bool { // identicalTypes returns true if both lists a and b have the // same length and corresponding objects have identical types. -func identicalTypes(a, b []*Var) bool { - if len(a) != len(b) { +func identicalTypes(a, b *Tuple) bool { + if a.Len() != b.Len() { return false } - for i, x := range a { - y := b[i] - if !IsIdentical(x.Type, y.Type) { - return false + if a != nil { + for i, x := range a.vars { + y := b.vars[i] + if !IsIdentical(x.typ, y.typ) { + return false + } } } return true } -// identicalMethods returns true if both lists a and b have the +// identicalMethods returns true if both object sets a and b have the // same length and corresponding methods have identical types. // TODO(gri) make this more efficient -func identicalMethods(a, b []*Method) bool { - if len(a) != len(b) { +func identicalMethods(a, b ObjSet) bool { + if len(a.entries) != len(b.entries) { return false } - m := make(map[QualifiedName]*Method) - for _, x := range a { - assert(m[x.QualifiedName] == nil) // method list must not have duplicate entries - m[x.QualifiedName] = x + m := make(map[string]*Func) + for _, obj := range a.entries { + x := obj.(*Func) + qname := x.pkg.path + "." + x.name + assert(m[qname] == nil) // method list must not have duplicate entries + m[qname] = x } - for _, y := range b { - if x := m[y.QualifiedName]; x == nil || !IsIdentical(x.Type, y.Type) { + for _, obj := range b.entries { + y := obj.(*Func) + qname := y.pkg.path + "." + y.name + if x := m[qname]; x == nil || !IsIdentical(x.typ, y.typ) { return false } } return true } -// underlying returns the underlying type of typ. -func underlying(typ Type) Type { - // Basic types are representing themselves directly even though they are named. - if typ, ok := typ.(*NamedType); ok { - return typ.Underlying // underlying types are never NamedTypes - } - return typ -} - -// deref returns a pointer's base type; otherwise it returns typ. -func deref(typ Type) Type { - if typ, ok := underlying(typ).(*Pointer); ok { - return typ.Base - } - return typ -} - // defaultType returns the default "typed" type for an "untyped" type; // it returns the incoming type for all other types. If there is no // corresponding untyped type, the result is Typ[Invalid]. @@ -248,7 +237,7 @@ func deref(typ Type) Type { func defaultType(typ Type) Type { if t, ok := typ.(*Basic); ok { k := Invalid - switch t.Kind { + switch t.kind { // case UntypedNil: // There is no default type for nil. For a good error message, // catch this case before calling this function. @@ -273,16 +262,18 @@ func defaultType(typ Type) Type { // missingMethod returns (nil, false) if typ implements T, otherwise // it returns the first missing method required by T and whether it // is missing or simply has the wrong type. +// TODO(gri) make method of Type and/or stand-alone predicate. // -func missingMethod(typ Type, T *Interface) (method *Method, wrongType bool) { +func missingMethod(typ Type, T *Interface) (method *Func, wrongType bool) { // TODO(gri): this needs to correctly compare method names (taking package into account) // TODO(gri): distinguish pointer and non-pointer receivers // an interface type implements T if it has no methods with conflicting signatures // Note: This is stronger than the current spec. Should the spec require this? - if ityp, _ := underlying(typ).(*Interface); ityp != nil { - for _, m := range T.Methods { - res := lookupField(ityp, m.QualifiedName) // TODO(gri) no need to go via lookupField - if res.mode != invalid && !IsIdentical(res.typ, m.Type) { + if ityp, _ := typ.Underlying().(*Interface); ityp != nil { + for _, obj := range T.methods.entries { + m := obj.(*Func) + res := lookupField(ityp, m.pkg, m.name) // TODO(gri) no need to go via lookupField + if res.mode != invalid && !IsIdentical(res.typ, m.typ) { return m, true } } @@ -290,12 +281,13 @@ func missingMethod(typ Type, T *Interface) (method *Method, wrongType bool) { } // a concrete type implements T if it implements all methods of T. - for _, m := range T.Methods { - res := lookupField(typ, m.QualifiedName) + for _, obj := range T.methods.entries { + m := obj.(*Func) + res := lookupField(typ, m.pkg, m.name) if res.mode == invalid { return m, false } - if !IsIdentical(res.typ, m.Type) { + if !IsIdentical(res.typ, m.typ) { return m, true } } diff --git a/go/types/resolve.go b/go/types/resolve.go index 43db60708f..0b3a77401f 100644 --- a/go/types/resolve.go +++ b/go/types/resolve.go @@ -15,28 +15,28 @@ func (check *checker) declareObj(scope, altScope *Scope, obj Object, dotImport t alt := scope.Insert(obj) if alt == nil && altScope != nil { // see if there is a conflicting declaration in altScope - alt = altScope.Lookup(obj.GetName()) + alt = altScope.Lookup(obj.Name()) } if alt != nil { prevDecl := "" // for dot-imports, local declarations are declared first - swap messages if dotImport.IsValid() { - if pos := alt.GetPos(); pos.IsValid() { + if pos := alt.Pos(); pos.IsValid() { check.errorf(pos, fmt.Sprintf("%s redeclared in this block by dot-import at %s", - obj.GetName(), check.fset.Position(dotImport))) + obj.Name(), check.fset.Position(dotImport))) return } // get by w/o other position - check.errorf(dotImport, fmt.Sprintf("dot-import redeclares %s", obj.GetName())) + check.errorf(dotImport, fmt.Sprintf("dot-import redeclares %s", obj.Name())) return } - if pos := alt.GetPos(); pos.IsValid() { + if pos := alt.Pos(); pos.IsValid() { prevDecl = fmt.Sprintf("\n\tother declaration at %s", check.fset.Position(pos)) } - check.errorf(obj.GetPos(), fmt.Sprintf("%s redeclared in this block%s", obj.GetName(), prevDecl)) + check.errorf(obj.Pos(), fmt.Sprintf("%s redeclared in this block%s", obj.Name(), prevDecl)) } } @@ -51,18 +51,17 @@ func (check *checker) resolveIdent(scope *Scope, ident *ast.Ident) bool { } func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) { - pkg := &Package{Scope: &Scope{Outer: Universe}, Imports: make(map[string]*Package)} - check.pkg = pkg + pkg := check.pkg // complete package scope i := 0 for _, file := range check.files { // package names must match switch name := file.Name.Name; { - case pkg.Name == "": - pkg.Name = name - case name != pkg.Name: - check.errorf(file.Package, "package %s; expected %s", name, pkg.Name) + case pkg.name == "": + pkg.name = name + case name != pkg.name: + check.errorf(file.Package, "package %s; expected %s", name, pkg.name) continue // ignore this file } @@ -92,13 +91,13 @@ func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) { if name.Name == "_" { continue } - pkg.Scope.Insert(check.lookup(name)) + pkg.scope.Insert(check.lookup(name)) } case *ast.TypeSpec: if s.Name.Name == "_" { continue } - pkg.Scope.Insert(check.lookup(s.Name)) + pkg.scope.Insert(check.lookup(s.Name)) default: check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s) } @@ -112,7 +111,7 @@ func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) { if d.Name.Name == "_" || d.Name.Name == "init" { continue // blank (_) and init functions are inaccessible } - pkg.Scope.Insert(check.lookup(d.Name)) + pkg.scope.Insert(check.lookup(d.Name)) default: check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) } @@ -124,14 +123,14 @@ func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) { for _, file := range check.files { // build file scope by processing all imports importErrors := false - fileScope := &Scope{Outer: pkg.Scope} + fileScope := &Scope{Outer: pkg.scope} for _, spec := range file.Imports { if importer == nil { importErrors = true continue } path, _ := strconv.Unquote(spec.Path.Value) - imp, err := importer(pkg.Imports, path) + imp, err := importer(pkg.imports, path) if err != nil { check.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err) importErrors = true @@ -142,7 +141,7 @@ func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) { // import failed. Consider adjusting the logic here a bit. // local name overrides imported package name - name := imp.Name + name := imp.name if spec.Name != nil { name = spec.Name.Name } @@ -150,12 +149,12 @@ func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) { // add import to file scope if name == "." { // merge imported scope with file scope - for _, obj := range imp.Scope.Entries { + for _, obj := range imp.scope.Entries { // gcimported package scopes contain non-exported // objects such as types used in partially exported // objects - do not accept them - if ast.IsExported(obj.GetName()) { - check.declareObj(fileScope, pkg.Scope, obj, spec.Pos()) + if ast.IsExported(obj.Name()) { + check.declareObj(fileScope, pkg.scope, obj, spec.Pos()) } } // TODO(gri) consider registering the "." identifier @@ -167,8 +166,8 @@ func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) { // (do not re-use imp in the file scope but create // a new object instead; the Decl field is different // for different files) - obj := &Package{Name: name, Scope: imp.Scope, spec: spec} - check.declareObj(fileScope, pkg.Scope, obj, token.NoPos) + obj := &Package{name: name, scope: imp.scope, spec: spec} + check.declareObj(fileScope, pkg.scope, obj, token.NoPos) } } @@ -178,7 +177,7 @@ func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) { // (objects in the universe may be shadowed by imports; // with missing imports, identifiers might get resolved // incorrectly to universe objects) - pkg.Scope.Outer = nil + pkg.scope.Outer = nil } i := 0 for _, ident := range file.Unresolved { @@ -190,7 +189,7 @@ func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) { } file.Unresolved = file.Unresolved[0:i] - pkg.Scope.Outer = Universe // reset outer scope (is nil if there were importErrors) + pkg.scope.Outer = Universe // reset outer scope (is nil if there were importErrors) } return diff --git a/go/types/resolver_test.go b/go/types/resolver_test.go index d4e364451d..ea49111924 100644 --- a/go/types/resolver_test.go +++ b/go/types/resolver_test.go @@ -66,14 +66,14 @@ func TestResolveQualifiedIdents(t *testing.T) { idents := make(map[*ast.Ident]Object) var ctxt Context ctxt.Ident = func(id *ast.Ident, obj Object) { idents[id] = obj } - pkg, err := ctxt.Check(fset, files) + pkg, err := ctxt.Check("testResolveQualifiedIdents", fset, files...) if err != nil { t.Fatal(err) } // check that all packages were imported for _, name := range pkgnames { - if pkg.Imports[name] == nil { + if pkg.imports[name] == nil { t.Errorf("package %s not imported", name) } } @@ -116,14 +116,14 @@ func TestResolveQualifiedIdents(t *testing.T) { for _, list := range x.Fields.List { for _, f := range list.Names { assert(idents[f] == nil) - idents[f] = &Var{Pkg: pkg, Name: f.Name} + idents[f] = &Var{pkg: pkg, name: f.Name} } } case *ast.InterfaceType: for _, list := range x.Methods.List { for _, f := range list.Names { assert(idents[f] == nil) - idents[f] = &Func{Pkg: pkg, Name: f.Name} + idents[f] = &Func{pkg: pkg, name: f.Name} } } case *ast.CompositeLit: @@ -131,7 +131,7 @@ func TestResolveQualifiedIdents(t *testing.T) { if kv, ok := e.(*ast.KeyValueExpr); ok { if k, ok := kv.Key.(*ast.Ident); ok { assert(idents[k] == nil) - idents[k] = &Var{Pkg: pkg, Name: k.Name} + idents[k] = &Var{pkg: pkg, name: k.Name} } } } diff --git a/go/types/return.go b/go/types/return.go index 8644d28c91..6c69ef4ff8 100644 --- a/go/types/return.go +++ b/go/types/return.go @@ -36,7 +36,7 @@ func (check *checker) isTerminating(s ast.Stmt, label string) bool { // rather then ordinary functions that have a predeclared // function type. This would simplify code here and else- // where. - if f, _ := obj.(*Func); f != nil && f.Type == predeclaredFunctions[_Panic] { + if f, _ := obj.(*Func); f != nil && f.typ == predeclaredFunctions[_Panic] { return true } } diff --git a/go/types/scope.go b/go/types/scope.go index 463ee40c54..7d8ab56bbd 100644 --- a/go/types/scope.go +++ b/go/types/scope.go @@ -28,7 +28,7 @@ func (s *Scope) Lookup(name string) Object { return s.large[name] } for _, obj := range s.Entries { - if obj.GetName() == name { + if obj.Name() == name { return obj } } @@ -41,7 +41,7 @@ func (s *Scope) Lookup(name string) Object { // Otherwise it inserts obj and returns nil. // func (s *Scope) Insert(obj Object) Object { - name := obj.GetName() + name := obj.Name() if alt := s.Lookup(name); alt != nil { return alt } @@ -53,7 +53,7 @@ func (s *Scope) Insert(obj Object) Object { if s.large == nil { m := make(map[string]Object, len(s.Entries)) for _, obj := range s.Entries { - m[obj.GetName()] = obj + m[obj.Name()] = obj } s.large = m } @@ -70,7 +70,7 @@ func (s *Scope) String() string { 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, "\t%s\t%T\n", obj.Name(), obj) } } fmt.Fprintf(&buf, "}\n") diff --git a/go/types/sizes.go b/go/types/sizes.go index ef6499ba43..be53eb71dd 100644 --- a/go/types/sizes.go +++ b/go/types/sizes.go @@ -21,9 +21,9 @@ func (ctxt *Context) offsetsof(s *Struct) []int64 { if offsets == nil { // compute offsets on demand if f := ctxt.Offsetsof; f != nil { - offsets = f(s.Fields) + offsets = f(s.fields) // sanity checks - if len(offsets) != len(s.Fields) { + if len(offsets) != len(s.fields) { panic("Context.Offsetsof returned the wrong number of offsets") } for _, o := range offsets { @@ -32,7 +32,7 @@ func (ctxt *Context) offsetsof(s *Struct) []int64 { } } } else { - offsets = DefaultOffsetsof(s.Fields) + offsets = DefaultOffsetsof(s.fields) } s.offsets = offsets } @@ -45,12 +45,12 @@ func (ctxt *Context) offsetsof(s *Struct) []int64 { func (ctxt *Context) offsetof(typ Type, index []int) int64 { var o int64 for _, i := range index { - s, _ := underlying(typ).(*Struct) + s, _ := typ.Underlying().(*Struct) if s == nil { return -1 } o += ctxt.offsetsof(s)[i] - typ = s.Fields[i].Type + typ = s.fields[i].Type } return o } @@ -74,17 +74,17 @@ const DefaultMaxAlign = 8 func DefaultAlignof(typ Type) int64 { // For arrays and structs, alignment is defined in terms // of alignment of the elements and fields, respectively. - switch t := underlying(typ).(type) { + switch t := typ.Underlying().(type) { case *Array: // spec: "For a variable x of array type: unsafe.Alignof(x) // is the same as unsafe.Alignof(x[0]), but at least 1." - return DefaultAlignof(t.Elt) + return DefaultAlignof(t.elt) case *Struct: // spec: "For a variable x of struct type: unsafe.Alignof(x) // is the largest of the values unsafe.Alignof(x.f) for each // field f of x, but at least 1." max := int64(1) - for _, f := range t.Fields { + for _, f := range t.fields { if a := DefaultAlignof(f.Type); a > max { max = a } @@ -129,32 +129,32 @@ const DefaultPtrSize = 8 // DefaultSizeof implements the default size computation // for unsafe.Sizeof. It is used if Context.Sizeof == nil. func DefaultSizeof(typ Type) int64 { - switch t := underlying(typ).(type) { + switch t := typ.Underlying().(type) { case *Basic: if s := t.size; s > 0 { return s } - if t.Kind == String { + if t.kind == String { return DefaultPtrSize * 2 } case *Array: - a := DefaultAlignof(t.Elt) - s := DefaultSizeof(t.Elt) - return align(s, a) * t.Len // may be 0 + a := DefaultAlignof(t.elt) + s := DefaultSizeof(t.elt) + return align(s, a) * t.len // may be 0 case *Slice: return DefaultPtrSize * 3 case *Struct: - n := len(t.Fields) + n := len(t.fields) if n == 0 { return 0 } offsets := t.offsets if t.offsets == nil { // compute offsets on demand - offsets = DefaultOffsetsof(t.Fields) + offsets = DefaultOffsetsof(t.fields) t.offsets = offsets } - return offsets[n-1] + DefaultSizeof(t.Fields[n-1].Type) + return offsets[n-1] + DefaultSizeof(t.fields[n-1].Type) case *Signature: return DefaultPtrSize * 2 } diff --git a/go/types/stdlib_test.go b/go/types/stdlib_test.go index 8b264119d5..de03fd8be4 100644 --- a/go/types/stdlib_test.go +++ b/go/types/stdlib_test.go @@ -42,7 +42,7 @@ var excluded = map[string]bool{ } // typecheck typechecks the given package files. -func typecheck(t *testing.T, filenames []string) { +func typecheck(t *testing.T, path string, filenames []string) { fset := token.NewFileSet() // parse package files @@ -75,7 +75,7 @@ func typecheck(t *testing.T, filenames []string) { ctxt := Context{ Error: func(err error) { t.Error(err) }, } - ctxt.Check(fset, files) + ctxt.Check(path, fset, files...) pkgCount++ } @@ -121,7 +121,7 @@ func walkDirs(t *testing.T, dir string) { // typecheck package in directory if files := pkgfiles(t, dir); files != nil { - typecheck(t, files) + typecheck(t, dir, files) } // traverse subdirectories, but don't walk into testdata diff --git a/go/types/stmt.go b/go/types/stmt.go index 224386849d..3aa501962c 100644 --- a/go/types/stmt.go +++ b/go/types/stmt.go @@ -24,9 +24,9 @@ func (check *checker) assignment(x *operand, to Type) bool { return false } - if t, ok := x.typ.(*Result); ok { + if t, ok := x.typ.(*Tuple); ok { // TODO(gri) elsewhere we use "assignment count mismatch" (consolidate) - check.errorf(x.pos(), "%d-valued expression %s used as single value", len(t.Values), x) + check.errorf(x.pos(), "%d-valued expression %s used as single value", t.Len(), x) x.mode = invalid return false } @@ -102,17 +102,17 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota unreachable() case *Const: - typ = obj.Type // may already be Typ[Invalid] + typ = obj.typ // may already be Typ[Invalid] if typ == nil { typ = Typ[Invalid] if x.mode != invalid { typ = x.typ } - obj.Type = typ + obj.typ = typ } case *Var: - typ = obj.Type // may already be Typ[Invalid] + typ = obj.typ // may already be Typ[Invalid] if typ == nil { typ = Typ[Invalid] if x.mode != invalid { @@ -127,7 +127,7 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota } } } - obj.Type = typ + obj.typ = typ } } @@ -147,10 +147,10 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota // for constants, set their value if obj, _ := obj.(*Const); obj != nil { - obj.Val = exact.MakeUnknown() // failure case: we don't know the constant value + obj.val = exact.MakeUnknown() // failure case: we don't know the constant value if x.mode == constant { if isConstType(x.typ) { - obj.Val = x.val + obj.val = x.val } else if x.typ != Typ[Invalid] { check.errorf(x.pos(), "%s has invalid constant type", x) } @@ -190,12 +190,13 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) { goto Error } - if t, _ := x.typ.(*Result); t != nil && len(lhs) == len(t.Values) { + if t, ok := x.typ.(*Tuple); ok && len(lhs) == t.Len() { // function result x.mode = value - for i, obj := range t.Values { + for i := 0; i < len(lhs); i++ { + obj := t.At(i) x.expr = nil // TODO(gri) should do better here - x.typ = obj.Type + x.typ = obj.typ check.assign1to1(lhs[i], nil, &x, decl, iota) } return @@ -225,9 +226,9 @@ Error: } switch obj := check.lookup(ident).(type) { case *Const: - obj.Type = Typ[Invalid] + obj.typ = Typ[Invalid] case *Var: - obj.Type = Typ[Invalid] + obj.typ = Typ[Invalid] default: unreachable() } @@ -313,7 +314,7 @@ func (check *checker) stmt(s ast.Stmt) { // check.register. Perhaps this can be avoided.) check.expr(&x, e.Fun, nil, -1) if x.mode != invalid { - if b, ok := x.typ.(*builtin); ok && !b.isStatement { + if b, ok := x.typ.(*Builtin); ok && !b.isStatement { used = false } } @@ -339,7 +340,7 @@ func (check *checker) stmt(s ast.Stmt) { if ch.mode == invalid || x.mode == invalid { return } - if tch, ok := underlying(ch.typ).(*Chan); !ok || tch.Dir&ast.SEND == 0 || !check.assignment(&x, tch.Elt) { + if tch, ok := ch.typ.Underlying().(*Chan); !ok || tch.dir&ast.SEND == 0 || !check.assignment(&x, tch.elt) { if x.mode != invalid { check.invalidOp(ch.pos(), "cannot send %s to channel %s", &x, &ch) } @@ -423,18 +424,18 @@ func (check *checker) stmt(s ast.Stmt) { case *ast.ReturnStmt: sig := check.funcsig - if n := len(sig.Results); n > 0 { + if n := sig.results.Len(); n > 0 { // TODO(gri) should not have to compute lhs, named every single time - clean this up lhs := make([]ast.Expr, n) named := false // if set, function has named results - for i, res := range sig.Results { - if len(res.Name) > 0 { + for i, res := range sig.results.vars { + if len(res.name) > 0 { // a blank (_) result parameter is a named result named = true } - name := ast.NewIdent(res.Name) + name := ast.NewIdent(res.name) name.NamePos = s.Pos() - check.register(name, &Var{Name: res.Name, Type: res.Type}) // Pkg == nil + check.register(name, &Var{name: res.name, typ: res.typ}) // Pkg == nil lhs[i] = name } if len(s.Results) > 0 || !named { @@ -570,7 +571,7 @@ func (check *checker) stmt(s ast.Stmt) { return } var T *Interface - if T, _ = underlying(x.typ).(*Interface); T == nil { + if T, _ = x.typ.Underlying().(*Interface); T == nil { check.errorf(x.pos(), "%s is not an interface", &x) return } @@ -593,7 +594,7 @@ func (check *checker) stmt(s ast.Stmt) { } else { msg = "%s cannot have dynamic type %s (missing method %s)" } - check.errorf(expr.Pos(), msg, &x, typ, method.Name) + check.errorf(expr.Pos(), msg, &x, typ, method.name) // ok to continue } } @@ -605,7 +606,7 @@ func (check *checker) stmt(s ast.Stmt) { if len(clause.List) != 1 || typ == nil { typ = x.typ } - lhs.Type = typ + lhs.typ = typ } check.stmtList(clause.Body) } @@ -614,7 +615,7 @@ func (check *checker) stmt(s ast.Stmt) { // assumes different types for different clauses. Set it back to the type of the // TypeSwitchGuard expression so that that variable always has a valid type. if lhs != nil { - lhs.Type = x.typ + lhs.typ = x.typ } case *ast.SelectStmt: @@ -655,7 +656,7 @@ func (check *checker) stmt(s ast.Stmt) { // determine key/value types var key, val Type - switch typ := underlying(x.typ).(type) { + switch typ := x.typ.Underlying().(type) { case *Basic: if isString(typ) { key = Typ[UntypedInt] @@ -663,21 +664,21 @@ func (check *checker) stmt(s ast.Stmt) { } case *Array: key = Typ[UntypedInt] - val = typ.Elt + val = typ.elt case *Slice: key = Typ[UntypedInt] - val = typ.Elt + val = typ.elt case *Pointer: - if typ, _ := underlying(typ.Base).(*Array); typ != nil { + if typ, _ := typ.base.Underlying().(*Array); typ != nil { key = Typ[UntypedInt] - val = typ.Elt + val = typ.elt } case *Map: - key = typ.Key - val = typ.Elt + key = typ.key + val = typ.elt case *Chan: - key = typ.Elt - if typ.Dir&ast.RECV == 0 { + key = typ.elt + if typ.dir&ast.RECV == 0 { check.errorf(x.pos(), "cannot range over send-only channel %s", &x) // ok to continue } diff --git a/go/types/types.go b/go/types/types.go index b9a17ebc36..6ee4e7eada 100644 --- a/go/types/types.go +++ b/go/types/types.go @@ -6,10 +6,23 @@ package types import "go/ast" +// A Type represents a type of Go. // All types implement the Type interface. type Type interface { + // Underlying returns the underlying type of a type. + Underlying() Type + + // For a pointer type (or a named type denoting a pointer type), + // Deref returns the pointer's element type. For all other types, + // Deref returns the receiver. + Deref() Type + + // String returns a string representation of a type. String() string - aType() + + // TODO(gri) Which other functionality should move here? + // Candidates are all predicates (IsIdentical(), etc.), + // and some others. What is the design principle? } // BasicKind describes the kind of basic type. @@ -72,86 +85,176 @@ const ( // A Basic represents a basic type. type Basic struct { - Kind BasicKind - Info BasicInfo + kind BasicKind + info BasicInfo size int64 // use DefaultSizeof to get size - Name string + name string } -// An Array represents an array type [Len]Elt. +// Kind returns the kind of basic type b. +func (b *Basic) Kind() BasicKind { return b.kind } + +// Info returns information about properties of basic type b. +func (b *Basic) Info() BasicInfo { return b.info } + +// Name returns the name of basic type b. +func (b *Basic) Name() string { return b.name } + +// An Array represents an array type. type Array struct { - Len int64 - Elt Type + len int64 + elt Type } -// A Slice represents a slice type []Elt. +// NewArray returns a new array type for the given element type and length. +func NewArray(elem Type, len int64) *Array { return &Array{len, elem} } + +// Len returns the length of array a. +func (a *Array) Len() int64 { return a.len } + +// Elem returns element type of array a. +func (a *Array) Elem() Type { return a.elt } + +// A Slice represents a slice type. type Slice struct { - Elt Type + elt Type } -// 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 { - Pkg *Package // nil only for predeclared error.Error (exported) - Name string // unqualified type name for anonymous fields +// NewSlice returns a new slice type for the given element type. +func NewSlice(elem Type) *Slice { return &Slice{elem} } + +// Elem returns the element type of slice s. +func (s *Slice) Elem() Type { return s.elt } + +// A Field represents a field of a struct. +// TODO(gri): Should make this just a Var? +type Field struct { + Pkg *Package + Name string + Type Type + IsAnonymous bool } -// IsSame reports whether p and q are the same. -func (p QualifiedName) IsSame(q QualifiedName) bool { +// A Struct represents a struct type. +type Struct struct { + fields []*Field + tags []string // field tags; nil of there are no tags + offsets []int64 // field offsets in bytes, lazily computed +} + +func NewStruct(fields []*Field, tags []string) *Struct { + return &Struct{fields: fields, tags: tags} +} + +func (s *Struct) NumFields() int { return len(s.fields) } +func (s *Struct) Field(i int) *Field { return s.fields[i] } +func (s *Struct) Tag(i int) string { + if i < len(s.tags) { + return s.tags[i] + } + return "" +} +func (s *Struct) ForEachField(f func(*Field)) { + for _, fld := range s.fields { + f(fld) + } +} + +func (f *Field) isMatch(pkg *Package, name string) bool { // spec: // "Two identifiers are different if they are spelled differently, // or if they appear in different packages and are not exported. // Otherwise, they are the same." - if p.Name != q.Name { + if name != f.Name { return false } - // p.Name == q.Name - return ast.IsExported(p.Name) || p.Pkg.Path == q.Pkg.Path + // f.Name == name + return ast.IsExported(name) || pkg.path == f.Pkg.path } -// A Field represents a field of a struct. -type Field struct { - QualifiedName - Type Type - Tag string - IsAnonymous bool -} - -// A Struct represents a struct type struct{...}. -type Struct struct { - Fields []*Field - offsets []int64 // field offsets in bytes, lazily computed -} - -func (typ *Struct) fieldIndex(name QualifiedName) int { - for i, f := range typ.Fields { - if f.QualifiedName.IsSame(name) { +func (s *Struct) fieldIndex(pkg *Package, name string) int { + for i, f := range s.fields { + if f.isMatch(pkg, name) { return i } } return -1 } -// A Pointer represents a pointer type *Base. +// A Pointer represents a pointer type. type Pointer struct { - Base Type + base Type } -// A Result represents a (multi-value) function call result. -type Result struct { - Values []*Var // Signature.Results of the function called +// NewPointer returns a new pointer type for the given element (base) type. +func NewPointer(elem Type) *Pointer { return &Pointer{elem} } + +// Elem returns the element type for the given pointer p. +func (p *Pointer) Elem() Type { return p.base } + +// A Tuple represents an ordered list of variables; a nil *Tuple is a valid (empty) tuple. +// Tuples are used as components of signatures and to represent the type of multiple +// assignments; they are not first class types of Go. +type Tuple struct { + vars []*Var } -// A Signature represents a user-defined function type func(...) (...). +// NewTuple returns a new tuple for the given variables. +func NewTuple(x ...*Var) *Tuple { + if len(x) > 0 { + return &Tuple{x} + } + return nil +} + +// Len returns the number variables of tuple t. +func (t *Tuple) Len() int { + if t != nil { + return len(t.vars) + } + return 0 +} + +// At returns the i'th variable of tuple t. +func (t *Tuple) At(i int) *Var { return t.vars[i] } + +// ForEach calls f with each variable of tuple t in index order. +// TODO(gri): Do we keep ForEach or should we abandon it in favor or Len and At? +func (t *Tuple) ForEach(f func(*Var)) { + if t != nil { + for _, x := range t.vars { + f(x) + } + } +} + +// A Signature represents a (non-builtin) function type. type Signature struct { - Recv *Var // nil if not a method - Params []*Var // (incoming) parameters from left to right; or nil - Results []*Var // (outgoing) results from left to right; or nil - IsVariadic bool // true if the last parameter's type is of the form ...T + recv *Var // nil if not a method + params *Tuple // (incoming) parameters from left to right; or nil + results *Tuple // (outgoing) results from left to right; or nil + isVariadic bool // true if the last parameter's type is of the form ...T } +// NewSignature returns a new function type for the given receiver, parameters, +// and results, either of which may be nil. If isVariadic is set, the function +// is variadic. +func NewSignature(recv *Var, params, results *Tuple, isVariadic bool) *Signature { + return &Signature{recv, params, results, isVariadic} +} + +// Recv returns the receiver of signature s, or nil. +func (s *Signature) Recv() *Var { return s.recv } + +// Params returns the parameters of signature s, or nil. +func (s *Signature) Params() *Tuple { return s.params } + +// Results returns the results of signature s, or nil. +func (s *Signature) Results() *Tuple { return s.results } + +// IsVariadic reports whether the signature s is variadic. +func (s *Signature) IsVariadic() bool { return s.isVariadic } + // builtinId is an id of a builtin function. type builtinId int @@ -184,8 +287,8 @@ const ( _Trace ) -// A builtin represents the type of a built-in function. -type builtin struct { +// A Builtin represents the type of a built-in function. +type Builtin struct { id builtinId name string nargs int // number of arguments (minimum if variadic) @@ -193,44 +296,145 @@ type builtin struct { isStatement bool // true if the built-in is valid as an expression statement } -// A Method represents a method. -type Method struct { - QualifiedName - Type *Signature +// Name returns the name of the built-in function b. +func (b *Builtin) Name() string { + return b.name } -// An Interface represents an interface type interface{...}. +// An Interface represents an interface type. type Interface struct { - Methods []*Method // TODO(gri) consider keeping them in sorted order + methods ObjSet } -// A Map represents a map type map[Key]Elt. +// NumMethods returns the number of methods of interface t. +func (t *Interface) NumMethods() int { return len(t.methods.entries) } + +// Method returns the i'th method of interface t for 0 <= i < t.NumMethods(). +func (t *Interface) Method(i int) *Func { + return t.methods.entries[i].(*Func) +} + +// IsEmpty() reports whether t is an empty interface. +func (t *Interface) IsEmpty() bool { return len(t.methods.entries) == 0 } + +// ForEachMethod calls f with each method of interface t in index order. +// TODO(gri) Should we abandon this in favor of NumMethods and Method? +func (t *Interface) ForEachMethod(f func(*Func)) { + for _, obj := range t.methods.entries { + f(obj.(*Func)) + } +} + +// A Map represents a map type. type Map struct { - Key, Elt Type + key, elt Type } -// A Chan represents a channel type chan Elt, <-chan Elt, or chan<-Elt. +// NewMap returns a new map for the given key and element types. +func NewMap(key, elem Type) *Map { + return &Map{key, elem} +} + +// Key returns the key type of map m. +func (m *Map) Key() Type { return m.key } + +// Elem returns the element type of map m. +func (m *Map) Elem() Type { return m.elt } + +// A Chan represents a channel type. type Chan struct { - Dir ast.ChanDir - Elt Type + dir ast.ChanDir + elt Type } -// A NamedType represents a named type as declared in a type declaration. -type NamedType struct { - Obj *TypeName // corresponding declared object - Underlying Type // nil if not fully declared yet; never a *NamedType - Methods []*Method // TODO(gri) consider keeping them in sorted order +// NewChan returns a new channel type for the given direction and element type. +func NewChan(dir ast.ChanDir, elem Type) *Chan { + return &Chan{dir, elem} } -func (*Basic) aType() {} -func (*Array) aType() {} -func (*Slice) aType() {} -func (*Struct) aType() {} -func (*Pointer) aType() {} -func (*Result) aType() {} -func (*Signature) aType() {} -func (*builtin) aType() {} -func (*Interface) aType() {} -func (*Map) aType() {} -func (*Chan) aType() {} -func (*NamedType) aType() {} +// Dir returns the direction of channel c. +func (c *Chan) Dir() ast.ChanDir { return c.dir } + +// Elem returns the element type of channel c. +func (c *Chan) Elem() Type { return c.elt } + +// A Named represents a named type. +type Named struct { + obj *TypeName // corresponding declared object + underlying Type // nil if not fully declared yet; never a *Named + methods ObjSet // directly associated methods (not the method set of this type) +} + +// NewNamed returns a new named type for the given type name, underlying type, and associated methods. +func NewNamed(obj *TypeName, underlying Type, methods ObjSet) *Named { + typ := &Named{obj, underlying, methods} + if obj.typ == nil { + obj.typ = typ + } + return typ +} + +// TypeName returns the type name for the named type t. +func (t *Named) Obj() *TypeName { return t.obj } + +// NumMethods returns the number of methods directly associated with named type t. +func (t *Named) NumMethods() int { return len(t.methods.entries) } + +// 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.entries[i].(*Func) +} + +// ForEachMethod calls f with each method associated with t in index order. +// TODO(gri) Should we abandon this in favor of NumMethods and Method? +func (t *Named) ForEachMethod(fn func(*Func)) { + for _, obj := range t.methods.entries { + fn(obj.(*Func)) + } +} + +// Implementations for Type methods. + +func (t *Basic) Underlying() Type { return t } +func (t *Array) Underlying() Type { return t } +func (t *Slice) Underlying() Type { return t } +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 } +func (t *Named) Underlying() Type { return t.underlying } + +func (t *Basic) Deref() Type { return t } +func (t *Array) Deref() Type { return t } +func (t *Slice) Deref() Type { return t } +func (t *Struct) Deref() Type { return t } +func (t *Pointer) Deref() Type { return t.base } +func (t *Tuple) Deref() Type { return t } +func (t *Signature) Deref() Type { return t } +func (t *Builtin) Deref() Type { return t } +func (t *Interface) Deref() Type { return t } +func (t *Map) Deref() Type { return t } +func (t *Chan) Deref() Type { return t } +func (t *Named) Deref() Type { + if p, ok := t.underlying.(*Pointer); ok { + return p.base + } + return t +} + +func (t *Basic) String() string { return typeString(t) } +func (t *Array) String() string { return typeString(t) } +func (t *Slice) String() string { return typeString(t) } +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) } +func (t *Named) String() string { return typeString(t) } diff --git a/go/types/types_test.go b/go/types/types_test.go index 8e228fa677..6b08872275 100644 --- a/go/types/types_test.go +++ b/go/types/types_test.go @@ -20,7 +20,7 @@ func makePkg(t *testing.T, src string) (*Package, error) { if err != nil { return nil, err } - pkg, err := Check(fset, []*ast.File{file}) + pkg, err := Check("", fset, file) return pkg, err } @@ -110,7 +110,7 @@ func TestTypes(t *testing.T) { t.Errorf("%s: %s", src, err) continue } - typ := underlying(pkg.Scope.Lookup("T").GetType()) + typ := pkg.scope.Lookup("T").Type().Underlying() str := typeString(typ) 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 3658352bf5..68cafc6790 100644 --- a/go/types/universe.go +++ b/go/types/universe.go @@ -57,13 +57,13 @@ var aliases = [...]*Basic{ } var predeclaredConstants = [...]*Const{ - {Name: "true", Type: Typ[UntypedBool], Val: exact.MakeBool(true)}, - {Name: "false", Type: Typ[UntypedBool], Val: exact.MakeBool(false)}, - {Name: "iota", Type: Typ[UntypedInt], Val: exact.MakeInt64(0)}, - {Name: "nil", Type: Typ[UntypedNil], Val: exact.MakeNil()}, + {name: "true", typ: Typ[UntypedBool], val: exact.MakeBool(true)}, + {name: "false", typ: Typ[UntypedBool], val: exact.MakeBool(false)}, + {name: "iota", typ: Typ[UntypedInt], val: exact.MakeInt64(0)}, + {name: "nil", typ: Typ[UntypedNil], val: exact.MakeNil()}, } -var predeclaredFunctions = [...]*builtin{ +var predeclaredFunctions = [...]*Builtin{ {_Append, "append", 1, true, false}, {_Cap, "cap", 1, false, false}, {_Close, "close", 1, false, true}, @@ -87,21 +87,23 @@ var predeclaredFunctions = [...]*builtin{ func init() { Universe = new(Scope) - Unsafe = &Package{Name: "unsafe", Scope: new(Scope)} + Unsafe = &Package{name: "unsafe", scope: new(Scope)} // predeclared types for _, t := range Typ { - def(&TypeName{Name: t.Name, Type: t}) + def(&TypeName{name: t.name, typ: t}) } for _, t := range aliases { - def(&TypeName{Name: t.Name, Type: t}) + def(&TypeName{name: t.name, typ: t}) } // error type { // Error has a nil package in its qualified name since it is in no package - err := &Method{QualifiedName{nil, "Error"}, &Signature{Results: []*Var{{Name: "", Type: Typ[String]}}}} - def(&TypeName{Name: "error", Type: &NamedType{Underlying: &Interface{Methods: []*Method{err}}}}) + var methods ObjSet + sig := &Signature{results: NewTuple(&Var{name: "", typ: Typ[String]})} + methods.Insert(&Func{nil, "Error", sig, nil}) + def(&TypeName{name: "error", typ: &Named{underlying: &Interface{methods: methods}}}) } for _, c := range predeclaredConstants { @@ -109,7 +111,7 @@ func init() { } for _, f := range predeclaredFunctions { - def(&Func{Name: f.name, Type: f}) + def(&Func{name: f.name, typ: f}) } universeIota = Universe.Lookup("iota").(*Const) @@ -120,24 +122,24 @@ func init() { // scope; other objects are inserted in the universe scope. // func def(obj Object) { - name := obj.GetName() + name := obj.Name() if strings.Index(name, " ") >= 0 { return // nothing to do } // fix Obj link for named types - if typ, ok := obj.GetType().(*NamedType); ok { - typ.Obj = obj.(*TypeName) + if typ, ok := obj.Type().(*Named); ok { + typ.obj = obj.(*TypeName) } // exported identifiers go into package unsafe scope := Universe if ast.IsExported(name) { - scope = Unsafe.Scope + scope = Unsafe.scope // set Pkg field switch obj := obj.(type) { case *TypeName: - obj.Pkg = Unsafe + obj.pkg = Unsafe case *Func: - obj.Pkg = Unsafe + obj.pkg = Unsafe default: unreachable() } diff --git a/ssa/builder.go b/ssa/builder.go index 3604517c5d..9f304b4ba4 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -34,8 +34,15 @@ import ( "code.google.com/p/go.tools/go/types" ) +type opaqueType struct { + types.Type + name string +} + +func (t *opaqueType) String() string { return t.name } + var ( - varOk = &types.Var{Name: "ok", Type: tBool} + varOk = types.NewVar(nil, "ok", tBool) // Type constants. tBool = types.Typ[types.Bool] @@ -47,15 +54,14 @@ var ( tInt = types.Typ[types.Int] tInvalid = types.Typ[types.Invalid] tUntypedNil = types.Typ[types.UntypedNil] - tRangeIter = &types.Basic{Name: "iter"} // the type of all "range" iterators + tRangeIter = &opaqueType{nil, "iter"} // the type of all "range" iterators tEface = new(types.Interface) // The result type of a "select". - tSelect = &types.Result{Values: []*types.Var{ - {Name: "index", Type: tInt}, - {Name: "recv", Type: tEface}, - varOk, - }} + tSelect = types.NewTuple( + types.NewVar(nil, "index", tInt), + types.NewVar(nil, "recv", tEface), + varOk) // SSA Value constants. vZero = intLiteral(0) @@ -77,7 +83,7 @@ type Context struct { // Loader is a SourceLoader function that finds, loads and // parses Go source files for a given import path. (It is // ignored if the mode bits include UseGCImporter.) - // See (e.g.) GoRootLoader. + // See (e.g.) MakeGoBuildLoader. Loader SourceLoader // RetainAST is an optional user predicate that determines @@ -149,7 +155,7 @@ func NewBuilder(context *Context) *Builder { Packages: make(map[string]*Package), Builtins: make(map[types.Object]*Builtin), methodSets: make(map[types.Type]MethodSet), - concreteMethods: make(map[*types.Method]*Function), + concreteMethods: make(map[*types.Func]*Function), mode: context.Mode, }, Context: context, @@ -322,24 +328,28 @@ func (b *Builder) exprN(fn *Function, e ast.Expr) Value { return fn.emit(&c) case *ast.IndexExpr: - mapt := underlyingType(fn.Pkg.TypeOf(e.X)).(*types.Map) - typ = mapt.Elt - tuple = fn.emit(&Lookup{ + mapt := fn.Pkg.TypeOf(e.X).Underlying().(*types.Map) + typ = mapt.Elem() + lookup := &Lookup{ X: b.expr(fn, e.X), - Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key), + Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key()), CommaOk: true, - }) + } + lookup.setPos(e.Lbrack) + tuple = fn.emit(lookup) case *ast.TypeAssertExpr: return emitTypeTest(fn, b.expr(fn, e.X), fn.Pkg.TypeOf(e)) case *ast.UnaryExpr: // must be receive <- - typ = underlyingType(fn.Pkg.TypeOf(e.X)).(*types.Chan).Elt - tuple = fn.emit(&UnOp{ + typ = fn.Pkg.TypeOf(e.X).Underlying().(*types.Chan).Elem() + unop := &UnOp{ Op: token.ARROW, X: b.expr(fn, e.X), CommaOk: true, - }) + } + unop.setPos(e.OpPos) + tuple = fn.emit(unop) default: panic(fmt.Sprintf("unexpected exprN: %T", e)) @@ -351,10 +361,10 @@ func (b *Builder) exprN(fn *Function, e ast.Expr) Value { tuple.(interface { setType(types.Type) - }).setType(&types.Result{Values: []*types.Var{ - {Name: "value", Type: typ}, + }).setType(types.NewTuple( + types.NewVar(nil, "value", typ), varOk, - }}) + )) return tuple } @@ -369,7 +379,7 @@ func (b *Builder) exprN(fn *Function, e ast.Expr) Value { func (b *Builder) builtin(fn *Function, name string, args []ast.Expr, typ types.Type, pos token.Pos) Value { switch name { case "make": - switch underlyingType(typ).(type) { + switch typ.Underlying().(type) { case *types.Slice: n := b.expr(fn, args[1]) m := n @@ -379,8 +389,8 @@ func (b *Builder) builtin(fn *Function, name string, args []ast.Expr, typ types. v := &MakeSlice{ Len: n, Cap: m, - Pos: pos, } + v.setPos(pos) v.setType(typ) return fn.emit(v) @@ -389,7 +399,8 @@ func (b *Builder) builtin(fn *Function, name string, args []ast.Expr, typ types. if len(args) == 2 { res = b.expr(fn, args[1]) } - v := &MakeMap{Reserve: res, Pos: pos} + v := &MakeMap{Reserve: res} + v.setPos(pos) v.setType(typ) return fn.emit(v) @@ -398,13 +409,14 @@ func (b *Builder) builtin(fn *Function, name string, args []ast.Expr, typ types. if len(args) == 2 { sz = b.expr(fn, args[1]) } - v := &MakeChan{Size: sz, Pos: pos} + v := &MakeChan{Size: sz} + v.setPos(pos) v.setType(typ) return fn.emit(v) } case "new": - return emitNew(fn, indirectType(underlyingType(typ)), pos) + return emitNew(fn, typ.Underlying().Deref(), pos) case "len", "cap": // Special case: len or cap of an array or *array is @@ -412,15 +424,18 @@ func (b *Builder) builtin(fn *Function, name string, args []ast.Expr, typ types. // We must still evaluate the value, though. (If it // was side-effect free, the whole call would have // been constant-folded.) - t := underlyingType(deref(fn.Pkg.TypeOf(args[0]))) + t := fn.Pkg.TypeOf(args[0]).Deref().Underlying() if at, ok := t.(*types.Array); ok { b.expr(fn, args[0]) // for effects only - return intLiteral(at.Len) + return intLiteral(at.Len()) } // Otherwise treat as normal. case "panic": - fn.emit(&Panic{X: emitConv(fn, b.expr(fn, args[0]), tEface)}) + fn.emit(&Panic{ + X: emitConv(fn, b.expr(fn, args[0]), tEface), + pos: pos, + }) fn.currentBlock = fn.newBasicBlock("unreachable") return vFalse // any non-nil Value will do } @@ -433,11 +448,12 @@ func (b *Builder) builtin(fn *Function, name string, args []ast.Expr, typ types. // in a potentially escaping way. // func (b *Builder) selector(fn *Function, e *ast.SelectorExpr, wantAddr, escaping bool) Value { - id := makeId(e.Sel.Name, fn.Pkg.Types) - st := underlyingType(deref(fn.Pkg.TypeOf(e.X))).(*types.Struct) + id := MakeId(e.Sel.Name, fn.Pkg.Types) + st := fn.Pkg.TypeOf(e.X).Deref().Underlying().(*types.Struct) index := -1 - for i, f := range st.Fields { - if IdFromQualifiedName(f.QualifiedName) == id { + for i, n := 0, st.NumFields(); i < n; i++ { + f := st.Field(i) + if MakeId(f.Name, f.Pkg) == id { index = i break } @@ -451,10 +467,11 @@ func (b *Builder) selector(fn *Function, e *ast.SelectorExpr, wantAddr, escaping } } fieldType := fn.Pkg.TypeOf(e) + pos := e.Sel.Pos() if wantAddr { - return b.fieldAddr(fn, e.X, path, index, fieldType, escaping) + return b.fieldAddr(fn, e.X, path, index, fieldType, pos, escaping) } - return b.fieldExpr(fn, e.X, path, index, fieldType) + return b.fieldExpr(fn, e.X, path, index, fieldType, pos) } // fieldAddr evaluates the base expression (a struct or *struct), @@ -464,17 +481,17 @@ func (b *Builder) selector(fn *Function, e *ast.SelectorExpr, wantAddr, escaping // // (fieldType can be derived from base+index.) // -func (b *Builder) fieldAddr(fn *Function, base ast.Expr, path *anonFieldPath, index int, fieldType types.Type, escaping bool) Value { +func (b *Builder) fieldAddr(fn *Function, base ast.Expr, path *anonFieldPath, index int, fieldType types.Type, pos token.Pos, escaping bool) Value { var x Value if path != nil { - switch underlyingType(path.field.Type).(type) { + switch path.field.Type.Underlying().(type) { case *types.Struct: - x = b.fieldAddr(fn, base, path.tail, path.index, path.field.Type, escaping) + x = b.fieldAddr(fn, base, path.tail, path.index, path.field.Type, token.NoPos, escaping) case *types.Pointer: - x = b.fieldExpr(fn, base, path.tail, path.index, path.field.Type) + x = b.fieldExpr(fn, base, path.tail, path.index, path.field.Type, token.NoPos) } } else { - switch underlyingType(fn.Pkg.TypeOf(base)).(type) { + switch fn.Pkg.TypeOf(base).Underlying().(type) { case *types.Struct: x = b.addr(fn, base, escaping).(address).addr case *types.Pointer: @@ -485,6 +502,7 @@ func (b *Builder) fieldAddr(fn *Function, base ast.Expr, path *anonFieldPath, in X: x, Field: index, } + v.setPos(pos) v.setType(pointer(fieldType)) return fn.emit(v) } @@ -496,19 +514,20 @@ func (b *Builder) fieldAddr(fn *Function, base ast.Expr, path *anonFieldPath, in // // (fieldType can be derived from base+index.) // -func (b *Builder) fieldExpr(fn *Function, base ast.Expr, path *anonFieldPath, index int, fieldType types.Type) Value { +func (b *Builder) fieldExpr(fn *Function, base ast.Expr, path *anonFieldPath, index int, fieldType types.Type, pos token.Pos) Value { var x Value if path != nil { - x = b.fieldExpr(fn, base, path.tail, path.index, path.field.Type) + x = b.fieldExpr(fn, base, path.tail, path.index, path.field.Type, token.NoPos) } else { x = b.expr(fn, base) } - switch underlyingType(x.Type()).(type) { + switch x.Type().Underlying().(type) { case *types.Struct: v := &Field{ X: x, Field: index, } + v.setPos(pos) v.setType(fieldType) return fn.emit(v) @@ -517,6 +536,7 @@ func (b *Builder) fieldExpr(fn *Function, base ast.Expr, path *anonFieldPath, in X: x, Field: index, } + v.setPos(pos) v.setType(pointer(fieldType)) return emitLoad(fn, fn.emit(v)) } @@ -557,7 +577,7 @@ func (b *Builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { return address{v} case *ast.CompositeLit: - t := deref(fn.Pkg.TypeOf(e)) + t := fn.Pkg.TypeOf(e).Deref() var v Value if escaping { v = emitNew(fn, t, e.Lbrace) @@ -576,7 +596,7 @@ func (b *Builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { if v, ok := b.lookup(fn.Pkg, obj); ok { return address{v} } - panic("undefined package-qualified name: " + obj.GetName()) + panic("undefined package-qualified name: " + obj.Name()) } // e.f where e is an expression. @@ -585,21 +605,21 @@ func (b *Builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { case *ast.IndexExpr: var x Value var et types.Type - switch t := underlyingType(fn.Pkg.TypeOf(e.X)).(type) { + switch t := fn.Pkg.TypeOf(e.X).Underlying().(type) { case *types.Array: x = b.addr(fn, e.X, escaping).(address).addr - et = pointer(t.Elt) + et = pointer(t.Elem()) case *types.Pointer: // *array x = b.expr(fn, e.X) - et = pointer(underlyingType(t.Base).(*types.Array).Elt) + et = pointer(t.Elem().Underlying().(*types.Array).Elem()) case *types.Slice: x = b.expr(fn, e.X) - et = pointer(t.Elt) + et = pointer(t.Elem()) case *types.Map: return &element{ m: b.expr(fn, e.X), - k: emitConv(fn, b.expr(fn, e.Index), t.Key), - t: t.Elt, + k: emitConv(fn, b.expr(fn, e.Index), t.Key()), + t: t.Elem(), } default: panic("unexpected container type in IndexExpr: " + t.String()) @@ -629,7 +649,7 @@ func (b *Builder) exprInPlace(fn *Function, loc lvalue, e ast.Expr) { if addr, ok := loc.(address); ok { if e, ok := e.(*ast.CompositeLit); ok { typ := addr.typ() - switch underlyingType(typ).(type) { + switch typ.Underlying().(type) { case *types.Pointer: // implicit & -- possibly escaping ptr := b.addr(fn, e, true).(address).addr addr.store(fn, ptr) // copy address @@ -665,8 +685,8 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { posn := b.Prog.Files.Position(e.Type.Func) fn2 := &Function{ Name_: fmt.Sprintf("func@%d.%d", posn.Line, posn.Column), - Signature: underlyingType(fn.Pkg.TypeOf(e.Type)).(*types.Signature), - Pos: e.Type.Func, + Signature: fn.Pkg.TypeOf(e.Type).Underlying().(*types.Signature), + pos: e.Type.Func, Enclosing: fn, Pkg: fn.Pkg, Prog: b.Prog, @@ -697,8 +717,20 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { case *ast.CallExpr: typ := fn.Pkg.TypeOf(e) if fn.Pkg.IsType(e.Fun) { - // Type conversion, e.g. string(x) or big.Int(x) - return emitConv(fn, b.expr(fn, e.Args[0]), typ) + // Explicit type conversion, e.g. string(x) or big.Int(x) + x := b.expr(fn, e.Args[0]) + y := emitConv(fn, x, typ) + if y != x { + switch y := y.(type) { + case *Convert: + y.pos = e.Lparen + case *ChangeType: + y.pos = e.Lparen + case *MakeInterface: + y.pos = e.Lparen + } + } + return y } // Call to "intrinsic" built-ins, e.g. new, make, panic. if id, ok := e.Fun.(*ast.Ident); ok { @@ -726,6 +758,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { Op: e.Op, X: b.expr(fn, e.X), } + v.setPos(e.OpPos) v.setType(fn.Pkg.TypeOf(e)) return fn.emit(v) default: @@ -750,7 +783,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { case *ast.SliceExpr: var low, high Value var x Value - switch underlyingType(fn.Pkg.TypeOf(e.X)).(type) { + switch fn.Pkg.TypeOf(e.X).Underlying().(type) { case *types.Array: // Potentially escaping. x = b.addr(fn, e.X, true).(address).addr @@ -793,7 +826,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { // (*T).f or T.f, the method f from the method-set of type T. if fn.Pkg.IsType(e.X) { - id := makeId(e.Sel.Name, fn.Pkg.Types) + id := MakeId(e.Sel.Name, fn.Pkg.Types) typ := fn.Pkg.TypeOf(e.X) if m := b.Prog.MethodSet(typ)[id]; m != nil { return m @@ -807,24 +840,25 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { return b.selector(fn, e, false, false) case *ast.IndexExpr: - switch t := underlyingType(fn.Pkg.TypeOf(e.X)).(type) { + switch t := fn.Pkg.TypeOf(e.X).Underlying().(type) { case *types.Array: // Non-addressable array (in a register). v := &Index{ X: b.expr(fn, e.X), Index: emitConv(fn, b.expr(fn, e.Index), tInt), } - v.setType(t.Elt) + v.setType(t.Elem()) return fn.emit(v) case *types.Map: // Maps are not addressable. - mapt := underlyingType(fn.Pkg.TypeOf(e.X)).(*types.Map) + mapt := fn.Pkg.TypeOf(e.X).Underlying().(*types.Map) v := &Lookup{ X: b.expr(fn, e.X), - Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key), + Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key()), } - v.setType(mapt.Elt) + v.setPos(e.Lbrack) + v.setType(mapt.Elem()) return fn.emit(v) case *types.Basic: // => string @@ -833,6 +867,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { X: b.expr(fn, e.X), Index: b.expr(fn, e.Index), } + v.setPos(e.Lbrack) v.setType(tByte) return fn.emit(v) @@ -864,7 +899,7 @@ func (b *Builder) stmtList(fn *Function, list []ast.Stmt) { // occurring in e. // func (b *Builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) { - c.Pos = e.Lparen + c.pos = e.Lparen c.HasEllipsis = e.Ellipsis != 0 // Is the call of the form x.f()? @@ -886,7 +921,7 @@ func (b *Builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) { c.Func = v return } - panic("undefined package-qualified name: " + obj.GetName()) + panic("undefined package-qualified name: " + obj.Name()) } // Case 2a: X.f() or (*X).f(): a statically dipatched call to @@ -905,12 +940,12 @@ func (b *Builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) { // Case 2: x.f(): a statically dispatched call to a method // from the method-set of X or perhaps *X (if x is addressable // but not a pointer). - id := makeId(sel.Sel.Name, fn.Pkg.Types) + id := MakeId(sel.Sel.Name, fn.Pkg.Types) // Consult method-set of X. if m := b.Prog.MethodSet(typ)[id]; m != nil { var recv Value aptr := isPointer(typ) - fptr := isPointer(m.Signature.Recv.Type) + fptr := isPointer(m.Signature.Recv().Type()) if aptr == fptr { // Actual's and formal's "pointerness" match. recv = b.expr(fn, sel.X) @@ -937,7 +972,7 @@ func (b *Builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) { } } - switch t := underlyingType(typ).(type) { + switch t := typ.Underlying().(type) { case *types.Struct, *types.Pointer: // Case 3: x.f() where x.f is a function value in a // struct field f; not a method call. f is a 'var' @@ -949,7 +984,7 @@ func (b *Builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) { // Case 4: x.f() where a dynamically dispatched call // to an interface method f. f is a 'func' object in // the Methods of types.Interface X - c.Method, _ = methodIndex(t, t.Methods, id) + c.Method, _ = methodIndex(t, id) c.Recv = b.expr(fn, sel.X) default: @@ -967,9 +1002,9 @@ func (b *Builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallEx for i, arg := range e.Args { // TODO(gri): annoyingly Signature.Params doesn't // reflect the slice type for a final ...T param. - t := sig.Params[i].Type - if sig.IsVariadic && i == len(e.Args)-1 { - t = &types.Slice{Elt: t} + t := sig.Params().At(i).Type() + if sig.IsVariadic() && i == len(e.Args)-1 { + t = types.NewSlice(t) } args = append(args, emitConv(fn, b.expr(fn, arg), t)) } @@ -985,9 +1020,9 @@ func (b *Builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallEx // args; a suffix of them may end up in a varargs slice. for _, arg := range e.Args { v := b.expr(fn, arg) - if ttuple, ok := v.Type().(*types.Result); ok { // MRV chain - for i, t := range ttuple.Values { - args = append(args, emitExtract(fn, v, i, t.Type)) + if ttuple, ok := v.Type().(*types.Tuple); ok { // MRV chain + for i, n := 0, ttuple.Len(); i < n; i++ { + args = append(args, emitExtract(fn, v, i, ttuple.At(i).Type())) } } else { args = append(args, v) @@ -995,28 +1030,25 @@ func (b *Builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallEx } // Actual->formal assignability conversions for normal parameters. - np := len(sig.Params) // number of normal parameters - if sig.IsVariadic { + np := sig.Params().Len() // number of normal parameters + if sig.IsVariadic() { np-- } for i := 0; i < np; i++ { - args[offset+i] = emitConv(fn, args[offset+i], sig.Params[i].Type) + args[offset+i] = emitConv(fn, args[offset+i], sig.Params().At(i).Type()) } // Actual->formal assignability conversions for variadic parameter, // and construction of slice. - if sig.IsVariadic { + if sig.IsVariadic() { varargs := args[offset+np:] - vt := sig.Params[np].Type - st := &types.Slice{Elt: vt} + vt := sig.Params().At(np).Type() + st := types.NewSlice(vt) if len(varargs) == 0 { args = append(args, nilLiteral(st)) } else { // Replace a suffix of args with a slice containing it. - at := &types.Array{ - Elt: vt, - Len: int64(len(varargs)), - } + at := types.NewArray(vt, int64(len(varargs))) a := emitNew(fn, at, e.Lparen) for i, arg := range varargs { iaddr := &IndexAddr{ @@ -1044,7 +1076,7 @@ func (b *Builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) { b.setCallFunc(fn, e, c) // Then append the other actual parameters. - sig, _ := underlyingType(fn.Pkg.TypeOf(e.Fun)).(*types.Signature) + sig, _ := fn.Pkg.TypeOf(e.Fun).Underlying().(*types.Signature) if sig == nil { sig = builtinCallSignature(&fn.Pkg.TypeInfo, e) } @@ -1161,13 +1193,12 @@ func (b *Builder) globalValueSpec(init *Function, spec *ast.ValueSpec, g *Global defer logStack("build globals %s", spec.Names)() } tuple := b.exprN(init, spec.Values[0]) - rtypes := tuple.Type().(*types.Result).Values + result := tuple.Type().(*types.Tuple) for i, id := range spec.Names { if !isBlankIdent(id) { g := b.globals[init.Pkg.ObjectOf(id)].(*Global) g.spec = nil // just an optimization - emitStore(init, g, - emitExtract(init, tuple, i, rtypes[i].Type)) + emitStore(init, g, emitExtract(init, tuple, i, result.At(i).Type())) } } } @@ -1206,11 +1237,11 @@ func (b *Builder) localValueSpec(fn *Function, spec *ast.ValueSpec) { default: // e.g. var x, y = pos() tuple := b.exprN(fn, spec.Values[0]) - rtypes := tuple.Type().(*types.Result).Values + result := tuple.Type().(*types.Tuple) for i, id := range spec.Names { if !isBlankIdent(id) { lhs := fn.addNamedLocal(fn.Pkg.ObjectOf(id)) - emitStore(fn, lhs, emitExtract(fn, tuple, i, rtypes[i].Type)) + emitStore(fn, lhs, emitExtract(fn, tuple, i, result.At(i).Type())) } } } @@ -1262,13 +1293,30 @@ func (b *Builder) assignStmt(fn *Function, lhss, rhss []ast.Expr, isDef bool) { } else { // e.g. x, y = pos() tuple := b.exprN(fn, rhss[0]) - rtypes := tuple.Type().(*types.Result).Values + result := tuple.Type().(*types.Tuple) for i, lval := range lvals { - lval.store(fn, emitExtract(fn, tuple, i, rtypes[i].Type)) + lval.store(fn, emitExtract(fn, tuple, i, result.At(i).Type())) } } } +// arrayLen returns the length of the array whose composite literal elements are elts. +func (b *Builder) arrayLen(fn *Function, elts []ast.Expr) int64 { + var max int64 = -1 + var i int64 = -1 + for _, e := range elts { + if kv, ok := e.(*ast.KeyValueExpr); ok { + i = b.expr(fn, kv.Key).(*Literal).Int64() + } else { + i++ + } + if i > max { + max = i + } + } + return max + 1 +} + // compLit emits to fn code to initialize a composite literal e at // address addr with type typ, typically allocated by Alloc. // Nested composite literals are recursively initialized in place @@ -1278,13 +1326,14 @@ func (b *Builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ typ // TODO(adonovan): document how and why typ ever differs from // fn.Pkg.TypeOf(e). - switch t := underlyingType(typ).(type) { + switch t := typ.Underlying().(type) { case *types.Struct: for i, e := range e.Elts { fieldIndex := i if kv, ok := e.(*ast.KeyValueExpr); ok { fname := kv.Key.(*ast.Ident).Name - for i, sf := range t.Fields { + for i, n := 0, t.NumFields(); i < n; i++ { + sf := t.Field(i) if sf.Name == fname { fieldIndex = i e = kv.Value @@ -1292,7 +1341,7 @@ func (b *Builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ typ } } } - sf := t.Fields[fieldIndex] + sf := t.Field(fieldIndex) faddr := &FieldAddr{ X: addr, Field: fieldIndex, @@ -1307,14 +1356,14 @@ func (b *Builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ typ var array Value switch t := t.(type) { case *types.Slice: - at = &types.Array{Elt: t.Elt} // set Len later + at = types.NewArray(t.Elem(), b.arrayLen(fn, e.Elts)) array = emitNew(fn, at, e.Lbrace) case *types.Array: at = t array = addr } + var idx *Literal - var max int64 = -1 for _, e := range e.Elts { if kv, ok := e.(*ast.KeyValueExpr); ok { idx = b.expr(fn, kv.Key).(*Literal) @@ -1326,34 +1375,33 @@ func (b *Builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ typ } idx = intLiteral(idxval) } - if idx.Int64() > max { - max = idx.Int64() - } iaddr := &IndexAddr{ X: array, Index: idx, } - iaddr.setType(pointer(at.Elt)) + iaddr.setType(pointer(at.Elem())) fn.emit(iaddr) b.exprInPlace(fn, address{iaddr}, e) } if t != at { // slice - at.Len = max + 1 s := &Slice{X: array} + s.setPos(e.Lbrace) s.setType(t) emitStore(fn, addr, fn.emit(s)) } case *types.Map: - m := &MakeMap{Reserve: intLiteral(int64(len(e.Elts))), Pos: e.Lbrace} + m := &MakeMap{Reserve: intLiteral(int64(len(e.Elts)))} + m.setPos(e.Lbrace) m.setType(typ) emitStore(fn, addr, fn.emit(m)) for _, e := range e.Elts { e := e.(*ast.KeyValueExpr) up := &MapUpdate{ Map: m, - Key: emitConv(fn, b.expr(fn, e.Key), t.Key), - Value: emitConv(fn, b.expr(fn, e.Value), t.Elt), + Key: emitConv(fn, b.expr(fn, e.Key), t.Key()), + Value: emitConv(fn, b.expr(fn, e.Value), t.Elem()), + pos: e.Colon, } fn.emit(up) } @@ -1614,7 +1662,7 @@ func (b *Builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) { Dir: ast.SEND, Chan: ch, Send: emitConv(fn, b.expr(fn, comm.Value), - underlyingType(ch.Type()).(*types.Chan).Elt), + ch.Type().Underlying().(*types.Chan).Elem()), }) case *ast.AssignStmt: // x := <-ch @@ -1647,6 +1695,7 @@ func (b *Builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) { States: states, Blocking: blocking, } + triple.setPos(s.Select) triple.setType(tSelect) fn.emit(triple) idx := emitExtract(fn, triple, 0, tInt) @@ -1675,12 +1724,12 @@ func (b *Builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) { switch comm := clause.Comm.(type) { case *ast.AssignStmt: // x := <-states[state].Chan xdecl := fn.addNamedLocal(fn.Pkg.ObjectOf(comm.Lhs[0].(*ast.Ident))) - recv := emitTypeAssert(fn, emitExtract(fn, triple, 1, tEface), indirectType(xdecl.Type())) + recv := emitTypeAssert(fn, emitExtract(fn, triple, 1, tEface), xdecl.Type().Deref()) emitStore(fn, xdecl, recv) if len(comm.Lhs) == 2 { // x, ok := ... okdecl := fn.addNamedLocal(fn.Pkg.ObjectOf(comm.Lhs[1].(*ast.Ident))) - emitStore(fn, okdecl, emitExtract(fn, triple, 2, indirectType(okdecl.Type()))) + emitStore(fn, okdecl, emitExtract(fn, triple, 2, okdecl.Type().Deref())) } } b.stmtList(fn, clause.Body) @@ -1776,13 +1825,13 @@ func (b *Builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value // Determine number of iterations. var length Value - if arr, ok := deref(x.Type()).(*types.Array); ok { + if arr, ok := x.Type().Deref().(*types.Array); ok { // For array or *array, the number of iterations is // known statically thanks to the type. We avoid a // data dependence upon x, permitting later dead-code // elimination if x is pure, static unrolling, etc. // Ranging over a nil *array may have >0 iterations. - length = intLiteral(arr.Len) + length = intLiteral(arr.Len()) } else { // length = len(x). var c Call @@ -1814,13 +1863,13 @@ func (b *Builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value k = emitLoad(fn, index) if tv != nil { - switch t := underlyingType(x.Type()).(type) { + switch t := x.Type().Underlying().(type) { case *types.Array: instr := &Index{ X: x, Index: k, } - instr.setType(t.Elt) + instr.setType(t.Elem()) v = fn.emit(instr) case *types.Pointer: // *array @@ -1828,7 +1877,7 @@ func (b *Builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value X: x, Index: k, } - instr.setType(pointer(t.Base.(*types.Array).Elt)) + instr.setType(pointer(t.Elem().(*types.Array).Elem())) v = emitLoad(fn, fn.emit(instr)) case *types.Slice: @@ -1836,7 +1885,7 @@ func (b *Builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value X: x, Index: k, } - instr.setType(pointer(t.Elt)) + instr.setType(pointer(t.Elem())) v = emitLoad(fn, fn.emit(instr)) default: @@ -1851,7 +1900,7 @@ func (b *Builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value // tk and tv are the types of the key/value results k and v, or nil // if the respective component is not wanted. // -func (b *Builder) rangeIter(fn *Function, x Value, tk, tv types.Type) (k, v Value, loop, done *BasicBlock) { +func (b *Builder) rangeIter(fn *Function, x Value, tk, tv types.Type, pos token.Pos) (k, v Value, loop, done *BasicBlock) { // // it = range x // loop: (target of continue) @@ -1874,6 +1923,7 @@ func (b *Builder) rangeIter(fn *Function, x Value, tk, tv types.Type) (k, v Valu } rng := &Range{X: x} + rng.setPos(pos) rng.setType(tRangeIter) it := fn.emit(rng) @@ -1881,17 +1931,17 @@ func (b *Builder) rangeIter(fn *Function, x Value, tk, tv types.Type) (k, v Valu emitJump(fn, loop) fn.currentBlock = loop - _, isString := underlyingType(x.Type()).(*types.Basic) + _, isString := x.Type().Underlying().(*types.Basic) okv := &Next{ Iter: it, IsString: isString, } - okv.setType(&types.Result{Values: []*types.Var{ + okv.setType(types.NewTuple( varOk, - {Name: "k", Type: tk}, - {Name: "v", Type: tv}, - }}) + types.NewVar(nil, "k", tk), + types.NewVar(nil, "v", tv), + )) fn.emit(okv) body := fn.newBasicBlock("rangeiter.body") @@ -1933,10 +1983,10 @@ func (b *Builder) rangeChan(fn *Function, x Value, tk types.Type) (k Value, loop X: x, CommaOk: true, } - recv.setType(&types.Result{Values: []*types.Var{ - {Name: "k", Type: tk}, + recv.setType(types.NewTuple( + types.NewVar(nil, "k", tk), varOk, - }}) + )) ko := fn.emit(recv) body := fn.newBasicBlock("rangechan.body") done = fn.newBasicBlock("rangechan.done") @@ -1979,7 +2029,7 @@ func (b *Builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock) { var k, v Value var loop, done *BasicBlock - switch rt := underlyingType(x.Type()).(type) { + switch rt := x.Type().Underlying().(type) { case *types.Slice, *types.Array, *types.Pointer: // *array k, v, loop, done = b.rangeIndexed(fn, x, tv) @@ -1987,7 +2037,7 @@ func (b *Builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock) { k, loop, done = b.rangeChan(fn, x, tk) case *types.Map, *types.Basic: // string - k, v, loop, done = b.rangeIter(fn, x, tk, tv) + k, v, loop, done = b.rangeIter(fn, x, tk, tv, s.For) default: panic("Cannot range over: " + rt.String()) @@ -2058,7 +2108,8 @@ start: fn.emit(&Send{ Chan: b.expr(fn, s.Chan), X: emitConv(fn, b.expr(fn, s.Value), - underlyingType(fn.Pkg.TypeOf(s.Chan)).(*types.Chan).Elt), + fn.Pkg.TypeOf(s.Chan).Underlying().(*types.Chan).Elem()), + pos: s.Arrow, }) case *ast.IncDecStmt: @@ -2112,18 +2163,19 @@ start: } var results []Value - if len(s.Results) == 1 && len(fn.Signature.Results) > 1 { + if len(s.Results) == 1 && fn.Signature.Results().Len() > 1 { // Return of one expression in a multi-valued function. tuple := b.exprN(fn, s.Results[0]) - for i, v := range tuple.Type().(*types.Result).Values { + ttuple := tuple.Type().(*types.Tuple) + for i, n := 0, ttuple.Len(); i < n; i++ { results = append(results, - emitConv(fn, emitExtract(fn, tuple, i, v.Type), - fn.Signature.Results[i].Type)) + emitConv(fn, emitExtract(fn, tuple, i, ttuple.At(i).Type()), + fn.Signature.Results().At(i).Type())) } } else { // 1:1 return, or no-arg return in non-void function. for i, r := range s.Results { - v := emitConv(fn, b.expr(fn, r), fn.Signature.Results[i].Type) + v := emitConv(fn, b.expr(fn, r), fn.Signature.Results().At(i).Type()) results = append(results, v) } } @@ -2144,7 +2196,7 @@ start: results = append(results, emitLoad(fn, r)) } } - fn.emit(&Ret{Results: results}) + fn.emit(&Ret{Results: results, pos: s.Return}) fn.currentBlock = fn.newBasicBlock("unreachable") case *ast.BranchStmt: @@ -2248,18 +2300,18 @@ func (b *Builder) buildFunction(fn *Function) { // We set Function.Params even though there is no body // code to reference them. This simplifies clients. - if recv := fn.Signature.Recv; recv != nil { - fn.addParam(recv.Name, recv.Type) - } - for _, param := range fn.Signature.Params { - fn.addParam(param.Name, param.Type) + if recv := fn.Signature.Recv(); recv != nil { + fn.addParam(recv.Name(), recv.Type()) } + fn.Signature.Params().ForEach(func(p *types.Var) { + fn.addParam(p.Name(), p.Type()) + }) } return } if fn.Prog.mode&LogSource != 0 { defer logStack("build function %s @ %s", - fn.FullName(), fn.Prog.Files.Position(fn.Pos))() + fn.FullName(), fn.Prog.Files.Position(fn.pos))() } fn.startBody() fn.createSyntacticParams(fn.Pkg.idents) @@ -2281,16 +2333,16 @@ func (b *Builder) buildFunction(fn *Function) { // phase. // func (b *Builder) memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { - name := obj.GetName() + name := obj.Name() switch obj := obj.(type) { case *types.TypeName: - pkg.Members[name] = &Type{NamedType: obj.Type.(*types.NamedType)} + pkg.Members[name] = &Type{NamedType: obj.Type().(*types.Named)} case *types.Const: pkg.Members[name] = &Constant{ Name_: name, - Value: newLiteral(obj.Val, obj.Type), - Pos: obj.GetPos(), + Value: newLiteral(obj.Val(), obj.Type()), + pos: obj.Pos(), } case *types.Var: @@ -2298,8 +2350,8 @@ func (b *Builder) memberFromObject(pkg *Package, obj types.Object, syntax ast.No g := &Global{ Pkg: pkg, Name_: name, - Type_: pointer(obj.Type), // address - Pos: obj.GetPos(), + Type_: pointer(obj.Type()), // address + pos: obj.Pos(), spec: spec, } b.globals[obj] = g @@ -2307,7 +2359,6 @@ func (b *Builder) memberFromObject(pkg *Package, obj types.Object, syntax ast.No case *types.Func: var fs *funcSyntax - var pos token.Pos if decl, ok := syntax.(*ast.FuncDecl); ok { fs = &funcSyntax{ recvField: decl.Recv, @@ -2315,28 +2366,24 @@ func (b *Builder) memberFromObject(pkg *Package, obj types.Object, syntax ast.No resultFields: decl.Type.Results, body: decl.Body, } - // TODO(gri): make GcImported types.Object - // implement the full object interface - // including Pos(). Or at least not crash. - pos = obj.GetPos() } - sig := obj.Type.(*types.Signature) + sig := obj.Type().(*types.Signature) fn := &Function{ Name_: name, Signature: sig, - Pos: pos, + pos: obj.Pos(), // (iff syntax) Pkg: pkg, Prog: b.Prog, syntax: fs, } - if sig.Recv == nil { + if sig.Recv() == nil { // Function declaration. b.globals[obj] = fn pkg.Members[name] = fn } else { // Method declaration. - nt := deref(sig.Recv.Type).(*types.NamedType) - _, method := methodIndex(nt, nt.Methods, makeId(name, pkg.Types)) + nt := sig.Recv().Type().Deref().(*types.Named) + _, method := methodIndex(nt, MakeId(name, pkg.Types)) b.Prog.concreteMethods[method] = fn } @@ -2383,8 +2430,8 @@ func (b *Builder) membersFromDecl(pkg *Package, decl ast.Decl) { case *ast.FuncDecl: id := decl.Name if decl.Recv == nil && id.Name == "init" { - if !pkg.Init.Pos.IsValid() { - pkg.Init.Pos = decl.Name.Pos() + if !pkg.Init.pos.IsValid() { + pkg.Init.pos = decl.Name.Pos() } return // init blocks aren't functions } @@ -2397,7 +2444,7 @@ func (b *Builder) membersFromDecl(pkg *Package, decl ast.Decl) { // typecheck invokes the type-checker on files and returns the // type-checker's package so formed, plus the AST type information. // -func (b *Builder) typecheck(files []*ast.File) (*types.Package, *TypeInfo, error) { +func (b *Builder) typecheck(importPath string, files []*ast.File) (*types.Package, *TypeInfo, error) { info := &TypeInfo{ types: make(map[ast.Expr]types.Type), idents: make(map[*ast.Ident]types.Object), @@ -2416,7 +2463,7 @@ func (b *Builder) typecheck(files []*ast.File) (*types.Package, *TypeInfo, error // - isBlankIdent(ident) <=> obj.GetType()==nil info.idents[ident] = obj } - typkg, firstErr := tc.Check(b.Prog.Files, files) + typkg, firstErr := tc.Check(importPath, b.Prog.Files, files...) tc.Expr = nil tc.Ident = nil if firstErr != nil { @@ -2437,7 +2484,7 @@ func (b *Builder) typecheck(files []*ast.File) (*types.Package, *TypeInfo, error // source files. // func (b *Builder) CreatePackage(importPath string, files []*ast.File) (*Package, error) { - typkg, info, err := b.typecheck(files) + typkg, info, err := b.typecheck(importPath, files) if err != nil { return nil, err } @@ -2458,15 +2505,6 @@ func (b *Builder) CreatePackage(importPath string, files []*ast.File) (*Package, // from the gc compiler's object files; no code will be available. // func (b *Builder) createPackageImpl(typkg *types.Package, importPath string, files []*ast.File, info *TypeInfo) *Package { - // The typechecker sets types.Package.Path only for GcImported - // packages, since it doesn't know import path until after typechecking is done. - // Here we ensure it is always set, since we know the correct path. - if typkg.Path == "" { - typkg.Path = importPath - } else if typkg.Path != importPath { - panic(fmt.Sprintf("%s != %s", typkg.Path, importPath)) - } - p := &Package{ Prog: b.Prog, Types: typkg, @@ -2507,7 +2545,7 @@ func (b *Builder) createPackageImpl(typkg *types.Package, importPath string, fil // No code. // No position information. - for _, obj := range p.Types.Scope.Entries { + for _, obj := range p.Types.Scope().Entries { b.memberFromObject(p, obj, nil) } } @@ -2555,9 +2593,10 @@ func (b *Builder) buildDecl(pkg *Package, decl ast.Decl) { continue } obj := pkg.ObjectOf(id).(*types.TypeName) - for _, method := range obj.Type.(*types.NamedType).Methods { - b.buildFunction(b.Prog.concreteMethods[method]) - } + nt := obj.Type().(*types.Named) + nt.ForEachMethod(func(m *types.Func) { + b.buildFunction(b.Prog.concreteMethods[m]) + }) } } @@ -2645,32 +2684,35 @@ func (b *Builder) BuildPackage(p *Package) { init.currentBlock = doinit emitStore(init, initguard, vTrue) - // TODO(gri): fix: the types.Package.Imports map may contains - // entries for other package's import statements, if produced - // by GcImport. Project it down to just the ones for us. - imports := make(map[string]*types.Package) + // Call the init() function of each package we import. + // We iterate over the syntax (p.Files.Imports) not the types + // (p.Types.Imports()) because the latter may contain the + // transitive closure of dependencies, + // e.g. when using GcImporter. + seen := make(map[*types.Package]bool) for _, file := range p.Files { for _, imp := range file.Imports { path, _ := strconv.Unquote(imp.Path.Value) - if path != "unsafe" { - imports[path] = p.Types.Imports[path] + if path == "unsafe" { + continue } - } - } + typkg := p.Types.Imports()[path] + if seen[typkg] { + continue + } + seen[typkg] = true - // Call the init() function of each package we import. - // Order is unspecified (and is in fact nondeterministic). - for name, imported := range imports { - p2 := b.packages[imported] - if p2 == nil { - panic("Building " + p.Name() + ": CreatePackage has not been called for package " + name) - } + p2 := b.packages[typkg] + if p2 == nil { + panic("Building " + p.Name() + ": CreatePackage has not been called for package " + path) + } - var v Call - v.Call.Func = p2.Init - v.Call.Pos = init.Pos - v.setType(new(types.Result)) - init.emit(&v) + var v Call + v.Call.Func = p2.Init + v.Call.pos = init.pos + v.setType(types.NewTuple()) + init.emit(&v) + } } // Visit the package's var decls and init funcs in source diff --git a/ssa/doc.go b/ssa/doc.go index 11f93c5517..1ebbedc570 100644 --- a/ssa/doc.go +++ b/ssa/doc.go @@ -27,8 +27,9 @@ // By supplying an instance of the SourceLocator function prototype, // clients may control how the builder locates, loads and parses Go // sources files for imported packages. This package provides -// GorootLoader, which uses go/build to locate packages in the Go -// source distribution, and go/parser to parse them. +// MakeGoBuildLoader, which creates a loader that uses go/build to +// locate packages in the Go source distribution, and go/parser to +// parse them. // // The builder initially builds a naive SSA form in which all local // variables are addresses of stack locations with explicit loads and @@ -38,6 +39,61 @@ // subsequent analyses; this pass can be skipped by setting the // NaiveForm builder flag. // +// The primary interfaces of this package are: +// +// - Member: a named member of a Go package. +// - Value: an expression that yields a value. +// - Instruction: a statement that consumes values and performs computation. +// +// A computation that yields a result implements both the Value and +// Instruction interfaces. The following table shows for each +// concrete type which of these interfaces it implements. +// +// Value? Instruction? Member? +// *Alloc ✔ ✔ +// *BinOp ✔ ✔ +// *Builtin ✔ ✔ +// *Call ✔ ✔ +// *Capture ✔ +// *ChangeInterface ✔ ✔ +// *ChangeType ✔ ✔ +// *Constant ✔ (const) +// *Convert ✔ ✔ +// *Defer ✔ +// *Extract ✔ ✔ +// *Field ✔ ✔ +// *FieldAddr ✔ ✔ +// *Function ✔ ✔ (func) +// *Global ✔ ✔ (var) +// *Go ✔ +// *If ✔ +// *Index ✔ ✔ +// *IndexAddr ✔ ✔ +// *Jump ✔ +// *Literal ✔ +// *Lookup ✔ ✔ +// *MakeChan ✔ ✔ +// *MakeClosure ✔ ✔ +// *MakeInterface ✔ ✔ +// *MakeMap ✔ ✔ +// *MakeSlice ✔ ✔ +// *MapUpdate ✔ +// *Next ✔ ✔ +// *Panic ✔ +// *Parameter ✔ +// *Phi ✔ ✔ +// *Range ✔ ✔ +// *Ret ✔ +// *RunDefers ✔ +// *Select ✔ ✔ +// *Slice ✔ ✔ +// *Type ✔ (type) +// *TypeAssert ✔ ✔ +// *UnOp ✔ ✔ +// +// Other key types in this package include: Program, Package, Function +// and BasicBlock. +// // The program representation constructed by this package is fully // resolved internally, i.e. it does not rely on the names of Values, // Packages, Functions, Types or BasicBlocks for the correct @@ -59,32 +115,31 @@ // // const message = "Hello, World!" // -// func hello() { +// func main() { // fmt.Println(message) // } // // The SSA Builder creates a *Program containing a main *Package such // as this: // -// Package(Name: "main") +// Package (Name: "main") // Members: -// "message": *Literal (Type: untyped string, Value: "Hello, World!") +// "message": *Constant (Type: untyped string, Value: "Hello, World!") // "init·guard": *Global (Type: *bool) -// "hello": *Function (Type: func()) +// "main": *Function (Type: func()) // Init: *Function (Type: func()) // -// The printed representation of the function main.hello is shown +// The printed representation of the function main.main is shown // below. Within the function listing, the name of each BasicBlock // such as ".0.entry" is printed left-aligned, followed by the block's -// instructions, i.e. implementations of Instruction. +// Instructions. // For each instruction that defines an SSA virtual register // (i.e. implements Value), the type of that value is shown in the // right column. // -// # Name: main.hello +// # Name: main.main // # Declared at hello.go:7:6 -// # Type: func() -// func hello(): +// func main(): // .0.entry: // t0 = new [1]interface{} *[1]interface{} // t1 = &t0[0:untyped integer] *interface{} @@ -102,14 +157,21 @@ // TODO(adonovan): demonstrate more features in the example: // parameters and control flow at the least. // -// TODO(adonovan): Consider how token.Pos source location information -// should be made available generally. Currently it is only present in -// Package, Function and CallCommon. -// // TODO(adonovan): Consider the exceptional control-flow implications // of defer and recover(). // -// TODO(adonovan): build tables/functions that relate source variables -// to SSA variables to assist user interfaces that make queries about -// specific source entities. +// TODO(adonovan): Consider how token.Pos source location information +// should be made available generally. Currently it is only present +// in package Members and selected Instructions for which there is a +// direct source correspondence. We'll need to work harder to tie all +// defs/uses of named variables together, esp. because SSA splits them +// into separate webs. +// +// TODO(adonovan): it is practically impossible for clients to +// construct well-formed SSA functions/packages/programs directly; we +// assume this is the job of the ssa.Builder alone. +// Nonetheless it may be wise to give clients a little more +// flexibility. For example, analysis tools may wish to construct a +// fake ssa.Function for the root of the callgraph, a fake "reflect" +// package, etc. package ssa diff --git a/ssa/emit.go b/ssa/emit.go index 7b15288b0e..786ff67a48 100644 --- a/ssa/emit.go +++ b/ssa/emit.go @@ -15,7 +15,7 @@ func emitNew(f *Function, typ types.Type, pos token.Pos) Value { return f.emit(&Alloc{ Type_: pointer(typ), Heap: true, - Pos: pos, + pos: pos, }) } @@ -24,7 +24,7 @@ func emitNew(f *Function, typ types.Type, pos token.Pos) Value { // func emitLoad(f *Function, addr Value) Value { v := &UnOp{Op: token.MUL, X: addr} - v.setType(indirectType(addr.Type())) + v.setType(addr.Type().Deref()) return f.emit(v) } @@ -60,8 +60,8 @@ func emitArith(f *Function, op token.Token, x, y Value, t types.Type) Value { // comparison comparison 'x op y'. // func emitCompare(f *Function, op token.Token, x, y Value) Value { - xt := underlyingType(x.Type()) - yt := underlyingType(y.Type()) + xt := x.Type().Underlying() + yt := y.Type().Underlying() // Special case to optimise a tagless SwitchStmt so that // these are equivalent @@ -71,7 +71,7 @@ func emitCompare(f *Function, op token.Token, x, y Value) Value { // even in the case when e's type is an interface. // TODO(adonovan): opt: generalise to x==true, false!=y, etc. if x == vTrue && op == token.EQL { - if yt, ok := yt.(*types.Basic); ok && yt.Info&types.IsBoolean != 0 { + if yt, ok := yt.(*types.Basic); ok && yt.Info()&types.IsBoolean != 0 { return y } } @@ -99,38 +99,55 @@ func emitCompare(f *Function, op token.Token, x, y Value) Value { return f.emit(v) } -// emitConv emits to f code to convert Value val to exactly type typ, -// and returns the converted value. Implicit conversions are implied -// by language assignability rules in the following operations: +// isValuePreserving returns true if a conversion from ut_src to +// ut_dst is value-preserving, i.e. just a change of type. +// Precondition: neither argument is a named type. // -// - from rvalue type to lvalue type in assignments. -// - from actual- to formal-parameter types in function calls. -// - from return value type to result type in return statements. -// - population of struct fields, array and slice elements, and map -// keys and values within compoisite literals -// - from index value to index type in indexing expressions. -// - for both arguments of comparisons. -// - from value type to channel type in send expressions. +func isValuePreserving(ut_src, ut_dst types.Type) bool { + // Identical underlying types? + if types.IsIdentical(ut_dst, ut_src) { + return true + } + + switch ut_dst.(type) { + case *types.Chan: + // Conversion between channel types? + _, ok := ut_src.(*types.Chan) + return ok + + case *types.Pointer: + // Conversion between pointers with identical base types? + _, ok := ut_src.(*types.Pointer) + return ok + + case *types.Signature: + // Conversion between f(T) function and (T) func f() method? + // TODO(adonovan): is this sound? Discuss with gri. + _, ok := ut_src.(*types.Signature) + return ok + } + return false +} + +// emitConv emits to f code to convert Value val to exactly type typ, +// and returns the converted value. Implicit conversions are required +// by language assignability rules in assignments, parameter passing, +// etc. // func emitConv(f *Function, val Value, typ types.Type) Value { - // fmt.Printf("emitConv %s -> %s, %T", val.Type(), typ, val) // debugging + t_src := val.Type() // Identical types? Conversion is a no-op. - if types.IsIdentical(val.Type(), typ) { + if types.IsIdentical(t_src, typ) { return val } - ut_dst := underlyingType(typ) - ut_src := underlyingType(val.Type()) + ut_dst := typ.Underlying() + ut_src := t_src.Underlying() - // Identical underlying types? Conversion is a name change. - if types.IsIdentical(ut_dst, ut_src) { - // TODO(adonovan): make this use a distinct - // instruction, ChangeType. This instruction must - // also cover the cases of channel type restrictions and - // conversions between pointers to identical base - // types. - c := &Conv{X: val} + // Just a change of type, but not value or representation? + if isValuePreserving(ut_src, ut_dst) { + c := &ChangeType{X: val} c.setType(typ) return f.emit(c) } @@ -150,13 +167,13 @@ func emitConv(f *Function, val Value, typ types.Type) Value { // Convert (non-nil) "untyped" literals to their default type. // TODO(gri): expose types.isUntyped(). - if t, ok := ut_src.(*types.Basic); ok && t.Info&types.IsUntyped != 0 { + if t, ok := ut_src.(*types.Basic); ok && t.Info()&types.IsUntyped != 0 { val = emitConv(f, val, DefaultType(ut_src)) } mi := &MakeInterface{ X: val, - Methods: f.Prog.MethodSet(val.Type()), + Methods: f.Prog.MethodSet(t_src), } mi.setType(typ) return f.emit(mi) @@ -172,7 +189,7 @@ func emitConv(f *Function, val Value, typ types.Type) Value { } // A representation-changing conversion. - c := &Conv{X: val} + c := &Convert{X: val} c.setType(typ) return f.emit(c) } @@ -183,7 +200,7 @@ func emitConv(f *Function, val Value, typ types.Type) Value { func emitStore(f *Function, addr, val Value) { f.emit(&Store{ Addr: addr, - Val: emitConv(f, val, indirectType(addr.Type())), + Val: emitConv(f, val, addr.Type().Deref()), }) } @@ -226,8 +243,8 @@ func emitExtract(f *Function, tuple Value, index int, typ types.Type) Value { // func emitTypeAssert(f *Function, x Value, t types.Type) Value { // Simplify infallible assertions. - txi := underlyingType(x.Type()).(*types.Interface) - if ti, ok := underlyingType(t).(*types.Interface); ok { + txi := x.Type().Underlying().(*types.Interface) + if ti, ok := t.Underlying().(*types.Interface); ok { if types.IsIdentical(ti, txi) { return x } @@ -256,10 +273,10 @@ func emitTypeTest(f *Function, x Value, t types.Type) Value { AssertedType: t, CommaOk: true, } - a.setType(&types.Result{Values: []*types.Var{ - {Name: "value", Type: t}, + a.setType(types.NewTuple( + types.NewVar(nil, "value", t), varOk, - }}) + )) return f.emit(a) } @@ -273,11 +290,12 @@ func emitTailCall(f *Function, call *Call) { for _, arg := range f.Params[1:] { call.Call.Args = append(call.Call.Args, arg) } - nr := len(f.Signature.Results) + tresults := f.Signature.Results() + nr := tresults.Len() if nr == 1 { - call.Type_ = f.Signature.Results[0].Type + call.Type_ = tresults.At(0).Type() } else { - call.Type_ = &types.Result{Values: f.Signature.Results} + call.Type_ = tresults } tuple := f.emit(call) var ret Ret @@ -287,8 +305,8 @@ func emitTailCall(f *Function, call *Call) { case 1: ret.Results = []Value{tuple} default: - for i, o := range call.Type().(*types.Result).Values { - v := emitExtract(f, tuple, i, o.Type) + for i := 0; i < nr; i++ { + v := emitExtract(f, tuple, i, tresults.At(i).Type()) // TODO(adonovan): in principle, this is required: // v = emitConv(f, o.Type, f.Signature.Results[i].Type) // but in practice emitTailCall is only used when diff --git a/ssa/example_test.go b/ssa/example_test.go new file mode 100644 index 0000000000..a7c6629215 --- /dev/null +++ b/ssa/example_test.go @@ -0,0 +1,62 @@ +package ssa_test + +import ( + "code.google.com/p/go.tools/ssa" + "fmt" + "go/ast" + "go/parser" + "os" +) + +// This example demonstrates the SSA builder. +func Example() { + const hello = ` +package main + +import "fmt" + +func main() { + fmt.Println("Hello, World!") +} +` + + // Construct a builder. Imports will be loaded as if by 'go build'. + builder := ssa.NewBuilder(&ssa.Context{Loader: ssa.MakeGoBuildLoader(nil)}) + + // Parse the input file. + file, err := parser.ParseFile(builder.Prog.Files, "hello.go", hello, parser.DeclarationErrors) + if err != nil { + fmt.Printf("Parsing failed: %s\n", err.Error()) + return + } + + // Create a "main" package containing one file. + mainPkg, err := builder.CreatePackage("main", []*ast.File{file}) + if err != nil { + fmt.Printf("Type-checking failed: %s\n", err.Error()) + return + } + + // Build SSA code for bodies of functions in mainPkg. + builder.BuildPackage(mainPkg) + + // Print out the package-level functions. + for _, mem := range mainPkg.Members { + if fn, ok := mem.(*ssa.Function); ok { + fn.DumpTo(os.Stdout) + } + } + + // Output: + // # Name: main.main + // # Declared at hello.go:6:6 + // func main(): + // .0.entry: P:0 S:0 + // a0 = new [1]interface{} *[1]interface{} + // t0 = &a0[0:untyped integer] *interface{} + // t1 = make interface interface{} <- string ("Hello, World!":string) interface{} + // *t0 = t1 + // t2 = slice a0[:] []interface{} + // t3 = fmt.Println(t2) (n int, err error) + // ret +} diff --git a/ssa/func.go b/ssa/func.go index 4b93cf7730..d16dd11982 100644 --- a/ssa/func.go +++ b/ssa/func.go @@ -179,11 +179,12 @@ func (f *Function) addParam(name string, typ types.Type) *Parameter { // Subsequent lifting will eliminate spills where possible. // func (f *Function) addSpilledParam(obj types.Object) { - name := obj.GetName() - param := f.addParam(name, obj.GetType()) + name := obj.Name() + param := f.addParam(name, obj.Type()) spill := &Alloc{ Name_: name + "~", // "~" means "spilled" - Type_: pointer(obj.GetType()), + Type_: pointer(obj.Type()), + pos: obj.Pos(), } f.objects[obj] = spill f.Locals = append(f.Locals, spill) @@ -210,7 +211,7 @@ func (f *Function) startBody() { // f.syntax != nil, i.e. this is a Go source function. // f.startBody() was called. // Postcondition: -// len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv ? 1 : 0) +// len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv() ? 1 : 0) // func (f *Function) createSyntacticParams(idents map[*ast.Ident]types.Object) { // Receiver (at most one inner iteration). @@ -221,8 +222,8 @@ func (f *Function) createSyntacticParams(idents map[*ast.Ident]types.Object) { } // Anonymous receiver? No need to spill. if field.Names == nil { - recvVar := f.Signature.Recv - f.addParam(recvVar.Name, recvVar.Type) + recvVar := f.Signature.Recv() + f.addParam(recvVar.Name(), recvVar.Type()) } } } @@ -236,8 +237,8 @@ func (f *Function) createSyntacticParams(idents map[*ast.Ident]types.Object) { } // Anonymous parameter? No need to spill. if field.Names == nil { - paramVar := f.Signature.Params[len(f.Params)-n] - f.addParam(paramVar.Name, paramVar.Type) + paramVar := f.Signature.Params().At(len(f.Params) - n) + f.addParam(paramVar.Name(), paramVar.Type()) } } } @@ -371,8 +372,8 @@ func (f *Function) removeNilBlocks() { // Precondition: f.syntax != nil (i.e. a Go source function). // func (f *Function) addNamedLocal(obj types.Object) *Alloc { - l := f.addLocal(obj.GetType(), obj.GetPos()) - l.Name_ = obj.GetName() + l := f.addLocal(obj.Type(), obj.Pos()) + l.Name_ = obj.Name() f.objects[obj] = l return l } @@ -381,7 +382,7 @@ func (f *Function) addNamedLocal(obj types.Object) *Alloc { // to function f and returns it. pos is the optional source location. // func (f *Function) addLocal(typ types.Type, pos token.Pos) *Alloc { - v := &Alloc{Type_: pointer(typ), Pos: pos} + v := &Alloc{Type_: pointer(typ), pos: pos} f.Locals = append(f.Locals, v) f.emit(v) return v @@ -414,7 +415,7 @@ func (f *Function) lookup(obj types.Object, escaping bool) Value { // Definition must be in an enclosing function; // plumb it through intervening closures. if f.Enclosing == nil { - panic("no Value for type.Object " + obj.GetName()) + panic("no Value for type.Object " + obj.Name()) } v := &Capture{Outer: f.Enclosing.lookup(obj, true)} // escaping f.objects[obj] = v @@ -453,13 +454,13 @@ func (f *Function) fullName(from *Package) string { return f.Name_ } - recv := f.Signature.Recv + recv := f.Signature.Recv() // Synthetic? if f.Pkg == nil { var recvType types.Type if recv != nil { - recvType = recv.Type // bridge method + recvType = recv.Type() // bridge method } else { recvType = f.Params[0].Type() // interface method thunk } @@ -468,13 +469,13 @@ func (f *Function) fullName(from *Package) string { // Declared method? if recv != nil { - return fmt.Sprintf("(%s).%s", recv.Type, f.Name_) + return fmt.Sprintf("(%s).%s", recv.Type(), f.Name_) } // Package-level function. // Prefix with package name for cross-package references only. if from != f.Pkg { - return fmt.Sprintf("%s.%s", f.Pkg.Types.Path, f.Name_) + return fmt.Sprintf("%s.%s", f.Pkg.Types.Path(), f.Name_) } return f.Name_ } @@ -484,7 +485,7 @@ func (f *Function) fullName(from *Package) string { // func writeSignature(w io.Writer, name string, sig *types.Signature, params []*Parameter) { io.WriteString(w, "func ") - if sig.Recv != nil { + if recv := sig.Recv(); recv != nil { io.WriteString(w, "(") if n := params[0].Name(); n != "" { io.WriteString(w, n) @@ -502,23 +503,22 @@ func writeSignature(w io.Writer, name string, sig *types.Signature, params []*Pa } io.WriteString(w, v.Name()) io.WriteString(w, " ") - if sig.IsVariadic && i == len(params)-1 { + if sig.IsVariadic() && i == len(params)-1 { io.WriteString(w, "...") - io.WriteString(w, underlyingType(v.Type()).(*types.Slice).Elt.String()) + io.WriteString(w, v.Type().Underlying().(*types.Slice).Elem().String()) } else { io.WriteString(w, v.Type().String()) } } io.WriteString(w, ")") - if res := sig.Results; res != nil { + if n := sig.Results().Len(); n > 0 { io.WriteString(w, " ") - var t types.Type - if len(res) == 1 && res[0].Name == "" { - t = res[0].Type + r := sig.Results() + if n == 1 && r.At(0).Name() == "" { + io.WriteString(w, r.At(0).Type().String()) } else { - t = &types.Result{Values: res} + io.WriteString(w, r.String()) } - io.WriteString(w, t.String()) } } @@ -527,7 +527,7 @@ func writeSignature(w io.Writer, name string, sig *types.Signature, params []*Pa // func (f *Function) DumpTo(w io.Writer) { fmt.Fprintf(w, "# Name: %s\n", f.FullName()) - fmt.Fprintf(w, "# Declared at %s\n", f.Prog.Files.Position(f.Pos)) + fmt.Fprintf(w, "# Declared at %s\n", f.Prog.Files.Position(f.Pos())) if f.Enclosing != nil { fmt.Fprintf(w, "# Parent: %s\n", f.Enclosing.Name()) @@ -543,7 +543,7 @@ func (f *Function) DumpTo(w io.Writer) { if len(f.Locals) > 0 { io.WriteString(w, "# Locals:\n") for i, l := range f.Locals { - fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, l.Name(), indirectType(l.Type())) + fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, l.Name(), l.Type().Deref()) } } diff --git a/ssa/importer.go b/ssa/importer.go index d6b01061df..0e1ebc2f09 100644 --- a/ssa/importer.go +++ b/ssa/importer.go @@ -53,7 +53,7 @@ func (b *Builder) doImport(imports map[string]*types.Package, path string) (typk } else { files, err = b.Context.Loader(b.Prog.Files, path) if err == nil { - typkg, info, err = b.typecheck(files) + typkg, info, err = b.typecheck(path, files) } } if err != nil { @@ -69,28 +69,35 @@ func (b *Builder) doImport(imports map[string]*types.Package, path string) (typk return typkg, nil } -// GorootLoader is an implementation of the SourceLoader function -// prototype that loads and parses Go source files from the package -// directory beneath $GOROOT/src/pkg. +// MakeGoBuildLoader returns an implementation of the SourceLoader +// function prototype that locates packages using the go/build +// libraries. It may return nil upon gross misconfiguration +// (e.g. os.Getwd() failed). // -// TODO(adonovan): get rsc and adg (go/build owners) to review this. -// TODO(adonovan): permit clients to specify a non-default go/build.Context. +// ctxt specifies the go/build.Context to use; if nil, the default +// Context is used. // -func GorootLoader(fset *token.FileSet, path string) (files []*ast.File, err error) { - // TODO(adonovan): fix: Do we need cwd? Shouldn't ImportDir(path) / $GOROOT suffice? +func MakeGoBuildLoader(ctxt *build.Context) SourceLoader { srcDir, err := os.Getwd() if err != nil { - return // serious misconfiguration + return nil // serious misconfiguration } - bp, err := build.Import(path, srcDir, 0) - if err != nil { - return // import failed + if ctxt == nil { + ctxt = &build.Default } - files, err = ParseFiles(fset, bp.Dir, bp.GoFiles...) - if err != nil { - return nil, err + return func(fset *token.FileSet, path string) (files []*ast.File, err error) { + // TODO(adonovan): fix: Do we need cwd? Shouldn't + // ImportDir(path) / $GOROOT suffice? + bp, err := ctxt.Import(path, srcDir, 0) + if err != nil { + return // import failed + } + files, err = ParseFiles(fset, bp.Dir, bp.GoFiles...) + if err != nil { + return nil, err + } + return } - return } // ParseFiles parses the Go source files files within directory dir diff --git a/ssa/interp/interp.go b/ssa/interp/interp.go index ad6db3ab54..c428237438 100644 --- a/ssa/interp/interp.go +++ b/ssa/interp/interp.go @@ -158,14 +158,17 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation { case *ssa.Call: fn, args := prepareCall(fr, &instr.Call) - fr.env[instr] = call(fr.i, fr, instr.Call.Pos, fn, args) - - case *ssa.Conv: - fr.env[instr] = conv(instr.Type(), instr.X.Type(), fr.get(instr.X)) + fr.env[instr] = call(fr.i, fr, instr.Pos(), fn, args) case *ssa.ChangeInterface: fr.env[instr] = fr.get(instr.X) // (can't fail) + case *ssa.ChangeType: + fr.env[instr] = fr.get(instr.X) // (can't fail) + + case *ssa.Convert: + fr.env[instr] = conv(instr.Type(), instr.X.Type(), fr.get(instr.X)) + case *ssa.MakeInterface: fr.env[instr] = iface{t: instr.X.Type(), v: fr.get(instr.X)} @@ -214,13 +217,13 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation { return kJump case *ssa.Defer: - pos := instr.Call.Pos // TODO(gri): workaround for go/types bug in typeswitch+funclit. + pos := instr.Pos() // TODO(gri): workaround for go/types bug in typeswitch+funclit. fn, args := prepareCall(fr, &instr.Call) fr.defers = append(fr.defers, func() { call(fr.i, fr, pos, fn, args) }) case *ssa.Go: fn, args := prepareCall(fr, &instr.Call) - go call(fr.i, nil, instr.Call.Pos, fn, args) + go call(fr.i, nil, instr.Pos(), fn, args) case *ssa.MakeChan: fr.env[instr] = make(chan value, asInt(fr.get(instr.Size))) @@ -235,11 +238,11 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation { // local addr = fr.env[instr].(*value) } - *addr = zero(indirectType(instr.Type())) + *addr = zero(instr.Type().Deref()) case *ssa.MakeSlice: slice := make([]value, asInt(fr.get(instr.Cap))) - tElt := underlyingType(instr.Type()).(*types.Slice).Elt + tElt := instr.Type().Underlying().(*types.Slice).Elem() for i := range slice { slice[i] = zero(tElt) } @@ -250,7 +253,7 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation { if instr.Reserve != nil { reserve = asInt(fr.get(instr.Reserve)) } - fr.env[instr] = makeMap(underlyingType(instr.Type()).(*types.Map).Key, reserve) + fr.env[instr] = makeMap(instr.Type().Underlying().(*types.Map).Key(), reserve) case *ssa.Range: fr.env[instr] = rangeIter(fr.get(instr.X), instr.X.Type()) @@ -344,7 +347,7 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation { } var recvV iface if chosen != -1 { - recvV.t = underlyingType(instr.States[chosen].Chan.Type()).(*types.Chan).Elt + recvV.t = instr.States[chosen].Chan.Type().Underlying().(*types.Chan).Elem() if recvOk { // No need to copy since send makes an unaliased copy. recvV.v = recv.Interface().(value) @@ -385,8 +388,8 @@ func prepareCall(fr *frame, call *ssa.CallCommon) (fn value, args []value) { // Unreachable in well-typed programs. panic(fmt.Sprintf("method set for dynamic type %v does not contain %s", recv.t, id)) } - _, aptr := recv.v.(*value) // actual pointerness - _, fptr := m.Signature.Recv.Type.(*types.Pointer) // formal pointerness + _, aptr := recv.v.(*value) // actual pointerness + _, fptr := m.Signature.Recv().Type().(*types.Pointer) // formal pointerness switch { case aptr == fptr: args = append(args, copyVal(recv.v)) @@ -438,7 +441,7 @@ func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function, if i.mode&EnableTracing != 0 { fset := fn.Prog.Files // TODO(adonovan): fix: loc() lies for external functions. - fmt.Fprintf(os.Stderr, "Entering %s%s.\n", fn.FullName(), loc(fset, fn.Pos)) + fmt.Fprintf(os.Stderr, "Entering %s%s.\n", fn.FullName(), loc(fset, fn.Pos())) suffix := "" if caller != nil { suffix = ", resuming " + caller.fn.FullName() + loc(fset, callpos) @@ -466,7 +469,7 @@ func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function, locals: make([]value, len(fn.Locals)), } for i, l := range fn.Locals { - fr.locals[i] = zero(indirectType(l.Type())) + fr.locals[i] = zero(l.Type().Deref()) fr.env[l] = &fr.locals[i] } for i, p := range fn.Params { @@ -550,7 +553,7 @@ func Interpret(mainpkg *ssa.Package, mode Mode, filename string, args []string) for _, m := range pkg.Members { switch v := m.(type) { case *ssa.Global: - cell := zero(indirectType(v.Type())) + cell := zero(v.Type().Deref()) i.globals[v] = &cell } } diff --git a/ssa/interp/interp_test.go b/ssa/interp/interp_test.go index 678381671e..3b9c3f89f1 100644 --- a/ssa/interp/interp_test.go +++ b/ssa/interp/interp_test.go @@ -140,7 +140,10 @@ func run(t *testing.T, dir, input string) bool { inputs = append(inputs, dir+i) } - b := ssa.NewBuilder(&ssa.Context{Mode: ssa.SanityCheckFunctions, Loader: ssa.GorootLoader}) + b := ssa.NewBuilder(&ssa.Context{ + Mode: ssa.SanityCheckFunctions, + Loader: ssa.MakeGoBuildLoader(nil), + }) files, err := ssa.ParseFiles(b.Prog.Files, ".", inputs...) if err != nil { t.Errorf("ssa.ParseFiles(%s) failed: %s", inputs, err.Error()) diff --git a/ssa/interp/ops.go b/ssa/interp/ops.go index d0b31af385..49c8b8484b 100644 --- a/ssa/interp/ops.go +++ b/ssa/interp/ops.go @@ -3,7 +3,6 @@ package interp import ( "fmt" "go/token" - "os" "runtime" "strings" "unsafe" @@ -29,9 +28,9 @@ func literalValue(l *ssa.Literal) value { } // By destination type: - switch t := underlyingType(l.Type()).(type) { + switch t := l.Type().Underlying().(type) { case *types.Basic: - switch t.Kind { + switch t.Kind() { case types.Bool, types.UntypedBool: return exact.BoolVal(l.Value) case types.Int, types.UntypedInt: @@ -79,9 +78,9 @@ func literalValue(l *ssa.Literal) value { } case *types.Slice: - switch et := underlyingType(t.Elt).(type) { + switch et := t.Elem().Underlying().(type) { case *types.Basic: - switch et.Kind { + switch et.Kind() { case types.Byte: // string -> []byte var v []value for _, b := range []byte(exact.StringVal(l.Value)) { @@ -155,13 +154,13 @@ func asUint64(x value) uint64 { func zero(t types.Type) value { switch t := t.(type) { case *types.Basic: - if t.Kind == types.UntypedNil { + if t.Kind() == types.UntypedNil { panic("untyped nil has no zero value") } - if t.Info&types.IsUntyped != 0 { + if t.Info()&types.IsUntyped != 0 { t = ssa.DefaultType(t).(*types.Basic) } - switch t.Kind { + switch t.Kind() { case types.Bool: return false case types.Int: @@ -204,27 +203,27 @@ func zero(t types.Type) value { case *types.Pointer: return (*value)(nil) case *types.Array: - a := make(array, t.Len) + a := make(array, t.Len()) for i := range a { - a[i] = zero(t.Elt) + a[i] = zero(t.Elem()) } return a - case *types.NamedType: - return zero(t.Underlying) + case *types.Named: + return zero(t.Underlying()) case *types.Interface: return iface{} // nil type, methodset and value case *types.Slice: return []value(nil) case *types.Struct: - s := make(structure, len(t.Fields)) + s := make(structure, t.NumFields()) for i := range s { - s[i] = zero(t.Fields[i].Type) + s[i] = zero(t.Field(i).Type) } return s case *types.Chan: return chan value(nil) case *types.Map: - if usesBuiltinMap(t.Key) { + if usesBuiltinMap(t.Key()) { return map[value]value(nil) } return (*hashmap)(nil) @@ -277,7 +276,7 @@ func lookup(instr *ssa.Lookup, x, idx value) value { if ok { v = copyVal(v) } else { - v = zero(underlyingType(instr.X.Type()).(*types.Map).Elt) + v = zero(instr.X.Type().Underlying().(*types.Map).Elem()) } if instr.CommaOk { v = tuple{v, ok} @@ -759,7 +758,7 @@ func unop(instr *ssa.UnOp, x value) value { case token.ARROW: // receive v, ok := <-x.(chan value) if !ok { - v = zero(underlyingType(instr.X.Type()).(*types.Chan).Elt) + v = zero(instr.X.Type().Underlying().(*types.Chan).Elem()) } if instr.CommaOk { v = tuple{v, ok} @@ -834,7 +833,7 @@ func unop(instr *ssa.UnOp, x value) value { func typeAssert(i *interpreter, instr *ssa.TypeAssert, itf iface) value { var v value err := "" - if idst, ok := underlyingType(instr.AssertedType).(*types.Interface); ok { + if idst, ok := instr.AssertedType.Underlying().(*types.Interface); ok { v = itf err = checkInterface(i, idst, itf) @@ -1089,31 +1088,24 @@ func widen(x value) value { } // conv converts the value x of type t_src to type t_dst and returns -// the result. Possible cases are described with the ssa.Conv -// operator. Panics if the dynamic conversion fails. +// the result. +// Possible cases are described with the ssa.Convert operator. // func conv(t_dst, t_src types.Type, x value) value { - ut_src := underlyingType(t_src) - ut_dst := underlyingType(t_dst) - - // Same underlying types? - // TODO(adonovan): consider a dedicated ssa.ChangeType instruction. - // TODO(adonovan): fix: what about channels of different direction? - if types.IsIdentical(ut_dst, ut_src) { - return x - } + ut_src := t_src.Underlying() + ut_dst := t_dst.Underlying() // Destination type is not an "untyped" type. - if b, ok := ut_dst.(*types.Basic); ok && b.Info&types.IsUntyped != 0 { - panic("conversion to 'untyped' type: " + b.String()) + if b, ok := ut_dst.(*types.Basic); ok && b.Info()&types.IsUntyped != 0 { + panic("oops: conversion to 'untyped' type: " + b.String()) } // Nor is it an interface type. if _, ok := ut_dst.(*types.Interface); ok { if _, ok := ut_src.(*types.Interface); ok { - panic("oops: Conv should be ChangeInterface") + panic("oops: Convert should be ChangeInterface") } else { - panic("oops: Conv should be MakeInterface") + panic("oops: Convert should be MakeInterface") } } @@ -1126,34 +1118,23 @@ func conv(t_dst, t_src types.Type, x value) value { // + string -> []byte/[]rune. // // All are treated the same: first we extract the value to the - // widest representation (bool, int64, uint64, float64, - // complex128, or string), then we convert it to the desired - // type. + // widest representation (int64, uint64, float64, complex128, + // or string), then we convert it to the desired type. switch ut_src := ut_src.(type) { - case *types.Signature: - // TODO(adonovan): fix: this is a hacky workaround for the - // unsound conversion of Signature types from - // func(T)() to func()(T), i.e. arg0 <-> receiver - // conversion. Talk to gri about correct approach. - fmt.Fprintln(os.Stderr, "Warning: unsound Signature conversion") - return x - case *types.Pointer: switch ut_dst := ut_dst.(type) { case *types.Basic: // *value to unsafe.Pointer? - if ut_dst.Kind == types.UnsafePointer { + if ut_dst.Kind() == types.UnsafePointer { return unsafe.Pointer(x.(*value)) } - case *types.Pointer: - return x } case *types.Slice: // []byte or []rune -> string // TODO(adonovan): fix: type B byte; conv([]B -> string). - switch ut_src.Elt.(*types.Basic).Kind { + switch ut_src.Elem().(*types.Basic).Kind() { case types.Byte: x := x.([]value) b := make([]byte, 0, len(x)) @@ -1174,15 +1155,10 @@ func conv(t_dst, t_src types.Type, x value) value { case *types.Basic: x = widen(x) - // bool? - if _, ok := x.(bool); ok { - return x - } - // integer -> string? // TODO(adonovan): fix: test integer -> named alias of string. - if ut_src.Info&types.IsInteger != 0 { - if ut_dst, ok := ut_dst.(*types.Basic); ok && ut_dst.Kind == types.String { + if ut_src.Info()&types.IsInteger != 0 { + if ut_dst, ok := ut_dst.(*types.Basic); ok && ut_dst.Kind() == types.String { return string(asInt(x)) } } @@ -1193,7 +1169,7 @@ func conv(t_dst, t_src types.Type, x value) value { case *types.Slice: var res []value // TODO(adonovan): fix: test named alias of rune, byte. - switch ut_dst.Elt.(*types.Basic).Kind { + switch ut_dst.Elem().(*types.Basic).Kind() { case types.Rune: for _, r := range []rune(s) { res = append(res, r) @@ -1206,7 +1182,7 @@ func conv(t_dst, t_src types.Type, x value) value { return res } case *types.Basic: - if ut_dst.Kind == types.String { + if ut_dst.Kind() == types.String { return x.(string) } } @@ -1214,7 +1190,7 @@ func conv(t_dst, t_src types.Type, x value) value { } // unsafe.Pointer -> *value - if ut_src.Kind == types.UnsafePointer { + if ut_src.Kind() == types.UnsafePointer { // TODO(adonovan): this is wrong and cannot // really be fixed with the current design. // @@ -1230,8 +1206,8 @@ func conv(t_dst, t_src types.Type, x value) value { } // Conversions between complex numeric types? - if ut_src.Info&types.IsComplex != 0 { - switch ut_dst.(*types.Basic).Kind { + if ut_src.Info()&types.IsComplex != 0 { + switch ut_dst.(*types.Basic).Kind() { case types.Complex64: return complex64(x.(complex128)) case types.Complex128: @@ -1241,8 +1217,8 @@ func conv(t_dst, t_src types.Type, x value) value { } // Conversions between non-complex numeric types? - if ut_src.Info&types.IsNumeric != 0 { - kind := ut_dst.(*types.Basic).Kind + if ut_src.Info()&types.IsNumeric != 0 { + kind := ut_dst.(*types.Basic).Kind() switch x := x.(type) { case int64: // signed integer -> numeric? switch kind { @@ -1344,32 +1320,15 @@ func conv(t_dst, t_src types.Type, x value) value { // interface itype. // On success it returns "", on failure, an error message. // -func checkInterface(i *interpreter, itype types.Type, x iface) string { +func checkInterface(i *interpreter, itype *types.Interface, x iface) string { mset := findMethodSet(i, x.t) - for _, m := range underlyingType(itype).(*types.Interface).Methods { - id := ssa.IdFromQualifiedName(m.QualifiedName) + it := itype.Underlying().(*types.Interface) + for i, n := 0, it.NumMethods(); i < n; i++ { + m := it.Method(i) + id := ssa.MakeId(m.Name(), m.Pkg()) if mset[id] == nil { return fmt.Sprintf("interface conversion: %v is not %v: missing method %v", x.t, itype, id) } } return "" // ok } - -// underlyingType returns the underlying type of typ. -// Copied from go/types.underlying. -// -func underlyingType(typ types.Type) types.Type { - if typ, ok := typ.(*types.NamedType); ok { - return typ.Underlying - } - return typ -} - -// indirectType(typ) assumes that typ is a pointer type, -// or named alias thereof, and returns its base type. -// Panic ensues if it is not a pointer. -// Copied from exp/ssa.indirectType. -// -func indirectType(ptr types.Type) types.Type { - return underlyingType(ptr).(*types.Pointer).Base -} diff --git a/ssa/interp/reflect.go b/ssa/interp/reflect.go index fd25c39321..4c3008614e 100644 --- a/ssa/interp/reflect.go +++ b/ssa/interp/reflect.go @@ -15,19 +15,22 @@ import ( "code.google.com/p/go.tools/ssa" ) -// A bogus "reflect" type-checker package. Shared across interpreters. -var reflectTypesPackage = &types.Package{ - Name: "reflect", - Path: "reflect", - Complete: true, +type opaqueType struct { + types.Type + name string } +func (t *opaqueType) String() string { return t.name } + +// A bogus "reflect" type-checker package. Shared across interpreters. +var reflectTypesPackage = types.NewPackage("reflect", "reflect") + // rtype is the concrete type the interpreter uses to implement the // reflect.Type interface. Since its type is opaque to the target // language, we use a types.Basic. // // type rtype -var rtypeType = makeNamedType("rtype", &types.Basic{Name: "rtype"}) +var rtypeType = makeNamedType("rtype", &opaqueType{nil, "rtype"}) // error is an (interpreted) named type whose underlying type is string. // The interpreter uses it for all implementations of the built-in error @@ -35,16 +38,11 @@ var rtypeType = makeNamedType("rtype", &types.Basic{Name: "rtype"}) // We put it in the "reflect" package for expedience. // // type error string -var errorType = makeNamedType("error", &types.Basic{Name: "error"}) +var errorType = makeNamedType("error", &opaqueType{nil, "error"}) -func makeNamedType(name string, underlying types.Type) *types.NamedType { - nt := &types.NamedType{Underlying: underlying} - nt.Obj = &types.TypeName{ - Name: name, - Type: nt, - Pkg: reflectTypesPackage, - } - return nt +func makeNamedType(name string, underlying types.Type) *types.Named { + obj := types.NewTypeName(reflectTypesPackage, name, nil) + return types.NewNamed(obj, underlying, types.ObjSet{}) } func makeReflectValue(t types.Type, v value) value { @@ -74,11 +72,11 @@ func ext۰reflect۰Init(fn *ssa.Function, args []value) value { func ext۰reflect۰rtype۰Bits(fn *ssa.Function, args []value) value { // Signature: func (t reflect.rtype) int rt := args[0].(rtype).t - basic, ok := underlyingType(rt).(*types.Basic) + basic, ok := rt.Underlying().(*types.Basic) if !ok { panic(fmt.Sprintf("reflect.Type.Bits(%T): non-basic type", rt)) } - switch basic.Kind { + switch basic.Kind() { case types.Int8, types.Uint8: return 8 case types.Int16, types.Uint16: @@ -109,22 +107,9 @@ func ext۰reflect۰rtype۰Bits(fn *ssa.Function, args []value) value { func ext۰reflect۰rtype۰Elem(fn *ssa.Function, args []value) value { // Signature: func (t reflect.rtype) reflect.Type - var elem types.Type - switch rt := underlyingType(args[0].(rtype).t).(type) { - case *types.Array: - elem = rt.Elt - case *types.Chan: - elem = rt.Elt - case *types.Map: - elem = rt.Elt - case *types.Pointer: - elem = rt.Base - case *types.Slice: - elem = rt.Elt - default: - panic(fmt.Sprintf("reflect.Type.Elem(%T)", rt)) - } - return makeReflectType(rtype{elem}) + return makeReflectType(rtype{args[0].(rtype).t.Underlying().(interface { + Elem() types.Type + }).Elem()}) } func ext۰reflect۰rtype۰Kind(fn *ssa.Function, args []value) value { @@ -134,13 +119,13 @@ func ext۰reflect۰rtype۰Kind(fn *ssa.Function, args []value) value { func ext۰reflect۰rtype۰NumOut(fn *ssa.Function, args []value) value { // Signature: func (t reflect.rtype) int - return len(args[0].(rtype).t.(*types.Signature).Results) + return args[0].(rtype).t.(*types.Signature).Results().Len() } func ext۰reflect۰rtype۰Out(fn *ssa.Function, args []value) value { // Signature: func (t reflect.rtype, i int) int i := args[1].(int) - return makeReflectType(rtype{args[0].(rtype).t.(*types.Signature).Results[i].Type}) + return makeReflectType(rtype{args[0].(rtype).t.(*types.Signature).Results().At(i).Type()}) } func ext۰reflect۰rtype۰String(fn *ssa.Function, args []value) value { @@ -161,10 +146,10 @@ func ext۰reflect۰ValueOf(fn *ssa.Function, args []value) value { func reflectKind(t types.Type) reflect.Kind { switch t := t.(type) { - case *types.NamedType: - return reflectKind(t.Underlying) + case *types.Named: + return reflectKind(t.Underlying()) case *types.Basic: - switch t.Kind { + switch t.Kind() { case types.Bool: return reflect.Bool case types.Int: @@ -287,12 +272,12 @@ func ext۰reflect۰Value۰Pointer(fn *ssa.Function, args []value) value { func ext۰reflect۰Value۰Index(fn *ssa.Function, args []value) value { // Signature: func (v reflect.Value, i int) Value i := args[1].(int) - t := underlyingType(rV2T(args[0]).t) + t := rV2T(args[0]).t.Underlying() switch v := rV2V(args[0]).(type) { case array: - return makeReflectValue(t.(*types.Array).Elt, v[i]) + return makeReflectValue(t.(*types.Array).Elem(), v[i]) case []value: - return makeReflectValue(t.(*types.Slice).Elt, v[i]) + return makeReflectValue(t.(*types.Slice).Elem(), v[i]) default: panic(fmt.Sprintf("reflect.(Value).Index(%T)", v)) } @@ -322,7 +307,7 @@ func ext۰reflect۰Value۰Elem(fn *ssa.Function, args []value) value { case iface: return makeReflectValue(x.t, x.v) case *value: - return makeReflectValue(underlyingType(rV2T(args[0]).t).(*types.Pointer).Base, *x) + return makeReflectValue(rV2T(args[0]).t.Underlying().(*types.Pointer).Elem(), *x) default: panic(fmt.Sprintf("reflect.(Value).Elem(%T)", x)) } @@ -333,7 +318,7 @@ func ext۰reflect۰Value۰Field(fn *ssa.Function, args []value) value { // Signature: func (v reflect.Value, i int) reflect.Value v := args[0] i := args[1].(int) - return makeReflectValue(underlyingType(rV2T(v).t).(*types.Struct).Fields[i].Type, rV2V(v).(structure)[i]) + return makeReflectValue(rV2T(v).t.Underlying().(*types.Struct).Field(i).Type, rV2V(v).(structure)[i]) } func ext۰reflect۰Value۰Interface(fn *ssa.Function, args []value) value { @@ -413,10 +398,7 @@ func newMethod(pkg *ssa.Package, recvType types.Type, name string) *ssa.Function // that is needed is the "pointerness" of Recv.Type, and for // now, we'll set it to always be false since we're only // concerned with rtype. Encapsulate this better. - fn.Signature = &types.Signature{Recv: &types.Var{ - Name: "recv", - Type: recvType, - }} + fn.Signature = types.NewSignature(types.NewVar(nil, "recv", recvType), nil, nil, false) return fn } diff --git a/ssa/interp/value.go b/ssa/interp/value.go index f66392c6d5..3c2542ab88 100644 --- a/ssa/interp/value.go +++ b/ssa/interp/value.go @@ -103,8 +103,8 @@ func usesBuiltinMap(t types.Type) bool { switch t := t.(type) { case *types.Basic, *types.Chan, *types.Pointer: return true - case *types.NamedType: - return usesBuiltinMap(t.Underlying) + case *types.Named: + return usesBuiltinMap(t.Underlying()) case *types.Interface, *types.Array, *types.Struct: return false } diff --git a/ssa/lift.go b/ssa/lift.go index 62f99931c9..bc836a4a76 100644 --- a/ssa/lift.go +++ b/ssa/lift.go @@ -306,7 +306,7 @@ type newPhiMap map[*BasicBlock][]newPhi func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool { // Don't lift aggregates into registers. // We'll need a separate SRA pass for that. - switch underlyingType(indirectType(alloc.Type())).(type) { + switch alloc.Type().Deref().Underlying().(type) { case *types.Array, *types.Struct: return false } @@ -374,7 +374,7 @@ func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool { Edges: make([]Value, len(v.Preds)), Comment: alloc.Name(), } - phi.setType(indirectType(alloc.Type())) + phi.setType(alloc.Type().Deref()) phi.Block_ = v if debugLifting { fmt.Fprintf(os.Stderr, "place %s = %s at block %s\n", phi.Name(), phi, v) @@ -421,7 +421,7 @@ func replaceAll(x, y Value) { func renamed(renaming []Value, alloc *Alloc) Value { v := renaming[alloc.index] if v == nil { - v = zeroLiteral(indirectType(alloc.Type())) + v = zeroLiteral(alloc.Type().Deref()) renaming[alloc.index] = v } return v diff --git a/ssa/literal.go b/ssa/literal.go index d34d43da26..ad07ad4bb5 100644 --- a/ssa/literal.go +++ b/ssa/literal.go @@ -39,23 +39,23 @@ func zeroLiteral(t types.Type) *Literal { switch t := t.(type) { case *types.Basic: switch { - case t.Info&types.IsBoolean != 0: + case t.Info()&types.IsBoolean != 0: return newLiteral(exact.MakeBool(false), t) - case t.Info&types.IsNumeric != 0: + case t.Info()&types.IsNumeric != 0: return newLiteral(exact.MakeInt64(0), t) - case t.Info&types.IsString != 0: + case t.Info()&types.IsString != 0: return newLiteral(exact.MakeString(""), t) - case t.Kind == types.UnsafePointer: + case t.Kind() == types.UnsafePointer: fallthrough - case t.Kind == types.UntypedNil: + case t.Kind() == types.UntypedNil: return nilLiteral(t) default: panic(fmt.Sprint("zeroLiteral for unexpected type:", t)) } case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature: return nilLiteral(t) - case *types.NamedType: - return newLiteral(zeroLiteral(t.Underlying).Value, t) + case *types.Named: + return newLiteral(zeroLiteral(t.Underlying()).Value, t) case *types.Array, *types.Struct: panic(fmt.Sprint("zeroLiteral applied to aggregate:", t)) } @@ -63,13 +63,16 @@ func zeroLiteral(t types.Type) *Literal { } func (l *Literal) Name() string { - s := l.Value.String() + var s string if l.Value.Kind() == exact.String { - const n = 20 - if len(s) > n { - s = s[:n-3] + "..." // abbreviate + s = exact.StringVal(l.Value) + const maxLit = 20 + if len(s) > maxLit { + s = s[:maxLit-3] + "..." // abbreviate } s = strconv.Quote(s) + } else { + s = l.Value.String() } return s + ":" + l.Type_.String() } diff --git a/ssa/lvalue.go b/ssa/lvalue.go index 358deeae26..1f5b4cfb6c 100644 --- a/ssa/lvalue.go +++ b/ssa/lvalue.go @@ -31,7 +31,7 @@ func (a address) store(fn *Function, v Value) { } func (a address) typ() types.Type { - return indirectType(a.addr.Type()) + return a.addr.Type().Deref() } // An element is an lvalue represented by m[k], the location of an diff --git a/ssa/print.go b/ssa/print.go index e950c77908..f92add1b84 100644 --- a/ssa/print.go +++ b/ssa/print.go @@ -48,7 +48,7 @@ func relName(v Value, i Instruction) string { // It never appears in disassembly, which uses Value.Name(). func (v *Literal) String() string { - return fmt.Sprintf("literal %s rep=%T", v.Name(), v.Value) + return fmt.Sprintf("literal %s", v.Name()) } func (v *Parameter) String() string { @@ -73,7 +73,7 @@ func (v *Function) String() string { // FullName returns g's package-qualified name. func (g *Global) FullName() string { - return fmt.Sprintf("%s.%s", g.Pkg.Types.Path, g.Name_) + return fmt.Sprintf("%s.%s", g.Pkg.Types.Path(), g.Name_) } // Instruction.String() @@ -83,7 +83,7 @@ func (v *Alloc) String() string { if v.Heap { op = "new" } - return fmt.Sprintf("%s %s", op, indirectType(v.Type())) + return fmt.Sprintf("%s %s", op, v.Type().Deref()) } func (v *Phi) String() string { @@ -120,7 +120,7 @@ func printCall(v *CallCommon, prefix string, instr Instruction) string { if !v.IsInvoke() { b.WriteString(relName(v.Func, instr)) } else { - name := underlyingType(v.Recv.Type()).(*types.Interface).Methods[v.Method].Name + name := v.Recv.Type().Underlying().(*types.Interface).Method(v.Method).Name() fmt.Fprintf(&b, "invoke %s.%s [#%d]", relName(v.Recv, instr), name, v.Method) } b.WriteString("(") @@ -145,6 +145,10 @@ func (v *Call) String() string { return printCall(&v.Call, "", v) } +func (v *ChangeType) String() string { + return fmt.Sprintf("changetype %s <- %s (%s)", v.Type(), v.X.Type(), relName(v.X, v)) +} + func (v *BinOp) String() string { return fmt.Sprintf("%s %s %s", relName(v.X, v), v.Op.String(), relName(v.Y, v)) } @@ -153,7 +157,7 @@ func (v *UnOp) String() string { return fmt.Sprintf("%s%s%s", v.Op, relName(v.X, v), commaOk(v.CommaOk)) } -func (v *Conv) String() string { +func (v *Convert) String() string { return fmt.Sprintf("convert %s <- %s (%s)", v.Type(), v.X.Type(), relName(v.X, v)) } @@ -221,21 +225,21 @@ func (v *MakeChan) String() string { } func (v *FieldAddr) String() string { - fields := underlyingType(indirectType(v.X.Type())).(*types.Struct).Fields + st := v.X.Type().Deref().Underlying().(*types.Struct) // Be robust against a bad index. name := "?" - if v.Field >= 0 && v.Field < len(fields) { - name = fields[v.Field].Name + if 0 <= v.Field && v.Field < st.NumFields() { + name = st.Field(v.Field).Name } return fmt.Sprintf("&%s.%s [#%d]", relName(v.X, v), name, v.Field) } func (v *Field) String() string { - fields := underlyingType(v.X.Type()).(*types.Struct).Fields + st := v.X.Type().Underlying().(*types.Struct) // Be robust against a bad index. name := "?" - if v.Field >= 0 && v.Field < len(fields) { - name = fields[v.Field].Name + if 0 <= v.Field && v.Field < st.NumFields() { + name = st.Field(v.Field).Name } return fmt.Sprintf("%s.%s [#%d]", relName(v.X, v), name, v.Field) } @@ -352,7 +356,7 @@ func (s *MapUpdate) String() string { } func (p *Package) String() string { - return "Package " + p.Types.Path + return "Package " + p.Types.Path() } func (p *Package) DumpTo(w io.Writer) { @@ -377,7 +381,7 @@ func (p *Package) DumpTo(w io.Writer) { fmt.Fprintf(w, " func %-*s %s\n", maxname, name, mem.Type()) case *Type: - fmt.Fprintf(w, " type %-*s %s\n", maxname, name, mem.NamedType.Underlying) + fmt.Fprintf(w, " type %-*s %s\n", maxname, name, mem.NamedType.Underlying()) // We display only PtrMethods since its keys // are a superset of Methods' keys, though the // methods themselves may differ, diff --git a/ssa/promote.go b/ssa/promote.go index 89f9d955fa..1d6f4fa4e5 100644 --- a/ssa/promote.go +++ b/ssa/promote.go @@ -70,7 +70,7 @@ func (p *anonFieldPath) isIndirect() bool { // type's method-set. // type candidate struct { - method *types.Method // method object of abstract or concrete type + method *types.Func // method object of abstract or concrete type concrete *Function // actual method (iff concrete) path *anonFieldPath // desugared selector path } @@ -82,12 +82,12 @@ func (c candidate) String() string { for p := c.path; p != nil; p = p.tail { s = "." + p.field.Name + s } - return "@" + s + "." + c.method.Name + return "@" + s + "." + c.method.Name() } // ptrRecv returns true if this candidate has a pointer receiver. func (c candidate) ptrRecv() bool { - return c.concrete != nil && isPointer(c.concrete.Signature.Recv.Type) + return c.concrete != nil && isPointer(c.concrete.Signature.Recv().Type()) } // MethodSet returns the method set for type typ, @@ -146,24 +146,25 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet { if node != nil { t = node.field.Type } - t = deref(t) + t = t.Deref() - if nt, ok := t.(*types.NamedType); ok { - for _, meth := range nt.Methods { - addCandidate(nextcands, IdFromQualifiedName(meth.QualifiedName), meth, prog.concreteMethods[meth], node) - } - t = nt.Underlying + if nt, ok := t.(*types.Named); ok { + nt.ForEachMethod(func(m *types.Func) { + addCandidate(nextcands, MakeId(m.Name(), m.Pkg()), m, prog.concreteMethods[m], node) + }) + t = nt.Underlying() } switch t := t.(type) { case *types.Interface: - for _, meth := range t.Methods { - addCandidate(nextcands, IdFromQualifiedName(meth.QualifiedName), meth, nil, node) - } + t.ForEachMethod(func(m *types.Func) { + addCandidate(nextcands, MakeId(m.Name(), m.Pkg()), m, nil, node) + }) case *types.Struct: - for i, f := range t.Fields { - nextcands[IdFromQualifiedName(f.QualifiedName)] = nil // a field: block id + for i, n := 0, t.NumFields(); i < n; i++ { + f := t.Field(i) + nextcands[MakeId(f.Name, f.Pkg)] = nil // a field: block id // Queue up anonymous fields for next iteration. // Break cycles to ensure termination. if f.IsAnonymous && !node.contains(f) { @@ -226,7 +227,7 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet { // If m[id] already exists (whether nil or not), m[id] is set to nil. // If method denotes a concrete method, concrete is its implementation. // -func addCandidate(m map[Id]*candidate, id Id, method *types.Method, concrete *Function, node *anonFieldPath) { +func addCandidate(m map[Id]*candidate, id Id, method *types.Func, concrete *Function, node *anonFieldPath) { prev, found := m[id] switch { case prev != nil: @@ -259,20 +260,20 @@ func addCandidate(m map[Id]*candidate, id Id, method *types.Method, concrete *Fu // method. // func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function { - sig := *cand.method.Type // make a copy, sharing underlying Values - sig.Recv = &types.Var{Name: "recv", Type: typ} + old := cand.method.Type().(*types.Signature) + sig := types.NewSignature(types.NewVar(nil, "recv", typ), old.Params(), old.Results(), old.IsVariadic()) if prog.mode&LogSource != 0 { defer logStack("makeBridgeMethod %s, %s, type %s", typ, cand, &sig)() } fn := &Function{ - Name_: cand.method.Name, - Signature: &sig, + Name_: cand.method.Name(), + Signature: sig, Prog: prog, } fn.startBody() - fn.addSpilledParam(sig.Recv) + fn.addSpilledParam(sig.Recv()) createParams(fn) // Each bridge method performs a sequence of selections, @@ -287,7 +288,7 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function // Iterate over selections e.A.B.C.f in the natural order [A,B,C]. for _, p := range cand.path.reverse() { // Loop invariant: v holds a pointer to a struct. - if _, ok := underlyingType(indirectType(v.Type())).(*types.Struct); !ok { + if _, ok := v.Type().Deref().Underlying().(*types.Struct); !ok { panic(fmt.Sprint("not a *struct: ", v.Type(), p.field.Type)) } sel := &FieldAddr{ @@ -307,8 +308,8 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function var c Call if cand.concrete != nil { c.Call.Func = cand.concrete - fn.Pos = c.Call.Func.(*Function).Pos // TODO(adonovan): fix: wrong. - c.Call.Pos = fn.Pos // TODO(adonovan): fix: wrong. + fn.pos = c.Call.Func.(*Function).pos // TODO(adonovan): fix: wrong. + c.Call.pos = fn.pos // TODO(adonovan): fix: wrong. c.Call.Args = append(c.Call.Args, v) } else { c.Call.Recv = v @@ -322,15 +323,17 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function // createParams creates parameters for bridge method fn based on its Signature. func createParams(fn *Function) { var last *Parameter - for i, p := range fn.Signature.Params { - name := p.Name + tparams := fn.Signature.Params() + for i, n := 0, tparams.Len(); i < n; i++ { + p := tparams.At(i) + name := p.Name() if name == "" { name = fmt.Sprintf("arg%d", i) } - last = fn.addParam(name, p.Type) + last = fn.addParam(name, p.Type()) } - if fn.Signature.IsVariadic { - last.Type_ = &types.Slice{Elt: last.Type_} + if fn.Signature.IsVariadic() { + last.Type_ = types.NewSlice(last.Type_) } } @@ -365,11 +368,11 @@ func makeImethodThunk(prog *Program, typ types.Type, id Id) *Function { if prog.mode&LogSource != 0 { defer logStack("makeImethodThunk %s.%s", typ, id)() } - itf := underlyingType(typ).(*types.Interface) - index, meth := methodIndex(itf, itf.Methods, id) - sig := *meth.Type // copy; shared Values + itf := typ.Underlying().(*types.Interface) + index, meth := methodIndex(itf, id) + sig := *meth.Type().(*types.Signature) // copy; shared Values fn := &Function{ - Name_: meth.Name, + Name_: meth.Name(), Signature: &sig, Prog: prog, } @@ -398,41 +401,45 @@ func makeImethodThunk(prog *Program, typ types.Type, id Id) *Function { // func findPromotedField(st *types.Struct, id Id) (*anonFieldPath, int) { // visited records the types that have been searched already. - // Invariant: keys are all *types.NamedType. + // Invariant: keys are all *types.Named. // (types.Type is not a sound map key in general.) visited := make(map[types.Type]bool) var list, next []*anonFieldPath - for i, f := range st.Fields { + i := 0 + st.ForEachField(func(f *types.Field) { if f.IsAnonymous { list = append(list, &anonFieldPath{nil, i, f}) } - } + i++ + }) // Search the current level if there is any work to do and collect // embedded types of the next lower level in the next list. for { // look for name in all types at this level for _, node := range list { - typ := deref(node.field.Type).(*types.NamedType) + typ := node.field.Type.Deref().(*types.Named) if visited[typ] { continue } visited[typ] = true - switch typ := typ.Underlying.(type) { + switch typ := typ.Underlying().(type) { case *types.Struct: - for i, f := range typ.Fields { - if IdFromQualifiedName(f.QualifiedName) == id { + for i, n := 0, typ.NumFields(); i < n; i++ { + f := typ.Field(i) + if MakeId(f.Name, f.Pkg) == id { return node, i } } - for i, f := range typ.Fields { + i := 0 + typ.ForEachField(func(f *types.Field) { if f.IsAnonymous { next = append(next, &anonFieldPath{node, i, f}) } - } - + i++ + }) } } diff --git a/ssa/sanity.go b/ssa/sanity.go index 9a6a81d522..e94653731d 100644 --- a/ssa/sanity.go +++ b/ssa/sanity.go @@ -123,7 +123,8 @@ func (s *sanity) checkInstr(idx int, instr Instruction) { case *BinOp: case *Call: case *ChangeInterface: - case *Conv: + case *ChangeType: + case *Convert: case *Defer: case *Extract: case *Field: diff --git a/ssa/ssa.go b/ssa/ssa.go index a25766ef98..3a24b0ade5 100644 --- a/ssa/ssa.go +++ b/ssa/ssa.go @@ -22,10 +22,10 @@ type Program struct { Packages map[string]*Package // all loaded Packages, keyed by import path Builtins map[types.Object]*Builtin // all built-in functions, keyed by typechecker objects. - methodSets map[types.Type]MethodSet // concrete method sets for all needed types [TODO(adonovan): de-dup] - methodSetsMu sync.Mutex // serializes all accesses to methodSets - concreteMethods map[*types.Method]*Function // maps named concrete methods to their code - mode BuilderMode // set of mode bits + methodSets map[types.Type]MethodSet // concrete method sets for all needed types [TODO(adonovan): de-dup] + methodSetsMu sync.Mutex // serializes all accesses to methodSets + concreteMethods map[*types.Func]*Function // maps named concrete methods to their code + mode BuilderMode // set of mode bits } // A Package is a single analyzed Go package containing Members for @@ -57,7 +57,7 @@ type Package struct { type Member interface { Name() string // the declared name of the package member String() string // human-readable information about the value - Posn() token.Pos // position of member's declaration, if known + Pos() token.Pos // position of member's declaration, if known Type() types.Type // the type of the package member ImplementsMember() // dummy method to indicate the "implements" relation. } @@ -93,7 +93,7 @@ type MethodSet map[Id]*Function // type and method set of a named type declared at package scope. // type Type struct { - NamedType *types.NamedType + NamedType *types.Named Methods MethodSet // concrete method set of N PtrMethods MethodSet // concrete method set of (*N) } @@ -101,10 +101,16 @@ type Type struct { // A Constant is a Member of Package representing a package-level // constant value. // +// Pos() returns the position of the declaring ast.ValueSpec.Names[*] +// identifier. +// +// NB: a Constant is not a Value; it contains a literal Value, which +// it augments with the name and position of its 'const' declaration. +// type Constant struct { Name_ string Value *Literal - Pos token.Pos + pos token.Pos } // An SSA value that can be referenced by an instruction. @@ -202,6 +208,18 @@ type Instruction interface { // Values.) Operands(rands []*Value) []*Value + // Pos returns the location of the source construct that + // gave rise to this instruction, or token.NoPos if it was not + // explicit in the source. + // + // For each ast.Expr type, a particular field is designated as + // the canonical location for the expression, e.g. the Lparen + // for an *ast.CallExpr. This enables us to find the + // instruction corresponding to a given piece of source + // syntax. + // + Pos() token.Pos + // Dummy method to indicate the "implements" relation. ImplementsInstruction() } @@ -226,16 +244,20 @@ type Instruction interface { // MakeClosure which, via its Bindings, supplies values for these // parameters. Captures are always addresses. // -// If the function is a method (Signature.Recv != nil) then the first +// If the function is a method (Signature.Recv() != nil) then the first // element of Params is the receiver parameter. // +// Pos() returns the declaring ast.FuncLit.Type.Func or the position +// of the ast.FuncDecl.Name, if the function was explicit in the +// source. +// // Type() returns the function's Signature. // type Function struct { Name_ string Signature *types.Signature - Pos token.Pos // location of the definition + pos token.Pos Enclosing *Function // enclosing function if anon; nil if global Pkg *Package // enclosing package for Go source functions; otherwise nil Prog *Program // enclosing program @@ -303,19 +325,23 @@ type Parameter struct { referrers []Instruction } -// A Literal represents a literal nil, boolean, string or numeric -// (integer, fraction or complex) value. +// A Literal represents the value of a constant expression. // -// A literal's underlying Type() can be a basic type, possibly one of -// the "untyped" types. A nil literal can have any reference type: -// interface, map, channel, pointer, slice, or function---but not -// "untyped nil". +// It may have a nil, boolean, string or numeric (integer, fraction or +// complex) value, or a []byte or []rune conversion of a string +// literal. +// +// Literals may be of named types. A literal's underlying type can be +// a basic type, possibly one of the "untyped" types, or a slice type +// whose elements' underlying type is byte or rune. A nil literal can +// have any reference type: interface, map, channel, pointer, slice, +// or function---but not "untyped nil". // // All source-level constant expressions are represented by a Literal // of equal type and value. // // Value holds the exact value of the literal, independent of its -// Type(), using the same representation as package go/types uses for +// Type(), using the same representation as package go/exact uses for // constants. // // Example printed form: @@ -331,24 +357,27 @@ type Literal struct { // A Global is a named Value holding the address of a package-level // variable. // +// Pos() returns the position of the ast.ValueSpec.Names[*] +// identifier. +// type Global struct { Name_ string Type_ types.Type Pkg *Package - Pos token.Pos + pos token.Pos // The following fields are set transiently during building, // then cleared. spec *ast.ValueSpec // explained at buildGlobal } -// A built-in function, e.g. len. +// A Builtin represents a built-in function, e.g. len. // -// Builtins are immutable values; they do not have addresses. +// Builtins are immutable values. Builtins do not have addresses. // -// Type() returns an inscrutable *types.builtin. Built-in functions -// may have polymorphic or variadic types that are not expressible in -// Go's type system. +// Type() returns a *types.Builtin. +// Built-in functions may have polymorphic or variadic types that are +// not expressible in Go's type system. // type Builtin struct { Object *types.Func // canonical types.Universe object for this built-in @@ -377,6 +406,10 @@ type Builtin struct { // the result of MakeSlice, MakeMap or MakeChan in that location to // instantiate these types. // +// Pos() returns the ast.CompositeLit.Lbrace for a composite literal, +// or the ast.CallExpr.Lparen for a call to new() or for a call that +// allocates a varargs slice. +// // Example printed form: // t0 = local int // t1 = new int @@ -386,14 +419,17 @@ type Alloc struct { Name_ string Type_ types.Type Heap bool - Pos token.Pos + pos token.Pos referrers []Instruction index int // dense numbering; for lifting } -// Phi represents an SSA φ-node, which combines values that differ -// across incoming control-flow edges and yields a new value. Within -// a block, all φ-nodes must appear before all non-φ nodes. +// The Phi instruction represents an SSA φ-node, which combines values +// that differ across incoming control-flow edges and yields a new +// value. Within a block, all φ-nodes must appear before all non-φ +// nodes. +// +// Pos() returns NoPos. // // Example printed form: // t2 = phi [0.start: t0, 1.if.then: t1, ...] @@ -404,7 +440,7 @@ type Phi struct { Edges []Value // Edges[i] is value for Block().Preds[i] } -// Call represents a function or method call. +// The Call instruction represents a function or method call. // // The Call instruction yields the function result, if there is // exactly one, or a tuple (empty or len>1) whose components are @@ -412,6 +448,8 @@ type Phi struct { // // See CallCommon for generic function call documentation. // +// Pos() returns the ast.CallExpr.Lparen, if explicit in the source. +// // Example printed form: // t2 = println(t0, t1) // t4 = t3() @@ -422,7 +460,10 @@ type Call struct { Call CallCommon } -// BinOp yields the result of binary operation X Op Y. +// The BinOp instruction yields the result of binary operation X Op Y. +// +// Pos() returns the ast.BinaryExpr.OpPos, if explicit in the source. +// TODO(adonovan): implement. // // Example printed form: // t1 = t0 + 1:int @@ -437,7 +478,7 @@ type BinOp struct { X, Y Value } -// UnOp yields the result of Op X. +// The UnOp instruction yields the result of Op X. // ARROW is channel receive. // MUL is pointer indirection (load). // XOR is bitwise complement. @@ -447,6 +488,8 @@ type BinOp struct { // and a boolean indicating the success of the receive. The // components of the tuple are accessed using Extract. // +// Pos() returns the ast.UnaryExpr.OpPos, if explicit in the source. +// // Example printed form: // t0 = *x // t2 = <-t1,ok @@ -458,38 +501,53 @@ type UnOp struct { CommaOk bool } -// Conv yields the conversion of X to type Type(). +// The ChangeType instruction applies to X a value-preserving type +// change to Type(). // -// A conversion is one of the following kinds. The behaviour of the -// conversion operator may depend on both Type() and X.Type(), as well -// as the dynamic value. +// Type changes are permitted: +// - between a named type and its underlying type. +// - between two named types of the same underlying type. +// - between (possibly named) pointers to identical base types. +// - between f(T) functions and (T) func f() methods. +// - from a bidirectional channel to a read- or write-channel, +// optionally adding/removing a name. // -// A '+' indicates that a dynamic representation change may occur. -// A '-' indicates that the conversion is a value-preserving change -// to types only. +// This operation cannot fail dynamically. // -// 1. implicit conversions (arising from assignability rules): -// - adding/removing a name, same underlying types. -// - channel type restriction, possibly adding/removing a name. -// 2. explicit conversions (in addition to the above): -// - changing a name, same underlying types. -// - between pointers to identical base types. -// + conversions between real numeric types. -// + conversions between complex numeric types. -// + integer/[]byte/[]rune -> string. -// + string -> []byte/[]rune. +// Pos() returns the ast.CallExpr.Lparen, if the instruction arose +// from an explicit conversion in the source. // -// TODO(adonovan): split into two cases: -// - rename value (ChangeType) -// + value to type with different representation (Conv) +// Example printed form: +// t1 = changetype *int <- IntPtr (t0) +// +type ChangeType struct { + Register + X Value +} + +// The Convert instruction yields the conversion of value X to type +// Type(). +// +// A conversion may change the value and representation of its operand. +// Conversions are permitted: +// - between real numeric types. +// - between complex numeric types. +// - between string and []byte or []rune. +// - from (Unicode) integer to (UTF-8) string. +// A conversion may imply a type name change also. +// +// This operation cannot fail dynamically. // // Conversions of untyped string/number/bool constants to a specific // representation are eliminated during SSA construction. // -// Example printed form: -// t1 = convert interface{} <- int (t0) +// Pos() returns the ast.CallExpr.Lparen, if the instruction arose +// from an explicit conversion in the source. // -type Conv struct { +// Example printed form: +// t1 = convert []byte <- string (t0) +// +type Convert struct { Register X Value } @@ -499,7 +557,9 @@ type Conv struct { // // This operation cannot fail. Use TypeAssert for interface // conversions that may fail dynamically. -// TODO(adonovan): rename to "{Narrow,Restrict}Interface"? +// +// Pos() returns the ast.CallExpr.Lparen, if the instruction arose +// from an explicit conversion in the source. // // Example printed form: // t1 = change interface interface{} <- I (t0) @@ -513,7 +573,10 @@ type ChangeInterface struct { // value and its method-set. // // To construct the zero value of an interface type T, use: -// &Literal{types.nilType{}, T} +// &Literal{exact.MakeNil(), T} +// +// Pos() returns the ast.CallExpr.Lparen, if the instruction arose +// from an explicit conversion in the source. // // Example printed form: // t1 = make interface interface{} <- int (42:int) @@ -524,14 +587,18 @@ type MakeInterface struct { Methods MethodSet // method set of (non-interface) X } -// A MakeClosure instruction yields an anonymous function value whose -// code is Fn and whose lexical capture slots are populated by Bindings. +// The MakeClosure instruction yields an anonymous function value +// whose code is Fn and whose lexical capture slots are populated by +// Bindings. // // By construction, all captured variables are addresses of variables // allocated with 'new', i.e. Alloc(Heap=true). // // Type() returns a (possibly named) *types.Signature. // +// Pos() returns the ast.FuncLit.Type.Func of the function literal +// that created this closure. +// // Example printed form: // t0 = make closure anon@1.2 [x y z] // @@ -546,13 +613,15 @@ type MakeClosure struct { // // Type() returns a (possibly named) *types.Map. // +// Pos() returns the ast.CallExpr.Lparen, if created by make(map), or +// the ast.CompositeLit.Lbrack if created by a literal. +// // Example printed form: // t1 = make map[string]int t0 // type MakeMap struct { Register Reserve Value // initial space reservation; nil => default - Pos token.Pos } // The MakeChan instruction creates a new channel object and yields a @@ -560,17 +629,19 @@ type MakeMap struct { // // Type() returns a (possibly named) *types.Chan. // +// Pos() returns the ast.CallExpr.Lparen for the make(chan) that +// created it. +// // Example printed form: // t0 = make chan int 0 // type MakeChan struct { Register Size Value // int; size of buffer; zero => synchronous. - Pos token.Pos } -// MakeSlice yields a slice of length Len backed by a newly allocated -// array of length Cap. +// The MakeSlice instruction yields a slice of length Len backed by a +// newly allocated array of length Cap. // // Both Len and Cap must be non-nil Values of integer type. // @@ -579,6 +650,9 @@ type MakeChan struct { // // Type() returns a (possibly named) *types.Slice. // +// Pos() returns the ast.CallExpr.Lparen for the make([]T) that +// created it. +// // Example printed form: // t1 = make slice []string 1:int t0 // @@ -586,15 +660,18 @@ type MakeSlice struct { Register Len Value Cap Value - Pos token.Pos } -// Slice yields a slice of an existing string, slice or *array X -// between optional integer bounds Low and High. +// The Slice instruction yields a slice of an existing string, slice +// or *array X between optional integer bounds Low and High. // // Type() returns string if the type of X was string, otherwise a // *types.Slice with the same element type as X. // +// Pos() returns the ast.SliceExpr.Lbrack if created by a x[:] slice +// operation, the ast.CompositeLit.Lbrace if created by a literal, or +// NoPos if not explicit in the source (e.g. a variadic argument slice). +// // Example printed form: // t1 = slice t0[1:] // @@ -604,13 +681,16 @@ type Slice struct { Low, High Value // either may be nil } -// FieldAddr yields the address of Field of *struct X. +// The FieldAddr instruction yields the address of Field of *struct X. // // The field is identified by its index within the field list of the // struct type of X. // // Type() returns a (possibly named) *types.Pointer. // +// Pos() returns the position of the ast.SelectorExpr.Sel for the +// field, if explicit in the source. +// // Example printed form: // t1 = &t0.name [#1] // @@ -620,12 +700,15 @@ type FieldAddr struct { Field int // index into X.Type().(*types.Struct).Fields } -// Field yields the Field of struct X. +// The Field instruction yields the Field of struct X. // // The field is identified by its index within the field list of the // struct type of X; by using numeric indices we avoid ambiguity of // package-local identifiers and permit compact representations. // +// Pos() returns the position of the ast.SelectorExpr.Sel for the +// field, if explicit in the source. +// // Example printed form: // t1 = t0.name [#1] // @@ -635,14 +718,17 @@ type Field struct { Field int // index into X.Type().(*types.Struct).Fields } -// IndexAddr yields the address of the element at index Index of -// collection X. Index is an integer expression. +// The IndexAddr instruction yields the address of the element at +// index Index of collection X. Index is an integer expression. // // The elements of maps and strings are not addressable; use Lookup or // MapUpdate instead. // // Type() returns a (possibly named) *types.Pointer. // +// Pos() returns the ast.IndexExpr.Lbrack for the index operation, if +// explicit in the source. +// // Example printed form: // t2 = &t0[t1] // @@ -652,7 +738,10 @@ type IndexAddr struct { Index Value // numeric index } -// Index yields element Index of array X. +// The Index instruction yields element Index of array X. +// +// Pos() returns the ast.IndexExpr.Lbrack for the index operation, if +// explicit in the source. // // Example printed form: // t2 = t0[t1] @@ -663,14 +752,16 @@ type Index struct { Index Value // integer index } -// Lookup yields element Index of collection X, a map or string. -// Index is an integer expression if X is a string or the appropriate -// key type if X is a map. +// The Lookup instruction yields element Index of collection X, a map +// or string. Index is an integer expression if X is a string or the +// appropriate key type if X is a map. // // If CommaOk, the result is a 2-tuple of the value above and a // boolean indicating the result of a map membership test for the key. // The components of the tuple are accessed using Extract. // +// Pos() returns the ast.IndexExpr.Lbrack, if explicit in the source. +// // Example printed form: // t2 = t0[t1] // t5 = t3[t4],ok @@ -691,8 +782,8 @@ type SelectState struct { Send Value // value to send (for send) } -// Select tests whether (or blocks until) one or more of the specified -// sent or received states is entered. +// The Select instruction tests whether (or blocks until) one or more +// of the specified sent or received states is entered. // // It returns a triple (index int, recv interface{}, recvOk bool) // whose components, described below, must be accessed via the Extract @@ -714,6 +805,8 @@ type SelectState struct { // is true iff the selected operation was a receive and the receive // successfully yielded a value. // +// Pos() returns the ast.SelectStmt.Select. +// // Example printed form: // t3 = select nonblocking [<-t0, t1<-t2, ...] // t4 = select blocking [] @@ -724,13 +817,15 @@ type Select struct { Blocking bool } -// Range yields an iterator over the domain and range of X, -// which must be a string or map. +// The Range instruction yields an iterator over the domain and range +// of X, which must be a string or map. // // Elements are accessed via Next. // // Type() returns a (possibly named) *types.Result (tuple type). // +// Pos() returns the ast.RangeStmt.For. +// // Example printed form: // t0 = range "hello":string // @@ -739,11 +834,11 @@ type Range struct { X Value // string or map } -// Next reads and advances the (map or string) iterator Iter and -// returns a 3-tuple value (ok, k, v). If the iterator is not -// exhausted, ok is true and k and v are the next elements of the -// domain and range, respectively. Otherwise ok is false and k and v -// are undefined. +// The Next instruction reads and advances the (map or string) +// iterator Iter and returns a 3-tuple value (ok, k, v). If the +// iterator is not exhausted, ok is true and k and v are the next +// elements of the domain and range, respectively. Otherwise ok is +// false and k and v are undefined. // // Components of the tuple are accessed using Extract. // @@ -763,7 +858,8 @@ type Next struct { IsString bool // true => string iterator; false => map iterator. } -// TypeAssert tests whether interface value X has type AssertedType. +// The TypeAssert instruction tests whether interface value X has type +// AssertedType. // // If !CommaOk, on success it returns v, the result of the conversion // (defined below); on failure it panics. @@ -797,7 +893,7 @@ type TypeAssert struct { CommaOk bool } -// Extract yields component Index of Tuple. +// The Extract instruction yields component Index of Tuple. // // This is used to access the results of instructions with multiple // return values, such as Call, TypeAssert, Next, UnOp(ARROW) and @@ -814,10 +910,12 @@ type Extract struct { // Instructions executed for effect. They do not yield a value. -------------------- -// Jump transfers control to the sole successor of its owning block. +// The Jump instruction transfers control to the sole successor of its +// owning block. // -// A Jump instruction must be the last instruction of its containing -// BasicBlock. +// A Jump must be the last instruction of its containing BasicBlock. +// +// Pos() returns NoPos. // // Example printed form: // jump done @@ -833,6 +931,8 @@ type Jump struct { // An If instruction must be the last instruction of its containing // BasicBlock. // +// Pos() returns NoPos. +// // Example printed form: // if t0 goto done else body // @@ -841,7 +941,8 @@ type If struct { Cond Value } -// Ret returns values and control back to the calling function. +// The Ret instruction returns values and control back to the calling +// function. // // len(Results) is always equal to the number of results in the // function's signature. @@ -856,6 +957,8 @@ type If struct { // Ret must be the last instruction of its containing BasicBlock. // Such a block has no successors. // +// Pos() returns the ast.ReturnStmt.Return, if explicit in the source. +// // Example printed form: // ret // ret nil:I, 2:int @@ -863,15 +966,18 @@ type If struct { type Ret struct { anInstruction Results []Value + pos token.Pos } -// RunDefers pops and invokes the entire stack of procedure calls -// pushed by Defer instructions in this function. +// The RunDefers instruction pops and invokes the entire stack of +// procedure calls pushed by Defer instructions in this function. // // It is legal to encounter multiple 'rundefers' instructions in a // single control-flow path through a function; this is useful in // the combined init() function, for example. // +// Pos() returns NoPos. +// // Example printed form: // rundefers // @@ -879,7 +985,7 @@ type RunDefers struct { anInstruction } -// Panic initiates a panic with value X. +// The Panic instruction initiates a panic with value X. // // A Panic instruction must be the last instruction of its containing // BasicBlock, which must have no successors. @@ -887,16 +993,20 @@ type RunDefers struct { // NB: 'go panic(x)' and 'defer panic(x)' do not use this instruction; // they are treated as calls to a built-in function. // +// Pos() returns the ast.CallExpr.Lparen if this panic was explicit +// in the source. +// // Example printed form: // panic t0 // type Panic struct { anInstruction - X Value // an interface{} + X Value // an interface{} + pos token.Pos } -// Go creates a new goroutine and calls the specified function -// within it. +// The Go instruction creates a new goroutine and calls the specified +// function within it. // // See CallCommon for generic function call documentation. // @@ -910,8 +1020,8 @@ type Go struct { Call CallCommon } -// Defer pushes the specified call onto a stack of functions -// to be called by a RunDefers instruction or by a panic. +// The Defer instruction pushes the specified call onto a stack of +// functions to be called by a RunDefers instruction or by a panic. // // See CallCommon for generic function call documentation. // @@ -925,7 +1035,9 @@ type Defer struct { Call CallCommon } -// Send sends X on channel Chan. +// The Send instruction sends X on channel Chan. +// +// Pos() returns the ast.SendStmt.Arrow, if explicit in the source. // // Example printed form: // send t0 <- t1 @@ -933,11 +1045,15 @@ type Defer struct { type Send struct { anInstruction Chan, X Value + pos token.Pos } -// Store stores Val at address Addr. +// The Store instruction stores Val at address Addr. // Stores can be of arbitrary types. // +// Pos() returns the ast.StarExpr.Star, if explicit in the source. +// TODO(addr): implement. +// // Example printed form: // *x = y // @@ -945,9 +1061,13 @@ type Store struct { anInstruction Addr Value Val Value + pos token.Pos } -// MapUpdate updates the association of Map[Key] to Value. +// The MapUpdate instruction updates the association of Map[Key] to +// Value. +// +// Pos() returns the ast.KeyValueExpr.Colon, if explicit in the source. // // Example printed form: // t0[t1] = t2 @@ -957,6 +1077,7 @@ type MapUpdate struct { Map Value Key Value Value Value + pos token.Pos } // Embeddable mix-ins and helpers for common parts of other structs. ----------- @@ -978,6 +1099,7 @@ type MapUpdate struct { type Register struct { anInstruction num int // "name" of virtual register, e.g. "t0". Not guaranteed unique. + pos token.Pos // position of source expression, or NoPos Type_ types.Type // type of virtual register referrers []Instruction } @@ -1045,7 +1167,7 @@ type CallCommon struct { Func Value // target of call, iff function call Args []Value // actual parameters, including receiver in invoke mode HasEllipsis bool // true iff last Args is a slice of '...' args (needed?) - Pos token.Pos // position of call expression + pos token.Pos // position of CallExpr.Lparen, iff explicit in source } // IsInvoke returns true if this call has "invoke" (not "call") mode. @@ -1053,6 +1175,8 @@ func (c *CallCommon) IsInvoke() bool { return c.Recv != nil } +func (c *CallCommon) Pos() token.Pos { return c.pos } + // StaticCallee returns the called function if this is a trivially // static "call"-mode call. func (c *CallCommon) StaticCallee() *Function { @@ -1068,8 +1192,8 @@ func (c *CallCommon) StaticCallee() *Function { // MethodId returns the Id for the method called by c, which must // have "invoke" mode. func (c *CallCommon) MethodId() Id { - meth := underlyingType(c.Recv.Type()).(*types.Interface).Methods[c.Method] - return IdFromQualifiedName(meth.QualifiedName) + m := c.Recv.Type().Underlying().(*types.Interface).Method(c.Method) + return MakeId(m.Name(), m.Pkg()) } // Description returns a description of the mode of this call suitable @@ -1081,7 +1205,7 @@ func (c *CallCommon) Description() string { case *MakeClosure: return "static function closure call" case *Function: - if fn.Signature.Recv != nil { + if fn.Signature.Recv() != nil { return "static method call" } return "static function call" @@ -1089,8 +1213,8 @@ func (c *CallCommon) Description() string { return "dynamic function call" } -func (v *Builtin) Type() types.Type { return v.Object.GetType() } -func (v *Builtin) Name() string { return v.Object.GetName() } +func (v *Builtin) Type() types.Type { return v.Object.Type() } +func (v *Builtin) Name() string { return v.Object.Name() } func (*Builtin) Referrers() *[]Instruction { return nil } func (v *Capture) Type() types.Type { return v.Outer.Type() } @@ -1099,12 +1223,12 @@ func (v *Capture) Referrers() *[]Instruction { return &v.referrers } func (v *Global) Type() types.Type { return v.Type_ } func (v *Global) Name() string { return v.Name_ } -func (v *Global) Posn() token.Pos { return v.Pos } +func (v *Global) Pos() token.Pos { return v.pos } func (*Global) Referrers() *[]Instruction { return nil } func (v *Function) Name() string { return v.Name_ } func (v *Function) Type() types.Type { return v.Signature } -func (v *Function) Posn() token.Pos { return v.Pos } +func (v *Function) Pos() token.Pos { return v.pos } func (*Function) Referrers() *[]Instruction { return nil } func (v *Parameter) Type() types.Type { return v.Type_ } @@ -1121,19 +1245,21 @@ func (v *Register) Name() string { return fmt.Sprintf("t%d", v.num) func (v *Register) setNum(num int) { v.num = num } func (v *Register) Referrers() *[]Instruction { return &v.referrers } func (v *Register) asRegister() *Register { return v } +func (v *Register) Pos() token.Pos { return v.pos } +func (v *Register) setPos(pos token.Pos) { v.pos = pos } func (v *anInstruction) Block() *BasicBlock { return v.Block_ } func (v *anInstruction) SetBlock(block *BasicBlock) { v.Block_ = block } -func (t *Type) Name() string { return t.NamedType.Obj.Name } -func (t *Type) Posn() token.Pos { return t.NamedType.Obj.GetPos() } +func (t *Type) Name() string { return t.NamedType.Obj().Name() } +func (t *Type) Pos() token.Pos { return t.NamedType.Obj().Pos() } func (t *Type) String() string { return t.Name() } func (t *Type) Type() types.Type { return t.NamedType } -func (p *Package) Name() string { return p.Types.Name } +func (p *Package) Name() string { return p.Types.Name() } func (c *Constant) Name() string { return c.Name_ } -func (c *Constant) Posn() token.Pos { return c.Pos } +func (c *Constant) Pos() token.Pos { return c.pos } func (c *Constant) String() string { return c.Name() } func (c *Constant) Type() types.Type { return c.Value.Type() } @@ -1179,7 +1305,8 @@ func (*Builtin) ImplementsValue() {} func (*Call) ImplementsValue() {} func (*Capture) ImplementsValue() {} func (*ChangeInterface) ImplementsValue() {} -func (*Conv) ImplementsValue() {} +func (*ChangeType) ImplementsValue() {} +func (*Convert) ImplementsValue() {} func (*Extract) ImplementsValue() {} func (*Field) ImplementsValue() {} func (*FieldAddr) ImplementsValue() {} @@ -1212,7 +1339,8 @@ func (*Alloc) ImplementsInstruction() {} func (*BinOp) ImplementsInstruction() {} func (*Call) ImplementsInstruction() {} func (*ChangeInterface) ImplementsInstruction() {} -func (*Conv) ImplementsInstruction() {} +func (*ChangeType) ImplementsInstruction() {} +func (*Convert) ImplementsInstruction() {} func (*Defer) ImplementsInstruction() {} func (*Extract) ImplementsInstruction() {} func (*Field) ImplementsInstruction() {} @@ -1242,9 +1370,20 @@ func (*Store) ImplementsInstruction() {} func (*TypeAssert) ImplementsInstruction() {} func (*UnOp) ImplementsInstruction() {} -// Operands. +func (v *Alloc) Pos() token.Pos { return v.pos } +func (v *Call) Pos() token.Pos { return v.Call.pos } +func (s *Defer) Pos() token.Pos { return s.Call.pos } +func (s *Go) Pos() token.Pos { return s.Call.pos } +func (s *MapUpdate) Pos() token.Pos { return s.pos } +func (s *Panic) Pos() token.Pos { return s.pos } +func (s *Ret) Pos() token.Pos { return s.pos } +func (s *Send) Pos() token.Pos { return s.pos } +func (s *Store) Pos() token.Pos { return s.pos } +func (s *If) Pos() token.Pos { return token.NoPos } +func (s *Jump) Pos() token.Pos { return token.NoPos } +func (s *RunDefers) Pos() token.Pos { return token.NoPos } -// REVIEWERS: Should this method be defined nearer each type to avoid skew? +// Operands. func (v *Alloc) Operands(rands []*Value) []*Value { return rands @@ -1278,7 +1417,11 @@ func (v *ChangeInterface) Operands(rands []*Value) []*Value { return append(rands, &v.X) } -func (v *Conv) Operands(rands []*Value) []*Value { +func (v *ChangeType) Operands(rands []*Value) []*Value { + return append(rands, &v.X) +} + +func (v *Convert) Operands(rands []*Value) []*Value { return append(rands, &v.X) } diff --git a/ssa/ssadump.go b/ssa/ssadump.go index d7f64aab9e..0f4417dac4 100644 --- a/ssa/ssadump.go +++ b/ssa/ssadump.go @@ -101,7 +101,7 @@ func main() { context := &ssa.Context{ Mode: mode, - Loader: ssa.GorootLoader, + Loader: ssa.MakeGoBuildLoader(nil), } b := ssa.NewBuilder(context) mainpkg, args, err := ssa.CreatePackageFromArgs(b, args) diff --git a/ssa/typeinfo.go b/ssa/typeinfo.go index 38673fdb8a..0f8cbd5a14 100644 --- a/ssa/typeinfo.go +++ b/ssa/typeinfo.go @@ -32,7 +32,7 @@ func (info *TypeInfo) TypeOf(e ast.Expr) types.Type { // TODO(gri): This is a typechecker bug. When fixed, // eliminate this case and panic. if id, ok := e.(*ast.Ident); ok { - return info.ObjectOf(id).GetType() + return info.ObjectOf(id).Type() } panic("no type for expression") } @@ -56,6 +56,8 @@ func (info *TypeInfo) ObjectOf(id *ast.Ident) types.Object { // IsType returns true iff expression e denotes a type. // Precondition: e belongs to the package's ASTs. +// e must be a true expression, not a KeyValueExpr, or an Ident +// appearing in a SelectorExpr or declaration. // func (info *TypeInfo) IsType(e ast.Expr) bool { switch e := e.(type) { @@ -83,7 +85,7 @@ func (info *TypeInfo) IsType(e ast.Expr) bool { func (info *TypeInfo) isPackageRef(sel *ast.SelectorExpr) types.Object { if id, ok := sel.X.(*ast.Ident); ok { if obj := info.ObjectOf(id); objKind(obj) == ast.Pkg { - return obj.(*types.Package).Scope.Lookup(sel.Sel.Name) + return obj.(*types.Package).Scope().Lookup(sel.Sel.Name) } } return nil @@ -115,52 +117,52 @@ func builtinCallSignature(info *TypeInfo, e *ast.CallExpr) *types.Signature { t1 = info.TypeOf(e.Args[1]) // no conversion } else { // append([]T, ...T) []T - t1 = underlyingType(t0).(*types.Slice).Elt + t1 = t0.Underlying().(*types.Slice).Elem() isVariadic = true } params = append(params, - &types.Var{Type: t0}, - &types.Var{Type: t1}) + types.NewVar(nil, "", t0), + types.NewVar(nil, "", t1)) case "print", "println": // print{,ln}(any, ...interface{}) isVariadic = true // Note, arg0 may have any type, not necessarily tEface. params = append(params, - &types.Var{Type: info.TypeOf(e.Args[0])}, - &types.Var{Type: tEface}) + types.NewVar(nil, "", info.TypeOf(e.Args[0])), + types.NewVar(nil, "", tEface)) case "close": - params = append(params, &types.Var{Type: info.TypeOf(e.Args[0])}) + params = append(params, types.NewVar(nil, "", info.TypeOf(e.Args[0]))) case "copy": // copy([]T, []T) int // Infer arg types from each other. Sleazy. var st *types.Slice - if t, ok := underlyingType(info.TypeOf(e.Args[0])).(*types.Slice); ok { + if t, ok := info.TypeOf(e.Args[0]).Underlying().(*types.Slice); ok { st = t - } else if t, ok := underlyingType(info.TypeOf(e.Args[1])).(*types.Slice); ok { + } else if t, ok := info.TypeOf(e.Args[1]).Underlying().(*types.Slice); ok { st = t } else { panic("cannot infer types in call to copy()") } - stvar := &types.Var{Type: st} + stvar := types.NewVar(nil, "", st) params = append(params, stvar, stvar) case "delete": // delete(map[K]V, K) tmap := info.TypeOf(e.Args[0]) - tkey := underlyingType(tmap).(*types.Map).Key + tkey := tmap.Underlying().(*types.Map).Key() params = append(params, - &types.Var{Type: tmap}, - &types.Var{Type: tkey}) + types.NewVar(nil, "", tmap), + types.NewVar(nil, "", tkey)) case "len", "cap": - params = append(params, &types.Var{Type: info.TypeOf(e.Args[0])}) + params = append(params, types.NewVar(nil, "", info.TypeOf(e.Args[0]))) case "real", "imag": // Reverse conversion to "complex" case below. var argType types.Type - switch info.TypeOf(e).(*types.Basic).Kind { + switch info.TypeOf(e).(*types.Basic).Kind() { case types.UntypedFloat: argType = types.Typ[types.UntypedComplex] case types.Float64: @@ -170,11 +172,11 @@ func builtinCallSignature(info *TypeInfo, e *ast.CallExpr) *types.Signature { default: unreachable() } - params = append(params, &types.Var{Type: argType}) + params = append(params, types.NewVar(nil, "", argType)) case "complex": var argType types.Type - switch info.TypeOf(e).(*types.Basic).Kind { + switch info.TypeOf(e).(*types.Basic).Kind() { case types.UntypedComplex: argType = types.Typ[types.UntypedFloat] case types.Complex128: @@ -184,11 +186,11 @@ func builtinCallSignature(info *TypeInfo, e *ast.CallExpr) *types.Signature { default: unreachable() } - v := &types.Var{Type: argType} + v := types.NewVar(nil, "", argType) params = append(params, v, v) case "panic": - params = append(params, &types.Var{Type: tEface}) + params = append(params, types.NewVar(nil, "", tEface)) case "recover": // no params @@ -197,5 +199,5 @@ func builtinCallSignature(info *TypeInfo, e *ast.CallExpr) *types.Signature { panic("unknown builtin: " + builtin) } - return &types.Signature{Params: params, IsVariadic: isVariadic} + return types.NewSignature(nil, types.NewTuple(params...), nil, isVariadic) } diff --git a/ssa/util.go b/ssa/util.go index 09682d9d5e..acb4d33757 100644 --- a/ssa/util.go +++ b/ssa/util.go @@ -43,23 +43,10 @@ func isBlankIdent(e ast.Expr) bool { //// Type utilities. Some of these belong in go/types. -// underlyingType returns the underlying type of typ. -// TODO(gri): this is a copy of go/types.underlying; export that function. -// -func underlyingType(typ types.Type) types.Type { - if typ, ok := typ.(*types.NamedType); ok { - return typ.Underlying // underlying types are never NamedTypes - } - if typ == nil { - panic("underlyingType(nil)") - } - return typ -} - // isPointer returns true for types whose underlying type is a pointer. func isPointer(typ types.Type) bool { - if nt, ok := typ.(*types.NamedType); ok { - typ = nt.Underlying + if nt, ok := typ.(*types.Named); ok { + typ = nt.Underlying() } _, ok := typ.(*types.Pointer) return ok @@ -67,39 +54,22 @@ func isPointer(typ types.Type) bool { // pointer(typ) returns the type that is a pointer to typ. func pointer(typ types.Type) *types.Pointer { - return &types.Pointer{Base: typ} -} - -// indirect(typ) assumes that typ is a pointer type, -// or named alias thereof, and returns its base type. -// Panic ensures if it is not a pointer. -// -func indirectType(ptr types.Type) types.Type { - if v, ok := underlyingType(ptr).(*types.Pointer); ok { - return v.Base - } - // When debugging it is convenient to comment out this line - // and let it continue to print the (illegal) SSA form. - panic("indirect() of non-pointer type: " + ptr.String()) - return nil -} - -// deref returns a pointer's base type; otherwise it returns typ. -func deref(typ types.Type) types.Type { - if typ, ok := underlyingType(typ).(*types.Pointer); ok { - return typ.Base - } - return typ + return types.NewPointer(typ) } // methodIndex returns the method (and its index) named id within the -// method table methods of named or interface type typ. If not found, +// method table of named or interface type typ. If not found, // panic ensues. // -func methodIndex(typ types.Type, methods []*types.Method, id Id) (i int, m *types.Method) { - for i, m = range methods { - if IdFromQualifiedName(m.QualifiedName) == id { - return +func methodIndex(typ types.Type, id Id) (int, *types.Func) { + t := typ.(interface { + NumMethods() int + Method(i int) *types.Func + }) + for i, n := 0, t.NumMethods(); i < n; i++ { + m := t.Method(i) + if MakeId(m.Name(), m.Pkg()) == id { + return i, m } } panic(fmt.Sprint("method not found: ", id, " in interface ", typ)) @@ -109,15 +79,17 @@ func methodIndex(typ types.Type, methods []*types.Method, id Id) (i int, m *type // i.e. x's methods are a subset of y's. // func isSuperinterface(x, y *types.Interface) bool { - if len(y.Methods) < len(x.Methods) { + if y.NumMethods() < x.NumMethods() { return false } // TODO(adonovan): opt: this is quadratic. outer: - for _, xm := range x.Methods { - for _, ym := range y.Methods { - if IdFromQualifiedName(xm.QualifiedName) == IdFromQualifiedName(ym.QualifiedName) { - if !types.IsIdentical(xm.Type, ym.Type) { + for i, n := 0, x.NumMethods(); i < n; i++ { + xm := x.Method(i) + for j, m := 0, y.NumMethods(); j < m; j++ { + ym := y.Method(j) + if MakeId(xm.Name(), xm.Pkg()) == MakeId(ym.Name(), ym.Pkg()) { + if !types.IsIdentical(xm.Type(), ym.Type()) { return false // common name but conflicting types } continue outer @@ -152,9 +124,9 @@ func objKind(obj types.Object) ast.ObjKind { func canHaveConcreteMethods(typ types.Type, allowPtr bool) bool { switch typ := typ.(type) { case *types.Pointer: - return allowPtr && canHaveConcreteMethods(typ.Base, false) - case *types.NamedType: - switch typ.Underlying.(type) { + return allowPtr && canHaveConcreteMethods(typ.Elem(), false) + case *types.Named: + switch typ.Underlying().(type) { case *types.Pointer, *types.Interface: return false } @@ -176,7 +148,7 @@ func canHaveConcreteMethods(typ types.Type, allowPtr bool) bool { func DefaultType(typ types.Type) types.Type { if t, ok := typ.(*types.Basic); ok { k := types.Invalid - switch t.Kind { + switch t.Kind() { // case UntypedNil: // There is no default type for nil. For a good error message, // catch this case before calling this function. @@ -201,7 +173,9 @@ func DefaultType(typ types.Type) types.Type { // makeId returns the Id (name, pkg) if the name is exported or // (name, nil) otherwise. // -func makeId(name string, pkg *types.Package) (id Id) { +// Exported to exp/ssa/interp. +// +func MakeId(name string, pkg *types.Package) (id Id) { id.Name = name if !ast.IsExported(name) { id.Pkg = pkg @@ -213,15 +187,6 @@ func makeId(name string, pkg *types.Package) (id Id) { return } -// IdFromQualifiedName returns the Id (qn.Name, qn.Pkg) if qn is an -// exported name or (qn.Name, nil) otherwise. -// -// Exported to exp/ssa/interp. -// -func IdFromQualifiedName(qn types.QualifiedName) Id { - return makeId(qn.Name, qn.Pkg) -} - type ids []Id // a sortable slice of Id func (p ids) Len() int { return len(p) }