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
This commit is contained in:
Robert Griesemer 2013-07-12 21:09:33 -07:00
parent b042505490
commit f1a889124d
25 changed files with 160 additions and 167 deletions

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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
}
}

View File

@ -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] }

View File

@ -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

View File

@ -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
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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() {

View File

@ -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) }

View File

@ -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) {

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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()))
}
}

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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() {

View File

@ -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{

View File

@ -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.