blob: 427c4f0872562ed8edb16ae4f3a3155b8187e159 [file] [log] [blame]
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.exp/go/types"
"fmt"
"go/ast"
)
// TypeInfo contains information provided by the type checker about
// the abstract syntax for a single package.
type TypeInfo struct {
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 {
return info.ObjectOf(id).Type()
}
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
}
panic(fmt.Sprintf("no types.Object for ast.Ident %s @ %p", id.Name, id))
}
// IsType returns true iff expression e denotes a type.
// Precondition: e belongs to the package's ASTs.
//
func (info *TypeInfo) IsType(e ast.Expr) bool {
switch e := e.(type) {
case *ast.SelectorExpr: // pkg.Type
if obj := info.isPackageRef(e); obj != nil {
return objKind(obj) == ast.Typ
}
case *ast.StarExpr: // *T
return info.IsType(e.X)
case *ast.Ident:
return objKind(info.ObjectOf(e)) == ast.Typ
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 {
if obj := info.ObjectOf(id); objKind(obj) == ast.Pkg {
return obj.(*types.Package).Scope().Lookup(sel.Sel.Name)
}
}
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
t1 = t0.Underlying().(*types.Slice).Elem()
isVariadic = true
}
params = append(params,
types.NewVar(nil, "", t0),
types.NewVar(nil, "", t1))
case "print", "println": // print{,ln}(any, ...interface{})
isVariadic = true
// Note, arg0 may have any type, not necessarily tEface.
params = append(params,
types.NewVar(nil, "", info.TypeOf(e.Args[0])),
types.NewVar(nil, "", tEface))
case "close":
params = append(params, types.NewVar(nil, "", info.TypeOf(e.Args[0])))
case "copy":
// copy([]T, []T) int
// Infer arg types from each other. Sleazy.
var st *types.Slice
if t, ok := info.TypeOf(e.Args[0]).Underlying().(*types.Slice); ok {
st = t
} else if t, ok := info.TypeOf(e.Args[1]).Underlying().(*types.Slice); ok {
st = t
} else {
panic("cannot infer types in call to copy()")
}
stvar := types.NewVar(nil, "", st)
params = append(params, stvar, stvar)
case "delete":
// delete(map[K]V, K)
tmap := info.TypeOf(e.Args[0])
tkey := tmap.Underlying().(*types.Map).Key()
params = append(params,
types.NewVar(nil, "", tmap),
types.NewVar(nil, "", tkey))
case "len", "cap":
params = append(params, types.NewVar(nil, "", info.TypeOf(e.Args[0])))
case "real", "imag":
// Reverse conversion to "complex" case below.
var argType types.Type
switch info.TypeOf(e).(*types.Basic).Kind() {
case types.UntypedFloat:
argType = types.Typ[types.UntypedComplex]
case types.Float64:
argType = tComplex128
case types.Float32:
argType = tComplex64
default:
unreachable()
}
params = append(params, types.NewVar(nil, "", argType))
case "complex":
var argType types.Type
switch info.TypeOf(e).(*types.Basic).Kind() {
case types.UntypedComplex:
argType = types.Typ[types.UntypedFloat]
case types.Complex128:
argType = tFloat64
case types.Complex64:
argType = tFloat32
default:
unreachable()
}
v := types.NewVar(nil, "", argType)
params = append(params, v, v)
case "panic":
params = append(params, types.NewVar(nil, "", tEface))
case "recover":
// no params
default:
panic("unknown builtin: " + builtin)
}
return types.NewSignature(nil, types.NewTuple(params...), nil, isVariadic)
}