From 22b7915ff5acd262d08734919baa5447d58287a1 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 25 Jun 2013 15:40:28 -0700 Subject: [PATCH] go.tools/go/types: factored out code for calls and selectors No other changes. R=adonovan CC=golang-dev https://golang.org/cl/10573043 --- go/types/call.go | 325 +++++++++++++++++++++++++++++++++++++++++++++++ go/types/expr.go | 286 +---------------------------------------- go/types/stmt.go | 14 +- 3 files changed, 333 insertions(+), 292 deletions(-) create mode 100644 go/types/call.go diff --git a/go/types/call.go b/go/types/call.go new file mode 100644 index 0000000000..f226092f58 --- /dev/null +++ b/go/types/call.go @@ -0,0 +1,325 @@ +// 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 call and selector expressions. + +package types + +import ( + "go/ast" + "go/token" +) + +func (check *checker) call(x *operand, e *ast.CallExpr, iota int) { + check.exprOrType(x, e.Fun, iota, false) + 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) + } + goto Error + + } else if x.mode == typexpr { + check.conversion(x, e, x.typ, iota) + + } else if sig, ok := x.typ.Underlying().(*Signature); ok { + // check parameters + + // If we have a trailing ... at the end of the parameter + // list, the last argument must match the parameter type + // []T of a variadic function parameter x ...T. + passSlice := false + if e.Ellipsis.IsValid() { + if sig.isVariadic { + passSlice = true + } else { + check.errorf(e.Ellipsis, "cannot use ... in call to %s", e.Fun) + // ok to continue + } + } + + // If we have a single argument that is a function call + // we need to handle it separately. Determine if this + // is the case without checking the argument. + var call *ast.CallExpr + if len(e.Args) == 1 { + call, _ = unparen(e.Args[0]).(*ast.CallExpr) + } + + n := 0 // parameter count + if call != nil { + // We have a single argument that is a function call. + check.expr(x, call, nil, -1) + if x.mode == invalid { + goto Error // TODO(gri): we can do better + } + if t, ok := x.typ.(*Tuple); ok { + // multiple result values + n = t.Len() + for i := 0; i < n; i++ { + obj := t.At(i) + x.mode = value + x.expr = nil // TODO(gri) can we do better here? (for good error messages) + x.typ = obj.typ + check.argument(sig, i, nil, x, passSlice && i+1 == n) + } + } else { + // single result value + n = 1 + check.argument(sig, 0, nil, x, passSlice) + } + + } else { + // We don't have a single argument or it is not a function call. + n = len(e.Args) + for i, arg := range e.Args { + check.argument(sig, i, arg, x, passSlice && i+1 == n) + } + } + + // determine if we have enough arguments + if sig.isVariadic { + // a variadic function accepts an "empty" + // last argument: count one extra + n++ + } + if n < sig.params.Len() { + check.errorf(e.Fun.Pos(), "too few arguments in call to %s", e.Fun) + // ok to continue + } + + // determine result + switch sig.results.Len() { + case 0: + x.mode = novalue + case 1: + x.mode = value + x.typ = sig.results.vars[0].typ + default: + x.mode = value + x.typ = sig.results + } + + } else if bin, ok := x.typ.(*Builtin); ok { + check.builtin(x, e, bin, iota) + + } else { + check.invalidOp(x.pos(), "cannot call non-function %s", x) + goto Error + } + + // everything went well + x.expr = e + return + +Error: + x.mode = invalid + x.expr = e +} + +// argument typechecks passing an argument arg (if arg != nil) or +// x (if arg == nil) to the i'th parameter of the given signature. +// If passSlice is set, the argument is followed by ... in the call. +// +func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand, passSlice bool) { + // determine parameter + var par *Var + n := sig.params.Len() + if i < n { + par = sig.params.vars[i] + } else if sig.isVariadic { + par = sig.params.vars[n-1] + } else { + var pos token.Pos + switch { + case arg != nil: + pos = arg.Pos() + case x != nil: + pos = x.pos() + default: + // TODO(gri) what position to use? + } + check.errorf(pos, "too many arguments") + return + } + + // determine argument + var z operand + z.mode = variable + z.expr = nil // TODO(gri) can we do better here? (for good error messages) + z.typ = par.typ + + if arg != nil { + check.expr(x, arg, z.typ, -1) + } + if x.mode == invalid { + return // ignore this argument + } + + // check last argument of the form x... + if passSlice { + if i+1 != n { + check.errorf(x.pos(), "can only use ... with matching parameter") + return // ignore this argument + } + // spec: "If the final argument is assignable to a slice type []T, + // it may be passed unchanged as the value for a ...T parameter if + // the argument is followed by ..." + z.typ = &Slice{elt: z.typ} // change final parameter type to []T + } + + if !check.assignment(x, z.typ) && x.mode != invalid { + check.errorf(x.pos(), "cannot pass argument %s to %s", x, &z) + } +} + +func (check *checker) selector(x *operand, e *ast.SelectorExpr, iota int) { + // these must be declared before the "goto Error" statements + var ( + obj Object + index []int + indirect bool + ) + + sel := e.Sel.Name + // If the identifier refers to a package, handle everything here + // so we don't need a "package" mode for operands: package names + // can only appear in qualified identifiers which are mapped to + // selector expressions. + if ident, ok := e.X.(*ast.Ident); ok { + if pkg, ok := check.topScope.LookupParent(ident.Name).(*Package); ok { + check.callIdent(ident, pkg) + exp := pkg.scope.Lookup(nil, sel) + if exp == nil { + check.errorf(e.Pos(), "%s not declared by package %s", sel, ident) + goto Error + } else if !ast.IsExported(exp.Name()) { + // gcimported package scopes contain non-exported + // objects such as types used in partially exported + // objects - do not accept them + check.errorf(e.Pos(), "%s not exported by package %s", sel, ident) + goto Error + } + check.callIdent(e.Sel, exp) + // Simplified version of the code for *ast.Idents: + // - imported packages use types.Scope and types.Objects + // - imported objects are always fully initialized + switch exp := exp.(type) { + case *Const: + assert(exp.Val != nil) + x.mode = constant + x.typ = exp.typ + x.val = exp.val + case *TypeName: + x.mode = typexpr + x.typ = exp.typ + case *Var: + x.mode = variable + x.typ = exp.typ + case *Func: + x.mode = value + x.typ = exp.typ + default: + unreachable() + } + x.expr = e + return + } + } + + check.exprOrType(x, e.X, iota, false) + if x.mode == invalid { + goto Error + } + + obj, index, indirect = LookupFieldOrMethod(x.typ, check.pkg, sel) + if obj == nil { + if index != nil { + // TODO(gri) should provide actual type where the conflict happens + check.invalidOp(e.Pos(), "ambiguous selector %s", sel) + } else { + check.invalidOp(e.Pos(), "%s has no field or method %s", x, sel) + } + goto Error + } + + check.callIdent(e.Sel, obj) + + if x.mode == typexpr { + // method expression + m, _ := obj.(*Func) + if m == nil { + check.invalidOp(e.Pos(), "%s has no method %s", x, sel) + goto Error + } + + // verify that m is in the method set of x.typ + // (the receiver is nil if f is an interface method) + if recv := m.typ.(*Signature).recv; recv != nil { + if _, isPtr := deref(recv.typ); isPtr && !indirect { + check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x.typ) + goto Error + } + } + + // the receiver type becomes the type of the first function + // argument of the method expression's function type + var params []*Var + sig := m.typ.(*Signature) + if sig.params != nil { + params = sig.params.vars + } + x.mode = value + x.typ = &Signature{ + params: NewTuple(append([]*Var{NewVar(token.NoPos, check.pkg, "", x.typ)}, params...)...), + results: sig.results, + isVariadic: sig.isVariadic, + } + + } else { + // regular selector + switch obj := obj.(type) { + case *Field: + x.mode = variable + x.typ = obj.typ + case *Func: + // TODO(gri) Temporary check to verify corresponding lookup via method sets. + // Remove eventually. + if m := NewMethodSet(x.typ).Lookup(check.pkg, sel); m != obj { + check.dump("%s: %v", e.Pos(), obj.name) + panic("method sets and lookup don't agree") + } + + // TODO(gri) This code appears elsewhere, too. Factor! + // verify that obj is in the method set of x.typ (or &(x.typ) if x is addressable) + // (the receiver is nil if obj is an interface method) + // + // spec: "A method call x.m() is valid if the method set of (the type of) x + // contains m and the argument list can be assigned to the parameter + // list of m. If x is addressable and &x's method set contains m, x.m() + // is shorthand for (&x).m()". + if recv := obj.typ.(*Signature).recv; recv != nil { + if _, isPtr := deref(recv.typ); isPtr && !indirect && x.mode != variable { + check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x) + goto Error + } + } + + x.mode = value + x.typ = obj.typ + default: + unreachable() + } + } + + // everything went well + x.expr = e + return + +Error: + x.mode = invalid + x.expr = e +} diff --git a/go/types/expr.go b/go/types/expr.go index b8b6b667a2..c0d8028255 100644 --- a/go/types/expr.go +++ b/go/types/expr.go @@ -934,62 +934,6 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64, iota return max } -// argument typechecks passing an argument arg (if arg != nil) or -// x (if arg == nil) to the i'th parameter of the given signature. -// If passSlice is set, the argument is followed by ... in the call. -// -func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand, passSlice bool) { - // determine parameter - var par *Var - n := sig.params.Len() - if i < n { - par = sig.params.vars[i] - } else if sig.isVariadic { - par = sig.params.vars[n-1] - } else { - var pos token.Pos - switch { - case arg != nil: - pos = arg.Pos() - case x != nil: - pos = x.pos() - default: - // TODO(gri) what position to use? - } - check.errorf(pos, "too many arguments") - return - } - - // determine argument - var z operand - z.mode = variable - z.expr = nil // TODO(gri) can we do better here? (for good error messages) - z.typ = par.typ - - if arg != nil { - check.expr(x, arg, z.typ, -1) - } - if x.mode == invalid { - return // ignore this argument - } - - // check last argument of the form x... - if passSlice { - if i+1 != n { - check.errorf(x.pos(), "can only use ... with matching parameter") - return // ignore this argument - } - // spec: "If the final argument is assignable to a slice type []T, - // it may be passed unchanged as the value for a ...T parameter if - // the argument is followed by ..." - z.typ = &Slice{elt: z.typ} // change final parameter type to []T - } - - if !check.assignment(x, z.typ) && x.mode != invalid { - check.errorf(x.pos(), "cannot pass argument %s to %s", x, &z) - } -} - func (check *checker) callExpr(x *operand) { // convert x into a user-friendly set of values var typ Type @@ -1279,136 +1223,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle check.rawExpr(x, e.X, nil, iota, cycleOk) case *ast.SelectorExpr: - sel := e.Sel.Name - // If the identifier refers to a package, handle everything here - // so we don't need a "package" mode for operands: package names - // can only appear in qualified identifiers which are mapped to - // selector expressions. - if ident, ok := e.X.(*ast.Ident); ok { - if pkg, ok := check.topScope.LookupParent(ident.Name).(*Package); ok { - check.callIdent(ident, pkg) - exp := pkg.scope.Lookup(nil, sel) - if exp == nil { - check.errorf(e.Pos(), "%s not declared by package %s", sel, ident) - goto Error - } else if !ast.IsExported(exp.Name()) { - // gcimported package scopes contain non-exported - // objects such as types used in partially exported - // objects - do not accept them - check.errorf(e.Pos(), "%s not exported by package %s", sel, ident) - goto Error - } - check.callIdent(e.Sel, exp) - // Simplified version of the code for *ast.Idents: - // - imported packages use types.Scope and types.Objects - // - imported objects are always fully initialized - switch exp := exp.(type) { - case *Const: - assert(exp.Val != nil) - x.mode = constant - x.typ = exp.typ - x.val = exp.val - case *TypeName: - x.mode = typexpr - x.typ = exp.typ - case *Var: - x.mode = variable - x.typ = exp.typ - case *Func: - x.mode = value - x.typ = exp.typ - default: - unreachable() - } - x.expr = e - return - } - } - - check.exprOrType(x, e.X, iota, false) - if x.mode == invalid { - goto Error - } - - obj, index, indirect := LookupFieldOrMethod(x.typ, check.pkg, sel) - if obj == nil { - if index != nil { - // TODO(gri) should provide actual type where the conflict happens - check.invalidOp(e.Pos(), "ambiguous selector %s", sel) - } else { - check.invalidOp(e.Pos(), "%s has no field or method %s", x, sel) - } - goto Error - } - - check.callIdent(e.Sel, obj) - - if x.mode == typexpr { - // method expression - m, _ := obj.(*Func) - if m == nil { - check.invalidOp(e.Pos(), "%s has no method %s", x, sel) - goto Error - } - - // verify that m is in the method set of x.typ - // (the receiver is nil if f is an interface method) - if recv := m.typ.(*Signature).recv; recv != nil { - if _, isPtr := deref(recv.typ); isPtr && !indirect { - check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x.typ) - goto Error - } - } - - // the receiver type becomes the type of the first function - // argument of the method expression's function type - var params []*Var - sig := m.typ.(*Signature) - if sig.params != nil { - params = sig.params.vars - } - x.mode = value - x.typ = &Signature{ - params: NewTuple(append([]*Var{NewVar(token.NoPos, check.pkg, "", x.typ)}, params...)...), - results: sig.results, - isVariadic: sig.isVariadic, - } - - } else { - // regular selector - switch obj := obj.(type) { - case *Field: - x.mode = variable - x.typ = obj.typ - case *Func: - // TODO(gri) Temporary check to verify corresponding lookup via method sets. - // Remove eventually. - if m := NewMethodSet(x.typ).Lookup(check.pkg, sel); m != obj { - check.dump("%s: %v", e.Pos(), obj.name) - panic("method sets and lookup don't agree") - } - - // TODO(gri) This code appears elsewhere, too. Factor! - // verify that obj is in the method set of x.typ (or &(x.typ) if x is addressable) - // (the receiver is nil if obj is an interface method) - // - // spec: "A method call x.m() is valid if the method set of (the type of) x - // contains m and the argument list can be assigned to the parameter - // list of m. If x is addressable and &x's method set contains m, x.m() - // is shorthand for (&x).m()". - if recv := obj.typ.(*Signature).recv; recv != nil { - if _, isPtr := deref(recv.typ); isPtr && !indirect && x.mode != variable { - check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x) - goto Error - } - } - - x.mode = value - x.typ = obj.typ - default: - unreachable() - } - } + check.selector(x, e, iota) case *ast.IndexExpr: check.expr(x, e.X, nil, iota) @@ -1596,104 +1411,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle x.typ = typ case *ast.CallExpr: - check.exprOrType(x, e.Fun, iota, false) - 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) - } - goto Error - - } else if x.mode == typexpr { - check.conversion(x, e, x.typ, iota) - - } else if sig, ok := x.typ.Underlying().(*Signature); ok { - // check parameters - - // If we have a trailing ... at the end of the parameter - // list, the last argument must match the parameter type - // []T of a variadic function parameter x ...T. - passSlice := false - if e.Ellipsis.IsValid() { - if sig.isVariadic { - passSlice = true - } else { - check.errorf(e.Ellipsis, "cannot use ... in call to %s", e.Fun) - // ok to continue - } - } - - // If we have a single argument that is a function call - // we need to handle it separately. Determine if this - // is the case without checking the argument. - var call *ast.CallExpr - if len(e.Args) == 1 { - call, _ = unparen(e.Args[0]).(*ast.CallExpr) - } - - n := 0 // parameter count - if call != nil { - // We have a single argument that is a function call. - check.expr(x, call, nil, -1) - if x.mode == invalid { - goto Error // TODO(gri): we can do better - } - if t, ok := x.typ.(*Tuple); ok { - // multiple result values - n = t.Len() - for i := 0; i < n; i++ { - obj := t.At(i) - x.mode = value - x.expr = nil // TODO(gri) can we do better here? (for good error messages) - x.typ = obj.typ - check.argument(sig, i, nil, x, passSlice && i+1 == n) - } - } else { - // single result value - n = 1 - check.argument(sig, 0, nil, x, passSlice) - } - - } else { - // We don't have a single argument or it is not a function call. - n = len(e.Args) - for i, arg := range e.Args { - check.argument(sig, i, arg, x, passSlice && i+1 == n) - } - } - - // determine if we have enough arguments - if sig.isVariadic { - // a variadic function accepts an "empty" - // last argument: count one extra - n++ - } - if n < sig.params.Len() { - check.errorf(e.Fun.Pos(), "too few arguments in call to %s", e.Fun) - // ok to continue - } - - // determine result - switch sig.results.Len() { - case 0: - x.mode = novalue - case 1: - x.mode = value - x.typ = sig.results.vars[0].typ - default: - x.mode = value - x.typ = sig.results - } - - } else if bin, ok := x.typ.(*Builtin); ok { - check.builtin(x, e, bin, iota) - - } else { - check.invalidOp(x.pos(), "cannot call non-function %s", x) - goto Error - } + check.call(x, e, iota) case *ast.StarExpr: check.exprOrType(x, e.X, iota, true) diff --git a/go/types/stmt.go b/go/types/stmt.go index c0327a4e34..95625a87ba 100644 --- a/go/types/stmt.go +++ b/go/types/stmt.go @@ -267,12 +267,6 @@ func (check *checker) stmtList(list []ast.Stmt) { } } -func (check *checker) call(call *ast.CallExpr) { - var x operand - check.rawExpr(&x, call, nil, -1, false) // don't check if value is used - // TODO(gri) If a builtin is called, the builtin must be valid in statement context. -} - func (check *checker) multipleDefaults(list []ast.Stmt) { var first ast.Stmt for _, s := range list { @@ -471,10 +465,14 @@ func (check *checker) stmt(s ast.Stmt) { } case *ast.GoStmt: - check.call(s.Call) + var x operand + check.call(&x, s.Call, -1) + // TODO(gri) If a builtin is called, the builtin must be valid this context. case *ast.DeferStmt: - check.call(s.Call) + var x operand + check.call(&x, s.Call, -1) + // TODO(gri) If a builtin is called, the builtin must be valid this context. case *ast.ReturnStmt: sig := check.funcsig