go.tools/go/types: remove defers from critical paths
Various minor cleanups. R=adonovan CC=golang-dev https://golang.org/cl/10925043
This commit is contained in:
parent
1fa3f78146
commit
4ca3d7e9da
|
|
@ -33,7 +33,6 @@ package types
|
||||||
// pass without errors. Please do not file issues against these for now
|
// pass without errors. Please do not file issues against these for now
|
||||||
// since they are known already:
|
// since they are known already:
|
||||||
//
|
//
|
||||||
// BUG(gri): Method expressions and method values work accidentally and may not be fully checked.
|
|
||||||
// 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).
|
||||||
// BUG(gri): Some built-ins don't check parameters fully, yet (e.g. append).
|
// BUG(gri): Some built-ins don't check parameters fully, yet (e.g. append).
|
||||||
// BUG(gri): Use of labels is not checked.
|
// BUG(gri): Use of labels is not checked.
|
||||||
|
|
@ -89,7 +88,8 @@ type Context struct {
|
||||||
|
|
||||||
// If Expr != nil, it is called exactly once for each expression x
|
// If Expr != nil, it is called exactly once for each expression x
|
||||||
// that is type-checked: typ is the expression type, and val is the
|
// that is type-checked: typ is the expression type, and val is the
|
||||||
// value if x is constant, val is nil otherwise.
|
// value if x is constant, val is nil otherwise. Expr is not called
|
||||||
|
// for identifiers appearing on the lhs of declarations.
|
||||||
//
|
//
|
||||||
// If x is a literal value (constant, composite literal), typ is always
|
// If x is a literal value (constant, composite literal), typ is always
|
||||||
// the dynamic type of x (never an interface type). Otherwise, typ is x's
|
// the dynamic type of x (never an interface type). Otherwise, typ is x's
|
||||||
|
|
|
||||||
|
|
@ -51,9 +51,11 @@ type checker struct {
|
||||||
iota exact.Value // value of iota in a constant declaration; nil otherwise
|
iota exact.Value // value of iota in a constant declaration; nil otherwise
|
||||||
|
|
||||||
// functions
|
// functions
|
||||||
funclist []function // list of functions/methods with correct signatures and non-empty bodies
|
funclist []function // list of functions/methods with correct signatures and non-empty bodies
|
||||||
funcsig *Signature // signature of currently typechecked function
|
funcsig *Signature // signature of currently typechecked function
|
||||||
pos []token.Pos // stack of expr positions; debugging support, used if trace is set
|
|
||||||
|
// debugging
|
||||||
|
indent int // indentation for tracing
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check *checker) callIdent(id *ast.Ident, obj Object) {
|
func (check *checker) callIdent(id *ast.Ident, obj Object) {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(gri) eventually assert should disappear.
|
// TODO(gri) eventually assert should disappear.
|
||||||
|
|
@ -24,32 +25,6 @@ func unreachable() {
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check *checker) printTrace(format string, args []interface{}) {
|
|
||||||
const dots = ". . . . . . . . . . . . . . . . . . . . "
|
|
||||||
n := len(check.pos) - 1
|
|
||||||
i := 3 * n
|
|
||||||
for i > len(dots) {
|
|
||||||
fmt.Print(dots)
|
|
||||||
i -= len(dots)
|
|
||||||
}
|
|
||||||
// i <= len(dots)
|
|
||||||
fmt.Printf("%s:\t", check.fset.Position(check.pos[n]))
|
|
||||||
fmt.Print(dots[0:i])
|
|
||||||
fmt.Println(check.formatMsg(format, args))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (check *checker) trace(pos token.Pos, format string, args ...interface{}) {
|
|
||||||
check.pos = append(check.pos, pos)
|
|
||||||
check.printTrace(format, args)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (check *checker) untrace(format string, args ...interface{}) {
|
|
||||||
if len(format) > 0 {
|
|
||||||
check.printTrace(format, args)
|
|
||||||
}
|
|
||||||
check.pos = check.pos[:len(check.pos)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (check *checker) formatMsg(format string, args []interface{}) string {
|
func (check *checker) formatMsg(format string, args []interface{}) string {
|
||||||
for i, arg := range args {
|
for i, arg := range args {
|
||||||
switch a := arg.(type) {
|
switch a := arg.(type) {
|
||||||
|
|
@ -72,6 +47,14 @@ func (check *checker) formatMsg(format string, args []interface{}) string {
|
||||||
return fmt.Sprintf(format, args...)
|
return fmt.Sprintf(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (check *checker) trace(pos token.Pos, format string, args ...interface{}) {
|
||||||
|
fmt.Printf("%s:\t%s%s\n",
|
||||||
|
check.fset.Position(pos),
|
||||||
|
strings.Repeat(". ", check.indent),
|
||||||
|
check.formatMsg(format, args),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// dump is only needed for debugging
|
// dump is only needed for debugging
|
||||||
func (check *checker) dump(format string, args ...interface{}) {
|
func (check *checker) dump(format string, args ...interface{}) {
|
||||||
fmt.Println(check.formatMsg(format, args))
|
fmt.Println(check.formatMsg(format, args))
|
||||||
|
|
|
||||||
108
go/types/expr.go
108
go/types/expr.go
|
|
@ -764,63 +764,67 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64) int64
|
||||||
return max
|
return max
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check *checker) callExpr(x *operand, ignore *bool) {
|
|
||||||
if *ignore {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert x into a user-friendly set of values
|
|
||||||
var typ Type
|
|
||||||
var val exact.Value
|
|
||||||
switch x.mode {
|
|
||||||
case invalid:
|
|
||||||
return // nothing to do
|
|
||||||
case novalue:
|
|
||||||
typ = (*Tuple)(nil)
|
|
||||||
case constant:
|
|
||||||
typ = x.typ
|
|
||||||
val = x.val
|
|
||||||
default:
|
|
||||||
typ = x.typ
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the operand is untyped, delay notification
|
|
||||||
// until it becomes typed or until the end of
|
|
||||||
// type checking
|
|
||||||
if isUntyped(typ) {
|
|
||||||
check.untyped[x.expr] = exprInfo{false, typ.(*Basic), val}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(gri) ensure that literals always report
|
|
||||||
// their dynamic (never interface) type.
|
|
||||||
// This is not the case yet.
|
|
||||||
|
|
||||||
if check.ctxt.Expr != nil {
|
|
||||||
assert(x.expr != nil && typ != nil)
|
|
||||||
check.ctxt.Expr(x.expr, typ, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// rawExpr typechecks expression e and initializes x with the expression
|
// rawExpr typechecks expression e and initializes x with the expression
|
||||||
// value or type. If an error occurred, x.mode is set to invalid.
|
// value or type. If an error occurred, x.mode is set to invalid.
|
||||||
// If hint != nil, it is the type of a composite literal element.
|
// If hint != nil, it is the type of a composite literal element.
|
||||||
//
|
//
|
||||||
func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) {
|
func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) {
|
||||||
// make sure x has a valid state for deferred functions in case of bailout
|
if trace {
|
||||||
|
check.trace(e.Pos(), "%s", e)
|
||||||
|
check.indent++
|
||||||
|
}
|
||||||
|
|
||||||
|
check.expr0(x, e, hint)
|
||||||
|
|
||||||
|
// convert x into a user-friendly set of values
|
||||||
|
notify := check.ctxt.Expr
|
||||||
|
var typ Type
|
||||||
|
var val exact.Value
|
||||||
|
switch x.mode {
|
||||||
|
case invalid:
|
||||||
|
typ = Typ[Invalid]
|
||||||
|
notify = nil // nothing to do
|
||||||
|
case novalue:
|
||||||
|
typ = (*Tuple)(nil)
|
||||||
|
case constant:
|
||||||
|
typ = x.typ
|
||||||
|
val = x.val
|
||||||
|
case typexprn:
|
||||||
|
x.mode = typexpr
|
||||||
|
typ = x.typ
|
||||||
|
notify = nil // clients were already notified
|
||||||
|
default:
|
||||||
|
typ = x.typ
|
||||||
|
}
|
||||||
|
assert(x.expr != nil && typ != nil)
|
||||||
|
|
||||||
|
if isUntyped(typ) {
|
||||||
|
// delay notification until it becomes typed
|
||||||
|
// or until the end of type checking
|
||||||
|
check.untyped[x.expr] = exprInfo{false, typ.(*Basic), val}
|
||||||
|
} else if notify != nil {
|
||||||
|
// notify clients
|
||||||
|
// TODO(gri) ensure that literals always report
|
||||||
|
// their dynamic (never interface) type.
|
||||||
|
// This is not the case yet.
|
||||||
|
notify(x.expr, typ, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
if trace {
|
||||||
|
check.indent--
|
||||||
|
check.trace(e.Pos(), "=> %s", x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// expr0 contains the core of type checking of expressions.
|
||||||
|
// Must only be called by rawExpr.
|
||||||
|
//
|
||||||
|
func (check *checker) expr0(x *operand, e ast.Expr, hint Type) {
|
||||||
|
// make sure x has a valid state in case of bailout
|
||||||
// (was issue 5770)
|
// (was issue 5770)
|
||||||
x.mode = invalid
|
x.mode = invalid
|
||||||
x.typ = Typ[Invalid]
|
x.typ = Typ[Invalid]
|
||||||
|
|
||||||
if trace {
|
|
||||||
check.trace(e.Pos(), "%s", e)
|
|
||||||
defer check.untrace("=> %s", x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// record final type of x if untyped, notify clients of type otherwise
|
|
||||||
ignore := false // if set, don't do anything in the deferred call
|
|
||||||
defer check.callExpr(x, &ignore)
|
|
||||||
|
|
||||||
switch e := e.(type) {
|
switch e := e.(type) {
|
||||||
case *ast.BadExpr:
|
case *ast.BadExpr:
|
||||||
goto Error // error was reported before
|
goto Error // error was reported before
|
||||||
|
|
@ -1065,7 +1069,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) {
|
||||||
|
|
||||||
if e.Index == nil {
|
if e.Index == nil {
|
||||||
check.invalidAST(e.Pos(), "missing index expression for %s", x)
|
check.invalidAST(e.Pos(), "missing index expression for %s", x)
|
||||||
return
|
goto Error
|
||||||
}
|
}
|
||||||
|
|
||||||
check.index(e.Index, length)
|
check.index(e.Index, length)
|
||||||
|
|
@ -1182,7 +1186,6 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) {
|
||||||
// ok to continue
|
// ok to continue
|
||||||
}
|
}
|
||||||
x.mode = valueok
|
x.mode = valueok
|
||||||
x.expr = e
|
|
||||||
x.typ = typ
|
x.typ = typ
|
||||||
|
|
||||||
case *ast.CallExpr:
|
case *ast.CallExpr:
|
||||||
|
|
@ -1228,11 +1231,8 @@ func (check *checker) rawExpr(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 = typexpr
|
x.mode = typexprn // not typexpr; Context.Expr callback was invoked by check.typ
|
||||||
x.typ = check.typ(e, nil, false)
|
x.typ = check.typ(e, nil, false)
|
||||||
// check.typ is already notifying clients
|
|
||||||
// of e's type; don't do it a 2nd time
|
|
||||||
ignore = true
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if debug {
|
if debug {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ const (
|
||||||
invalid operandMode = iota // operand is invalid (due to an earlier error) - ignore
|
invalid operandMode = iota // operand is invalid (due to an earlier error) - ignore
|
||||||
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
|
||||||
|
|
@ -32,6 +33,7 @@ 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",
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ var sources = []string{
|
||||||
func (_ T) m() {}
|
func (_ T) m() {}
|
||||||
var i I
|
var i I
|
||||||
var _ = i.m
|
var _ = i.m
|
||||||
|
func _(s []int) { for i, x := range s {} }
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,7 +54,7 @@ var pkgnames = []string{
|
||||||
"math",
|
"math",
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolveQualifiedIdents(t *testing.T) {
|
func TestResolveIdents(t *testing.T) {
|
||||||
// parse package files
|
// parse package files
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
var files []*ast.File
|
var files []*ast.File
|
||||||
|
|
@ -74,7 +75,7 @@ func TestResolveQualifiedIdents(t *testing.T) {
|
||||||
}
|
}
|
||||||
idents[id] = obj
|
idents[id] = obj
|
||||||
}
|
}
|
||||||
pkg, err := ctxt.Check("testResolveQualifiedIdents", fset, files...)
|
pkg, err := ctxt.Check("testResolveIdents", fset, files...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -738,6 +738,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||||
check.expr(&x, s.X)
|
check.expr(&x, s.X)
|
||||||
if x.mode == invalid {
|
if x.mode == invalid {
|
||||||
// if we don't have a declaration, we can still check the loop's body
|
// if we don't have a declaration, we can still check the loop's body
|
||||||
|
// (otherwise we can't because we are missing the declared variables)
|
||||||
if !decl {
|
if !decl {
|
||||||
check.stmt(s.Body)
|
check.stmt(s.Body)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,20 +98,32 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool)
|
||||||
// If cycleOk is set, e (or elements of e) may refer to a named type that is not
|
// If cycleOk is set, e (or elements of e) may refer to a named type that is not
|
||||||
// yet completely set up.
|
// yet completely set up.
|
||||||
//
|
//
|
||||||
func (check *checker) typ(e ast.Expr, def *Named, cycleOk bool) (res Type) {
|
func (check *checker) typ(e ast.Expr, def *Named, cycleOk bool) Type {
|
||||||
if trace {
|
if trace {
|
||||||
check.trace(e.Pos(), "%s", e)
|
check.trace(e.Pos(), "%s", e)
|
||||||
defer check.untrace("=> %s", res)
|
check.indent++
|
||||||
}
|
}
|
||||||
|
|
||||||
// notify clients of type
|
t := check.typ0(e, def, cycleOk)
|
||||||
if f := check.ctxt.Expr; f != nil {
|
assert(e != nil && t != nil && !isUntyped(t))
|
||||||
defer func() {
|
|
||||||
assert(e != nil && res != nil && !isUntyped(res))
|
// notify clients
|
||||||
f(e, res, nil)
|
if notify := check.ctxt.Expr; notify != nil {
|
||||||
}()
|
notify(e, t, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if trace {
|
||||||
|
check.indent--
|
||||||
|
check.trace(e.Pos(), "=> %s", t)
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// typ0 contains the core of type checking of types.
|
||||||
|
// Must only be called by typ.
|
||||||
|
//
|
||||||
|
func (check *checker) typ0(e ast.Expr, def *Named, cycleOk bool) Type {
|
||||||
switch e := e.(type) {
|
switch e := e.(type) {
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
var x operand
|
var x operand
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue