go.tools/go/types: rename Context -> Config (more apt name)
Also: Various minor cleanups. R=adonovan, r CC=golang-dev https://golang.org/cl/11445044
This commit is contained in:
parent
9460d02473
commit
40a278e5ee
|
|
@ -19,9 +19,9 @@ func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
|
||||||
pkg.spans = make(map[types.Object]Span)
|
pkg.spans = make(map[types.Object]Span)
|
||||||
pkg.types = make(map[ast.Expr]types.Type)
|
pkg.types = make(map[ast.Expr]types.Type)
|
||||||
pkg.values = make(map[ast.Expr]exact.Value)
|
pkg.values = make(map[ast.Expr]exact.Value)
|
||||||
// By providing the Context with our own error function, it will continue
|
// By providing a Config with our own error function, it will continue
|
||||||
// past the first error. There is no need for that function to do anything.
|
// past the first error. There is no need for that function to do anything.
|
||||||
context := types.Context{
|
config := types.Config{
|
||||||
Error: func(error) {},
|
Error: func(error) {},
|
||||||
}
|
}
|
||||||
info := &types.Info{
|
info := &types.Info{
|
||||||
|
|
@ -29,7 +29,7 @@ func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
|
||||||
Values: pkg.values,
|
Values: pkg.values,
|
||||||
Objects: pkg.idents,
|
Objects: pkg.idents,
|
||||||
}
|
}
|
||||||
_, err := context.Check(pkg.path, fs, astFiles, info)
|
_, err := config.Check(pkg.path, fs, astFiles, info)
|
||||||
// update spans
|
// update spans
|
||||||
for id, obj := range pkg.idents {
|
for id, obj := range pkg.idents {
|
||||||
pkg.growSpan(id, obj)
|
pkg.growSpan(id, obj)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
// Package types declares the data types and implements
|
// Package types declares the data types and implements
|
||||||
// the algorithms for type-checking of Go packages.
|
// the algorithms for type-checking of Go packages.
|
||||||
// Use Check and Context.Check to invoke the type-checker.
|
// Use Check and Config.Check to invoke the type-checker.
|
||||||
//
|
//
|
||||||
// Type-checking consists of several interdependent phases:
|
// Type-checking consists of several interdependent phases:
|
||||||
//
|
//
|
||||||
|
|
@ -34,19 +34,19 @@ import (
|
||||||
// list of *ast.Files and corresponding file set, and the import path
|
// list of *ast.Files and corresponding file set, and the import path
|
||||||
// the package is identified with. The path must not be empty or dot (".").
|
// the package is identified with. The path must not be empty or dot (".").
|
||||||
//
|
//
|
||||||
// For more control over type-checking and results, use Context.Check.
|
// For more control over type-checking and results, use Config.Check.
|
||||||
func Check(path string, fset *token.FileSet, files []*ast.File) (*Package, error) {
|
func Check(path string, fset *token.FileSet, files []*ast.File) (*Package, error) {
|
||||||
var ctxt Context
|
var conf Config
|
||||||
pkg, err := ctxt.check(path, fset, files, nil)
|
pkg, err := conf.check(path, fset, files, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return pkg, nil
|
return pkg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Context specifies the supporting context for type checking.
|
// A Config specifies the configuration for type checking.
|
||||||
// The zero value for a Context is a ready-to-use default context.
|
// The zero value for Config is a ready-to-use default configuration.
|
||||||
type Context struct {
|
type Config struct {
|
||||||
// If Error != nil, it is called with each error found
|
// If Error != nil, it is called with each error found
|
||||||
// during type checking. The error strings of errors with
|
// during type checking. The error strings of errors with
|
||||||
// detailed position information are formatted as follows:
|
// detailed position information are formatted as follows:
|
||||||
|
|
@ -103,7 +103,7 @@ type Info struct {
|
||||||
// are not recorded.
|
// are not recorded.
|
||||||
Objects map[*ast.Ident]Object
|
Objects map[*ast.Ident]Object
|
||||||
|
|
||||||
// If Implicits != nil, it records the object for each node the implicitly
|
// If Implicits != nil, it records the object for each node that implicitly
|
||||||
// declares objects. The following node and object types may appear:
|
// declares objects. The following node and object types may appear:
|
||||||
//
|
//
|
||||||
// node obj
|
// node obj
|
||||||
|
|
@ -118,15 +118,15 @@ type Info struct {
|
||||||
// error if any, and if info != nil, additional type information. The package is
|
// error if any, and if info != nil, additional type information. The package is
|
||||||
// specified by a list of *ast.Files and corresponding file set, and the import
|
// specified by a list of *ast.Files and corresponding file set, and the import
|
||||||
// path the package is identified with. The path must not be empty or dot (".").
|
// path the package is identified with. The path must not be empty or dot (".").
|
||||||
func (ctxt *Context) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (*Package, error) {
|
func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (*Package, error) {
|
||||||
return ctxt.check(path, fset, files, info)
|
return conf.check(path, fset, files, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsAssignableTo reports whether a value of type V
|
// IsAssignableTo reports whether a value of type V
|
||||||
// is assignable to a variable of type T.
|
// is assignable to a variable of type T.
|
||||||
func IsAssignableTo(V, T Type) bool {
|
func IsAssignableTo(V, T Type) bool {
|
||||||
x := operand{mode: value, typ: V}
|
x := operand{mode: value, typ: V}
|
||||||
return x.isAssignableTo(nil, T) // context not needed for non-constant x
|
return x.isAssignableTo(nil, T) // config not needed for non-constant x
|
||||||
}
|
}
|
||||||
|
|
||||||
// BUG(gri): Conversions of constants only change the type, not the value (e.g., int(1.1) is wrong).
|
// BUG(gri): Conversions of constants only change the type, not the value (e.g., int(1.1) is wrong).
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ func (check *checker) assignment(x *operand, to Type) bool {
|
||||||
|
|
||||||
check.convertUntyped(x, to)
|
check.convertUntyped(x, to)
|
||||||
|
|
||||||
return x.mode != invalid && x.isAssignableTo(check.ctxt, to)
|
return x.mode != invalid && x.isAssignableTo(check.conf, to)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check *checker) initConst(lhs *Const, x *operand) {
|
func (check *checker) initConst(lhs *Const, x *operand) {
|
||||||
|
|
|
||||||
|
|
@ -236,7 +236,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin) {
|
||||||
if x.mode == invalid {
|
if x.mode == invalid {
|
||||||
goto Error
|
goto Error
|
||||||
}
|
}
|
||||||
if !x.isAssignableTo(check.ctxt, m.key) {
|
if !x.isAssignableTo(check.conf, m.key) {
|
||||||
check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key)
|
check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key)
|
||||||
goto Error
|
goto Error
|
||||||
}
|
}
|
||||||
|
|
@ -327,7 +327,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin) {
|
||||||
|
|
||||||
case _Alignof:
|
case _Alignof:
|
||||||
x.mode = constant
|
x.mode = constant
|
||||||
x.val = exact.MakeInt64(check.ctxt.alignof(x.typ))
|
x.val = exact.MakeInt64(check.conf.alignof(x.typ))
|
||||||
x.typ = Typ[Uintptr]
|
x.typ = Typ[Uintptr]
|
||||||
|
|
||||||
case _Offsetof:
|
case _Offsetof:
|
||||||
|
|
@ -355,14 +355,14 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin) {
|
||||||
check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, base)
|
check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, base)
|
||||||
goto Error
|
goto Error
|
||||||
}
|
}
|
||||||
offs := check.ctxt.offsetof(base, index)
|
offs := check.conf.offsetof(base, index)
|
||||||
x.mode = constant
|
x.mode = constant
|
||||||
x.val = exact.MakeInt64(offs)
|
x.val = exact.MakeInt64(offs)
|
||||||
x.typ = Typ[Uintptr]
|
x.typ = Typ[Uintptr]
|
||||||
|
|
||||||
case _Sizeof:
|
case _Sizeof:
|
||||||
x.mode = constant
|
x.mode = constant
|
||||||
x.val = exact.MakeInt64(check.ctxt.sizeof(x.typ))
|
x.val = exact.MakeInt64(check.conf.sizeof(x.typ))
|
||||||
x.typ = Typ[Uintptr]
|
x.typ = Typ[Uintptr]
|
||||||
|
|
||||||
case _Assert:
|
case _Assert:
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ type exprInfo struct {
|
||||||
|
|
||||||
// A checker is an instance of the type checker.
|
// A checker is an instance of the type checker.
|
||||||
type checker struct {
|
type checker struct {
|
||||||
ctxt *Context
|
conf *Config
|
||||||
fset *token.FileSet
|
fset *token.FileSet
|
||||||
Info
|
Info
|
||||||
|
|
||||||
|
|
@ -59,9 +59,9 @@ type checker struct {
|
||||||
indent int // indentation for tracing
|
indent int // indentation for tracing
|
||||||
}
|
}
|
||||||
|
|
||||||
func newChecker(ctxt *Context, fset *token.FileSet, pkg *Package) *checker {
|
func newChecker(conf *Config, fset *token.FileSet, pkg *Package) *checker {
|
||||||
return &checker{
|
return &checker{
|
||||||
ctxt: ctxt,
|
conf: conf,
|
||||||
fset: fset,
|
fset: fset,
|
||||||
pkg: pkg,
|
pkg: pkg,
|
||||||
methods: make(map[*TypeName]*Scope),
|
methods: make(map[*TypeName]*Scope),
|
||||||
|
|
@ -133,14 +133,14 @@ func (check *checker) handleBailout(err *error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctxt *Context) check(pkgPath string, fset *token.FileSet, files []*ast.File, info *Info) (pkg *Package, err error) {
|
func (conf *Config) check(pkgPath string, fset *token.FileSet, files []*ast.File, info *Info) (pkg *Package, err error) {
|
||||||
pkg = &Package{
|
pkg = &Package{
|
||||||
path: pkgPath,
|
path: pkgPath,
|
||||||
scope: NewScope(Universe),
|
scope: NewScope(Universe),
|
||||||
imports: make(map[string]*Package),
|
imports: make(map[string]*Package),
|
||||||
}
|
}
|
||||||
|
|
||||||
check := newChecker(ctxt, fset, pkg)
|
check := newChecker(conf, fset, pkg)
|
||||||
defer check.handleBailout(&err)
|
defer check.handleBailout(&err)
|
||||||
|
|
||||||
// we need a reasonable path to continue
|
// we need a reasonable path to continue
|
||||||
|
|
@ -149,11 +149,6 @@ func (ctxt *Context) check(pkgPath string, fset *token.FileSet, files []*ast.Fil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// install optional info
|
|
||||||
if info != nil {
|
|
||||||
check.Info = *info
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine package name and files
|
// determine package name and files
|
||||||
i := 0
|
i := 0
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
|
|
@ -170,6 +165,11 @@ func (ctxt *Context) check(pkgPath string, fset *token.FileSet, files []*ast.Fil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// install optional info
|
||||||
|
if info != nil {
|
||||||
|
check.Info = *info
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(gri) resolveFiles needs to be split up and renamed (cleanup)
|
// TODO(gri) resolveFiles needs to be split up and renamed (cleanup)
|
||||||
check.resolveFiles(files[:i])
|
check.resolveFiles(files[:i])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -203,8 +203,8 @@ func checkFiles(t *testing.T, testfiles []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// typecheck and collect typechecker errors
|
// typecheck and collect typechecker errors
|
||||||
var ctxt Context
|
var conf Config
|
||||||
ctxt.Error = func(err error) {
|
conf.Error = func(err error) {
|
||||||
if *listErrors {
|
if *listErrors {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
|
|
@ -216,7 +216,7 @@ func checkFiles(t *testing.T, testfiles []string) {
|
||||||
errlist = append(errlist, err)
|
errlist = append(errlist, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctxt.Check(pkgName, fset, files, nil)
|
conf.Check(pkgName, fset, files, nil)
|
||||||
|
|
||||||
if *listErrors {
|
if *listErrors {
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type) {
|
||||||
// TODO(gri) verify the remaining conversions.
|
// TODO(gri) verify the remaining conversions.
|
||||||
} else {
|
} else {
|
||||||
// non-constant conversion
|
// non-constant conversion
|
||||||
if !x.isConvertible(check.ctxt, typ) {
|
if !x.isConvertible(check.conf, typ) {
|
||||||
goto ErrorMsg
|
goto ErrorMsg
|
||||||
}
|
}
|
||||||
x.mode = value
|
x.mode = value
|
||||||
|
|
@ -91,9 +91,9 @@ Error:
|
||||||
x.expr = conv
|
x.expr = conv
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *operand) isConvertible(ctxt *Context, T Type) bool {
|
func (x *operand) isConvertible(conf *Config, T Type) bool {
|
||||||
// "x is assignable to T"
|
// "x is assignable to T"
|
||||||
if x.isAssignableTo(ctxt, T) {
|
if x.isAssignableTo(conf, T) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ func (check *checker) err(err error) {
|
||||||
if check.firsterr == nil {
|
if check.firsterr == nil {
|
||||||
check.firsterr = err
|
check.firsterr = err
|
||||||
}
|
}
|
||||||
f := check.ctxt.Error
|
f := check.conf.Error
|
||||||
if f == nil {
|
if f == nil {
|
||||||
panic(bailout{}) // report only first error
|
panic(bailout{}) // report only first error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,8 +86,8 @@ func EvalNode(fset *token.FileSet, node ast.Expr, pkg *Package, scope *Scope) (t
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize checker
|
// initialize checker
|
||||||
var ctxt Context
|
var conf Config
|
||||||
check := newChecker(&ctxt, fset, pkg)
|
check := newChecker(&conf, fset, pkg)
|
||||||
check.topScope = scope
|
check.topScope = scope
|
||||||
defer check.handleBailout(&err)
|
defer check.handleBailout(&err)
|
||||||
|
|
||||||
|
|
@ -95,7 +95,7 @@ func EvalNode(fset *token.FileSet, node ast.Expr, pkg *Package, scope *Scope) (t
|
||||||
var x operand
|
var x operand
|
||||||
check.exprOrType(&x, node)
|
check.exprOrType(&x, node)
|
||||||
switch x.mode {
|
switch x.mode {
|
||||||
case invalid, novalue, typexprn:
|
case invalid, novalue:
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
unreachable() // or bailed out with error
|
unreachable() // or bailed out with error
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,8 @@ constant lhs must be representable as an integer.
|
||||||
|
|
||||||
When an expression gets its final type, either on the way out from rawExpr,
|
When an expression gets its final type, either on the way out from rawExpr,
|
||||||
on the way down in updateExprType, or at the end of the type checker run,
|
on the way down in updateExprType, or at the end of the type checker run,
|
||||||
if present the Context.Expr method is invoked to notify a go/types client.
|
the type (and constant value, if any) is recorded via Info.Types and Values,
|
||||||
|
if present.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type opPredicates map[token.Token]func(Type) bool
|
type opPredicates map[token.Token]func(Type) bool
|
||||||
|
|
@ -124,7 +125,7 @@ func (check *checker) unary(x *operand, op token.Token) {
|
||||||
typ := x.typ.Underlying().(*Basic)
|
typ := x.typ.Underlying().(*Basic)
|
||||||
size := -1
|
size := -1
|
||||||
if isUnsigned(typ) {
|
if isUnsigned(typ) {
|
||||||
size = int(check.ctxt.sizeof(typ))
|
size = int(check.conf.sizeof(typ))
|
||||||
}
|
}
|
||||||
x.val = exact.UnaryOp(op, x.val, size)
|
x.val = exact.UnaryOp(op, x.val, size)
|
||||||
// Typed constants must be representable in
|
// Typed constants must be representable in
|
||||||
|
|
@ -154,7 +155,7 @@ func isComparison(op token.Token) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func isRepresentableConst(x exact.Value, ctxt *Context, as BasicKind) bool {
|
func isRepresentableConst(x exact.Value, conf *Config, as BasicKind) bool {
|
||||||
switch x.Kind() {
|
switch x.Kind() {
|
||||||
case exact.Unknown:
|
case exact.Unknown:
|
||||||
return true
|
return true
|
||||||
|
|
@ -166,7 +167,7 @@ func isRepresentableConst(x exact.Value, ctxt *Context, as BasicKind) bool {
|
||||||
if x, ok := exact.Int64Val(x); ok {
|
if x, ok := exact.Int64Val(x); ok {
|
||||||
switch as {
|
switch as {
|
||||||
case Int:
|
case Int:
|
||||||
var s = uint(ctxt.sizeof(Typ[as])) * 8
|
var s = uint(conf.sizeof(Typ[as])) * 8
|
||||||
return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1
|
return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1
|
||||||
case Int8:
|
case Int8:
|
||||||
const s = 8
|
const s = 8
|
||||||
|
|
@ -180,7 +181,7 @@ func isRepresentableConst(x exact.Value, ctxt *Context, as BasicKind) bool {
|
||||||
case Int64:
|
case Int64:
|
||||||
return true
|
return true
|
||||||
case Uint, Uintptr:
|
case Uint, Uintptr:
|
||||||
if s := uint(ctxt.sizeof(Typ[as])) * 8; s < 64 {
|
if s := uint(conf.sizeof(Typ[as])) * 8; s < 64 {
|
||||||
return 0 <= x && x <= int64(1)<<s-1
|
return 0 <= x && x <= int64(1)<<s-1
|
||||||
}
|
}
|
||||||
return 0 <= x
|
return 0 <= x
|
||||||
|
|
@ -211,7 +212,7 @@ func isRepresentableConst(x exact.Value, ctxt *Context, as BasicKind) bool {
|
||||||
n := exact.BitLen(x)
|
n := exact.BitLen(x)
|
||||||
switch as {
|
switch as {
|
||||||
case Uint, Uintptr:
|
case Uint, Uintptr:
|
||||||
var s = uint(ctxt.sizeof(Typ[as])) * 8
|
var s = uint(conf.sizeof(Typ[as])) * 8
|
||||||
return exact.Sign(x) >= 0 && n <= int(s)
|
return exact.Sign(x) >= 0 && n <= int(s)
|
||||||
case Uint64:
|
case Uint64:
|
||||||
return exact.Sign(x) >= 0 && n <= 64
|
return exact.Sign(x) >= 0 && n <= 64
|
||||||
|
|
@ -270,7 +271,7 @@ func (check *checker) isRepresentable(x *operand, typ *Basic) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isRepresentableConst(x.val, check.ctxt, typ.kind) {
|
if !isRepresentableConst(x.val, check.conf, typ.kind) {
|
||||||
var msg string
|
var msg string
|
||||||
if isNumeric(x.typ) && isNumeric(typ) {
|
if isNumeric(x.typ) && isNumeric(typ) {
|
||||||
msg = "%s overflows (or cannot be accurately represented as) %s"
|
msg = "%s overflows (or cannot be accurately represented as) %s"
|
||||||
|
|
@ -287,7 +288,7 @@ func (check *checker) isRepresentable(x *operand, typ *Basic) {
|
||||||
// If typ is still an untyped and not the final type, updateExprType
|
// If typ is still an untyped and not the final type, updateExprType
|
||||||
// only updates the recorded untyped type for x and possibly its
|
// only updates the recorded untyped type for x and possibly its
|
||||||
// operands. Otherwise (i.e., typ is not an untyped type anymore,
|
// operands. Otherwise (i.e., typ is not an untyped type anymore,
|
||||||
// or it is the final type for x), Context.Expr is invoked, if present.
|
// or it is the final type for x), the type and value are recorded.
|
||||||
// Also, if x is a constant, it must be representable as a value of typ,
|
// Also, if x is a constant, it must be representable as a value of typ,
|
||||||
// and if x is the (formerly untyped) lhs operand of a non-constant
|
// and if x is the (formerly untyped) lhs operand of a non-constant
|
||||||
// shift, it must be an integer value.
|
// shift, it must be an integer value.
|
||||||
|
|
@ -469,7 +470,7 @@ func (check *checker) comparison(x, y *operand, op token.Token) {
|
||||||
// TODO(gri) deal with interface vs non-interface comparison
|
// TODO(gri) deal with interface vs non-interface comparison
|
||||||
|
|
||||||
valid := false
|
valid := false
|
||||||
if x.isAssignableTo(check.ctxt, y.typ) || y.isAssignableTo(check.ctxt, x.typ) {
|
if x.isAssignableTo(check.conf, y.typ) || y.isAssignableTo(check.conf, x.typ) {
|
||||||
switch op {
|
switch op {
|
||||||
case token.EQL, token.NEQ:
|
case token.EQL, token.NEQ:
|
||||||
valid = isComparable(x.typ) ||
|
valid = isComparable(x.typ) ||
|
||||||
|
|
@ -779,10 +780,6 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) {
|
||||||
case constant:
|
case constant:
|
||||||
typ = x.typ
|
typ = x.typ
|
||||||
val = x.val
|
val = x.val
|
||||||
case typexprn:
|
|
||||||
x.mode = typexpr
|
|
||||||
typ = x.typ
|
|
||||||
record = false // type was already recorded
|
|
||||||
default:
|
default:
|
||||||
typ = x.typ
|
typ = x.typ
|
||||||
}
|
}
|
||||||
|
|
@ -1220,8 +1217,13 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) {
|
||||||
|
|
||||||
case *ast.ArrayType, *ast.StructType, *ast.FuncType,
|
case *ast.ArrayType, *ast.StructType, *ast.FuncType,
|
||||||
*ast.InterfaceType, *ast.MapType, *ast.ChanType:
|
*ast.InterfaceType, *ast.MapType, *ast.ChanType:
|
||||||
x.mode = typexprn // not typexpr; Context.Expr callback was invoked by check.typ
|
x.mode = typexpr
|
||||||
x.typ = check.typ(e, nil, false)
|
x.typ = check.typ(e, nil, false)
|
||||||
|
// Note: rawExpr (caller of expr0) will call check.recordTypeAndValue
|
||||||
|
// even though check.typ has already called it. This is fine as both
|
||||||
|
// times the same expression and type are recorded. It is also not a
|
||||||
|
// performance issue because we only reach here for composite literal
|
||||||
|
// types, which are comparatively rare.
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if debug {
|
if debug {
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,9 @@ var (
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var ctxt Context
|
var conf Config
|
||||||
types := make(map[ast.Expr]Type)
|
types := make(map[ast.Expr]Type)
|
||||||
_, err = ctxt.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types})
|
_, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(gri) Move Method and accessors to objects.go.
|
// TODO(gri) Move Method and accessors to objects.go.
|
||||||
|
// TODO(gri) Method.Type() returns the wrong receiver type.
|
||||||
|
|
||||||
// A Method represents a concrete or abstract (interface)
|
// A Method represents a concrete or abstract (interface)
|
||||||
// method of a method set.
|
// method of a method set.
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ const (
|
||||||
invalid operandMode = iota // operand is invalid
|
invalid operandMode = iota // operand is invalid
|
||||||
novalue // operand represents no value (result of a function call w/o result)
|
novalue // operand represents no value (result of a function call w/o result)
|
||||||
typexpr // operand is a type
|
typexpr // operand is a type
|
||||||
typexprn // like typexpr; only used to communicate between checker.expr0 and checker.rawExpr
|
|
||||||
constant // operand is a constant; the operand's typ is a Basic type
|
constant // operand is a constant; the operand's typ is a Basic type
|
||||||
variable // operand is an addressable variable
|
variable // operand is an addressable variable
|
||||||
value // operand is a computed value
|
value // operand is a computed value
|
||||||
|
|
@ -33,7 +32,6 @@ var operandModeString = [...]string{
|
||||||
invalid: "invalid",
|
invalid: "invalid",
|
||||||
novalue: "no value",
|
novalue: "no value",
|
||||||
typexpr: "type",
|
typexpr: "type",
|
||||||
typexprn: "type/n",
|
|
||||||
constant: "constant",
|
constant: "constant",
|
||||||
variable: "variable",
|
variable: "variable",
|
||||||
value: "value",
|
value: "value",
|
||||||
|
|
@ -127,7 +125,7 @@ func (x *operand) isNil() bool {
|
||||||
// overlapping in functionality. Need to simplify and clean up.
|
// overlapping in functionality. Need to simplify and clean up.
|
||||||
|
|
||||||
// isAssignableTo reports whether x is assignable to a variable of type T.
|
// isAssignableTo reports whether x is assignable to a variable of type T.
|
||||||
func (x *operand) isAssignableTo(ctxt *Context, T Type) bool {
|
func (x *operand) isAssignableTo(conf *Config, T Type) bool {
|
||||||
if x.mode == invalid || T == Typ[Invalid] {
|
if x.mode == invalid || T == Typ[Invalid] {
|
||||||
return true // avoid spurious errors
|
return true // avoid spurious errors
|
||||||
}
|
}
|
||||||
|
|
@ -186,7 +184,7 @@ func (x *operand) isAssignableTo(ctxt *Context, T Type) bool {
|
||||||
switch t := Tu.(type) {
|
switch t := Tu.(type) {
|
||||||
case *Basic:
|
case *Basic:
|
||||||
if x.mode == constant {
|
if x.mode == constant {
|
||||||
return isRepresentableConst(x.val, ctxt, t.kind)
|
return isRepresentableConst(x.val, conf, t.kind)
|
||||||
}
|
}
|
||||||
// The result of a comparison is an untyped boolean,
|
// The result of a comparison is an untyped boolean,
|
||||||
// but may not be a constant.
|
// but may not be a constant.
|
||||||
|
|
@ -207,5 +205,5 @@ func (x *operand) isAssignableTo(ctxt *Context, T Type) bool {
|
||||||
func (x *operand) isInteger() bool {
|
func (x *operand) isInteger() bool {
|
||||||
return x.mode == invalid ||
|
return x.mode == invalid ||
|
||||||
isInteger(x.typ) ||
|
isInteger(x.typ) ||
|
||||||
x.mode == constant && isRepresentableConst(x.val, nil, UntypedInt) // no context required for UntypedInt
|
x.mode == constant && isRepresentableConst(x.val, nil, UntypedInt) // no *Config required for UntypedInt
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,14 +86,14 @@ func (check *checker) arityMatch(s, init *ast.ValueSpec) {
|
||||||
func (check *checker) resolveFiles(files []*ast.File) {
|
func (check *checker) resolveFiles(files []*ast.File) {
|
||||||
pkg := check.pkg
|
pkg := check.pkg
|
||||||
|
|
||||||
// Phase 1: Pre-declare all package scope objects so that they can be found
|
// Phase 1: Pre-declare all package-level objects so that they can be found
|
||||||
// when type-checking package objects.
|
// independent of source order.
|
||||||
|
|
||||||
var scopes []*Scope // corresponding file scope per file
|
var scopes []*Scope // corresponding file scope per file
|
||||||
var objList []Object
|
var objList []Object
|
||||||
var objMap = make(map[Object]*decl)
|
var objMap = make(map[Object]*decl)
|
||||||
var methods []*mdecl
|
var methods []*mdecl
|
||||||
var fileScope *Scope // current file scope, used by collect
|
var fileScope *Scope // current file scope, used by declare
|
||||||
|
|
||||||
declare := func(ident *ast.Ident, obj Object, typ, init ast.Expr) {
|
declare := func(ident *ast.Ident, obj Object, typ, init ast.Expr) {
|
||||||
assert(ident.Name == obj.Name())
|
assert(ident.Name == obj.Name())
|
||||||
|
|
@ -118,7 +118,7 @@ func (check *checker) resolveFiles(files []*ast.File) {
|
||||||
objMap[obj] = &decl{fileScope, typ, init}
|
objMap[obj] = &decl{fileScope, typ, init}
|
||||||
}
|
}
|
||||||
|
|
||||||
importer := check.ctxt.Import
|
importer := check.conf.Import
|
||||||
if importer == nil {
|
if importer == nil {
|
||||||
importer = GcImport
|
importer = GcImport
|
||||||
}
|
}
|
||||||
|
|
@ -146,7 +146,7 @@ func (check *checker) resolveFiles(files []*ast.File) {
|
||||||
path, _ := strconv.Unquote(s.Path.Value)
|
path, _ := strconv.Unquote(s.Path.Value)
|
||||||
imp, err := importer(pkg.imports, path)
|
imp, err := importer(pkg.imports, path)
|
||||||
if imp == nil && err == nil {
|
if imp == nil && err == nil {
|
||||||
err = errors.New("Context.Import returned nil")
|
err = errors.New("Config.Import returned nil")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err)
|
check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err)
|
||||||
|
|
|
||||||
|
|
@ -67,9 +67,9 @@ func TestResolveIdents(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve and type-check package AST
|
// resolve and type-check package AST
|
||||||
var ctxt Context
|
var conf Config
|
||||||
idents := make(map[*ast.Ident]Object)
|
idents := make(map[*ast.Ident]Object)
|
||||||
pkg, err := ctxt.Check("testResolveIdents", fset, files, &Info{Objects: idents})
|
pkg, err := conf.Check("testResolveIdents", fset, files, &Info{Objects: idents})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -103,7 +103,7 @@ func TestResolveIdents(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that each identifier in the source is enumerated by the Context.Ident callback
|
// check that each identifier in the source is found in the idents map
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
ast.Inspect(f, func(n ast.Node) bool {
|
ast.Inspect(f, func(n ast.Node) bool {
|
||||||
if x, ok := n.(*ast.Ident); ok {
|
if x, ok := n.(*ast.Ident); ok {
|
||||||
|
|
|
||||||
|
|
@ -6,29 +6,29 @@
|
||||||
|
|
||||||
package types
|
package types
|
||||||
|
|
||||||
func (ctxt *Context) alignof(typ Type) int64 {
|
func (conf *Config) alignof(typ Type) int64 {
|
||||||
if f := ctxt.Alignof; f != nil {
|
if f := conf.Alignof; f != nil {
|
||||||
if a := f(typ); a >= 1 {
|
if a := f(typ); a >= 1 {
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
panic("Context.Alignof returned an alignment < 1")
|
panic("Config.Alignof returned an alignment < 1")
|
||||||
}
|
}
|
||||||
return DefaultAlignof(typ)
|
return DefaultAlignof(typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctxt *Context) offsetsof(s *Struct) []int64 {
|
func (conf *Config) offsetsof(s *Struct) []int64 {
|
||||||
offsets := s.offsets
|
offsets := s.offsets
|
||||||
if offsets == nil && s.NumFields() > 0 {
|
if offsets == nil && s.NumFields() > 0 {
|
||||||
// compute offsets on demand
|
// compute offsets on demand
|
||||||
if f := ctxt.Offsetsof; f != nil {
|
if f := conf.Offsetsof; f != nil {
|
||||||
offsets = f(s.fields)
|
offsets = f(s.fields)
|
||||||
// sanity checks
|
// sanity checks
|
||||||
if len(offsets) != s.NumFields() {
|
if len(offsets) != s.NumFields() {
|
||||||
panic("Context.Offsetsof returned the wrong number of offsets")
|
panic("Config.Offsetsof returned the wrong number of offsets")
|
||||||
}
|
}
|
||||||
for _, o := range offsets {
|
for _, o := range offsets {
|
||||||
if o < 0 {
|
if o < 0 {
|
||||||
panic("Context.Offsetsof returned an offset < 0")
|
panic("Config.Offsetsof returned an offset < 0")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -42,22 +42,22 @@ func (ctxt *Context) offsetsof(s *Struct) []int64 {
|
||||||
// offsetof returns the offset of the field specified via
|
// offsetof returns the offset of the field specified via
|
||||||
// the index sequence relative to typ. All embedded fields
|
// the index sequence relative to typ. All embedded fields
|
||||||
// must be structs (rather than pointer to structs).
|
// must be structs (rather than pointer to structs).
|
||||||
func (ctxt *Context) offsetof(typ Type, index []int) int64 {
|
func (conf *Config) offsetof(typ Type, index []int) int64 {
|
||||||
var o int64
|
var o int64
|
||||||
for _, i := range index {
|
for _, i := range index {
|
||||||
s := typ.Underlying().(*Struct)
|
s := typ.Underlying().(*Struct)
|
||||||
o += ctxt.offsetsof(s)[i]
|
o += conf.offsetsof(s)[i]
|
||||||
typ = s.fields[i].typ
|
typ = s.fields[i].typ
|
||||||
}
|
}
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctxt *Context) sizeof(typ Type) int64 {
|
func (conf *Config) sizeof(typ Type) int64 {
|
||||||
if f := ctxt.Sizeof; f != nil {
|
if f := conf.Sizeof; f != nil {
|
||||||
if s := f(typ); s >= 0 {
|
if s := f(typ); s >= 0 {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
panic("Context.Sizeof returned a size < 0")
|
panic("Config.Sizeof returned a size < 0")
|
||||||
}
|
}
|
||||||
return DefaultSizeof(typ)
|
return DefaultSizeof(typ)
|
||||||
}
|
}
|
||||||
|
|
@ -67,7 +67,7 @@ func (ctxt *Context) sizeof(typ Type) int64 {
|
||||||
const DefaultMaxAlign = 8
|
const DefaultMaxAlign = 8
|
||||||
|
|
||||||
// DefaultAlignof implements the default alignment computation
|
// DefaultAlignof implements the default alignment computation
|
||||||
// for unsafe.Alignof. It is used if Context.Alignof == nil.
|
// for unsafe.Alignof. It is used if Config.Alignof == nil.
|
||||||
func DefaultAlignof(typ Type) int64 {
|
func DefaultAlignof(typ Type) int64 {
|
||||||
// For arrays and structs, alignment is defined in terms
|
// For arrays and structs, alignment is defined in terms
|
||||||
// of alignment of the elements and fields, respectively.
|
// of alignment of the elements and fields, respectively.
|
||||||
|
|
@ -106,7 +106,7 @@ func align(x, a int64) int64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultOffsetsof implements the default field offset computation
|
// DefaultOffsetsof implements the default field offset computation
|
||||||
// for unsafe.Offsetof. It is used if Context.Offsetsof == nil.
|
// for unsafe.Offsetof. It is used if Config.Offsetsof == nil.
|
||||||
func DefaultOffsetsof(fields []*Field) []int64 {
|
func DefaultOffsetsof(fields []*Field) []int64 {
|
||||||
offsets := make([]int64, len(fields))
|
offsets := make([]int64, len(fields))
|
||||||
var o int64
|
var o int64
|
||||||
|
|
@ -124,7 +124,7 @@ func DefaultOffsetsof(fields []*Field) []int64 {
|
||||||
const DefaultPtrSize = 8
|
const DefaultPtrSize = 8
|
||||||
|
|
||||||
// DefaultSizeof implements the default size computation
|
// DefaultSizeof implements the default size computation
|
||||||
// for unsafe.Sizeof. It is used if Context.Sizeof == nil.
|
// for unsafe.Sizeof. It is used if Config.Sizeof == nil.
|
||||||
func DefaultSizeof(typ Type) int64 {
|
func DefaultSizeof(typ Type) int64 {
|
||||||
switch t := typ.Underlying().(type) {
|
switch t := typ.Underlying().(type) {
|
||||||
case *Basic:
|
case *Basic:
|
||||||
|
|
|
||||||
|
|
@ -72,9 +72,9 @@ func typecheck(t *testing.T, path string, filenames []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// typecheck package files
|
// typecheck package files
|
||||||
var ctxt Context
|
var conf Config
|
||||||
ctxt.Error = func(err error) { t.Error(err) }
|
conf.Error = func(err error) { t.Error(err) }
|
||||||
ctxt.Check(path, fset, files, nil)
|
conf.Check(path, fset, files, nil)
|
||||||
pkgCount++
|
pkgCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ type Context struct {
|
||||||
// The Importer will override any user-supplied values for its
|
// The Importer will override any user-supplied values for its
|
||||||
// Expr, Ident, ImplicitObj and Import fields; other fields
|
// Expr, Ident, ImplicitObj and Import fields; other fields
|
||||||
// will be passed through to the type checker.
|
// will be passed through to the type checker.
|
||||||
TypeChecker types.Context
|
TypeChecker types.Config
|
||||||
|
|
||||||
// If Loader is non-nil, it is used to satisfy imports.
|
// If Loader is non-nil, it is used to satisfy imports.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue