| 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. |
| |
| // 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 |
| } |
| _, ok := typ.(*types.Pointer) |
| return ok |
| } |
| |
| // 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 |
| } |
| |
| // methodIndex returns the method (and its index) named id within the |
| // method table methods 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 |
| } |
| } |
| 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 len(y.Methods) < len(x.Methods) { |
| 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) { |
| return false // common name but conflicting types |
| } |
| continue outer |
| } |
| } |
| return false // y doesn't have this method |
| } |
| return true |
| } |
| |
| // objKind returns the syntactic category of the named entity denoted by obj. |
| func objKind(obj types.Object) ast.ObjKind { |
| switch obj.(type) { |
| case *types.Package: |
| return ast.Pkg |
| case *types.TypeName: |
| return ast.Typ |
| case *types.Const: |
| return ast.Con |
| case *types.Var: |
| return ast.Var |
| case *types.Func: |
| return ast.Fun |
| } |
| panic(fmt.Sprintf("unexpected Object type: %T", obj)) |
| } |
| |
| // 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.Base, false) |
| case *types.NamedType: |
| 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. |
| // |
| 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 |
| } |
| |
| // 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) } |
| 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") |
| } |
| } |