1
0
mirror of https://github.com/golang/go synced 2024-11-19 06:04:39 -07:00
go/ssa/util.go
Rob Pike 83f21b9226 go.tools: add missing files ssa/*.go
R=golang-dev, adonovan
CC=golang-dev
https://golang.org/cl/9500043
2013-05-17 13:25:48 -07:00

252 lines
6.5 KiB
Go

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