mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
go.tools/go/types: fix more cycle errors, lots of refactoring
- moved ident and typ expr checking into typexpr.go - as a result, fewer parameters are needed for expr checking - forward-chain type decls of the form type ( A B; B C; C *A) etc. so that cycles are getting the right types in all cases - fixed several corner case bugs, added more test cases R=adonovan CC=golang-dev https://golang.org/cl/10773043
This commit is contained in:
parent
6ae930a01c
commit
b58f98e9c2
@ -89,7 +89,7 @@ func (check *checker) assignMulti(lhs []Object, rhs []ast.Expr) {
|
||||
if len(lhs) == len(rhs) {
|
||||
var x operand
|
||||
for i, e := range rhs {
|
||||
check.expr(&x, e, nil, -1)
|
||||
check.expr(&x, e, -1)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
@ -106,7 +106,7 @@ func (check *checker) assignMulti(lhs []Object, rhs []ast.Expr) {
|
||||
// Start with rhs so we have expression types
|
||||
// for declarations with implicit types.
|
||||
var x operand
|
||||
check.expr(&x, rhs[0], nil, -1)
|
||||
check.expr(&x, rhs[0], -1)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
|
||||
// respective cases below do the work
|
||||
default:
|
||||
// argument must be an expression
|
||||
check.expr(x, arg0, nil, iota)
|
||||
check.expr(x, arg0, iota)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
@ -65,7 +65,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
|
||||
}
|
||||
resultTyp := x.typ
|
||||
for _, arg := range args[1:] {
|
||||
check.expr(x, arg, nil, iota)
|
||||
check.expr(x, arg, iota)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
@ -134,7 +134,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
|
||||
}
|
||||
|
||||
var y operand
|
||||
check.expr(&y, args[1], nil, iota)
|
||||
check.expr(&y, args[1], iota)
|
||||
if y.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
@ -196,7 +196,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
|
||||
|
||||
case _Copy:
|
||||
var y operand
|
||||
check.expr(&y, args[1], nil, iota)
|
||||
check.expr(&y, args[1], iota)
|
||||
if y.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
@ -233,7 +233,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
|
||||
check.invalidArg(x.pos(), "%s is not a map", x)
|
||||
goto Error
|
||||
}
|
||||
check.expr(x, args[1], nil, iota)
|
||||
check.expr(x, args[1], iota)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
@ -271,7 +271,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
|
||||
x.typ = Typ[k]
|
||||
|
||||
case _Make:
|
||||
resultTyp := check.typ(arg0, false)
|
||||
resultTyp := check.typ(arg0, iota, nil, false)
|
||||
if resultTyp == Typ[Invalid] {
|
||||
goto Error
|
||||
}
|
||||
@ -303,7 +303,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
|
||||
x.typ = resultTyp
|
||||
|
||||
case _New:
|
||||
resultTyp := check.typ(arg0, false)
|
||||
resultTyp := check.typ(arg0, iota, nil, false)
|
||||
if resultTyp == Typ[Invalid] {
|
||||
goto Error
|
||||
}
|
||||
@ -315,7 +315,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
|
||||
|
||||
case _Print, _Println:
|
||||
for _, arg := range args {
|
||||
check.expr(x, arg, nil, -1)
|
||||
check.expr(x, arg, iota)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
@ -337,7 +337,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
|
||||
check.invalidArg(arg0.Pos(), "%s is not a selector expression", arg0)
|
||||
goto Error
|
||||
}
|
||||
check.expr(x, arg.X, nil, -1)
|
||||
check.expr(x, arg.X, iota)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
@ -397,7 +397,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
|
||||
var t operand
|
||||
x1 := x
|
||||
for _, arg := range args {
|
||||
check.rawExpr(x1, arg, nil, iota, true) // permit trace for types, e.g.: new(trace(T))
|
||||
check.rawExpr(x1, arg, nil, iota) // permit trace for types, e.g.: new(trace(T))
|
||||
check.dump("%s: %s", x1.pos(), x1)
|
||||
x1 = &t // use incoming x only for first argument
|
||||
}
|
||||
|
@ -12,13 +12,13 @@ import (
|
||||
)
|
||||
|
||||
func (check *checker) call(x *operand, e *ast.CallExpr, iota int) {
|
||||
check.exprOrType(x, e.Fun, iota, false)
|
||||
check.exprOrType(x, e.Fun, iota)
|
||||
if x.mode == invalid {
|
||||
// We don't have a valid call or conversion but we have list of arguments.
|
||||
// Typecheck them independently for better partial type information in
|
||||
// the presence of type errors.
|
||||
for _, arg := range e.Args {
|
||||
check.expr(x, arg, nil, iota)
|
||||
check.expr(x, arg, iota)
|
||||
}
|
||||
goto Error
|
||||
|
||||
@ -52,7 +52,7 @@ func (check *checker) call(x *operand, e *ast.CallExpr, iota int) {
|
||||
n := 0 // parameter count
|
||||
if call != nil {
|
||||
// We have a single argument that is a function call.
|
||||
check.expr(x, call, nil, -1)
|
||||
check.expr(x, call, iota)
|
||||
if x.mode == invalid {
|
||||
goto Error // TODO(gri): we can do better
|
||||
}
|
||||
@ -153,7 +153,7 @@ func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand,
|
||||
z.typ = par.typ
|
||||
|
||||
if arg != nil {
|
||||
check.expr(x, arg, z.typ, -1)
|
||||
check.expr(x, arg, -1)
|
||||
}
|
||||
if x.mode == invalid {
|
||||
return // ignore this argument
|
||||
@ -230,7 +230,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr, iota int) {
|
||||
}
|
||||
}
|
||||
|
||||
check.exprOrType(x, e.X, iota, false)
|
||||
check.exprOrType(x, e.X, iota)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota
|
||||
}
|
||||
|
||||
// evaluate argument
|
||||
check.expr(x, conv.Args[0], nil, iota)
|
||||
check.expr(x, conv.Args[0], iota)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
|
404
go/types/expr.go
404
go/types/expr.go
@ -9,7 +9,6 @@ package types
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strconv"
|
||||
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
)
|
||||
@ -69,175 +68,6 @@ 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.
|
||||
*/
|
||||
|
||||
func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, isVariadic bool) {
|
||||
if list == nil {
|
||||
return
|
||||
}
|
||||
var last *Var
|
||||
for i, field := range list.List {
|
||||
ftype := field.Type
|
||||
if t, _ := ftype.(*ast.Ellipsis); t != nil {
|
||||
ftype = t.Elt
|
||||
if variadicOk && i == len(list.List)-1 {
|
||||
isVariadic = true
|
||||
} else {
|
||||
check.invalidAST(field.Pos(), "... not permitted")
|
||||
// ok to continue
|
||||
}
|
||||
}
|
||||
// the parser ensures that f.Tag is nil and we don't
|
||||
// care if a constructed AST contains a non-nil tag
|
||||
typ := check.typ(ftype, true)
|
||||
if len(field.Names) > 0 {
|
||||
// named parameter
|
||||
for _, name := range field.Names {
|
||||
par := NewVar(name.Pos(), check.pkg, name.Name, typ)
|
||||
check.declare(scope, name, par)
|
||||
|
||||
last = par
|
||||
copy := *par
|
||||
params = append(params, ©)
|
||||
}
|
||||
} else {
|
||||
// anonymous parameter
|
||||
par := NewVar(ftype.Pos(), check.pkg, "", typ)
|
||||
check.callImplicitObj(field, par)
|
||||
|
||||
last = nil // not accessible inside function
|
||||
params = append(params, par)
|
||||
}
|
||||
}
|
||||
// For a variadic function, change the last parameter's object type
|
||||
// from T to []T (this is the type used inside the function), but
|
||||
// keep the params list unchanged (this is the externally visible type).
|
||||
if isVariadic && last != nil {
|
||||
last.typ = &Slice{elt: last.typ}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (check *checker) collectMethods(list *ast.FieldList, cycleOk bool) (methods []*Func) {
|
||||
if list == nil {
|
||||
return nil
|
||||
}
|
||||
scope := NewScope(nil)
|
||||
for _, f := range list.List {
|
||||
typ := check.typ(f.Type, cycleOk)
|
||||
// the parser ensures that f.Tag is nil and we don't
|
||||
// care if a constructed AST contains a non-nil tag
|
||||
if len(f.Names) > 0 {
|
||||
// methods (the parser ensures that there's only one
|
||||
// and we don't care if a constructed AST has more)
|
||||
sig, ok := typ.(*Signature)
|
||||
if !ok {
|
||||
check.invalidAST(f.Type.Pos(), "%s is not a method signature", typ)
|
||||
continue
|
||||
}
|
||||
for _, name := range f.Names {
|
||||
m := NewFunc(name.Pos(), check.pkg, name.Name, sig)
|
||||
check.declare(scope, name, m)
|
||||
methods = append(methods, m)
|
||||
}
|
||||
} else {
|
||||
// embedded interface
|
||||
switch t := typ.Underlying().(type) {
|
||||
case nil:
|
||||
// The underlying type is in the process of being defined
|
||||
// but we need it in order to complete this type. For now
|
||||
// complain with an "unimplemented" error. This requires
|
||||
// a bit more work.
|
||||
// TODO(gri) finish this.
|
||||
check.errorf(f.Type.Pos(), "reference to incomplete type %s - unimplemented", f.Type)
|
||||
case *Interface:
|
||||
for _, m := range t.methods {
|
||||
check.declare(scope, nil, m)
|
||||
methods = append(methods, m)
|
||||
}
|
||||
default:
|
||||
if t != Typ[Invalid] {
|
||||
check.errorf(f.Type.Pos(), "%s is not an interface type", typ)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (check *checker) tag(t *ast.BasicLit) string {
|
||||
if t != nil {
|
||||
if t.Kind == token.STRING {
|
||||
if val, err := strconv.Unquote(t.Value); err == nil {
|
||||
return val
|
||||
}
|
||||
}
|
||||
check.invalidAST(t.Pos(), "incorrect tag syntax: %q", t.Value)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*Field, tags []string) {
|
||||
if list == nil {
|
||||
return
|
||||
}
|
||||
|
||||
scope := NewScope(nil)
|
||||
|
||||
var typ Type // current field typ
|
||||
var tag string // current field tag
|
||||
add := func(field *ast.Field, ident *ast.Ident, name string, anonymous bool, pos token.Pos) {
|
||||
if tag != "" && tags == nil {
|
||||
tags = make([]string, len(fields))
|
||||
}
|
||||
if tags != nil {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
|
||||
fld := NewField(pos, check.pkg, name, typ, anonymous)
|
||||
check.declare(scope, ident, fld)
|
||||
fields = append(fields, fld)
|
||||
}
|
||||
|
||||
for _, f := range list.List {
|
||||
typ = check.typ(f.Type, cycleOk)
|
||||
tag = check.tag(f.Tag)
|
||||
if len(f.Names) > 0 {
|
||||
// named fields
|
||||
for _, name := range f.Names {
|
||||
add(f, name, name.Name, false, name.Pos())
|
||||
}
|
||||
} else {
|
||||
// anonymous field
|
||||
pos := f.Type.Pos()
|
||||
t, isPtr := deref(typ)
|
||||
switch t := t.(type) {
|
||||
case *Basic:
|
||||
add(f, nil, t.name, true, pos)
|
||||
case *Named:
|
||||
// spec: "An embedded type must be specified as a type name
|
||||
// T or as a pointer to a non-interface type name *T, and T
|
||||
// itself may not be a pointer type."
|
||||
switch t.underlying.(type) {
|
||||
case *Pointer:
|
||||
check.errorf(pos, "anonymous field type cannot be a pointer")
|
||||
continue // ignore this field
|
||||
case *Interface:
|
||||
if isPtr {
|
||||
check.errorf(pos, "anonymous field type cannot be a pointer to an interface")
|
||||
continue // ignore this field
|
||||
}
|
||||
}
|
||||
add(f, nil, t.obj.name, true, pos)
|
||||
default:
|
||||
if typ != Typ[Invalid] {
|
||||
check.invalidAST(pos, "anonymous field type %s must be named", typ)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type opPredicates map[token.Token]func(Type) bool
|
||||
|
||||
var unaryOpPredicates = opPredicates{
|
||||
@ -784,8 +614,8 @@ var binaryOpPredicates = opPredicates{
|
||||
func (check *checker) binary(x *operand, lhs, rhs ast.Expr, op token.Token, iota int) {
|
||||
var y operand
|
||||
|
||||
check.expr(x, lhs, nil, iota)
|
||||
check.expr(&y, rhs, nil, iota)
|
||||
check.expr(x, lhs, iota)
|
||||
check.expr(&y, rhs, iota)
|
||||
|
||||
if x.mode == invalid {
|
||||
return
|
||||
@ -859,7 +689,7 @@ func (check *checker) binary(x *operand, lhs, rhs ast.Expr, op token.Token, iota
|
||||
// TODO(gri): Do we need iota?
|
||||
func (check *checker) index(arg ast.Expr, length int64, iota int) (i int64, ok bool) {
|
||||
var x operand
|
||||
check.expr(&x, arg, nil, iota)
|
||||
check.expr(&x, arg, iota)
|
||||
|
||||
// an untyped constant must be representable as Int
|
||||
check.convertUntyped(&x, Typ[Int])
|
||||
@ -931,7 +761,7 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64, iota
|
||||
|
||||
// check element against composite literal element type
|
||||
var x operand
|
||||
check.expr(&x, eval, typ, iota)
|
||||
check.exprWithHint(&x, eval, typ, iota)
|
||||
if !check.assignment(&x, typ) && x.mode != invalid {
|
||||
check.errorf(x.pos(), "cannot use %s as %s value in array or slice literal", &x, typ)
|
||||
}
|
||||
@ -939,7 +769,11 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64, iota
|
||||
return max
|
||||
}
|
||||
|
||||
func (check *checker) callExpr(x *operand) {
|
||||
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
|
||||
@ -977,86 +811,28 @@ func (check *checker) callExpr(x *operand) {
|
||||
// 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.
|
||||
// iota >= 0 indicates that the expression is part of a constant declaration.
|
||||
// cycleOk indicates whether it is ok for a type expression to refer to itself.
|
||||
//
|
||||
func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycleOk bool) {
|
||||
func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
|
||||
// make sure x has a valid state for deferred functions in case of bailout
|
||||
// (was issue 5770)
|
||||
x.mode = invalid
|
||||
x.typ = Typ[Invalid]
|
||||
|
||||
if trace {
|
||||
c := ""
|
||||
if cycleOk {
|
||||
c = " ⨁"
|
||||
}
|
||||
check.trace(e.Pos(), "%s%s", e, c)
|
||||
check.trace(e.Pos(), "%s", e)
|
||||
defer check.untrace("=> %s", x)
|
||||
}
|
||||
|
||||
// record final type of x if untyped, notify clients of type otherwise
|
||||
defer check.callExpr(x)
|
||||
ignore := false // if set, don't do anything in the deferred call
|
||||
defer check.callExpr(x, &ignore)
|
||||
|
||||
switch e := e.(type) {
|
||||
case *ast.BadExpr:
|
||||
goto Error // error was reported before
|
||||
|
||||
case *ast.Ident:
|
||||
obj := check.topScope.LookupParent(e.Name)
|
||||
check.callIdent(e, obj)
|
||||
if obj == nil {
|
||||
if e.Name == "_" {
|
||||
check.errorf(e.Pos(), "cannot use _ as value or type")
|
||||
} else {
|
||||
check.errorf(e.Pos(), "undeclared name: %s", e.Name)
|
||||
}
|
||||
goto Error // error was reported before
|
||||
}
|
||||
|
||||
typ := obj.Type()
|
||||
if check.objMap == nil {
|
||||
if typ == nil {
|
||||
check.dump("%s: %s not declared?", e.Pos(), e)
|
||||
}
|
||||
assert(typ != nil)
|
||||
} else if typ == nil {
|
||||
check.declareObject(obj, cycleOk)
|
||||
}
|
||||
|
||||
switch obj := obj.(type) {
|
||||
case *Package:
|
||||
check.errorf(e.Pos(), "use of package %s not in selector", obj.name)
|
||||
goto Error
|
||||
case *Const:
|
||||
if obj.typ == Typ[Invalid] {
|
||||
goto Error
|
||||
}
|
||||
x.mode = constant
|
||||
if obj == universeIota {
|
||||
if iota < 0 {
|
||||
check.invalidAST(e.Pos(), "cannot use iota outside constant declaration")
|
||||
goto Error
|
||||
}
|
||||
x.val = exact.MakeInt64(int64(iota))
|
||||
} else {
|
||||
x.val = obj.val // may be nil if we don't know the constant value
|
||||
}
|
||||
case *TypeName:
|
||||
x.mode = typexpr
|
||||
if !cycleOk && obj.typ.Underlying() == nil {
|
||||
check.errorf(obj.pos, "illegal cycle in declaration of %s", obj.name)
|
||||
x.expr = e
|
||||
x.typ = Typ[Invalid]
|
||||
return // don't goto Error - want x.mode == typexpr
|
||||
}
|
||||
case *Var:
|
||||
x.mode = variable
|
||||
case *Func:
|
||||
x.mode = value
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
x.typ = obj.Type()
|
||||
check.ident(x, e, iota, nil, false)
|
||||
|
||||
case *ast.Ellipsis:
|
||||
// ellipses are handled explicitly where they are legal
|
||||
@ -1072,7 +848,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
}
|
||||
|
||||
case *ast.FuncLit:
|
||||
if sig, ok := check.typ(e.Type, false).(*Signature); ok {
|
||||
if sig, ok := check.typ(e.Type, iota, nil, false).(*Signature); ok {
|
||||
x.mode = value
|
||||
x.typ = sig
|
||||
check.later(nil, sig, e.Body)
|
||||
@ -1093,12 +869,12 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
// We have an "open" [...]T array type.
|
||||
// Create a new ArrayType with unknown length (-1)
|
||||
// and finish setting it up after analyzing the literal.
|
||||
typ = &Array{len: -1, elt: check.typ(atyp.Elt, cycleOk)}
|
||||
typ = &Array{len: -1, elt: check.typ(atyp.Elt, iota, nil, false)}
|
||||
openArray = true
|
||||
}
|
||||
}
|
||||
if typ == nil {
|
||||
typ = check.typ(e.Type, false)
|
||||
typ = check.typ(e.Type, iota, nil, false)
|
||||
}
|
||||
}
|
||||
if typ == nil {
|
||||
@ -1139,7 +915,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
continue
|
||||
}
|
||||
visited[i] = true
|
||||
check.expr(x, kv.Value, nil, iota)
|
||||
check.expr(x, kv.Value, iota)
|
||||
etyp := fld.typ
|
||||
if !check.assignment(x, etyp) {
|
||||
if x.mode != invalid {
|
||||
@ -1155,7 +931,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
check.errorf(kv.Pos(), "mixture of field:value and value elements in struct literal")
|
||||
continue
|
||||
}
|
||||
check.expr(x, e, nil, iota)
|
||||
check.expr(x, e, iota)
|
||||
if i >= len(fields) {
|
||||
check.errorf(x.pos(), "too many values in struct literal")
|
||||
break // cannot continue
|
||||
@ -1193,7 +969,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
check.errorf(e.Pos(), "missing key in map literal")
|
||||
continue
|
||||
}
|
||||
check.expr(x, kv.Key, nil, iota)
|
||||
check.expr(x, kv.Key, iota)
|
||||
if !check.assignment(x, utyp.key) {
|
||||
if x.mode != invalid {
|
||||
check.errorf(x.pos(), "cannot use %s as %s key in map literal", x, utyp.key)
|
||||
@ -1207,7 +983,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
}
|
||||
visited[x.val] = true
|
||||
}
|
||||
check.expr(x, kv.Value, utyp.elt, iota)
|
||||
check.exprWithHint(x, kv.Value, utyp.elt, iota)
|
||||
if !check.assignment(x, utyp.elt) {
|
||||
if x.mode != invalid {
|
||||
check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.elt)
|
||||
@ -1225,13 +1001,13 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
x.typ = typ
|
||||
|
||||
case *ast.ParenExpr:
|
||||
check.rawExpr(x, e.X, nil, iota, cycleOk)
|
||||
check.rawExpr(x, e.X, nil, iota)
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
check.selector(x, e, iota)
|
||||
|
||||
case *ast.IndexExpr:
|
||||
check.expr(x, e.X, nil, iota)
|
||||
check.expr(x, e.X, iota)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
@ -1275,7 +1051,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
|
||||
case *Map:
|
||||
var key operand
|
||||
check.expr(&key, e.Index, nil, iota)
|
||||
check.expr(&key, e.Index, iota)
|
||||
if !check.assignment(&key, typ.key) {
|
||||
if key.mode != invalid {
|
||||
check.invalidOp(key.pos(), "cannot use %s as map index of type %s", &key, typ.key)
|
||||
@ -1302,7 +1078,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
// ok to continue
|
||||
|
||||
case *ast.SliceExpr:
|
||||
check.expr(x, e.X, nil, iota)
|
||||
check.expr(x, e.X, iota)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
@ -1378,7 +1154,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
}
|
||||
|
||||
case *ast.TypeAssertExpr:
|
||||
check.expr(x, e.X, nil, iota)
|
||||
check.expr(x, e.X, iota)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
@ -1392,7 +1168,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
check.invalidAST(e.Pos(), "use of .(type) outside type switch")
|
||||
goto Error
|
||||
}
|
||||
typ := check.typ(e.Type, false)
|
||||
typ := check.typ(e.Type, iota, nil, false)
|
||||
if typ == Typ[Invalid] {
|
||||
goto Error
|
||||
}
|
||||
@ -1419,7 +1195,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
check.call(x, e, iota)
|
||||
|
||||
case *ast.StarExpr:
|
||||
check.exprOrType(x, e.X, iota, true)
|
||||
check.exprOrType(x, e.X, iota)
|
||||
switch x.mode {
|
||||
case invalid:
|
||||
goto Error
|
||||
@ -1436,7 +1212,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
}
|
||||
|
||||
case *ast.UnaryExpr:
|
||||
check.expr(x, e.X, nil, iota)
|
||||
check.expr(x, e.X, iota)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
@ -1456,58 +1232,13 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
check.invalidAST(e.Pos(), "no key:value expected")
|
||||
goto Error
|
||||
|
||||
case *ast.ArrayType:
|
||||
if e.Len != nil {
|
||||
check.expr(x, e.Len, nil, iota)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
if x.mode != constant {
|
||||
if x.mode != invalid {
|
||||
check.errorf(x.pos(), "array length %s must be constant", x)
|
||||
}
|
||||
goto Error
|
||||
}
|
||||
if !x.isInteger() {
|
||||
check.errorf(x.pos(), "array length %s must be integer", x)
|
||||
goto Error
|
||||
}
|
||||
n, ok := exact.Int64Val(x.val)
|
||||
if !ok || n < 0 {
|
||||
check.errorf(x.pos(), "invalid array length %s", x)
|
||||
goto Error
|
||||
}
|
||||
x.typ = &Array{len: n, elt: check.typ(e.Elt, cycleOk)}
|
||||
} else {
|
||||
x.typ = &Slice{elt: check.typ(e.Elt, true)}
|
||||
}
|
||||
case *ast.ArrayType, *ast.StructType, *ast.FuncType,
|
||||
*ast.InterfaceType, *ast.MapType, *ast.ChanType:
|
||||
x.mode = typexpr
|
||||
|
||||
case *ast.StructType:
|
||||
x.mode = typexpr
|
||||
x.typ = NewStruct(check.collectFields(e.Fields, cycleOk))
|
||||
|
||||
case *ast.FuncType:
|
||||
scope := NewScope(check.topScope)
|
||||
if retainASTLinks {
|
||||
scope.node = e
|
||||
}
|
||||
params, isVariadic := check.collectParams(scope, e.Params, true)
|
||||
results, _ := check.collectParams(scope, e.Results, false)
|
||||
x.mode = typexpr
|
||||
x.typ = &Signature{scope: scope, recv: nil, params: NewTuple(params...), results: NewTuple(results...), isVariadic: isVariadic}
|
||||
|
||||
case *ast.InterfaceType:
|
||||
x.mode = typexpr
|
||||
x.typ = NewInterface(check.collectMethods(e.Methods, cycleOk))
|
||||
|
||||
case *ast.MapType:
|
||||
x.mode = typexpr
|
||||
x.typ = &Map{key: check.typ(e.Key, true), elt: check.typ(e.Value, true)}
|
||||
|
||||
case *ast.ChanType:
|
||||
x.mode = typexpr
|
||||
x.typ = &Chan{dir: e.Dir, elt: check.typ(e.Value, true)}
|
||||
x.typ = check.typ(e, iota, nil, false)
|
||||
// check.typ is already notifying clients
|
||||
// of e's type; don't do it a 2nd time
|
||||
ignore = true
|
||||
|
||||
default:
|
||||
if debug {
|
||||
@ -1525,18 +1256,12 @@ Error:
|
||||
x.expr = e
|
||||
}
|
||||
|
||||
// exprOrType is like rawExpr but reports an error if e doesn't represents a value or type.
|
||||
func (check *checker) exprOrType(x *operand, e ast.Expr, iota int, cycleOk bool) {
|
||||
check.rawExpr(x, e, nil, iota, cycleOk)
|
||||
if x.mode == novalue {
|
||||
check.errorf(x.pos(), "%s used as value or type", x)
|
||||
x.mode = invalid
|
||||
}
|
||||
}
|
||||
|
||||
// expr is like rawExpr but reports an error if e doesn't represents a value.
|
||||
func (check *checker) expr(x *operand, e ast.Expr, hint Type, iota int) {
|
||||
check.rawExpr(x, e, hint, iota, false)
|
||||
// expr typechecks expression e and initializes x with the expression value.
|
||||
// If an error occurred, x.mode is set to invalid.
|
||||
// iota >= 0 indicates that the expression is part of a constant declaration.
|
||||
//
|
||||
func (check *checker) expr(x *operand, e ast.Expr, iota int) {
|
||||
check.rawExpr(x, e, nil, iota)
|
||||
switch x.mode {
|
||||
case novalue:
|
||||
check.errorf(x.pos(), "%s used as value", x)
|
||||
@ -1547,37 +1272,32 @@ func (check *checker) expr(x *operand, e ast.Expr, hint Type, iota int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (check *checker) rawTyp(e ast.Expr, cycleOk, nilOk bool) Type {
|
||||
var x operand
|
||||
check.rawExpr(&x, e, nil, -1, cycleOk)
|
||||
// exprWithHint typechecks expression e and initializes x with the expression value.
|
||||
// If an error occurred, x.mode is set to invalid.
|
||||
// If hint != nil, it is the type of a composite literal element.
|
||||
// iota >= 0 indicates that the expression is part of a constant declaration.
|
||||
//
|
||||
func (check *checker) exprWithHint(x *operand, e ast.Expr, hint Type, iota int) {
|
||||
assert(hint != nil)
|
||||
check.rawExpr(x, e, hint, iota)
|
||||
switch x.mode {
|
||||
case invalid:
|
||||
// ignore - error reported before
|
||||
case novalue:
|
||||
check.errorf(x.pos(), "%s used as type", &x)
|
||||
check.errorf(x.pos(), "%s used as value", x)
|
||||
x.mode = invalid
|
||||
case typexpr:
|
||||
return x.typ
|
||||
case constant:
|
||||
if nilOk && x.isNil() {
|
||||
return nil
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
check.errorf(x.pos(), "%s is not a type", &x)
|
||||
check.errorf(x.pos(), "%s is not an expression", x)
|
||||
x.mode = invalid
|
||||
}
|
||||
return Typ[Invalid]
|
||||
}
|
||||
|
||||
// typOrNil is like rawExpr but reports an error if e doesn't represents a type or the predeclared value nil.
|
||||
// It returns e's type, nil, or Typ[Invalid] if an error occurred.
|
||||
// exprOrType typechecks expression or type e and initializes x with the expression value or type.
|
||||
// If an error occurred, x.mode is set to invalid.
|
||||
// iota >= 0 indicates that the expression is part of a constant declaration.
|
||||
//
|
||||
func (check *checker) typOrNil(e ast.Expr, cycleOk bool) Type {
|
||||
return check.rawTyp(e, cycleOk, true)
|
||||
}
|
||||
|
||||
// typ is like rawExpr but reports an error if e doesn't represents a type.
|
||||
// It returns e's type, or Typ[Invalid] if an error occurred.
|
||||
//
|
||||
func (check *checker) typ(e ast.Expr, cycleOk bool) Type {
|
||||
return check.rawTyp(e, cycleOk, false)
|
||||
func (check *checker) exprOrType(x *operand, e ast.Expr, iota int) {
|
||||
check.rawExpr(x, e, nil, iota)
|
||||
if x.mode == novalue {
|
||||
check.errorf(x.pos(), "%s used as value or type", x)
|
||||
x.mode = invalid
|
||||
}
|
||||
}
|
||||
|
@ -326,7 +326,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
check.topScope = pkg.scope
|
||||
for _, obj := range objList {
|
||||
if obj.Type() == nil {
|
||||
check.declareObject(obj, false)
|
||||
check.declareObject(obj, nil, false)
|
||||
}
|
||||
}
|
||||
check.objMap = nil // done with global declarations
|
||||
@ -335,7 +335,9 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
// - done by the caller for now
|
||||
}
|
||||
|
||||
func (check *checker) declareObject(obj Object, cycleOk bool) {
|
||||
// declareObject declares obj in the top-most scope.
|
||||
// See declareType for the details on def and cycleOk.
|
||||
func (check *checker) declareObject(obj Object, def *Named, cycleOk bool) {
|
||||
d := check.objMap[obj]
|
||||
|
||||
// adjust file scope for current object
|
||||
@ -348,7 +350,7 @@ func (check *checker) declareObject(obj Object, cycleOk bool) {
|
||||
case *Var:
|
||||
check.declareVar(obj, d.typ, d.init)
|
||||
case *TypeName:
|
||||
check.declareType(obj, d.typ, cycleOk)
|
||||
check.declareType(obj, d.typ, def, cycleOk)
|
||||
case *Func:
|
||||
check.declareFunc(obj)
|
||||
default:
|
||||
@ -367,11 +369,10 @@ func (check *checker) declareConst(obj *Const, typ, init ast.Expr) {
|
||||
obj.visited = true
|
||||
iota, ok := exact.Int64Val(obj.val) // set in phase 1
|
||||
assert(ok)
|
||||
// obj.val = exact.MakeUnknown() //do we need this? should we set val to nil?
|
||||
|
||||
// determine type, if any
|
||||
if typ != nil {
|
||||
obj.typ = check.typ(typ, false)
|
||||
obj.typ = check.typ(typ, int(iota), nil, false)
|
||||
}
|
||||
|
||||
var x operand
|
||||
@ -380,7 +381,7 @@ func (check *checker) declareConst(obj *Const, typ, init ast.Expr) {
|
||||
goto Error // error reported before
|
||||
}
|
||||
|
||||
check.expr(&x, init, nil, int(iota))
|
||||
check.expr(&x, init, int(iota))
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
@ -406,7 +407,7 @@ func (check *checker) declareVar(obj *Var, typ, init ast.Expr) {
|
||||
|
||||
// determine type, if any
|
||||
if typ != nil {
|
||||
obj.typ = check.typ(typ, false)
|
||||
obj.typ = check.typ(typ, -1, nil, false)
|
||||
}
|
||||
|
||||
if init == nil {
|
||||
@ -423,7 +424,7 @@ func (check *checker) declareVar(obj *Var, typ, init ast.Expr) {
|
||||
}
|
||||
|
||||
var x operand
|
||||
check.expr(&x, init, nil, -1)
|
||||
check.expr(&x, init, -1)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
@ -473,10 +474,46 @@ Error:
|
||||
return
|
||||
}
|
||||
|
||||
func (check *checker) declareType(obj *TypeName, typ ast.Expr, cycleOk bool) {
|
||||
func (check *checker) declareType(obj *TypeName, typ ast.Expr, def *Named, cycleOk bool) {
|
||||
assert(obj.Type() == nil)
|
||||
|
||||
named := &Named{obj: obj}
|
||||
obj.typ = named // mark object so recursion terminates in case of cycles
|
||||
named.underlying = check.typ(typ, cycleOk).Underlying()
|
||||
obj.typ = named // make sure recursive type declarations terminate
|
||||
|
||||
// If this type (named) defines the type of another (def) type declaration,
|
||||
// set def's underlying type to this type so that we can resolve the true
|
||||
// underlying of def later.
|
||||
if def != nil {
|
||||
def.underlying = named
|
||||
}
|
||||
|
||||
// Typecheck typ - it may be a named type that is not yet complete.
|
||||
// For instance, consider:
|
||||
//
|
||||
// type (
|
||||
// A B
|
||||
// B *C
|
||||
// C A
|
||||
// )
|
||||
//
|
||||
// When we declare obj = C, typ is the identifier A which is incomplete.
|
||||
u := check.typ(typ, -1, named, cycleOk)
|
||||
|
||||
// Determine the unnamed underlying type.
|
||||
// In the above example, the underlying type of A was (temporarily) set
|
||||
// to B whose underlying type was set to *C. Such "forward chains" always
|
||||
// end in an unnamed type (cycles are terminated with an invalid type).
|
||||
for {
|
||||
n, _ := u.(*Named)
|
||||
if n == nil {
|
||||
break
|
||||
}
|
||||
u = n.underlying
|
||||
}
|
||||
named.underlying = u
|
||||
|
||||
// the underlying type has been determined
|
||||
named.complete = true
|
||||
|
||||
// typecheck associated method signatures
|
||||
if scope := check.methods[obj]; scope != nil {
|
||||
@ -512,7 +549,7 @@ func (check *checker) declareType(obj *TypeName, typ ast.Expr, cycleOk bool) {
|
||||
oldScope := check.topScope
|
||||
check.topScope = fileScope
|
||||
|
||||
sig := check.typ(m.decl.Type, cycleOk).(*Signature)
|
||||
sig := check.typ(m.decl.Type, -1, nil, cycleOk).(*Signature)
|
||||
params, _ := check.collectParams(sig.scope, m.decl.Recv, false)
|
||||
|
||||
check.topScope = oldScope // reset topScope
|
||||
@ -534,7 +571,7 @@ func (check *checker) declareFunc(obj *Func) {
|
||||
// TODO(gri) there is no reason to make this a special case: receivers are simply parameters
|
||||
if fdecl.Recv == nil {
|
||||
obj.typ = Typ[Invalid] // guard against cycles
|
||||
sig := check.typ(fdecl.Type, false).(*Signature)
|
||||
sig := check.typ(fdecl.Type, -1, nil, false).(*Signature)
|
||||
if obj.name == "init" && (sig.params.Len() > 0 || sig.results.Len() > 0) {
|
||||
check.errorf(fdecl.Pos(), "func init must have no arguments and no return values")
|
||||
// ok to continue
|
||||
@ -643,7 +680,7 @@ func (check *checker) declStmt(decl ast.Decl) {
|
||||
case *ast.TypeSpec:
|
||||
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
|
||||
check.declare(check.topScope, s.Name, obj)
|
||||
check.declareType(obj, s.Type, false)
|
||||
check.declareType(obj, s.Type, nil, false)
|
||||
|
||||
default:
|
||||
check.invalidAST(s.Pos(), "const, type, or var declaration expected")
|
||||
|
@ -48,7 +48,7 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
|
||||
// for declarations with implicit type.
|
||||
if x == nil {
|
||||
x = new(operand)
|
||||
check.expr(x, rhs, nil, iota)
|
||||
check.expr(x, rhs, iota)
|
||||
// don't exit for declarations - we need the lhs first
|
||||
if x.mode == invalid && !decl {
|
||||
return
|
||||
@ -70,7 +70,7 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
|
||||
}
|
||||
|
||||
var z operand
|
||||
check.expr(&z, lhs, nil, -1)
|
||||
check.expr(&z, lhs, -1)
|
||||
if z.mode == invalid || z.typ == Typ[Invalid] {
|
||||
return
|
||||
}
|
||||
@ -195,7 +195,7 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int, isCon
|
||||
// Start with rhs so we have expression types
|
||||
// for declarations with implicit types.
|
||||
var x operand
|
||||
check.expr(&x, rhs[0], nil, iota)
|
||||
check.expr(&x, rhs[0], iota)
|
||||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
@ -335,7 +335,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
// (Caution: This evaluates e.Fun twice, once here and once
|
||||
// below as part of s.X. This has consequences for
|
||||
// check.callIdent. Perhaps this can be avoided.)
|
||||
check.expr(&x, e.Fun, nil, -1)
|
||||
check.expr(&x, e.Fun, -1)
|
||||
if x.mode != invalid {
|
||||
if b, ok := x.typ.(*Builtin); ok && !b.isStatement {
|
||||
used = false
|
||||
@ -351,15 +351,15 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
check.errorf(s.Pos(), "%s not used", s.X)
|
||||
// ok to continue
|
||||
}
|
||||
check.rawExpr(&x, s.X, nil, -1, false)
|
||||
check.rawExpr(&x, s.X, nil, -1)
|
||||
if x.mode == typexpr {
|
||||
check.errorf(x.pos(), "%s is not an expression", &x)
|
||||
}
|
||||
|
||||
case *ast.SendStmt:
|
||||
var ch, x operand
|
||||
check.expr(&ch, s.Chan, nil, -1)
|
||||
check.expr(&x, s.Value, nil, -1)
|
||||
check.expr(&ch, s.Chan, -1)
|
||||
check.expr(&x, s.Value, -1)
|
||||
if ch.mode == invalid || x.mode == invalid {
|
||||
return
|
||||
}
|
||||
@ -526,7 +526,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
check.openScope(s)
|
||||
check.optionalStmt(s.Init)
|
||||
var x operand
|
||||
check.expr(&x, s.Cond, nil, -1)
|
||||
check.expr(&x, s.Cond, -1)
|
||||
if x.mode != invalid && !isBoolean(x.typ) {
|
||||
check.errorf(s.Cond.Pos(), "non-boolean condition in if statement")
|
||||
}
|
||||
@ -545,7 +545,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
check.callIdent(ident, Universe.Lookup(nil, "true"))
|
||||
tag = ident
|
||||
}
|
||||
check.expr(&x, tag, nil, -1)
|
||||
check.expr(&x, tag, -1)
|
||||
|
||||
check.multipleDefaults(s.Body.List)
|
||||
// TODO(gri) check also correct use of fallthrough
|
||||
@ -559,7 +559,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
for _, expr := range clause.List {
|
||||
x := x // copy of x (don't modify original)
|
||||
var y operand
|
||||
check.expr(&y, expr, nil, -1)
|
||||
check.expr(&y, expr, -1)
|
||||
if y.mode == invalid {
|
||||
continue // error reported before
|
||||
}
|
||||
@ -645,7 +645,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
return
|
||||
}
|
||||
var x operand
|
||||
check.expr(&x, expr.X, nil, -1)
|
||||
check.expr(&x, expr.X, -1)
|
||||
if x.mode == invalid {
|
||||
return
|
||||
}
|
||||
@ -670,7 +670,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
// Check each type in this type switch case.
|
||||
var typ Type
|
||||
for _, expr := range clause.List {
|
||||
typ = check.typOrNil(expr, false)
|
||||
typ = check.typOrNil(expr)
|
||||
if typ != nil && typ != Typ[Invalid] {
|
||||
if method, wrongType := MissingMethod(typ, T); method != nil {
|
||||
var msg string
|
||||
@ -717,7 +717,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
check.optionalStmt(s.Init)
|
||||
if s.Cond != nil {
|
||||
var x operand
|
||||
check.expr(&x, s.Cond, nil, -1)
|
||||
check.expr(&x, s.Cond, -1)
|
||||
if x.mode != invalid && !isBoolean(x.typ) {
|
||||
check.errorf(s.Cond.Pos(), "non-boolean condition in for statement")
|
||||
}
|
||||
@ -731,7 +731,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
// check expression to iterate over
|
||||
decl := s.Tok == token.DEFINE
|
||||
var x operand
|
||||
check.expr(&x, s.X, nil, -1)
|
||||
check.expr(&x, s.X, -1)
|
||||
if x.mode == invalid {
|
||||
// if we don't have a declaration, we can still check the loop's body
|
||||
if !decl {
|
||||
|
7
go/types/testdata/const0.src
vendored
7
go/types/testdata/const0.src
vendored
@ -224,10 +224,15 @@ const (
|
||||
const (
|
||||
_b0 = iota
|
||||
_b1 = assert(iota + iota2 == 5)
|
||||
_b2 = len([iota]int{}) // iota may appear in a type!
|
||||
_b3 = assert(_b2 == 2)
|
||||
_b4 = len(A /* ERROR "not a valid composite literal" */ {})
|
||||
)
|
||||
|
||||
type A [iota /* ERROR "cannot use iota" */ ]int
|
||||
|
||||
// special cases
|
||||
const (
|
||||
_n0 = nil /* ERROR "invalid constant type" */
|
||||
_n1 = [ /* ERROR "not constant" */ ]int{}
|
||||
)
|
||||
)
|
||||
|
3
go/types/testdata/cycles.src
vendored
3
go/types/testdata/cycles.src
vendored
@ -59,9 +59,8 @@ type (
|
||||
I4 interface{ f(I4) }
|
||||
|
||||
// testcase for issue 5090
|
||||
// TODO(gri) fix this
|
||||
I5 interface{ f(I6) }
|
||||
I6 interface{ I5 /* ERROR "unimplemented" */ }
|
||||
I6 interface{ I5 }
|
||||
|
||||
// maps
|
||||
M0 map[M0]M0
|
||||
|
4
go/types/testdata/decls0.src
vendored
4
go/types/testdata/decls0.src
vendored
@ -188,9 +188,9 @@ type (
|
||||
// cycles in function/method declarations
|
||||
// (test cases for issue 5217 and variants)
|
||||
func f1(x f1 /* ERROR "not a type" */ ) {}
|
||||
func f2(x *f2 /* ERROR "cannot indirect" */ ) {}
|
||||
func f2(x *f2 /* ERROR "not a type" */ ) {}
|
||||
func f3() (x f3 /* ERROR "not a type" */ ) { return }
|
||||
func f4() (x *f4 /* ERROR "cannot indirect" */ ) { return }
|
||||
func f4() (x *f4 /* ERROR "not a type" */ ) { return }
|
||||
|
||||
func (S0) m1(x S0 /* ERROR "field or method" */ .m1) {}
|
||||
func (S0) m2(x *S0 /* ERROR "field or method" */ .m2) {}
|
||||
|
@ -326,7 +326,8 @@ func (c *Chan) Elem() Type { return c.elt }
|
||||
// A Named represents a named type.
|
||||
type Named struct {
|
||||
obj *TypeName // corresponding declared object
|
||||
underlying Type // nil if not fully declared yet; never a *Named
|
||||
underlying Type // possibly a *Named if !complete; never a *Named if complete
|
||||
complete bool // if set, the underlying type has been determined
|
||||
methods []*Func // methods declared for this type (not the method set of this type)
|
||||
}
|
||||
|
||||
@ -337,7 +338,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
|
||||
if _, ok := underlying.(*Named); ok {
|
||||
panic("types.NewNamed: underlying type must not be *Named")
|
||||
}
|
||||
typ := &Named{obj, underlying, methods}
|
||||
typ := &Named{obj, underlying, true, methods}
|
||||
if obj.typ == nil {
|
||||
obj.typ = typ
|
||||
}
|
||||
|
454
go/types/typexpr.go
Normal file
454
go/types/typexpr.go
Normal file
@ -0,0 +1,454 @@
|
||||
// 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.
|
||||
|
||||
// This file implements typechecking of identifiers and type expressions.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strconv"
|
||||
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
)
|
||||
|
||||
// ident typechecks identifier e and initializes x with the value or type of e.
|
||||
// If an error occurred, x.mode is set to invalid.
|
||||
// iota >= 0 indicates that the expression is part of a constant declaration.
|
||||
// For the meaning of def and cycleOk, see check.typ, below.
|
||||
//
|
||||
func (check *checker) ident(x *operand, e *ast.Ident, iota int, def *Named, cycleOk bool) {
|
||||
x.mode = invalid
|
||||
x.expr = e
|
||||
|
||||
obj := check.topScope.LookupParent(e.Name)
|
||||
check.callIdent(e, obj)
|
||||
if obj == nil {
|
||||
if e.Name == "_" {
|
||||
check.errorf(e.Pos(), "cannot use _ as value or type")
|
||||
} else {
|
||||
check.errorf(e.Pos(), "undeclared name: %s", e.Name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
typ := obj.Type()
|
||||
if typ == nil {
|
||||
// object not yet declared
|
||||
if check.objMap == nil {
|
||||
check.dump("%s: %s should have been declared (we are inside a function)", e.Pos(), e)
|
||||
unreachable()
|
||||
}
|
||||
check.declareObject(obj, def, cycleOk)
|
||||
typ = obj.Type()
|
||||
}
|
||||
assert(typ != nil)
|
||||
|
||||
switch obj := obj.(type) {
|
||||
case *Package:
|
||||
check.errorf(e.Pos(), "use of package %s not in selector", obj.name)
|
||||
return
|
||||
|
||||
case *Const:
|
||||
if typ == Typ[Invalid] {
|
||||
return
|
||||
}
|
||||
if obj == universeIota {
|
||||
if iota < 0 {
|
||||
check.invalidAST(e.Pos(), "cannot use iota outside constant declaration")
|
||||
return
|
||||
}
|
||||
x.val = exact.MakeInt64(int64(iota))
|
||||
} else {
|
||||
x.val = obj.val // may be nil if we don't know the constant value
|
||||
}
|
||||
x.mode = constant
|
||||
|
||||
case *TypeName:
|
||||
x.mode = typexpr
|
||||
named, _ := typ.(*Named)
|
||||
if !cycleOk && named != nil && !named.complete {
|
||||
check.errorf(obj.pos, "illegal cycle in declaration of %s", obj.name)
|
||||
// maintain x.mode == typexpr despite error
|
||||
typ = Typ[Invalid]
|
||||
}
|
||||
if def != nil {
|
||||
def.underlying = typ
|
||||
}
|
||||
|
||||
case *Var:
|
||||
x.mode = variable
|
||||
|
||||
case *Func:
|
||||
x.mode = value
|
||||
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
|
||||
x.typ = typ
|
||||
}
|
||||
|
||||
// typ typechecks the type expression e and initializes x with the type of e.
|
||||
// If an error occurred, x.mode is set to invalid.
|
||||
// iota >= 0 indicates that the expression is part of a constant declaration.
|
||||
// If def != nil, e is the type specification for the named type def, declared
|
||||
// in a type declaration, and def.underlying will be set to the type of e before
|
||||
// any components of e are typechecked.
|
||||
// If cycleOk is set, e (or elements of e) may refer to a named type that is not
|
||||
// yet completely set up.
|
||||
//
|
||||
func (check *checker) typ(e ast.Expr, iota int, def *Named, cycleOk bool) (res Type) {
|
||||
if trace {
|
||||
check.trace(e.Pos(), "%s", e)
|
||||
defer check.untrace("=> %s", res)
|
||||
}
|
||||
|
||||
// notify clients of type
|
||||
if f := check.ctxt.Expr; f != nil {
|
||||
defer func() {
|
||||
assert(e != nil && res != nil && !isUntyped(res))
|
||||
f(e, res, nil)
|
||||
}()
|
||||
}
|
||||
|
||||
switch e := e.(type) {
|
||||
case *ast.Ident:
|
||||
var x operand
|
||||
check.ident(&x, e, iota, def, cycleOk)
|
||||
|
||||
switch x.mode {
|
||||
case typexpr:
|
||||
return x.typ
|
||||
case invalid:
|
||||
// ignore - error reported before
|
||||
case novalue:
|
||||
check.errorf(x.pos(), "%s used as type", &x)
|
||||
default:
|
||||
check.errorf(x.pos(), "%s is not a type", &x)
|
||||
}
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
var x operand
|
||||
check.selector(&x, e, iota)
|
||||
|
||||
switch x.mode {
|
||||
case typexpr:
|
||||
return x.typ
|
||||
case invalid:
|
||||
// ignore - error reported before
|
||||
case novalue:
|
||||
check.errorf(x.pos(), "%s used as type", &x)
|
||||
default:
|
||||
check.errorf(x.pos(), "%s is not a type", &x)
|
||||
}
|
||||
|
||||
case *ast.ParenExpr:
|
||||
return check.typ(e.X, iota, def, cycleOk)
|
||||
|
||||
case *ast.ArrayType:
|
||||
if e.Len != nil {
|
||||
var x operand
|
||||
check.expr(&x, e.Len, iota)
|
||||
if x.mode != constant {
|
||||
if x.mode != invalid {
|
||||
check.errorf(x.pos(), "array length %s must be constant", &x)
|
||||
}
|
||||
break
|
||||
}
|
||||
if !x.isInteger() {
|
||||
check.errorf(x.pos(), "array length %s must be integer", &x)
|
||||
break
|
||||
}
|
||||
n, ok := exact.Int64Val(x.val)
|
||||
if !ok || n < 0 {
|
||||
check.errorf(x.pos(), "invalid array length %s", &x)
|
||||
break
|
||||
}
|
||||
|
||||
typ := new(Array)
|
||||
if def != nil {
|
||||
def.underlying = typ
|
||||
}
|
||||
|
||||
typ.len = n
|
||||
typ.elt = check.typ(e.Elt, iota, nil, cycleOk)
|
||||
return typ
|
||||
|
||||
} else {
|
||||
typ := new(Slice)
|
||||
if def != nil {
|
||||
def.underlying = typ
|
||||
}
|
||||
|
||||
typ.elt = check.typ(e.Elt, iota, nil, true)
|
||||
return typ
|
||||
}
|
||||
|
||||
case *ast.StructType:
|
||||
typ := new(Struct)
|
||||
if def != nil {
|
||||
def.underlying = typ
|
||||
}
|
||||
|
||||
typ.fields, typ.tags = check.collectFields(e.Fields, cycleOk)
|
||||
return typ
|
||||
|
||||
case *ast.StarExpr:
|
||||
typ := new(Pointer)
|
||||
if def != nil {
|
||||
def.underlying = typ
|
||||
}
|
||||
|
||||
typ.base = check.typ(e.X, iota, nil, true)
|
||||
return typ
|
||||
|
||||
case *ast.FuncType:
|
||||
typ := new(Signature)
|
||||
if def != nil {
|
||||
def.underlying = typ
|
||||
}
|
||||
|
||||
scope := NewScope(check.topScope)
|
||||
if retainASTLinks {
|
||||
scope.node = e
|
||||
}
|
||||
typ.scope = scope
|
||||
params, isVariadic := check.collectParams(scope, e.Params, true)
|
||||
results, _ := check.collectParams(scope, e.Results, false)
|
||||
typ.params = NewTuple(params...)
|
||||
typ.results = NewTuple(results...)
|
||||
typ.isVariadic = isVariadic
|
||||
return typ
|
||||
|
||||
case *ast.InterfaceType:
|
||||
typ := new(Interface)
|
||||
if def != nil {
|
||||
def.underlying = typ
|
||||
}
|
||||
|
||||
typ.methods = check.collectMethods(e.Methods, cycleOk)
|
||||
return typ
|
||||
|
||||
case *ast.MapType:
|
||||
typ := new(Map)
|
||||
if def != nil {
|
||||
def.underlying = typ
|
||||
}
|
||||
|
||||
typ.key = check.typ(e.Key, iota, nil, true)
|
||||
typ.elt = check.typ(e.Value, iota, nil, true)
|
||||
return typ
|
||||
|
||||
case *ast.ChanType:
|
||||
typ := new(Chan)
|
||||
if def != nil {
|
||||
def.underlying = typ
|
||||
}
|
||||
|
||||
typ.dir = e.Dir
|
||||
typ.elt = check.typ(e.Value, iota, nil, true)
|
||||
return typ
|
||||
|
||||
default:
|
||||
check.errorf(e.Pos(), "%s is not a type", e)
|
||||
}
|
||||
|
||||
return Typ[Invalid]
|
||||
}
|
||||
|
||||
// typeOrNil typechecks the type expression (or nil value) e
|
||||
// and returns the typ of e, or nil.
|
||||
// If e is neither a type nor nil, typOrNil returns Typ[Invalid].
|
||||
//
|
||||
func (check *checker) typOrNil(e ast.Expr) Type {
|
||||
var x operand
|
||||
check.rawExpr(&x, e, nil, -1)
|
||||
switch x.mode {
|
||||
case invalid:
|
||||
// ignore - error reported before
|
||||
case novalue:
|
||||
check.errorf(x.pos(), "%s used as type", &x)
|
||||
case typexpr:
|
||||
return x.typ
|
||||
case constant:
|
||||
if x.isNil() {
|
||||
return nil
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
check.errorf(x.pos(), "%s is not a type", &x)
|
||||
}
|
||||
return Typ[Invalid]
|
||||
}
|
||||
|
||||
func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, isVariadic bool) {
|
||||
if list == nil {
|
||||
return
|
||||
}
|
||||
var last *Var
|
||||
for i, field := range list.List {
|
||||
ftype := field.Type
|
||||
if t, _ := ftype.(*ast.Ellipsis); t != nil {
|
||||
ftype = t.Elt
|
||||
if variadicOk && i == len(list.List)-1 {
|
||||
isVariadic = true
|
||||
} else {
|
||||
check.invalidAST(field.Pos(), "... not permitted")
|
||||
// ok to continue
|
||||
}
|
||||
}
|
||||
// the parser ensures that f.Tag is nil and we don't
|
||||
// care if a constructed AST contains a non-nil tag
|
||||
typ := check.typ(ftype, -1, nil, true)
|
||||
if len(field.Names) > 0 {
|
||||
// named parameter
|
||||
for _, name := range field.Names {
|
||||
par := NewVar(name.Pos(), check.pkg, name.Name, typ)
|
||||
check.declare(scope, name, par)
|
||||
|
||||
last = par
|
||||
copy := *par
|
||||
params = append(params, ©)
|
||||
}
|
||||
} else {
|
||||
// anonymous parameter
|
||||
par := NewVar(ftype.Pos(), check.pkg, "", typ)
|
||||
check.callImplicitObj(field, par)
|
||||
|
||||
last = nil // not accessible inside function
|
||||
params = append(params, par)
|
||||
}
|
||||
}
|
||||
// For a variadic function, change the last parameter's object type
|
||||
// from T to []T (this is the type used inside the function), but
|
||||
// keep the params list unchanged (this is the externally visible type).
|
||||
if isVariadic && last != nil {
|
||||
last.typ = &Slice{elt: last.typ}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (check *checker) collectMethods(list *ast.FieldList, cycleOk bool) (methods []*Func) {
|
||||
if list == nil {
|
||||
return nil
|
||||
}
|
||||
scope := NewScope(nil)
|
||||
for _, f := range list.List {
|
||||
typ := check.typ(f.Type, -1, nil, cycleOk)
|
||||
// the parser ensures that f.Tag is nil and we don't
|
||||
// care if a constructed AST contains a non-nil tag
|
||||
if len(f.Names) > 0 {
|
||||
// methods (the parser ensures that there's only one
|
||||
// and we don't care if a constructed AST has more)
|
||||
sig, ok := typ.(*Signature)
|
||||
if !ok {
|
||||
check.invalidAST(f.Type.Pos(), "%s is not a method signature", typ)
|
||||
continue
|
||||
}
|
||||
for _, name := range f.Names {
|
||||
m := NewFunc(name.Pos(), check.pkg, name.Name, sig)
|
||||
check.declare(scope, name, m)
|
||||
methods = append(methods, m)
|
||||
}
|
||||
} else {
|
||||
// embedded interface
|
||||
switch t := typ.Underlying().(type) {
|
||||
case nil:
|
||||
// The underlying type is in the process of being defined
|
||||
// but we need it in order to complete this type. For now
|
||||
// complain with an "unimplemented" error. This requires
|
||||
// a bit more work.
|
||||
// TODO(gri) finish this.
|
||||
check.errorf(f.Type.Pos(), "reference to incomplete type %s - unimplemented", f.Type)
|
||||
case *Interface:
|
||||
for _, m := range t.methods {
|
||||
check.declare(scope, nil, m)
|
||||
methods = append(methods, m)
|
||||
}
|
||||
default:
|
||||
if t != Typ[Invalid] {
|
||||
check.errorf(f.Type.Pos(), "%s is not an interface type", typ)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (check *checker) tag(t *ast.BasicLit) string {
|
||||
if t != nil {
|
||||
if t.Kind == token.STRING {
|
||||
if val, err := strconv.Unquote(t.Value); err == nil {
|
||||
return val
|
||||
}
|
||||
}
|
||||
check.invalidAST(t.Pos(), "incorrect tag syntax: %q", t.Value)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*Field, tags []string) {
|
||||
if list == nil {
|
||||
return
|
||||
}
|
||||
|
||||
scope := NewScope(nil)
|
||||
|
||||
var typ Type // current field typ
|
||||
var tag string // current field tag
|
||||
add := func(field *ast.Field, ident *ast.Ident, name string, anonymous bool, pos token.Pos) {
|
||||
if tag != "" && tags == nil {
|
||||
tags = make([]string, len(fields))
|
||||
}
|
||||
if tags != nil {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
|
||||
fld := NewField(pos, check.pkg, name, typ, anonymous)
|
||||
check.declare(scope, ident, fld)
|
||||
fields = append(fields, fld)
|
||||
}
|
||||
|
||||
for _, f := range list.List {
|
||||
typ = check.typ(f.Type, -1, nil, cycleOk)
|
||||
tag = check.tag(f.Tag)
|
||||
if len(f.Names) > 0 {
|
||||
// named fields
|
||||
for _, name := range f.Names {
|
||||
add(f, name, name.Name, false, name.Pos())
|
||||
}
|
||||
} else {
|
||||
// anonymous field
|
||||
pos := f.Type.Pos()
|
||||
t, isPtr := deref(typ)
|
||||
switch t := t.(type) {
|
||||
case *Basic:
|
||||
add(f, nil, t.name, true, pos)
|
||||
case *Named:
|
||||
// spec: "An embedded type must be specified as a type name
|
||||
// T or as a pointer to a non-interface type name *T, and T
|
||||
// itself may not be a pointer type."
|
||||
switch t.Underlying().(type) {
|
||||
case *Pointer:
|
||||
check.errorf(pos, "anonymous field type cannot be a pointer")
|
||||
continue // ignore this field
|
||||
case *Interface:
|
||||
if isPtr {
|
||||
check.errorf(pos, "anonymous field type cannot be a pointer to an interface")
|
||||
continue // ignore this field
|
||||
}
|
||||
}
|
||||
add(f, nil, t.obj.name, true, pos)
|
||||
default:
|
||||
if typ != Typ[Invalid] {
|
||||
check.invalidAST(pos, "anonymous field type %s must be named", typ)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
@ -103,7 +103,7 @@ func init() {
|
||||
// Error has a nil package in its qualified name since it is in no package
|
||||
sig := &Signature{results: NewTuple(NewVar(token.NoPos, nil, "", Typ[String]))}
|
||||
methods := []*Func{NewFunc(token.NoPos, nil, "Error", sig)}
|
||||
def(NewTypeName(token.NoPos, nil, "error", &Named{underlying: NewInterface(methods)}))
|
||||
def(NewTypeName(token.NoPos, nil, "error", &Named{underlying: NewInterface(methods), complete: true}))
|
||||
}
|
||||
|
||||
for _, c := range predeclaredConstants {
|
||||
|
Loading…
x
Reference in New Issue
Block a user