2013-05-17 14:25:48 -06:00
|
|
|
package ssa
|
|
|
|
|
|
|
|
// This file defines utilities for querying the results of typechecker:
|
|
|
|
// types of expressions, values of constant expressions, referents of identifiers.
|
|
|
|
|
|
|
|
import (
|
|
|
|
"code.google.com/p/go.tools/go/types"
|
|
|
|
"fmt"
|
|
|
|
"go/ast"
|
2013-05-25 11:04:20 -06:00
|
|
|
"go/token"
|
2013-05-17 14:25:48 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
// TypeInfo contains information provided by the type checker about
|
|
|
|
// the abstract syntax for a single package.
|
|
|
|
type TypeInfo struct {
|
2013-05-25 11:04:20 -06:00
|
|
|
fset *token.FileSet
|
2013-05-17 14:25:48 -06:00
|
|
|
types map[ast.Expr]types.Type // inferred types of expressions
|
|
|
|
constants map[ast.Expr]*Literal // values of constant expressions
|
|
|
|
idents map[*ast.Ident]types.Object // canonical type objects for named entities
|
|
|
|
}
|
|
|
|
|
|
|
|
// TypeOf returns the type of expression e.
|
|
|
|
// Precondition: e belongs to the package's ASTs.
|
|
|
|
func (info *TypeInfo) TypeOf(e ast.Expr) types.Type {
|
|
|
|
// For Ident, b.types may be more specific than
|
|
|
|
// b.obj(id.(*ast.Ident)).GetType(),
|
|
|
|
// e.g. in the case of typeswitch.
|
|
|
|
if t, ok := info.types[e]; ok {
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
// The typechecker doesn't notify us of all Idents,
|
|
|
|
// e.g. s.Key and s.Value in a RangeStmt.
|
|
|
|
// So we have this fallback.
|
|
|
|
// TODO(gri): This is a typechecker bug. When fixed,
|
|
|
|
// eliminate this case and panic.
|
|
|
|
if id, ok := e.(*ast.Ident); ok {
|
2013-05-17 15:02:47 -06:00
|
|
|
return info.ObjectOf(id).Type()
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
|
|
|
panic("no type for expression")
|
|
|
|
}
|
|
|
|
|
|
|
|
// ValueOf returns the value of expression e if it is a constant,
|
|
|
|
// nil otherwise.
|
|
|
|
//
|
|
|
|
func (info *TypeInfo) ValueOf(e ast.Expr) *Literal {
|
|
|
|
return info.constants[e]
|
|
|
|
}
|
|
|
|
|
|
|
|
// ObjectOf returns the typechecker object denoted by the specified id.
|
|
|
|
// Precondition: id belongs to the package's ASTs.
|
|
|
|
//
|
|
|
|
func (info *TypeInfo) ObjectOf(id *ast.Ident) types.Object {
|
|
|
|
if obj, ok := info.idents[id]; ok {
|
|
|
|
return obj
|
|
|
|
}
|
2013-05-25 11:04:20 -06:00
|
|
|
panic(fmt.Sprintf("no types.Object for ast.Ident %s @ %s", id.Name, info.fset.Position(id.Pos())))
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// IsType returns true iff expression e denotes a type.
|
|
|
|
// Precondition: e belongs to the package's ASTs.
|
2013-05-17 15:02:47 -06:00
|
|
|
// e must be a true expression, not a KeyValueExpr, or an Ident
|
|
|
|
// appearing in a SelectorExpr or declaration.
|
2013-05-17 14:25:48 -06:00
|
|
|
//
|
|
|
|
func (info *TypeInfo) IsType(e ast.Expr) bool {
|
|
|
|
switch e := e.(type) {
|
|
|
|
case *ast.SelectorExpr: // pkg.Type
|
|
|
|
if obj := info.isPackageRef(e); obj != nil {
|
2013-05-30 10:13:42 -06:00
|
|
|
_, isType := obj.(*types.TypeName)
|
|
|
|
return isType
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
|
|
|
case *ast.StarExpr: // *T
|
|
|
|
return info.IsType(e.X)
|
|
|
|
case *ast.Ident:
|
2013-05-30 10:13:42 -06:00
|
|
|
_, isType := info.ObjectOf(e).(*types.TypeName)
|
|
|
|
return isType
|
2013-05-17 14:25:48 -06:00
|
|
|
case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType:
|
|
|
|
return true
|
|
|
|
case *ast.ParenExpr:
|
|
|
|
return info.IsType(e.X)
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// isPackageRef returns the identity of the object if sel is a
|
|
|
|
// package-qualified reference to a named const, var, func or type.
|
|
|
|
// Otherwise it returns nil.
|
|
|
|
// Precondition: sel belongs to the package's ASTs.
|
|
|
|
//
|
|
|
|
func (info *TypeInfo) isPackageRef(sel *ast.SelectorExpr) types.Object {
|
|
|
|
if id, ok := sel.X.(*ast.Ident); ok {
|
2013-05-30 10:13:42 -06:00
|
|
|
if pkg, ok := info.ObjectOf(id).(*types.Package); ok {
|
|
|
|
return pkg.Scope().Lookup(sel.Sel.Name)
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// builtinCallSignature returns a new Signature describing the
|
|
|
|
// effective type of a builtin operator for the particular call e.
|
|
|
|
//
|
|
|
|
// This requires ad-hoc typing rules for all variadic (append, print,
|
|
|
|
// println) and polymorphic (append, copy, delete, close) built-ins.
|
|
|
|
// This logic could be part of the typechecker, and should arguably
|
|
|
|
// be moved there and made accessible via an additional types.Context
|
|
|
|
// callback.
|
|
|
|
//
|
|
|
|
// The returned Signature is degenerate and only intended for use by
|
|
|
|
// emitCallArgs.
|
|
|
|
//
|
|
|
|
func builtinCallSignature(info *TypeInfo, e *ast.CallExpr) *types.Signature {
|
|
|
|
var params []*types.Var
|
|
|
|
var isVariadic bool
|
|
|
|
|
|
|
|
switch builtin := noparens(e.Fun).(*ast.Ident).Name; builtin {
|
|
|
|
case "append":
|
|
|
|
var t0, t1 types.Type
|
|
|
|
t0 = info.TypeOf(e) // infer arg[0] type from result type
|
|
|
|
if e.Ellipsis != 0 {
|
|
|
|
// append([]T, []T) []T
|
|
|
|
// append([]byte, string) []byte
|
|
|
|
t1 = info.TypeOf(e.Args[1]) // no conversion
|
|
|
|
} else {
|
|
|
|
// append([]T, ...T) []T
|
2013-05-17 15:02:47 -06:00
|
|
|
t1 = t0.Underlying().(*types.Slice).Elem()
|
2013-05-17 14:25:48 -06:00
|
|
|
isVariadic = true
|
|
|
|
}
|
|
|
|
params = append(params,
|
2013-05-17 15:02:47 -06:00
|
|
|
types.NewVar(nil, "", t0),
|
|
|
|
types.NewVar(nil, "", t1))
|
2013-05-17 14:25:48 -06:00
|
|
|
|
|
|
|
case "print", "println": // print{,ln}(any, ...interface{})
|
|
|
|
isVariadic = true
|
|
|
|
// Note, arg0 may have any type, not necessarily tEface.
|
|
|
|
params = append(params,
|
2013-05-17 15:02:47 -06:00
|
|
|
types.NewVar(nil, "", info.TypeOf(e.Args[0])),
|
|
|
|
types.NewVar(nil, "", tEface))
|
2013-05-17 14:25:48 -06:00
|
|
|
|
|
|
|
case "close":
|
2013-05-17 15:02:47 -06:00
|
|
|
params = append(params, types.NewVar(nil, "", info.TypeOf(e.Args[0])))
|
2013-05-17 14:25:48 -06:00
|
|
|
|
|
|
|
case "copy":
|
|
|
|
// copy([]T, []T) int
|
|
|
|
// Infer arg types from each other. Sleazy.
|
|
|
|
var st *types.Slice
|
2013-05-17 15:02:47 -06:00
|
|
|
if t, ok := info.TypeOf(e.Args[0]).Underlying().(*types.Slice); ok {
|
2013-05-17 14:25:48 -06:00
|
|
|
st = t
|
2013-05-17 15:02:47 -06:00
|
|
|
} else if t, ok := info.TypeOf(e.Args[1]).Underlying().(*types.Slice); ok {
|
2013-05-17 14:25:48 -06:00
|
|
|
st = t
|
|
|
|
} else {
|
|
|
|
panic("cannot infer types in call to copy()")
|
|
|
|
}
|
2013-05-17 15:02:47 -06:00
|
|
|
stvar := types.NewVar(nil, "", st)
|
2013-05-17 14:25:48 -06:00
|
|
|
params = append(params, stvar, stvar)
|
|
|
|
|
|
|
|
case "delete":
|
|
|
|
// delete(map[K]V, K)
|
|
|
|
tmap := info.TypeOf(e.Args[0])
|
2013-05-17 15:02:47 -06:00
|
|
|
tkey := tmap.Underlying().(*types.Map).Key()
|
2013-05-17 14:25:48 -06:00
|
|
|
params = append(params,
|
2013-05-17 15:02:47 -06:00
|
|
|
types.NewVar(nil, "", tmap),
|
|
|
|
types.NewVar(nil, "", tkey))
|
2013-05-17 14:25:48 -06:00
|
|
|
|
|
|
|
case "len", "cap":
|
2013-05-17 15:02:47 -06:00
|
|
|
params = append(params, types.NewVar(nil, "", info.TypeOf(e.Args[0])))
|
2013-05-17 14:25:48 -06:00
|
|
|
|
|
|
|
case "real", "imag":
|
|
|
|
// Reverse conversion to "complex" case below.
|
|
|
|
var argType types.Type
|
2013-05-17 15:02:47 -06:00
|
|
|
switch info.TypeOf(e).(*types.Basic).Kind() {
|
2013-05-17 14:25:48 -06:00
|
|
|
case types.UntypedFloat:
|
|
|
|
argType = types.Typ[types.UntypedComplex]
|
|
|
|
case types.Float64:
|
|
|
|
argType = tComplex128
|
|
|
|
case types.Float32:
|
|
|
|
argType = tComplex64
|
|
|
|
default:
|
|
|
|
unreachable()
|
|
|
|
}
|
2013-05-17 15:02:47 -06:00
|
|
|
params = append(params, types.NewVar(nil, "", argType))
|
2013-05-17 14:25:48 -06:00
|
|
|
|
|
|
|
case "complex":
|
|
|
|
var argType types.Type
|
2013-05-17 15:02:47 -06:00
|
|
|
switch info.TypeOf(e).(*types.Basic).Kind() {
|
2013-05-17 14:25:48 -06:00
|
|
|
case types.UntypedComplex:
|
|
|
|
argType = types.Typ[types.UntypedFloat]
|
|
|
|
case types.Complex128:
|
|
|
|
argType = tFloat64
|
|
|
|
case types.Complex64:
|
|
|
|
argType = tFloat32
|
|
|
|
default:
|
|
|
|
unreachable()
|
|
|
|
}
|
2013-05-17 15:02:47 -06:00
|
|
|
v := types.NewVar(nil, "", argType)
|
2013-05-17 14:25:48 -06:00
|
|
|
params = append(params, v, v)
|
|
|
|
|
|
|
|
case "panic":
|
2013-05-17 15:02:47 -06:00
|
|
|
params = append(params, types.NewVar(nil, "", tEface))
|
2013-05-17 14:25:48 -06:00
|
|
|
|
|
|
|
case "recover":
|
|
|
|
// no params
|
|
|
|
|
|
|
|
default:
|
|
|
|
panic("unknown builtin: " + builtin)
|
|
|
|
}
|
|
|
|
|
2013-05-17 15:02:47 -06:00
|
|
|
return types.NewSignature(nil, types.NewTuple(params...), nil, isVariadic)
|
2013-05-17 14:25:48 -06:00
|
|
|
}
|