feat: add builtin type checking
This adds type checking for builtin functions. It also refactors builtin names into constants due to the number of times they are used.
This commit is contained in:
@@ -39,9 +39,9 @@ var constOp = map[action]func(*node){
|
|||||||
}
|
}
|
||||||
|
|
||||||
var constBltn = map[string]func(*node){
|
var constBltn = map[string]func(*node){
|
||||||
"complex": complexConst,
|
bltnComplex: complexConst,
|
||||||
"imag": imagConst,
|
bltnImag: imagConst,
|
||||||
"real": realConst,
|
bltnReal: realConst,
|
||||||
}
|
}
|
||||||
|
|
||||||
var identifier = regexp.MustCompile(`([\pL_][\pL_\d]*)$`)
|
var identifier = regexp.MustCompile(`([\pL_][\pL_\d]*)$`)
|
||||||
@@ -799,6 +799,11 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
|||||||
wireChild(n)
|
wireChild(n)
|
||||||
switch {
|
switch {
|
||||||
case interp.isBuiltinCall(n):
|
case interp.isBuiltinCall(n):
|
||||||
|
err = check.builtin(n.child[0].ident, n, n.child[1:], n.action == aCallSlice)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
n.gen = n.child[0].sym.builtin
|
n.gen = n.child[0].sym.builtin
|
||||||
n.child[0].typ = &itype{cat: builtinT}
|
n.child[0].typ = &itype{cat: builtinT}
|
||||||
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
||||||
|
|||||||
@@ -252,6 +252,24 @@ func New(options Options) *Interpreter {
|
|||||||
return &i
|
return &i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
bltnAppend = "append"
|
||||||
|
bltnCap = "cap"
|
||||||
|
bltnClose = "close"
|
||||||
|
bltnComplex = "complex"
|
||||||
|
bltnImag = "imag"
|
||||||
|
bltnCopy = "copy"
|
||||||
|
bltnDelete = "delete"
|
||||||
|
bltnLen = "len"
|
||||||
|
bltnMake = "make"
|
||||||
|
bltnNew = "new"
|
||||||
|
bltnPanic = "panic"
|
||||||
|
bltnPrint = "print"
|
||||||
|
bltnPrintln = "println"
|
||||||
|
bltnReal = "real"
|
||||||
|
bltnRecover = "recover"
|
||||||
|
)
|
||||||
|
|
||||||
func initUniverse() *scope {
|
func initUniverse() *scope {
|
||||||
sc := &scope{global: true, sym: map[string]*symbol{
|
sc := &scope{global: true, sym: map[string]*symbol{
|
||||||
// predefined Go types
|
// predefined Go types
|
||||||
@@ -286,21 +304,21 @@ func initUniverse() *scope {
|
|||||||
"nil": {typ: &itype{cat: nilT, untyped: true}},
|
"nil": {typ: &itype{cat: nilT, untyped: true}},
|
||||||
|
|
||||||
// predefined Go builtins
|
// predefined Go builtins
|
||||||
"append": {kind: bltnSym, builtin: _append},
|
bltnAppend: {kind: bltnSym, builtin: _append},
|
||||||
"cap": {kind: bltnSym, builtin: _cap},
|
bltnCap: {kind: bltnSym, builtin: _cap},
|
||||||
"close": {kind: bltnSym, builtin: _close},
|
bltnClose: {kind: bltnSym, builtin: _close},
|
||||||
"complex": {kind: bltnSym, builtin: _complex},
|
bltnComplex: {kind: bltnSym, builtin: _complex},
|
||||||
"imag": {kind: bltnSym, builtin: _imag},
|
bltnImag: {kind: bltnSym, builtin: _imag},
|
||||||
"copy": {kind: bltnSym, builtin: _copy},
|
bltnCopy: {kind: bltnSym, builtin: _copy},
|
||||||
"delete": {kind: bltnSym, builtin: _delete},
|
bltnDelete: {kind: bltnSym, builtin: _delete},
|
||||||
"len": {kind: bltnSym, builtin: _len},
|
bltnLen: {kind: bltnSym, builtin: _len},
|
||||||
"make": {kind: bltnSym, builtin: _make},
|
bltnMake: {kind: bltnSym, builtin: _make},
|
||||||
"new": {kind: bltnSym, builtin: _new},
|
bltnNew: {kind: bltnSym, builtin: _new},
|
||||||
"panic": {kind: bltnSym, builtin: _panic},
|
bltnPanic: {kind: bltnSym, builtin: _panic},
|
||||||
"print": {kind: bltnSym, builtin: _print},
|
bltnPrint: {kind: bltnSym, builtin: _print},
|
||||||
"println": {kind: bltnSym, builtin: _println},
|
bltnPrintln: {kind: bltnSym, builtin: _println},
|
||||||
"real": {kind: bltnSym, builtin: _real},
|
bltnReal: {kind: bltnSym, builtin: _real},
|
||||||
"recover": {kind: bltnSym, builtin: _recover},
|
bltnRecover: {kind: bltnSym, builtin: _recover},
|
||||||
}}
|
}}
|
||||||
return sc
|
return sc
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,39 @@ func TestEvalBuiltin(t *testing.T) {
|
|||||||
{src: `c := []int{1}; d := []int{2, 3}; c = append(c, d...); c`, res: "[1 2 3]"},
|
{src: `c := []int{1}; d := []int{2, 3}; c = append(c, d...); c`, res: "[1 2 3]"},
|
||||||
{src: `string(append([]byte("hello "), "world"...))`, res: "hello world"},
|
{src: `string(append([]byte("hello "), "world"...))`, res: "hello world"},
|
||||||
{src: `e := "world"; string(append([]byte("hello "), e...))`, res: "hello world"},
|
{src: `e := "world"; string(append([]byte("hello "), e...))`, res: "hello world"},
|
||||||
|
{src: `b := []int{1}; b = append(1, 2, 3); b`, err: "1:54: first argument to append must be slice; have int"},
|
||||||
|
{src: `g := len(a)`, res: "1"},
|
||||||
|
{src: `g := cap(a)`, res: "1"},
|
||||||
|
{src: `g := len("test")`, res: "4"},
|
||||||
|
{src: `g := len(map[string]string{"a": "b"})`, res: "1"},
|
||||||
|
{src: `a := len()`, err: "not enough arguments in call to len"},
|
||||||
|
{src: `a := len([]int, 0)`, err: "too many arguments for len"},
|
||||||
|
{src: `g := cap("test")`, err: "1:37: invalid argument for cap"},
|
||||||
|
{src: `g := cap(map[string]string{"a": "b"})`, err: "1:37: invalid argument for cap"},
|
||||||
|
{src: `h := make(chan int, 1); close(h); len(h)`, res: "0"},
|
||||||
|
{src: `close(a)`, err: "1:34: invalid operation: non-chan type []int"},
|
||||||
|
{src: `h := make(chan int, 1); var i <-chan int = h; close(i)`, err: "1:80: invalid operation: cannot close receive-only channel"},
|
||||||
|
{src: `j := make([]int, 2)`, res: "[0 0]"},
|
||||||
|
{src: `j := make([]int, 2, 3)`, res: "[0 0]"},
|
||||||
|
{src: `j := make(int)`, err: "1:38: cannot make int; type must be slice, map, or channel"},
|
||||||
|
{src: `j := make([]int)`, err: "1:33: not enough arguments in call to make"},
|
||||||
|
{src: `j := make([]int, 0, 1, 2)`, err: "1:33: too many arguments for make"},
|
||||||
|
{src: `j := make([]int, 2, 1)`, err: "1:33: len larger than cap in make"},
|
||||||
|
{src: `j := make([]int, "test")`, err: "1:45: cannot convert \"test\" to int"},
|
||||||
|
{src: `k := []int{3, 4}; copy(k, []int{1,2}); k`, res: "[1 2]"},
|
||||||
{src: `f := []byte("Hello"); copy(f, "world"); string(f)`, res: "world"},
|
{src: `f := []byte("Hello"); copy(f, "world"); string(f)`, res: "world"},
|
||||||
|
{src: `copy(g, g)`, err: "1:28: copy expects slice arguments"},
|
||||||
|
{src: `copy(a, "world")`, err: "1:28: arguments to copy have different element types []int and string"},
|
||||||
|
{src: `l := map[string]int{"a": 1, "b": 2}; delete(l, "a"); l`, res: "map[b:2]"},
|
||||||
|
{src: `delete(a, 1)`, err: "1:35: first argument to delete must be map; have []int"},
|
||||||
|
{src: `l := map[string]int{"a": 1, "b": 2}; delete(l, 1)`, err: "1:75: cannot use int as type string in delete"},
|
||||||
|
{src: `a := []int{1,2}; println(a...)`, err: "invalid use of ... with builtin println"},
|
||||||
|
{src: `m := complex(3, 2); real(m)`, res: "3"},
|
||||||
|
{src: `m := complex(3, 2); imag(m)`, res: "2"},
|
||||||
|
{src: `m := complex("test", 2)`, err: "1:33: invalid types string and int"},
|
||||||
|
{src: `imag("test")`, err: "1:33: cannot convert \"test\" to complex128"},
|
||||||
|
{src: `imag(a)`, err: "1:33: invalid argument type []int for imag"},
|
||||||
|
{src: `real(a)`, err: "1:33: invalid argument type []int for real"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
|||||||
// Builtin types are special and may depend from their input arguments.
|
// Builtin types are special and may depend from their input arguments.
|
||||||
t.cat = builtinT
|
t.cat = builtinT
|
||||||
switch n.child[0].ident {
|
switch n.child[0].ident {
|
||||||
case "complex":
|
case bltnComplex:
|
||||||
var nt0, nt1 *itype
|
var nt0, nt1 *itype
|
||||||
if nt0, err = nodeType(interp, sc, n.child[1]); err != nil {
|
if nt0, err = nodeType(interp, sc, n.child[1]); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -299,7 +299,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
|||||||
case isFloat64(t0) && isFloat64(t1):
|
case isFloat64(t0) && isFloat64(t1):
|
||||||
t = sc.getType("complex128")
|
t = sc.getType("complex128")
|
||||||
case nt0.untyped && isNumber(t0) && nt1.untyped && isNumber(t1):
|
case nt0.untyped && isNumber(t0) && nt1.untyped && isNumber(t1):
|
||||||
t = &itype{cat: valueT, rtype: complexType, scope: sc}
|
t = untypedComplex
|
||||||
case nt0.untyped && isFloat32(t1) || nt1.untyped && isFloat32(t0):
|
case nt0.untyped && isFloat32(t1) || nt1.untyped && isFloat32(t0):
|
||||||
t = sc.getType("complex64")
|
t = sc.getType("complex64")
|
||||||
case nt0.untyped && isFloat64(t1) || nt1.untyped && isFloat64(t0):
|
case nt0.untyped && isFloat64(t1) || nt1.untyped && isFloat64(t0):
|
||||||
@@ -311,7 +311,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
|||||||
t.untyped = true
|
t.untyped = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "real", "imag":
|
case bltnReal, bltnImag:
|
||||||
if t, err = nodeType(interp, sc, n.child[1]); err != nil {
|
if t, err = nodeType(interp, sc, n.child[1]); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -327,14 +327,14 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
|||||||
err = n.cfgErrorf("invalid complex type %s", k)
|
err = n.cfgErrorf("invalid complex type %s", k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "cap", "copy", "len":
|
case bltnCap, bltnCopy, bltnLen:
|
||||||
t = sc.getType("int")
|
t = sc.getType("int")
|
||||||
case "append", "make":
|
case bltnAppend, bltnMake:
|
||||||
t, err = nodeType(interp, sc, n.child[1])
|
t, err = nodeType(interp, sc, n.child[1])
|
||||||
case "new":
|
case bltnNew:
|
||||||
t, err = nodeType(interp, sc, n.child[1])
|
t, err = nodeType(interp, sc, n.child[1])
|
||||||
t = &itype{cat: ptrT, val: t, incomplete: t.incomplete, scope: sc}
|
t = &itype{cat: ptrT, val: t, incomplete: t.incomplete, scope: sc}
|
||||||
case "recover":
|
case bltnRecover:
|
||||||
t = sc.getType("interface{}")
|
t = sc.getType("interface{}")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -630,41 +630,288 @@ func (check typecheck) conversion(n *node, typ *itype) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type param struct {
|
||||||
|
nod *node
|
||||||
|
typ *itype
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p param) Type() *itype {
|
||||||
|
if p.typ != nil {
|
||||||
|
return p.typ
|
||||||
|
}
|
||||||
|
return p.nod.typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// unpackParams unpacks child parameters into a slice of param.
|
||||||
|
// If there is only 1 child and it is a callExpr with an n-value return,
|
||||||
|
// the return types are returned, otherwise the original child nodes are
|
||||||
|
// returned with nil typ.
|
||||||
|
func (check typecheck) unpackParams(child []*node) (params []param) {
|
||||||
|
if len(child) == 1 && isCall(child[0]) && child[0].child[0].typ.numOut() > 1 {
|
||||||
|
c0 := child[0]
|
||||||
|
ftyp := child[0].child[0].typ
|
||||||
|
for i := 0; i < ftyp.numOut(); i++ {
|
||||||
|
params = append(params, param{nod: c0, typ: ftyp.out(i)})
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range child {
|
||||||
|
params = append(params, param{nod: c})
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
var builtinFuncs = map[string]struct {
|
||||||
|
args int
|
||||||
|
variadic bool
|
||||||
|
}{
|
||||||
|
bltnAppend: {args: 1, variadic: true},
|
||||||
|
bltnCap: {args: 1, variadic: false},
|
||||||
|
bltnClose: {args: 1, variadic: false},
|
||||||
|
bltnComplex: {args: 2, variadic: false},
|
||||||
|
bltnImag: {args: 1, variadic: false},
|
||||||
|
bltnCopy: {args: 2, variadic: false},
|
||||||
|
bltnDelete: {args: 2, variadic: false},
|
||||||
|
bltnLen: {args: 1, variadic: false},
|
||||||
|
bltnMake: {args: 1, variadic: true},
|
||||||
|
bltnNew: {args: 1, variadic: false},
|
||||||
|
bltnPanic: {args: 1, variadic: false},
|
||||||
|
bltnPrint: {args: 0, variadic: true},
|
||||||
|
bltnPrintln: {args: 0, variadic: true},
|
||||||
|
bltnReal: {args: 1, variadic: false},
|
||||||
|
bltnRecover: {args: 0, variadic: false},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check typecheck) builtin(name string, n *node, child []*node, ellipsis bool) error {
|
||||||
|
fun := builtinFuncs[name]
|
||||||
|
if ellipsis && name != bltnAppend {
|
||||||
|
return n.cfgErrorf("invalid use of ... with builtin %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
var params []param
|
||||||
|
nparams := len(child)
|
||||||
|
switch name {
|
||||||
|
case bltnMake, bltnNew:
|
||||||
|
// Special param handling
|
||||||
|
default:
|
||||||
|
params = check.unpackParams(child)
|
||||||
|
nparams = len(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
if nparams < fun.args {
|
||||||
|
return n.cfgErrorf("not enough arguments in call to %s", name)
|
||||||
|
} else if !fun.variadic && nparams > fun.args {
|
||||||
|
return n.cfgErrorf("too many arguments for %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch name {
|
||||||
|
case bltnAppend:
|
||||||
|
typ := params[0].Type()
|
||||||
|
t := typ.TypeOf()
|
||||||
|
if t.Kind() != reflect.Slice {
|
||||||
|
return params[0].nod.cfgErrorf("first argument to append must be slice; have %s", typ.id())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special case append([]byte, "test"...) is allowed.
|
||||||
|
t1 := params[1].Type()
|
||||||
|
if nparams == 2 && ellipsis && t.Elem().Kind() == reflect.Uint8 && t1.TypeOf().Kind() == reflect.String {
|
||||||
|
if t1.untyped {
|
||||||
|
return check.convertUntyped(params[1].nod, &itype{cat: stringT, name: "string"})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// We cannot check a recursive type.
|
||||||
|
if isRecursiveType(typ, typ.TypeOf()) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fun := &node{
|
||||||
|
typ: &itype{
|
||||||
|
cat: funcT,
|
||||||
|
arg: []*itype{
|
||||||
|
typ,
|
||||||
|
{cat: variadicT, val: &itype{cat: valueT, rtype: t.Elem()}},
|
||||||
|
},
|
||||||
|
ret: []*itype{typ},
|
||||||
|
},
|
||||||
|
ident: "append",
|
||||||
|
}
|
||||||
|
return check.arguments(n, child, fun, ellipsis)
|
||||||
|
case bltnCap, bltnLen:
|
||||||
|
typ := arrayDeref(params[0].Type())
|
||||||
|
ok := false
|
||||||
|
switch typ.TypeOf().Kind() {
|
||||||
|
case reflect.Array, reflect.Slice, reflect.Chan:
|
||||||
|
ok = true
|
||||||
|
case reflect.String, reflect.Map:
|
||||||
|
ok = name == bltnLen
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return params[0].nod.cfgErrorf("invalid argument for %s", name)
|
||||||
|
}
|
||||||
|
case bltnClose:
|
||||||
|
p := params[0]
|
||||||
|
typ := p.Type()
|
||||||
|
t := typ.TypeOf()
|
||||||
|
if t.Kind() != reflect.Chan {
|
||||||
|
return p.nod.cfgErrorf("invalid operation: non-chan type %s", p.nod.typ.id())
|
||||||
|
}
|
||||||
|
if t.ChanDir() == reflect.RecvDir {
|
||||||
|
return p.nod.cfgErrorf("invalid operation: cannot close receive-only channel")
|
||||||
|
}
|
||||||
|
case bltnComplex:
|
||||||
|
var err error
|
||||||
|
p0, p1 := params[0], params[1]
|
||||||
|
typ0, typ1 := p0.Type(), p1.Type()
|
||||||
|
switch {
|
||||||
|
case typ0.untyped && !typ1.untyped:
|
||||||
|
err = check.convertUntyped(p0.nod, typ1)
|
||||||
|
case !typ0.untyped && typ1.untyped:
|
||||||
|
err = check.convertUntyped(p1.nod, typ0)
|
||||||
|
case typ0.untyped && typ1.untyped:
|
||||||
|
fltType := &itype{cat: float64T, name: "float64"}
|
||||||
|
err = check.convertUntyped(p0.nod, fltType)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
err = check.convertUntyped(p1.nod, fltType)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check we have the correct types after conversion.
|
||||||
|
typ0, typ1 = p0.Type(), p1.Type()
|
||||||
|
if !typ0.equals(typ1) {
|
||||||
|
return n.cfgErrorf("invalid operation: mismatched types %s and %s", typ0.id(), typ1.id())
|
||||||
|
}
|
||||||
|
if !isFloat(typ0.TypeOf()) {
|
||||||
|
return n.cfgErrorf("invalid operation: arguments have type %s, expected floating-point", typ0.id())
|
||||||
|
}
|
||||||
|
case bltnImag, bltnReal:
|
||||||
|
p := params[0]
|
||||||
|
typ := p.Type()
|
||||||
|
if typ.untyped {
|
||||||
|
if err := check.convertUntyped(p.nod, &itype{cat: complex128T, name: "complex128"}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
typ = p.Type()
|
||||||
|
if !isComplex(typ.TypeOf()) {
|
||||||
|
return p.nod.cfgErrorf("invalid argument type %s for %s", typ.id(), name)
|
||||||
|
}
|
||||||
|
case bltnCopy:
|
||||||
|
typ0, typ1 := params[0].Type(), params[1].Type()
|
||||||
|
var t0, t1 reflect.Type
|
||||||
|
if t := typ0.TypeOf(); t.Kind() == reflect.Slice {
|
||||||
|
t0 = t.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t := typ1.TypeOf(); t.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
t1 = reflect.TypeOf(byte(1))
|
||||||
|
case reflect.Slice:
|
||||||
|
t1 = t.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if t0 == nil || t1 == nil {
|
||||||
|
return n.cfgErrorf("copy expects slice arguments")
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(t0, t1) {
|
||||||
|
return n.cfgErrorf("arguments to copy have different element types %s and %s", typ0.id(), typ1.id())
|
||||||
|
}
|
||||||
|
case bltnDelete:
|
||||||
|
typ := params[0].Type()
|
||||||
|
if typ.TypeOf().Kind() != reflect.Map {
|
||||||
|
return params[0].nod.cfgErrorf("first argument to delete must be map; have %s", typ.id())
|
||||||
|
}
|
||||||
|
ktyp := params[1].Type()
|
||||||
|
if !ktyp.assignableTo(typ.key) {
|
||||||
|
return params[1].nod.cfgErrorf("cannot use %s as type %s in delete", ktyp.id(), typ.key.id())
|
||||||
|
}
|
||||||
|
case bltnMake:
|
||||||
|
var min int
|
||||||
|
switch child[0].typ.TypeOf().Kind() {
|
||||||
|
case reflect.Slice:
|
||||||
|
min = 2
|
||||||
|
case reflect.Map, reflect.Chan:
|
||||||
|
min = 1
|
||||||
|
default:
|
||||||
|
return child[0].cfgErrorf("cannot make %s; type must be slice, map, or channel", child[0].typ.id())
|
||||||
|
}
|
||||||
|
if nparams < min {
|
||||||
|
return n.cfgErrorf("not enough arguments in call to make")
|
||||||
|
} else if nparams > min+1 {
|
||||||
|
return n.cfgErrorf("too many arguments for make")
|
||||||
|
}
|
||||||
|
|
||||||
|
var sizes []int
|
||||||
|
for _, c := range child[1:] {
|
||||||
|
if err := check.index(c, -1); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c.rval.IsValid() {
|
||||||
|
sizes = append(sizes, int(vInt(c.rval)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for len(sizes) == 2 && sizes[0] > sizes[1] {
|
||||||
|
return n.cfgErrorf("len larger than cap in make")
|
||||||
|
}
|
||||||
|
|
||||||
|
case bltnPanic:
|
||||||
|
return check.assignment(params[0].nod, &itype{cat: interfaceT}, "argument to panic")
|
||||||
|
case bltnPrint, bltnPrintln:
|
||||||
|
for _, param := range params {
|
||||||
|
if param.typ != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := check.assignment(param.nod, nil, "argument to "+name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case bltnRecover, bltnNew:
|
||||||
|
// Nothing to do.
|
||||||
|
default:
|
||||||
|
return n.cfgErrorf("unsupported builtin %s", name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// arrayDeref returns A if typ is *A, otherwise typ.
|
||||||
|
func arrayDeref(typ *itype) *itype {
|
||||||
|
if typ.cat == valueT && typ.TypeOf().Kind() == reflect.Ptr {
|
||||||
|
t := typ.TypeOf()
|
||||||
|
if t.Elem().Kind() == reflect.Array {
|
||||||
|
return &itype{cat: valueT, rtype: t.Elem()}
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ.cat == ptrT && typ.val.cat == arrayT && typ.val.sizedef {
|
||||||
|
return typ.val
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
// arguments type checks the call expression arguments.
|
// arguments type checks the call expression arguments.
|
||||||
func (check typecheck) arguments(n *node, child []*node, fun *node, ellipsis bool) error {
|
func (check typecheck) arguments(n *node, child []*node, fun *node, ellipsis bool) error {
|
||||||
|
params := check.unpackParams(child)
|
||||||
l := len(child)
|
l := len(child)
|
||||||
if ellipsis {
|
if ellipsis {
|
||||||
if !fun.typ.isVariadic() {
|
if !fun.typ.isVariadic() {
|
||||||
return n.cfgErrorf("invalid use of ..., corresponding parameter is non-variadic")
|
return n.cfgErrorf("invalid use of ..., corresponding parameter is non-variadic")
|
||||||
}
|
}
|
||||||
if len(child) == 1 && isCall(child[0]) && child[0].child[0].typ.numOut() > 1 {
|
if len(params) > l {
|
||||||
return child[0].cfgErrorf("cannot use ... with %d-valued %s", child[0].child[0].typ.numOut(), child[0].child[0].typ.id())
|
return child[0].cfgErrorf("cannot use ... with %d-valued %s", child[0].child[0].typ.numOut(), child[0].child[0].typ.id())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(child) == 1 && isCall(child[0]) && child[0].child[0].typ.numOut() > 1 {
|
|
||||||
// Handle the case of unpacking a n-valued function into the params.
|
|
||||||
c := child[0].child[0]
|
|
||||||
l := c.typ.numOut()
|
|
||||||
if l < fun.typ.numIn() {
|
|
||||||
return child[0].cfgErrorf("not enough arguments in call to %s", fun.name())
|
|
||||||
}
|
|
||||||
for i := 0; i < l; i++ {
|
|
||||||
arg := getArg(fun.typ, i)
|
|
||||||
if arg == nil {
|
|
||||||
return child[0].cfgErrorf("too many arguments")
|
|
||||||
}
|
|
||||||
if !c.typ.out(i).assignableTo(arg) {
|
|
||||||
return child[0].cfgErrorf("cannot use %s as type %s", c.typ.id(), getArgsID(fun.typ))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var cnt int
|
var cnt int
|
||||||
for i, arg := range child {
|
for i, param := range params {
|
||||||
ellip := i == l-1 && ellipsis
|
ellip := i == l-1 && ellipsis
|
||||||
if err := check.argument(arg, fun.typ, cnt, ellip); err != nil {
|
if err := check.argument(param, fun.typ, cnt, l, ellip); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cnt++
|
cnt++
|
||||||
@@ -679,29 +926,37 @@ func (check typecheck) arguments(n *node, child []*node, fun *node, ellipsis boo
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check typecheck) argument(n *node, ftyp *itype, i int, ellipsis bool) error {
|
func (check typecheck) argument(p param, ftyp *itype, i, l int, ellipsis bool) error {
|
||||||
typ := getArg(ftyp, i)
|
atyp := getArg(ftyp, i)
|
||||||
if typ == nil {
|
if atyp == nil {
|
||||||
return n.cfgErrorf("too many arguments")
|
return p.nod.cfgErrorf("too many arguments")
|
||||||
}
|
}
|
||||||
|
|
||||||
if isCall(n) && n.child[0].typ.numOut() != 1 {
|
if p.typ == nil && isCall(p.nod) && p.nod.child[0].typ.numOut() != 1 {
|
||||||
return n.cfgErrorf("cannot use %s as type %s", n.child[0].typ.id(), typ.id())
|
if l == 1 {
|
||||||
|
return p.nod.cfgErrorf("cannot use %s as type %s", p.nod.child[0].typ.id(), getArgsID(ftyp))
|
||||||
|
}
|
||||||
|
return p.nod.cfgErrorf("cannot use %s as type %s", p.nod.child[0].typ.id(), atyp.id())
|
||||||
}
|
}
|
||||||
|
|
||||||
if ellipsis {
|
if ellipsis {
|
||||||
if i != ftyp.numIn()-1 {
|
if i != ftyp.numIn()-1 {
|
||||||
return n.cfgErrorf("can only use ... with matching parameter")
|
return p.nod.cfgErrorf("can only use ... with matching parameter")
|
||||||
}
|
}
|
||||||
t := n.typ.TypeOf()
|
t := p.Type().TypeOf()
|
||||||
if t.Kind() != reflect.Slice || !(&itype{cat: valueT, rtype: t.Elem()}).assignableTo(typ) {
|
if t.Kind() != reflect.Slice || !(&itype{cat: valueT, rtype: t.Elem()}).assignableTo(atyp) {
|
||||||
return n.cfgErrorf("cannot use %s as type %s", n.typ.id(), (&itype{cat: arrayT, val: typ}).id())
|
return p.nod.cfgErrorf("cannot use %s as type %s", p.nod.typ.id(), (&itype{cat: arrayT, val: atyp}).id())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err := check.assignment(n, typ, "")
|
if p.typ != nil {
|
||||||
return err
|
if !p.typ.assignableTo(atyp) {
|
||||||
|
return p.nod.cfgErrorf("cannot use %s as type %s", p.nod.child[0].typ.id(), getArgsID(ftyp))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return check.assignment(p.nod, atyp, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getArg(ftyp *itype, i int) *itype {
|
func getArg(ftyp *itype, i int) *itype {
|
||||||
|
|||||||
Reference in New Issue
Block a user