| // Copyright 2013 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package importer |
| |
| // TODO(gri): absorb this into go/types. |
| |
| import ( |
| "fmt" |
| "go/ast" |
| "go/token" |
| |
| "code.google.com/p/go.tools/go/exact" |
| "code.google.com/p/go.tools/go/types" |
| ) |
| |
| // PackageInfo holds the ASTs and facts derived by the type-checker |
| // for a single package. |
| // |
| // Not mutated once constructed. |
| // |
| type PackageInfo struct { |
| Pkg *types.Package |
| Importable bool // true if 'import "Pkg.Path()"' would resolve to this |
| Err error // non-nil if the package had static errors |
| Files []*ast.File // abstract syntax for the package's files |
| types.Info // type-checker deductions. |
| } |
| |
| func (info *PackageInfo) String() string { |
| return fmt.Sprintf("PackageInfo(%s)", info.Pkg.Path()) |
| } |
| |
| // TypeOf returns the type of expression e. |
| // Precondition: e belongs to the package's ASTs. |
| // |
| func (info *PackageInfo) TypeOf(e ast.Expr) types.Type { |
| if t, ok := info.Types[e]; ok { |
| return t |
| } |
| // Defining ast.Idents (id := expr) get only Ident callbacks |
| // but not Expr callbacks. |
| 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. |
| // Precondition: e belongs to the package's ASTs. |
| // |
| func (info *PackageInfo) ValueOf(e ast.Expr) exact.Value { |
| return info.Values[e] |
| } |
| |
| // ObjectOf returns the typechecker object denoted by the specified id. |
| // Precondition: id belongs to the package's ASTs. |
| // |
| func (info *PackageInfo) ObjectOf(id *ast.Ident) types.Object { |
| return info.Objects[id] |
| } |
| |
| // IsType returns true iff expression e denotes a type. |
| // Precondition: e belongs to the package's ASTs. |
| // |
| // TODO(gri): move this into go/types. |
| // |
| func (info *PackageInfo) IsType(e ast.Expr) bool { |
| switch e := e.(type) { |
| case *ast.SelectorExpr: // pkg.Type |
| if sel := info.Selections[e]; sel.Kind() == types.PackageObj { |
| _, isType := sel.Obj().(*types.TypeName) |
| return isType |
| } |
| case *ast.StarExpr: // *T |
| return info.IsType(e.X) |
| case *ast.Ident: |
| _, isType := info.ObjectOf(e).(*types.TypeName) |
| return isType |
| 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 |
| } |
| |
| // TypeCaseVar returns the implicit variable created by a single-type |
| // case clause in a type switch, or nil if not found. |
| // |
| func (info *PackageInfo) TypeCaseVar(cc *ast.CaseClause) *types.Var { |
| if v := info.Implicits[cc]; v != nil { |
| return v.(*types.Var) |
| } |
| return nil |
| } |
| |
| var ( |
| tEface = new(types.Interface) |
| tComplex64 = types.Typ[types.Complex64] |
| tComplex128 = types.Typ[types.Complex128] |
| tFloat32 = types.Typ[types.Float32] |
| tFloat64 = types.Typ[types.Float64] |
| ) |
| |
| // 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. |
| // |
| func (info *PackageInfo) BuiltinCallSignature(e *ast.CallExpr) *types.Signature { |
| var params []*types.Var |
| var isVariadic bool |
| |
| switch builtin := unparen(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(tslice, tslice...) []T |
| // append(byteslice, "foo"...) []byte |
| t1 = info.TypeOf(e.Args[1]) // no conversion |
| } else { |
| // append([]T, x, y, z) []T |
| t1 = t0.Underlying() |
| isVariadic = true |
| } |
| params = append(params, |
| types.NewVar(token.NoPos, nil, "", t0), |
| types.NewVar(token.NoPos, 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(token.NoPos, nil, "", info.TypeOf(e.Args[0])), |
| types.NewVar(token.NoPos, nil, "", types.NewSlice(tEface))) |
| |
| case "close": |
| params = append(params, types.NewVar(token.NoPos, 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(token.NoPos, 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(token.NoPos, nil, "", tmap), |
| types.NewVar(token.NoPos, nil, "", tkey)) |
| |
| case "len", "cap": |
| params = append(params, types.NewVar(token.NoPos, 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(token.NoPos, 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(token.NoPos, nil, "", argType) |
| params = append(params, v, v) |
| |
| case "panic": |
| params = append(params, types.NewVar(token.NoPos, nil, "", tEface)) |
| |
| case "recover": |
| // no params |
| |
| default: |
| panic("unknown builtin: " + builtin) |
| } |
| |
| return types.NewSignature(nil, nil, types.NewTuple(params...), nil, isVariadic) |
| } |