From f1a889124ded24f445d5898bb3c19f8af64d4f58 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Fri, 12 Jul 2013 21:09:33 -0700 Subject: [PATCH] go.tools/go/types: cleanups Objects: - provide IsExported, SameName, uniqueName methods - clean up a lot of dependent code Scopes: - don't add children to Universe scope (!) - document Node, WriteTo Types: - remove Deref in favor of internal function deref ssa, ssa/interp: - introduced local deref, adjusted code - fixed some "Underlying" bugs (pun intended) R=adonovan CC=golang-dev https://golang.org/cl/11232043 --- go/types/builtins.go | 2 +- go/types/call.go | 2 +- go/types/eval.go | 6 --- go/types/expr.go | 4 +- go/types/gcimporter.go | 2 +- go/types/lookup.go | 18 ++------- go/types/methodset.go | 52 +++++++++--------------- go/types/objects.go | 78 ++++++++++++++++++++++++++---------- go/types/predicates.go | 27 +++---------- go/types/resolver.go | 2 +- go/types/scope.go | 34 +++++++++++----- go/types/testdata/const0.src | 2 +- go/types/testdata/expr3.src | 6 +++ go/types/types.go | 28 ------------- go/types/universe.go | 3 +- ssa/builder.go | 14 +++---- ssa/create.go | 2 +- ssa/emit.go | 4 +- ssa/func.go | 2 +- ssa/interp/interp.go | 15 +++++-- ssa/lift.go | 6 +-- ssa/lvalue.go | 2 +- ssa/print.go | 4 +- ssa/promote.go | 4 +- ssa/util.go | 8 ++++ 25 files changed, 160 insertions(+), 167 deletions(-) diff --git a/go/types/builtins.go b/go/types/builtins.go index f0203d4e..dcd5d1da 100644 --- a/go/types/builtins.go +++ b/go/types/builtins.go @@ -340,7 +340,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin) { if x.mode == invalid { goto Error } - base := x.typ.Deref() + base, _ := deref(x.typ) sel := arg.Sel.Name obj, index, indirect := LookupFieldOrMethod(base, check.pkg, arg.Sel.Name) switch obj.(type) { diff --git a/go/types/call.go b/go/types/call.go index e977d83b..066b8383 100644 --- a/go/types/call.go +++ b/go/types/call.go @@ -196,7 +196,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) { if exp == nil { check.errorf(e.Pos(), "%s not declared by package %s", sel, ident) goto Error - } else if !ast.IsExported(exp.Name()) { + } else if !exp.IsExported() { // gcimported package scopes contain non-exported // objects such as types used in partially exported // objects - do not accept them diff --git a/go/types/eval.go b/go/types/eval.go index 9232f685..4fc161dc 100644 --- a/go/types/eval.go +++ b/go/types/eval.go @@ -15,12 +15,6 @@ import ( "code.google.com/p/go.tools/go/exact" ) -// TODO(gri) Calling IsIdentity on struct or interface types created -// with New or Eval in the Universe context will crash if the types -// contain non-exported fields or methods because those require a non- -// nil package. The type checker should disallow non-exported methods -// and fields in types in Universe scope. - // New is a convenience function to create a new type from a given // expression or type literal string evaluated in Universe scope. // New(str) is shorthand for Eval(str, nil, nil), but only returns diff --git a/go/types/expr.go b/go/types/expr.go index 3a0205e0..5b7b9575 100644 --- a/go/types/expr.go +++ b/go/types/expr.go @@ -880,7 +880,7 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) { goto Error } - switch utyp := typ.Deref().Underlying().(type) { + switch typ, _ := deref(typ); utyp := typ.Underlying().(type) { case *Struct: if len(e.Elts) == 0 { break @@ -991,7 +991,7 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) { } default: - check.errorf(e.Pos(), "%s is not a valid composite literal type", typ) + check.errorf(e.Pos(), "invalid composite literal type %s", typ) goto Error } diff --git a/go/types/gcimporter.go b/go/types/gcimporter.go index 2ca0fd20..7cf524d8 100644 --- a/go/types/gcimporter.go +++ b/go/types/gcimporter.go @@ -466,7 +466,7 @@ func (p *gcParser) parseField() (*Field, string) { anonymous := false if name == "" { // anonymous field - typ must be T or *T and T must be a type name - switch typ := typ.Deref().(type) { + switch typ, _ := deref(typ); typ := typ.(type) { case *Basic: // basic types are named types pkg = nil name = typ.name diff --git a/go/types/lookup.go b/go/types/lookup.go index 2ed438dc..8b7842e4 100644 --- a/go/types/lookup.go +++ b/go/types/lookup.go @@ -6,8 +6,6 @@ package types -import "go/ast" - // TODO(gri) The named type consolidation and seen maps below must be // indexed by unique keys for a given type. Verify that named // types always have only one representation (even when imported @@ -129,7 +127,7 @@ func lookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index case *Struct: // look for a matching field and collect embedded types for i, f := range t.fields { - if f.isMatch(pkg, name) { + if f.SameName(pkg, name) { assert(f.typ != nil) index = concat(e.index, i) if obj != nil || e.multiples { @@ -274,8 +272,6 @@ func MissingMethod(typ Type, T *Interface) (method *Func, wrongType bool) { // Deref dereferences typ if it is a pointer and returns its base and true. // Otherwise it returns (typ, false). -// In contrast, Type.Deref also dereferenciates if type is a named type -// that is a pointer. func deref(typ Type) (Type, bool) { if p, _ := typ.(*Pointer); p != nil { return p.base, true @@ -297,11 +293,7 @@ func fieldIndex(fields []*Field, pkg *Package, name string) int { return -1 // blank identifiers are never found } for i, f := range fields { - // 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 f.name == name && (ast.IsExported(name) || f.pkg.path == pkg.path) { + if f.SameName(pkg, name) { return i } } @@ -312,11 +304,7 @@ func fieldIndex(fields []*Field, pkg *Package, name string) int { func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) { assert(name != "_") for i, m := range methods { - // 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 m.name == name && (ast.IsExported(name) || m.pkg.path == pkg.path) { + if m.SameName(pkg, name) { return i, m } } diff --git a/go/types/methodset.go b/go/types/methodset.go index 7595e582..70a838fb 100644 --- a/go/types/methodset.go +++ b/go/types/methodset.go @@ -9,7 +9,6 @@ package types import ( "bytes" "fmt" - "go/ast" "sort" ) @@ -25,7 +24,7 @@ func (s *MethodSet) String() string { fmt.Fprintln(&buf) } for _, m := range s.list { - fmt.Fprintf(&buf, "\t%s\n", key(m.pkg, m.name)) + fmt.Fprintf(&buf, "\t%s\n", m.uniqueName()) } fmt.Fprintln(&buf, "}") return buf.String() @@ -39,16 +38,16 @@ func (s *MethodSet) At(i int) *Func { return s.list[i] } // Lookup returns the method with matching package and name, or nil if not found. func (s *MethodSet) Lookup(pkg *Package, name string) *Func { - k := key(pkg, name) + key := (&object{pkg: pkg, name: name}).uniqueName() i := sort.Search(len(s.list), func(i int) bool { m := s.list[i] - return key(m.pkg, m.name) >= k + return m.uniqueName() >= key }) if i < len(s.list) { m := s.list[i] - if key(m.pkg, m.name) == k { + if m.uniqueName() == key { return m } } @@ -157,22 +156,11 @@ func NewMethodSet(typ Type) *MethodSet { list = append(list, m) } } - sort.Sort(byKey(list)) + sort.Sort(byUniqueName(list)) return &MethodSet{list} } -// key computes a unique (lookup and sort) key given a package and name. -func key(pkg *Package, name string) string { - if ast.IsExported(name) { - return name - } - if pkg == nil { - panic("unexported object without package information: " + name) - } - return pkg.path + "." + name -} - // A fieldSet is a set of fields and name collisions. // A conflict indicates that multiple fields with the same package and name appeared. type fieldSet map[string]*Field // a nil entry indicates a name collision @@ -181,15 +169,15 @@ type fieldSet map[string]*Field // a nil entry indicates a name collision // If multiples is set, f appears multiple times // and is treated as a collision at this level. func (s fieldSet) add(f *Field, multiples bool) { - k := key(f.pkg, f.name) + key := f.uniqueName() // if f is not in the set, add it if !multiples { - if _, found := s[k]; !found { - s[k] = f + if _, found := s[key]; !found { + s[key] = f return } } - s[k] = nil // collision + s[key] = nil // collision } // A methodSet is a set of methods and name collisions. @@ -201,25 +189,21 @@ type methodSet map[string]*Func // a nil entry indicates a name collision // and is treated as a collision at this level. func (s methodSet) add(list []*Func, multiples bool) { for _, m := range list { - k := key(m.pkg, m.name) + key := m.uniqueName() // if m is not in the set, add it if !multiples { - if _, found := s[k]; !found { - s[k] = m + if _, found := s[key]; !found { + s[key] = m continue } } - s[k] = nil // collision + s[key] = nil // collision } } -// byKey function lists can be sorted by key(pkg, name). -type byKey []*Func +// byUniqueName function lists can be sorted by their unique names. +type byUniqueName []*Func -func (a byKey) Len() int { return len(a) } -func (a byKey) Less(i, j int) bool { - x := a[i] - y := a[j] - return key(x.pkg, x.name) < key(y.pkg, y.name) -} -func (a byKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a byUniqueName) Len() int { return len(a) } +func (a byUniqueName) Less(i, j int) bool { return a[i].uniqueName() < a[j].uniqueName() } +func (a byUniqueName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } diff --git a/go/types/objects.go b/go/types/objects.go index e12c3c0e..a188c971 100644 --- a/go/types/objects.go +++ b/go/types/objects.go @@ -20,13 +20,21 @@ import ( // All objects implement the Object interface. // type Object interface { - Parent() *Scope // scope in which this object is declared - Pos() token.Pos // position of object identifier in declaration - Pkg() *Package // nil for objects in the Universe scope and labels - Name() string // package local object name - Type() Type // object type + Parent() *Scope // scope in which this object is declared + Pos() token.Pos // position of object identifier in declaration + Pkg() *Package // nil for objects in the Universe scope and labels + Name() string // package local object name + Type() Type // object type + IsExported() bool // reports whether the name starts with a capital letter + + // SameName reports whether the object's name is the same as some + // other qualified name, per the rules of Uniqueness of identifiers. + SameName(pkg *Package, name string) bool + + // String returns a human-readable string of the object. String() string + // setParent sets the parent scope of the object. setParent(*Scope) } @@ -39,11 +47,49 @@ type object struct { typ Type } -func (obj *object) Parent() *Scope { return obj.parent } -func (obj *object) Pos() token.Pos { return obj.pos } -func (obj *object) Pkg() *Package { return obj.pkg } -func (obj *object) Name() string { return obj.name } -func (obj *object) Type() Type { return obj.typ } +func (obj *object) Parent() *Scope { return obj.parent } +func (obj *object) Pos() token.Pos { return obj.pos } +func (obj *object) Pkg() *Package { return obj.pkg } +func (obj *object) Name() string { return obj.name } +func (obj *object) Type() Type { return obj.typ } +func (obj *object) IsExported() bool { return ast.IsExported(obj.name) } + +func (obj *object) SameName(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 name != obj.name { + return false + } + // obj.Name == name + if obj.IsExported() { + return true + } + // not exported, so packages must be the same (pkg == nil for + // fields in Universe scope; this can only happen for types + // introduced via Eval) + if pkg == nil || obj.pkg == nil { + return pkg == obj.pkg + } + // pkg != nil && obj.pkg != nil + return pkg.path == obj.pkg.path +} + +func (obj *object) uniqueName() string { + if obj.IsExported() { + return obj.name + } + // unexported names need the package path for differentiation + path := "" + if obj.pkg != nil { + path = obj.pkg.path + if path == "" { + path = "?" + } + } + return path + "." + obj.name +} func (obj *object) toString(kind string, typ Type) string { var buf bytes.Buffer @@ -136,18 +182,6 @@ func NewField(pos token.Pos, pkg *Package, name string, typ Type, anonymous bool func (obj *Field) String() string { return obj.toString("field", obj.typ) } func (obj *Field) Anonymous() bool { return obj.anonymous } -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 name != f.name { - return false - } - // f.Name == name - return ast.IsExported(name) || pkg.path == f.pkg.path -} - // A Func represents a declared function. type Func struct { object diff --git a/go/types/predicates.go b/go/types/predicates.go index ffdcb926..17d8253a 100644 --- a/go/types/predicates.go +++ b/go/types/predicates.go @@ -6,8 +6,6 @@ package types -import "go/ast" - func isNamed(typ Type) bool { if _, ok := typ.(*Basic); ok { return ok @@ -133,7 +131,7 @@ func IsIdentical(x, y Type) bool { g := y.fields[i] if f.anonymous != g.anonymous || x.Tag(i) != y.Tag(i) || - !f.isMatch(g.pkg, g.name) || + !f.SameName(g.pkg, g.name) || !IsIdentical(f.typ, g.typ) { return false } @@ -208,19 +206,6 @@ func identicalTypes(a, b *Tuple) bool { return true } -// qname computes the "qualified name" of a function. -// TODO(gri) This is similar in functionality to Field.isMatch. -// Try to consolidate. -func qname(f *Func) string { - if ast.IsExported(f.name) { - return f.name - } - if f.pkg == nil { - panic("unexported function without package information") - } - return f.pkg.path + "." + f.name -} - // identicalMethods returns true if both slices a and b have the // same length and corresponding entries have identical types. // TODO(gri) make this more efficient (e.g., sort them on completion) @@ -231,14 +216,14 @@ func identicalMethods(a, b []*Func) bool { m := make(map[string]*Func) for _, x := range a { - k := qname(x) - assert(m[k] == nil) // method list must not have duplicate entries - m[k] = x + key := x.uniqueName() + assert(m[key] == nil) // method list must not have duplicate entries + m[key] = x } for _, y := range b { - k := qname(y) - if x := m[k]; x == nil || !IsIdentical(x.typ, y.typ) { + key := y.uniqueName() + if x := m[key]; x == nil || !IsIdentical(x.typ, y.typ) { return false } } diff --git a/go/types/resolver.go b/go/types/resolver.go index f7e6b0a5..2e155864 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -175,7 +175,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) { // gcimported package scopes contain non-exported // objects such as types used in partially exported // objects - do not accept them - if ast.IsExported(obj.Name()) { + if obj.IsExported() { // Note: This will change each imported object's scope! // May be an issue for types aliases. check.declare(fileScope, nil, obj) diff --git a/go/types/scope.go b/go/types/scope.go index 19a77305..649dd339 100644 --- a/go/types/scope.go +++ b/go/types/scope.go @@ -31,7 +31,8 @@ type Scope struct { // scope, if any. func NewScope(parent *Scope) *Scope { scope := &Scope{parent: parent} - if parent != nil { + // don't add children to Universe scope! + if parent != nil && parent != Universe { parent.children = append(parent.children, scope) } return scope @@ -42,10 +43,22 @@ func (s *Scope) Parent() *Scope { return s.parent } -// Node returns the ast.Node responsible for this scope. +// Node returns the ast.Node responsible for this scope, +// which may be one of the following: +// +// ast.File +// ast.FuncType +// ast.BlockStmt +// ast.IfStmt +// ast.SwitchStmt +// ast.TypeSwitchStmt +// ast.CaseClause +// ast.CommClause +// ast.ForStmt +// ast.RangeStmt +// // The result is nil if there is no corresponding node -// (e.g., for the universe scope, package scope, or -// imported packages). +// (universe and package scopes). func (s *Scope) Node() ast.Node { return s.node } @@ -107,13 +120,8 @@ func (s *Scope) Lookup(pkg *Package, name string) Object { } // slow path: both pkg path and name must match - // TODO(gri) if packages were canonicalized, we could just compare the packages 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) { + if obj.SameName(pkg, name) { return obj } } @@ -138,6 +146,8 @@ func (s *Scope) LookupParent(name string) Object { return nil } +// TODO(gri): Should Insert not be exported? + // Insert attempts to insert an object obj into scope s. // If s already contains an object with the same package path // and name, Insert leaves s unchanged and returns that object. @@ -155,6 +165,10 @@ func (s *Scope) Insert(obj Object) Object { return nil } +// WriteTo writes a string representation of the scope to w. +// The level of indentation is controlled by n >= 0, with +// n == 0 for no indentation. +// If recurse is set, it also prints nested (children) scopes. func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) { const ind = ". " indn := strings.Repeat(ind, n) diff --git a/go/types/testdata/const0.src b/go/types/testdata/const0.src index 6bf50c34..b0a83279 100644 --- a/go/types/testdata/const0.src +++ b/go/types/testdata/const0.src @@ -226,7 +226,7 @@ const ( _b1 = assert(iota + iota2 == 5) _b2 = len([iota]int{}) // iota may appear in a type! _b3 = assert(_b2 == 2) - _b4 = len(A /* ERROR "not a valid composite literal" */ {}) + _b4 = len(A /* ERROR "invalid composite literal" */ {}) ) type A [iota /* ERROR "cannot use iota" */ ]int diff --git a/go/types/testdata/expr3.src b/go/types/testdata/expr3.src index a0f088db..d3cbf62a 100644 --- a/go/types/testdata/expr3.src +++ b/go/types/testdata/expr3.src @@ -146,6 +146,12 @@ func struct_literals() { _ = T0{1, 2} /* ERROR "too few values" */ _ = T0{1, 2, 3, 4 /* ERROR "too many values" */ } _ = T0{1, "foo" /* ERROR "cannot convert" */, 3.4 /* ERROR "overflows" */} + + // invalid type + type P *struct{ + x int + } + _ = P /* ERROR "invalid composite literal type" */ {} } func array_literals() { diff --git a/go/types/types.go b/go/types/types.go index d0c37529..dfedae41 100644 --- a/go/types/types.go +++ b/go/types/types.go @@ -14,17 +14,8 @@ 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 - - // 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. @@ -371,25 +362,6 @@ 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 { - // TODO(gri) Is this the right operation here given how Deref is used? - 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) } diff --git a/go/types/universe.go b/go/types/universe.go index 45b10a42..a06198c8 100644 --- a/go/types/universe.go +++ b/go/types/universe.go @@ -7,7 +7,6 @@ package types import ( - "go/ast" "go/token" "strings" @@ -132,7 +131,7 @@ func def(obj Object) { } // exported identifiers go into package unsafe scope := Universe - if ast.IsExported(name) { + if obj.IsExported() { scope = Unsafe.scope // set Pkg field switch obj := obj.(type) { diff --git a/ssa/builder.go b/ssa/builder.go index 58f93f05..1806ada7 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -302,7 +302,7 @@ func (b *builder) builtin(fn *Function, name string, args []ast.Expr, typ types. } case "new": - return emitNew(fn, typ.Underlying().Deref(), pos) + return emitNew(fn, deref(typ), pos) case "len", "cap": // Special case: len or cap of an array or *array is @@ -310,7 +310,7 @@ 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 := fn.Pkg.typeOf(args[0]).Deref().Underlying() + t := deref(fn.Pkg.typeOf(args[0])).Underlying() if at, ok := t.(*types.Array); ok { b.expr(fn, args[0]) // for effects only return intLiteral(at.Len()) @@ -357,7 +357,7 @@ func (b *builder) selectField(fn *Function, e *ast.SelectorExpr, wantAddr, escap } // Apply field selections. - st := tx.Deref().Underlying().(*types.Struct) + st := deref(tx).Underlying().(*types.Struct) for i, index := range indices { f := st.Field(index) ft := f.Type() @@ -407,7 +407,7 @@ func (b *builder) selectField(fn *Function, e *ast.SelectorExpr, wantAddr, escap } // May be nil at end of last iteration: - st, _ = ft.Deref().Underlying().(*types.Struct) + st, _ = deref(ft).Underlying().(*types.Struct) } return v @@ -447,7 +447,7 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { return address{addr: v} case *ast.CompositeLit: - t := fn.Pkg.typeOf(e).Deref() + t := deref(fn.Pkg.typeOf(e)) var v Value if escaping { v = emitNew(fn, t, e.Lbrace) @@ -1639,7 +1639,7 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) { fn.addLocalForIdent(comm.Lhs[1].(*ast.Ident)) } ok := b.addr(fn, comm.Lhs[1], false) // non-escaping - ok.store(fn, emitExtract(fn, sel, 1, ok.typ().Deref())) + ok.store(fn, emitExtract(fn, sel, 1, deref(ok.typ()))) } r++ } @@ -1736,7 +1736,7 @@ func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value // Determine number of iterations. var length Value - if arr, ok := x.Type().Deref().(*types.Array); ok { + if arr, ok := deref(x.Type()).Underlying().(*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 diff --git a/ssa/create.go b/ssa/create.go index d7d27480..37615cdf 100644 --- a/ssa/create.go +++ b/ssa/create.go @@ -136,7 +136,7 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { } else { // Method declaration. _, method := namedTypeMethodIndex( - recv.Type().Deref().(*types.Named), + deref(recv.Type()).(*types.Named), MakeId(name, pkg.Object)) pkg.Prog.concreteMethods[method] = fn } diff --git a/ssa/emit.go b/ssa/emit.go index a0e908bb..c34b8806 100644 --- a/ssa/emit.go +++ b/ssa/emit.go @@ -24,7 +24,7 @@ func emitNew(f *Function, typ types.Type, pos token.Pos) Value { // func emitLoad(f *Function, addr Value) *UnOp { v := &UnOp{Op: token.MUL, X: addr} - v.setType(addr.Type().Deref()) + v.setType(deref(addr.Type())) f.emit(v) return v } @@ -203,7 +203,7 @@ func emitConv(f *Function, val Value, typ types.Type) Value { func emitStore(f *Function, addr, val Value) *Store { s := &Store{ Addr: addr, - Val: emitConv(f, val, addr.Type().Deref()), + Val: emitConv(f, val, deref(addr.Type())), } f.emit(s) return s diff --git a/ssa/func.go b/ssa/func.go index 085dad6e..9de5fa45 100644 --- a/ssa/func.go +++ b/ssa/func.go @@ -557,7 +557,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(), l.Type().Deref()) + fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, l.Name(), deref(l.Type())) } } diff --git a/ssa/interp/interp.go b/ssa/interp/interp.go index 3ae36258..ee68c97c 100644 --- a/ssa/interp/interp.go +++ b/ssa/interp/interp.go @@ -241,7 +241,7 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation { // local addr = fr.env[instr].(*value) } - *addr = zero(instr.Type().Deref()) + *addr = zero(deref(instr.Type())) case *ssa.MakeSlice: slice := make([]value, asInt(fr.get(instr.Cap))) @@ -464,7 +464,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(l.Type().Deref()) + fr.locals[i] = zero(deref(l.Type())) fr.env[l] = &fr.locals[i] } for i, p := range fn.Params { @@ -549,7 +549,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(v.Type().Deref()) + cell := zero(deref(v.Type())) i.globals[v] = &cell } } @@ -619,3 +619,12 @@ func Interpret(mainpkg *ssa.Package, mode Mode, filename string, args []string) } return } + +// deref returns a pointer's element type; otherwise it returns typ. +// TODO(adonovan): Import from ssa? +func deref(typ types.Type) types.Type { + if p, ok := typ.Underlying().(*types.Pointer); ok { + return p.Elem() + } + return typ +} diff --git a/ssa/lift.go b/ssa/lift.go index d44ea106..bdffc133 100644 --- a/ssa/lift.go +++ b/ssa/lift.go @@ -310,7 +310,7 @@ func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool { // important optimisation for pointer analysis because the // extra indirection really hurts precision under Das's // algorithm. - switch alloc.Type().Deref().Underlying().(type) { + switch deref(alloc.Type()).Underlying().(type) { case *types.Array, *types.Struct: return false } @@ -379,7 +379,7 @@ func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool { Comment: alloc.Name(), } phi.pos = alloc.Pos() - phi.setType(alloc.Type().Deref()) + phi.setType(deref(alloc.Type())) phi.block = v if debugLifting { fmt.Fprintf(os.Stderr, "place %s = %s at block %s\n", phi.Name(), phi, v) @@ -426,7 +426,7 @@ func replaceAll(x, y Value) { func renamed(renaming []Value, alloc *Alloc) Value { v := renaming[alloc.index] if v == nil { - v = zeroLiteral(alloc.Type().Deref()) + v = zeroLiteral(deref(alloc.Type())) renaming[alloc.index] = v } return v diff --git a/ssa/lvalue.go b/ssa/lvalue.go index e1e18690..33b09bb7 100644 --- a/ssa/lvalue.go +++ b/ssa/lvalue.go @@ -35,7 +35,7 @@ func (a address) store(fn *Function, v Value) { } func (a address) typ() types.Type { - return a.addr.Type().Deref() + return deref(a.addr.Type()) } // An element is an lvalue represented by m[k], the location of an diff --git a/ssa/print.go b/ssa/print.go index 82647a9f..c6429dff 100644 --- a/ssa/print.go +++ b/ssa/print.go @@ -83,7 +83,7 @@ func (v *Alloc) String() string { if v.Heap { op = "new" } - return fmt.Sprintf("%s %s", op, v.Type().Deref()) + return fmt.Sprintf("%s %s", op, deref(v.Type())) } func (v *Phi) String() string { @@ -225,7 +225,7 @@ func (v *MakeChan) String() string { } func (v *FieldAddr) String() string { - st := v.X.Type().Deref().Underlying().(*types.Struct) + st := deref(v.X.Type()).Underlying().(*types.Struct) // Be robust against a bad index. name := "?" if 0 <= v.Field && v.Field < st.NumFields() { diff --git a/ssa/promote.go b/ssa/promote.go index a31d797f..df382edf 100644 --- a/ssa/promote.go +++ b/ssa/promote.go @@ -156,7 +156,7 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet { if node != nil { t = node.field.Type() } - t = t.Deref() + t = deref(t) if nt, ok := t.(*types.Named); ok { for i, n := 0, nt.NumMethods(); i < n; i++ { @@ -309,7 +309,7 @@ func promotionWrapper(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 := v.Type().Deref().Underlying().(*types.Struct); !ok { + if _, ok := deref(v.Type()).Underlying().(*types.Struct); !ok { panic(fmt.Sprint("not a *struct: ", v.Type(), p.field.Type)) } sel := &FieldAddr{ diff --git a/ssa/util.go b/ssa/util.go index 2554938b..1d0b5b4a 100644 --- a/ssa/util.go +++ b/ssa/util.go @@ -55,6 +55,14 @@ func pointer(typ types.Type) *types.Pointer { return types.NewPointer(typ) } +// deref returns a pointer's element type; otherwise it returns typ. +func deref(typ types.Type) types.Type { + if p, ok := typ.Underlying().(*types.Pointer); ok { + return p.Elem() + } + return typ +} + // namedTypeMethodIndex returns the method (and its index) named id // within the set of explicitly declared concrete methods of named // type typ. If not found, panic ensues.