| package ssa |
| |
| // This file defines a number of miscellaneous utility functions. |
| |
| import ( |
| "fmt" |
| "go/ast" |
| "io" |
| "os" |
| "reflect" |
| |
| "code.google.com/p/go.tools/go/types" |
| ) |
| |
| func unreachable() { |
| panic("unreachable") |
| } |
| |
| //// AST utilities |
| |
| // noparens returns e with any enclosing parentheses stripped. |
| func noparens(e ast.Expr) ast.Expr { |
| for { |
| p, ok := e.(*ast.ParenExpr) |
| if !ok { |
| break |
| } |
| e = p.X |
| } |
| return e |
| } |
| |
| // isBlankIdent returns true iff e is an Ident with name "_". |
| // They have no associated types.Object, and thus no type. |
| // |
| // TODO(gri): consider making typechecker not treat them differently. |
| // It's one less thing for clients like us to worry about. |
| // |
| func isBlankIdent(e ast.Expr) bool { |
| id, ok := e.(*ast.Ident) |
| return ok && id.Name == "_" |
| } |
| |
| //// Type utilities. Some of these belong in go/types. |
| |
| // isPointer returns true for types whose underlying type is a pointer. |
| func isPointer(typ types.Type) bool { |
| if nt, ok := typ.(*types.Named); ok { |
| typ = nt.Underlying() |
| } |
| _, ok := typ.(*types.Pointer) |
| return ok |
| } |
| |
| // pointer(typ) returns the type that is a pointer to typ. |
| // TODO(adonovan): inline and eliminate. |
| func pointer(typ types.Type) *types.Pointer { |
| return types.NewPointer(typ) |
| } |
| |
| // methodIndex returns the method (and its index) named id within the |
| // method table of named or interface type typ. If not found, |
| // panic ensues. |
| // |
| 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)) |
| } |
| |
| // isSuperinterface returns true if x is a superinterface of y, |
| // i.e. x's methods are a subset of y's. |
| // |
| func isSuperinterface(x, y *types.Interface) bool { |
| if y.NumMethods() < x.NumMethods() { |
| return false |
| } |
| // TODO(adonovan): opt: this is quadratic. |
| outer: |
| 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 |
| } |
| } |
| return false // y doesn't have this method |
| } |
| return true |
| } |
| |
| // canHaveConcreteMethods returns true iff typ may have concrete |
| // methods associated with it. Callers must supply allowPtr=true. |
| // |
| // TODO(gri): consider putting this in go/types. It's surprisingly subtle. |
| func canHaveConcreteMethods(typ types.Type, allowPtr bool) bool { |
| switch typ := typ.(type) { |
| case *types.Pointer: |
| return allowPtr && canHaveConcreteMethods(typ.Elem(), false) |
| case *types.Named: |
| switch typ.Underlying().(type) { |
| case *types.Pointer, *types.Interface: |
| return false |
| } |
| return true |
| case *types.Struct: |
| return true |
| } |
| return false |
| } |
| |
| // 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 types.Typ[types.Invalid]. |
| // |
| // Exported to exp/ssa/interp. |
| // |
| // TODO(gri): this is a copy of go/types.defaultType; export that function. |
| // |
| func DefaultType(typ types.Type) types.Type { |
| if t, ok := typ.(*types.Basic); ok { |
| k := types.Invalid |
| switch t.Kind() { |
| // case UntypedNil: |
| // There is no default type for nil. For a good error message, |
| // catch this case before calling this function. |
| case types.UntypedBool: |
| k = types.Bool |
| case types.UntypedInt: |
| k = types.Int |
| case types.UntypedRune: |
| k = types.Rune |
| case types.UntypedFloat: |
| k = types.Float64 |
| case types.UntypedComplex: |
| k = types.Complex128 |
| case types.UntypedString: |
| k = types.String |
| } |
| typ = types.Typ[k] |
| } |
| return typ |
| } |
| |
| // makeId returns the Id (name, pkg) if the name is exported or |
| // (name, nil) otherwise. |
| // |
| // Exported to exp/ssa/interp. |
| // |
| func MakeId(name string, pkg *types.Package) (id Id) { |
| id.Name = name |
| if !ast.IsExported(name) { |
| id.Pkg = pkg |
| // TODO(gri): fix |
| // if pkg.Path() == "" { |
| // panic("Package " + pkg.Name() + "has empty Path") |
| // } |
| } |
| return |
| } |
| |
| type ids []Id // a sortable slice of Id |
| |
| func (p ids) Len() int { return len(p) } |
| func (p ids) Less(i, j int) bool { |
| x, y := p[i], p[j] |
| // *Package pointers are canonical so order by them. |
| // Don't use x.Pkg.ImportPath because sometimes it's empty. |
| // (TODO(gri): fix that.) |
| return reflect.ValueOf(x.Pkg).Pointer() < reflect.ValueOf(y.Pkg).Pointer() || |
| x.Pkg == y.Pkg && x.Name < y.Name |
| } |
| func (p ids) Swap(i, j int) { p[i], p[j] = p[j], p[i] } |
| |
| // logStack prints the formatted "start" message to stderr and |
| // returns a closure that prints the corresponding "end" message. |
| // Call using 'defer logStack(...)()' to show builder stack on panic. |
| // Don't forget trailing parens! |
| // |
| func logStack(format string, args ...interface{}) func() { |
| msg := fmt.Sprintf(format, args...) |
| io.WriteString(os.Stderr, msg) |
| io.WriteString(os.Stderr, "\n") |
| return func() { |
| io.WriteString(os.Stderr, msg) |
| io.WriteString(os.Stderr, " end\n") |
| } |
| } |