1
0
mirror of https://github.com/golang/go synced 2024-11-18 14:24:44 -07:00
go/ssa/util.go
Alan Donovan 80ec883f7b go.tools/ssa: several small clean-ups.
- removed a number of obsolete TODO(gri) comments.
- bring ssa.DefaultType back into sync with types.defaultType.
- re-enable types.Package.Path()!="" assertion.
- use Path() (not reflect pointer) in sort routine.
- make interp.checkInterface use types.MissingMethod.
- un-export ssa.MakeId function.
- inline pointer() into all callers, and delete.
- enable two more interp_tests: $GOROOT/test/{method3,cmp}.go
- add links to bugs to other interp_tests.
- add runtime.NumCPU to ssa/interp/externals.go

R=gri
CC=golang-dev
https://golang.org/cl/11353043
2013-07-16 12:23:55 -04:00

198 lines
4.9 KiB
Go

package ssa
// This file defines a number of miscellaneous utility functions.
import (
"fmt"
"go/ast"
"io"
"os"
"code.google.com/p/go.tools/go/types"
)
func unreachable() {
panic("unreachable")
}
//// AST utilities
// unparen returns e with any enclosing parentheses stripped.
func unparen(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.
//
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 {
_, ok := typ.Underlying().(*types.Pointer)
return ok
}
// 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.
//
func namedTypeMethodIndex(typ *types.Named, id Id) (int, *types.Func) {
for i, n := 0, typ.NumMethods(); i < n; i++ {
m := typ.Method(i)
if makeId(m.Name(), m.Pkg()) == id {
return i, m
}
}
panic(fmt.Sprint("method not found: ", id, " in named type ", typ))
}
// interfaceMethodIndex returns the method (and its index) named id
// within the method-set of interface type typ. If not found, panic
// ensues.
//
func interfaceMethodIndex(typ *types.Interface, id Id) (int, *types.Func) {
for i, n := 0, typ.NumMethods(); i < n; i++ {
m := typ.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. The default type
// for untyped nil is untyped nil.
//
// 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 := t.Kind()
switch k {
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
if pkg.Path() == "" {
panic("empty types.Package.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]
// TODO(adonovan): opt: where's Go's 3-valued strings.Compare function?
return x.Pkg.Path() < y.Pkg.Path() ||
x.Pkg.Path() == y.Pkg.Path() && 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")
}
}