From a67d677caa6052c3cf13d37b88adf98f50885fcb Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Mon, 4 Nov 2024 23:40:52 +0100 Subject: [PATCH 01/11] fix(gnovm): improve error message for nil assignment in variable declaration --- gnovm/pkg/gnolang/type_check.go | 2 +- gnovm/tests/files/assign29.gno | 10 ++++++++++ gnovm/tests/files/var31.gno | 8 ++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 gnovm/tests/files/assign29.gno create mode 100644 gnovm/tests/files/var31.gno diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index f86c44e7921..8b1d1eb1e3d 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -289,7 +289,7 @@ func checkAssignableTo(xt, dt Type, autoNative bool) error { // case0 if xt == nil { // see test/files/types/eql_0f18 if !maybeNil(dt) { - panic(fmt.Sprintf("invalid operation, nil can not be compared to %v", dt)) + panic(fmt.Sprintf("cannot use nil as %v value in variable declaration", dt)) } return nil } else if dt == nil { // _ = xxx, assign8.gno, 0f31. else cases? diff --git a/gnovm/tests/files/assign29.gno b/gnovm/tests/files/assign29.gno new file mode 100644 index 00000000000..1856e85e49f --- /dev/null +++ b/gnovm/tests/files/assign29.gno @@ -0,0 +1,10 @@ +package main + +func main() { + a := 1 + a = nil + println(a) +} + +// Error: +// main/files/assign29.gno:5:2: cannot use nil as int value in variable declaration diff --git a/gnovm/tests/files/var31.gno b/gnovm/tests/files/var31.gno new file mode 100644 index 00000000000..d5571f148ca --- /dev/null +++ b/gnovm/tests/files/var31.gno @@ -0,0 +1,8 @@ +package main + +func main() { + var i int = nil +} + +// Error: +// main/files/var31.gno:4:6: cannot use nil as int value in variable declaration From a29ccd80a3ded71903d3726a03eb5c090edc3fb0 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Tue, 12 Nov 2024 22:31:55 +0100 Subject: [PATCH 02/11] feat: improve error handling --- gnovm/pkg/gnolang/preprocess.go | 180 ++++++++++++++++---------------- gnovm/pkg/gnolang/type_check.go | 47 +++++---- gnovm/pkg/gnolang/types.go | 38 +++---- gnovm/tests/files/assign29.gno | 10 -- gnovm/tests/files/assign30.gno | 10 ++ 5 files changed, 146 insertions(+), 139 deletions(-) delete mode 100644 gnovm/tests/files/assign29.gno create mode 100644 gnovm/tests/files/assign30.gno diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index a7a1e15bbf3..43a2cd98e9f 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -726,7 +726,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { for i, cx := range n.Cases { cx = Preprocess( store, last, cx).(Expr) - checkOrConvertType(store, last, &cx, tt, false) // #nosec G601 + checkOrConvertType(store, last, n, &cx, tt, false) // #nosec G601 n.Cases[i] = cx } } @@ -865,7 +865,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // Preprocess and convert tag if const. if n.X != nil { n.X = Preprocess(store, last, n.X).(Expr) - convertIfConst(store, last, n.X) + convertIfConst(store, last, n, n.X) } } return n, TRANS_CONTINUE @@ -1077,10 +1077,10 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // First, convert untyped as necessary. if !shouldSwapOnSpecificity(lcx.T, rcx.T) { // convert n.Left to right type. - checkOrConvertType(store, last, &n.Left, rcx.T, false) + checkOrConvertType(store, last, n, &n.Left, rcx.T, false) } else { // convert n.Right to left type. - checkOrConvertType(store, last, &n.Right, lcx.T, false) + checkOrConvertType(store, last, n, &n.Right, lcx.T, false) } // Then, evaluate the expression. cx := evalConst(store, last, n) @@ -1100,7 +1100,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { rnt.String())) } // convert n.Left to pt type, - checkOrConvertType(store, last, &n.Left, pt, false) + checkOrConvertType(store, last, n, &n.Left, pt, false) // if check pass, convert n.Right to (gno) pt type, rn := Expr(Call(pt.String(), n.Right)) // and convert result back. @@ -1129,7 +1129,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { } if !isUntyped(rt) { // right is typed - checkOrConvertType(store, last, &n.Left, rt, false) + checkOrConvertType(store, last, n, &n.Left, rt, false) } else { if shouldSwapOnSpecificity(lt, rt) { checkUntypedShiftExpr(n.Right) @@ -1140,10 +1140,10 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { } } else if lcx.T == nil { // LHS is nil. // convert n.Left to typed-nil type. - checkOrConvertType(store, last, &n.Left, rt, false) + checkOrConvertType(store, last, n, &n.Left, rt, false) } else { if isUntyped(rt) { - checkOrConvertType(store, last, &n.Right, lt, false) + checkOrConvertType(store, last, n, &n.Right, lt, false) } } } else if ric { // right is const, left is not @@ -1161,7 +1161,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // convert n.Left to (gno) pt type, ln := Expr(Call(pt.String(), n.Left)) // convert n.Right to pt type, - checkOrConvertType(store, last, &n.Right, pt, false) + checkOrConvertType(store, last, n, &n.Right, pt, false) // and convert result back. tx := constType(n, lnt) // reset/create n2 to preprocess left child. @@ -1187,7 +1187,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { } // both untyped, e.g. 1< float64. // (const) untyped bigint -> int. if !constConverted { - convertConst(store, last, arg0, nil) + convertConst(store, last, n, arg0, nil) } // evaluate the new expression. cx := evalConst(store, last, n) @@ -1372,15 +1372,15 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { if isUntyped(at) { switch arg0.Op { case EQL, NEQ, LSS, GTR, LEQ, GEQ: - assertAssignableTo(at, ct, false) + assertAssignableTo(n, at, ct, false) break default: - checkOrConvertType(store, last, &n.Args[0], ct, false) + checkOrConvertType(store, last, n, &n.Args[0], ct, false) } } case *UnaryExpr: if isUntyped(at) { - checkOrConvertType(store, last, &n.Args[0], ct, false) + checkOrConvertType(store, last, n, &n.Args[0], ct, false) } default: // do nothing @@ -1524,7 +1524,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { panic("should not happen") } // Specify function param/result generics. - sft := ft.Specify(store, argTVs, isVarg) + sft := ft.Specify(store, n, argTVs, isVarg) spts := sft.Params srts := FieldTypeList(sft.Results).Types() // If generics were specified, override attr @@ -1550,12 +1550,12 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { for i, tv := range argTVs { if hasVarg { if (len(spts) - 1) <= i { - assertAssignableTo(tv.T, spts[len(spts)-1].Type.Elem(), true) + assertAssignableTo(n, tv.T, spts[len(spts)-1].Type.Elem(), true) } else { - assertAssignableTo(tv.T, spts[i].Type, true) + assertAssignableTo(n, tv.T, spts[i].Type, true) } } else { - assertAssignableTo(tv.T, spts[i].Type, true) + assertAssignableTo(n, tv.T, spts[i].Type, true) } } } else { @@ -1566,16 +1566,16 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { if len(spts) <= i { panic("expected final vargs slice but got many") } - checkOrConvertType(store, last, &n.Args[i], spts[i].Type, true) + checkOrConvertType(store, last, n, &n.Args[i], spts[i].Type, true) } else { - checkOrConvertType(store, last, &n.Args[i], + checkOrConvertType(store, last, n, &n.Args[i], spts[len(spts)-1].Type.Elem(), true) } } else { - checkOrConvertType(store, last, &n.Args[i], spts[i].Type, true) + checkOrConvertType(store, last, n, &n.Args[i], spts[i].Type, true) } } else { - checkOrConvertType(store, last, &n.Args[i], spts[i].Type, true) + checkOrConvertType(store, last, n, &n.Args[i], spts[i].Type, true) } } } @@ -1596,10 +1596,10 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { case StringKind, ArrayKind, SliceKind: // Replace const index with int *ConstExpr, // or if not const, assert integer type.. - checkOrConvertIntegerKind(store, last, n.Index) + checkOrConvertIntegerKind(store, last, n, n.Index) case MapKind: mt := baseOf(gnoTypeOf(store, dt)).(*MapType) - checkOrConvertType(store, last, &n.Index, mt.Key, false) + checkOrConvertType(store, last, n, &n.Index, mt.Key, false) default: panic(fmt.Sprintf( "unexpected index base kind for type %s", @@ -1610,15 +1610,15 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { case *SliceExpr: // Replace const L/H/M with int *ConstExpr, // or if not const, assert integer type.. - checkOrConvertIntegerKind(store, last, n.Low) - checkOrConvertIntegerKind(store, last, n.High) - checkOrConvertIntegerKind(store, last, n.Max) + checkOrConvertIntegerKind(store, last, n, n.Low) + checkOrConvertIntegerKind(store, last, n, n.High) + checkOrConvertIntegerKind(store, last, n, n.Max) // if n.X is untyped, convert to corresponding type t := evalStaticTypeOf(store, last, n.X) if isUntyped(t) { dt := defaultTypeOf(t) - checkOrConvertType(store, last, &n.X, dt, false) + checkOrConvertType(store, last, n, &n.X, dt, false) } // TRANS_LEAVE ----------------------- @@ -1696,28 +1696,28 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { key := n.Elts[i].Key.(*NameExpr).Name path := cclt.GetPathForName(key) ft := cclt.GetStaticTypeOfAt(path) - checkOrConvertType(store, last, &n.Elts[i].Value, ft, false) + checkOrConvertType(store, last, n, &n.Elts[i].Value, ft, false) } } else { for i := 0; i < len(n.Elts); i++ { ft := cclt.Fields[i].Type - checkOrConvertType(store, last, &n.Elts[i].Value, ft, false) + checkOrConvertType(store, last, n, &n.Elts[i].Value, ft, false) } } case *ArrayType: for i := 0; i < len(n.Elts); i++ { - convertType(store, last, &n.Elts[i].Key, IntType) - checkOrConvertType(store, last, &n.Elts[i].Value, cclt.Elt, false) + convertType(store, last, n, &n.Elts[i].Key, IntType) + checkOrConvertType(store, last, n, &n.Elts[i].Value, cclt.Elt, false) } case *SliceType: for i := 0; i < len(n.Elts); i++ { - convertType(store, last, &n.Elts[i].Key, IntType) - checkOrConvertType(store, last, &n.Elts[i].Value, cclt.Elt, false) + convertType(store, last, n, &n.Elts[i].Key, IntType) + checkOrConvertType(store, last, n, &n.Elts[i].Value, cclt.Elt, false) } case *MapType: for i := 0; i < len(n.Elts); i++ { - checkOrConvertType(store, last, &n.Elts[i].Key, cclt.Key, false) - checkOrConvertType(store, last, &n.Elts[i].Value, cclt.Value, false) + checkOrConvertType(store, last, n, &n.Elts[i].Key, cclt.Key, false) + checkOrConvertType(store, last, n, &n.Elts[i].Value, cclt.Value, false) } case *NativeType: clt = cclt.GnoType(store) @@ -1917,7 +1917,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // TRANS_LEAVE ----------------------- case *FieldTypeExpr: // Replace const Tag with default *ConstExpr. - convertIfConst(store, last, n.Tag) + convertIfConst(store, last, n, n.Tag) // TRANS_LEAVE ----------------------- case *ArrayTypeExpr: @@ -1926,7 +1926,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { } else { // Replace const Len with int *ConstExpr. cx := evalConst(store, last, n.Len) - convertConst(store, last, cx, IntType) + convertConst(store, last, n, cx, IntType) n.Len = cx } // NOTE: For all TypeExprs, the node is not replaced @@ -1966,7 +1966,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // Rhs consts become default *ConstExprs. for _, rx := range n.Rhs { // NOTE: does nothing if rx is "nil". - convertIfConst(store, last, rx) + convertIfConst(store, last, n, rx) } if len(n.Lhs) > len(n.Rhs) { @@ -2019,7 +2019,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { } // if rhs is untyped if isUntyped(rt) { - checkOrConvertType(store, last, &n.Rhs[i], nil, false) + checkOrConvertType(store, last, n, &n.Rhs[i], nil, false) } } } @@ -2111,11 +2111,11 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { } else { // len(Lhs) == len(Rhs) if n.Op == SHL_ASSIGN || n.Op == SHR_ASSIGN { // Special case if shift assign <<= or >>=. - convertType(store, last, &n.Rhs[0], UintType) + convertType(store, last, n, &n.Rhs[0], UintType) } else if n.Op == ADD_ASSIGN || n.Op == SUB_ASSIGN || n.Op == MUL_ASSIGN || n.Op == QUO_ASSIGN || n.Op == REM_ASSIGN { // e.g. a += b, single value for lhs and rhs, lt := evalStaticTypeOf(store, last, n.Lhs[0]) - checkOrConvertType(store, last, &n.Rhs[0], lt, true) + checkOrConvertType(store, last, n, &n.Rhs[0], lt, true) } else { // all else, like BAND_ASSIGN, etc // General case: a, b = x, y. for i, lx := range n.Lhs { @@ -2125,7 +2125,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { } // if lt is interface, nothing will happen - checkOrConvertType(store, last, &n.Rhs[i], lt, true) + checkOrConvertType(store, last, n, &n.Rhs[i], lt, true) } } } @@ -2187,12 +2187,12 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // TRANS_LEAVE ----------------------- case *ForStmt: // Cond consts become bool *ConstExprs. - checkOrConvertBoolKind(store, last, n.Cond) + checkOrConvertBoolKind(store, last, n, n.Cond) // TRANS_LEAVE ----------------------- case *IfStmt: // Cond consts become bool *ConstExprs. - checkOrConvertBoolKind(store, last, n.Cond) + checkOrConvertBoolKind(store, last, n, n.Cond) // TRANS_LEAVE ----------------------- case *RangeStmt: @@ -2248,7 +2248,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // XXX how to deal? panic("not yet implemented") } else { - checkOrConvertType(store, last, &n.Results[i], rt, false) + checkOrConvertType(store, last, n, &n.Results[i], rt, false) } } } @@ -2256,7 +2256,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // TRANS_LEAVE ----------------------- case *SendStmt: // Value consts become default *ConstExprs. - checkOrConvertType(store, last, &n.Value, nil, false) + checkOrConvertType(store, last, n, &n.Value, nil, false) // TRANS_LEAVE ----------------------- case *SelectCaseStmt: @@ -2381,7 +2381,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { } // convert if const to nt. for i := range n.Values { - checkOrConvertType(store, last, &n.Values[i], nt, false) + checkOrConvertType(store, last, n, &n.Values[i], nt, false) } } else if n.Const { // derive static type from values. @@ -2393,10 +2393,10 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // convert n.Value to default type. for i, vx := range n.Values { if cx, ok := vx.(*ConstExpr); ok { - convertConst(store, last, cx, nil) + convertConst(store, last, n, cx, nil) // convertIfConst(store, last, vx) } else { - checkOrConvertType(store, last, &vx, nil, false) + checkOrConvertType(store, last, n, &vx, nil, false) } vt := evalStaticTypeOf(store, last, vx) sts[i] = vt @@ -3411,14 +3411,14 @@ func isConstType(x Expr) bool { } // check before convert type -func checkOrConvertType(store Store, last BlockNode, x *Expr, t Type, autoNative bool) { +func checkOrConvertType(store Store, last BlockNode, n Node, x *Expr, t Type, autoNative bool) { if debug { debug.Printf("checkOrConvertType, *x: %v:, t:%v \n", *x, t) } if cx, ok := (*x).(*ConstExpr); ok { if _, ok := t.(*NativeType); !ok { // not native type, refer to time4_native.gno. // e.g. int(1) == int8(1) - assertAssignableTo(cx.T, t, autoNative) + assertAssignableTo(n, cx.T, t, autoNative) } } else if bx, ok := (*x).(*BinaryExpr); ok && (bx.Op == SHL || bx.Op == SHR) { xt := evalStaticTypeOf(store, last, *x) @@ -3427,22 +3427,22 @@ func checkOrConvertType(store Store, last BlockNode, x *Expr, t Type, autoNative } if isUntyped(xt) { // check assignable first, see: types/shift_b6.gno - assertAssignableTo(xt, t, autoNative) + assertAssignableTo(n, xt, t, autoNative) if t == nil || t.Kind() == InterfaceKind { t = defaultTypeOf(xt) } bx.assertShiftExprCompatible2(t) - checkOrConvertType(store, last, &bx.Left, t, autoNative) + checkOrConvertType(store, last, n, &bx.Left, t, autoNative) } else { - assertAssignableTo(xt, t, autoNative) + assertAssignableTo(n, xt, t, autoNative) } return } else if *x != nil { xt := evalStaticTypeOf(store, last, *x) if t != nil { - assertAssignableTo(xt, t, autoNative) + assertAssignableTo(n, xt, t, autoNative) } if isUntyped(xt) { // Push type into expr if qualifying binary expr. @@ -3454,8 +3454,8 @@ func checkOrConvertType(store Store, last BlockNode, x *Expr, t Type, autoNative rt := evalStaticTypeOf(store, last, bx.Right) if t != nil { // push t into bx.Left and bx.Right - checkOrConvertType(store, last, &bx.Left, t, autoNative) - checkOrConvertType(store, last, &bx.Right, t, autoNative) + checkOrConvertType(store, last, n, &bx.Left, t, autoNative) + checkOrConvertType(store, last, n, &bx.Right, t, autoNative) return } else { if shouldSwapOnSpecificity(lt, rt) { @@ -3466,11 +3466,11 @@ func checkOrConvertType(store Store, last BlockNode, x *Expr, t Type, autoNative // without a specific context type, '1.0< Date: Tue, 12 Nov 2024 23:07:36 +0100 Subject: [PATCH 03/11] feat: handle more cases --- gnovm/pkg/gnolang/type_check.go | 6 ++++-- gnovm/tests/files/slice3.gno | 9 +++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 gnovm/tests/files/slice3.gno diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index c09ec329eea..a0e729f8db8 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -289,13 +289,15 @@ func checkAssignableTo(n Node, xt, dt Type, autoNative bool) error { // case0 if xt == nil { // see test/files/types/eql_0f18 if !maybeNil(dt) { - switch n := n.(type) { + switch n.(type) { case *ValueDecl: panic(fmt.Sprintf("cannot use nil as %v value in variable declaration", dt)) case *AssignStmt: panic(fmt.Sprintf("cannot use nil as %v value in assignment", dt)) + case *CompositeLitExpr: + panic(fmt.Sprintf("cannot use nil as %v value in array, slice literal or map literal", dt)) default: - panic(fmt.Sprintf("unexpected node type %T", n)) + panic(fmt.Sprintf("cannot use nil as %v value", dt)) } } return nil diff --git a/gnovm/tests/files/slice3.gno b/gnovm/tests/files/slice3.gno new file mode 100644 index 00000000000..1132da01420 --- /dev/null +++ b/gnovm/tests/files/slice3.gno @@ -0,0 +1,9 @@ +package main + +func main() { + i := []string{nil} + println(i) +} + +// Error: +// main/files/slice3.gno:4:7: cannot use nil as string value in array, slice literal or map literal From 6be8a9aa4a724385de543e5636317e6603b9eb5b Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Tue, 12 Nov 2024 23:15:37 +0100 Subject: [PATCH 04/11] feat: change file number for merge --- gnovm/tests/files/{assign30.gno => assign32.gno} | 2 +- gnovm/tests/files/{var31.gno => var34.gno} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename gnovm/tests/files/{assign30.gno => assign32.gno} (55%) rename gnovm/tests/files/{var31.gno => var34.gno} (53%) diff --git a/gnovm/tests/files/assign30.gno b/gnovm/tests/files/assign32.gno similarity index 55% rename from gnovm/tests/files/assign30.gno rename to gnovm/tests/files/assign32.gno index 7cc75973a71..d596c85f740 100644 --- a/gnovm/tests/files/assign30.gno +++ b/gnovm/tests/files/assign32.gno @@ -7,4 +7,4 @@ func main() { } // Error: -// main/files/assign30.gno:5:2: cannot use nil as int value in assignment +// main/files/assign32.gno:5:2: cannot use nil as int value in assignment diff --git a/gnovm/tests/files/var31.gno b/gnovm/tests/files/var34.gno similarity index 53% rename from gnovm/tests/files/var31.gno rename to gnovm/tests/files/var34.gno index d5571f148ca..2fae5e3c7bb 100644 --- a/gnovm/tests/files/var31.gno +++ b/gnovm/tests/files/var34.gno @@ -5,4 +5,4 @@ func main() { } // Error: -// main/files/var31.gno:4:6: cannot use nil as int value in variable declaration +// main/files/var33.gno:4:6: cannot use nil as int value in variable declaration From 8e8ecd405985d69b9f7da6a7ddf3b5fb448da1cd Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Wed, 13 Nov 2024 19:30:09 +0100 Subject: [PATCH 05/11] feat: add func --- gnovm/pkg/gnolang/type_check.go | 4 +++- gnovm/tests/files/fun28.gno | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 gnovm/tests/files/fun28.gno diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index a34e69324bc..09a8120e4ca 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -289,13 +289,15 @@ func checkAssignableTo(n Node, xt, dt Type, autoNative bool) error { // case0 if xt == nil { // see test/files/types/eql_0f18 if !maybeNil(dt) { - switch n.(type) { + switch n := n.(type) { case *ValueDecl: panic(fmt.Sprintf("cannot use nil as %v value in variable declaration", dt)) case *AssignStmt: panic(fmt.Sprintf("cannot use nil as %v value in assignment", dt)) case *CompositeLitExpr: panic(fmt.Sprintf("cannot use nil as %v value in array, slice literal or map literal", dt)) + case *CallExpr: + panic(fmt.Sprintf("cannot use nil as %v value in argument to %v", dt, n.Func)) default: panic(fmt.Sprintf("cannot use nil as %v value", dt)) } diff --git a/gnovm/tests/files/fun28.gno b/gnovm/tests/files/fun28.gno new file mode 100644 index 00000000000..cf969f9f34b --- /dev/null +++ b/gnovm/tests/files/fun28.gno @@ -0,0 +1,10 @@ +package main + +func f(i int) {} + +func main() { + f(nil) +} + +// Error: +// main/files/fun28.gno:6:2: cannot use nil as int value in argument to f From fc40836cb195f7d71f1e27fc1029ee9bc1d5a48c Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Sun, 17 Nov 2024 18:18:54 +0100 Subject: [PATCH 06/11] fix: correct test --- gnovm/tests/files/var34.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/tests/files/var34.gno b/gnovm/tests/files/var34.gno index 2fae5e3c7bb..57c181d5a64 100644 --- a/gnovm/tests/files/var34.gno +++ b/gnovm/tests/files/var34.gno @@ -5,4 +5,4 @@ func main() { } // Error: -// main/files/var33.gno:4:6: cannot use nil as int value in variable declaration +// main/files/var34.gno:4:6: cannot use nil as int value in variable declaration From f6180cc7ffaf6d5f600b8ffaed4d67683b6cd060 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Sun, 17 Nov 2024 21:42:11 +0100 Subject: [PATCH 07/11] feat: add binary expression --- gnovm/pkg/gnolang/type_check.go | 2 ++ gnovm/tests/files/add3.gno | 9 +++++++++ 2 files changed, 11 insertions(+) create mode 100644 gnovm/tests/files/add3.gno diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index 09a8120e4ca..9f5b87e2a69 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -298,6 +298,8 @@ func checkAssignableTo(n Node, xt, dt Type, autoNative bool) error { panic(fmt.Sprintf("cannot use nil as %v value in array, slice literal or map literal", dt)) case *CallExpr: panic(fmt.Sprintf("cannot use nil as %v value in argument to %v", dt, n.Func)) + case *BinaryExpr: + panic(fmt.Sprintf("invalid operation: %v (mismatched types %v and untyped nil)", n, dt)) default: panic(fmt.Sprintf("cannot use nil as %v value", dt)) } diff --git a/gnovm/tests/files/add3.gno b/gnovm/tests/files/add3.gno new file mode 100644 index 00000000000..c52e2c502dc --- /dev/null +++ b/gnovm/tests/files/add3.gno @@ -0,0 +1,9 @@ +package main + +func main() { + a := 1 + i := a + nil +} + +// Error: +// main/files/add3.gno:5:7: invalid operation: a + (const (undefined)) (mismatched types int and untyped nil) From 9f39fe2541fe5386e80be3693de7cdecf03a2c10 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Wed, 4 Dec 2024 00:07:15 +0100 Subject: [PATCH 08/11] feat: merge --- gnovm/tests/files/{assign32.gno => assign38.gno} | 0 gnovm/tests/files/{var34.gno => var35.gno} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename gnovm/tests/files/{assign32.gno => assign38.gno} (100%) rename gnovm/tests/files/{var34.gno => var35.gno} (100%) diff --git a/gnovm/tests/files/assign32.gno b/gnovm/tests/files/assign38.gno similarity index 100% rename from gnovm/tests/files/assign32.gno rename to gnovm/tests/files/assign38.gno diff --git a/gnovm/tests/files/var34.gno b/gnovm/tests/files/var35.gno similarity index 100% rename from gnovm/tests/files/var34.gno rename to gnovm/tests/files/var35.gno From 06404c1ef6295d412055e3a03f06fb01f03efe1b Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Wed, 4 Dec 2024 00:35:35 +0100 Subject: [PATCH 09/11] fix: typo test --- gnovm/tests/files/assign38.gno | 2 +- gnovm/tests/files/var35.gno | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gnovm/tests/files/assign38.gno b/gnovm/tests/files/assign38.gno index d596c85f740..5ef3549ccf6 100644 --- a/gnovm/tests/files/assign38.gno +++ b/gnovm/tests/files/assign38.gno @@ -7,4 +7,4 @@ func main() { } // Error: -// main/files/assign32.gno:5:2: cannot use nil as int value in assignment +// main/files/assign38.gno:5:2: cannot use nil as int value in assignment diff --git a/gnovm/tests/files/var35.gno b/gnovm/tests/files/var35.gno index 57c181d5a64..87b1cc68590 100644 --- a/gnovm/tests/files/var35.gno +++ b/gnovm/tests/files/var35.gno @@ -5,4 +5,4 @@ func main() { } // Error: -// main/files/var34.gno:4:6: cannot use nil as int value in variable declaration +// main/files/var35.gno:4:6: cannot use nil as int value in variable declaration From 7ae5d59fd3ba2303cc25c4e807a8ae6f685c617c Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Fri, 6 Dec 2024 22:22:17 +0100 Subject: [PATCH 10/11] refactor: reorder parameters in specifyType function and use nil when we don't use this val --- gnovm/pkg/gnolang/type_check_test.go | 3 +-- gnovm/pkg/gnolang/types.go | 32 ++++++++++++++-------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/gnovm/pkg/gnolang/type_check_test.go b/gnovm/pkg/gnolang/type_check_test.go index ba5a24385a9..7fddf2c65d0 100644 --- a/gnovm/pkg/gnolang/type_check_test.go +++ b/gnovm/pkg/gnolang/type_check_test.go @@ -7,7 +7,6 @@ func TestCheckAssignableTo(t *testing.T) { tests := []struct { name string - n Node xt Type dt Type autoNative bool @@ -52,7 +51,7 @@ func TestCheckAssignableTo(t *testing.T) { } }() } - checkAssignableTo(tt.n, tt.xt, tt.dt, tt.autoNative) + checkAssignableTo(nil, tt.xt, tt.dt, tt.autoNative) }) } } diff --git a/gnovm/pkg/gnolang/types.go b/gnovm/pkg/gnolang/types.go index b076b632c4f..18774fcc462 100644 --- a/gnovm/pkg/gnolang/types.go +++ b/gnovm/pkg/gnolang/types.go @@ -1248,9 +1248,9 @@ func (ft *FuncType) Specify(store Store, n Node, argTVs []TypedValue, isVarg boo for i, pf := range ft.Params { arg := &argTVs[i] if arg.T.Kind() == TypeKind { - specifyType(n, store, lookup, pf.Type, arg.T, arg.GetType()) + specifyType(store, n, lookup, pf.Type, arg.T, arg.GetType()) } else { - specifyType(n, store, lookup, pf.Type, arg.T, nil) + specifyType(store, n, lookup, pf.Type, arg.T, nil) } } // apply specifics to generic params and results. @@ -2427,7 +2427,7 @@ func IsImplementedBy(it Type, ot Type) bool { // specTypeval is Type if spec is TypeKind. // NOTE: type-checking isn't strictly necessary here, as the resulting lookup // map gets applied to produce the ultimate param and result types. -func specifyType(n Node, store Store, lookup map[Name]Type, tmpl Type, spec Type, specTypeval Type) { +func specifyType(store Store, n Node, lookup map[Name]Type, tmpl Type, spec Type, specTypeval Type) { if isGeneric(spec) { panic("spec must not be generic") } @@ -2441,11 +2441,11 @@ func specifyType(n Node, store Store, lookup map[Name]Type, tmpl Type, spec Type case *PointerType: switch pt := baseOf(spec).(type) { case *PointerType: - specifyType(n, store, lookup, ct.Elt, pt.Elt, nil) + specifyType(store, n, lookup, ct.Elt, pt.Elt, nil) case *NativeType: // NOTE: see note about type-checking. et := pt.Elem() - specifyType(n, store, lookup, ct.Elt, et, nil) + specifyType(store, n, lookup, ct.Elt, et, nil) default: panic(fmt.Sprintf( "expected pointer kind but got %s", @@ -2454,11 +2454,11 @@ func specifyType(n Node, store Store, lookup map[Name]Type, tmpl Type, spec Type case *ArrayType: switch at := baseOf(spec).(type) { case *ArrayType: - specifyType(n, store, lookup, ct.Elt, at.Elt, nil) + specifyType(store, n, lookup, ct.Elt, at.Elt, nil) case *NativeType: // NOTE: see note about type-checking. et := at.Elem() - specifyType(n, store, lookup, ct.Elt, et, nil) + specifyType(store, n, lookup, ct.Elt, et, nil) default: panic(fmt.Sprintf( "expected array kind but got %s", @@ -2469,7 +2469,7 @@ func specifyType(n Node, store Store, lookup map[Name]Type, tmpl Type, spec Type case PrimitiveType: if isGeneric(ct.Elt) { if st.Kind() == StringKind { - specifyType(n, store, lookup, ct.Elt, Uint8Type, nil) + specifyType(store, n, lookup, ct.Elt, Uint8Type, nil) } else { panic(fmt.Sprintf( "expected slice kind but got %s", @@ -2485,11 +2485,11 @@ func specifyType(n Node, store Store, lookup map[Name]Type, tmpl Type, spec Type spec.Kind())) } case *SliceType: - specifyType(n, store, lookup, ct.Elt, st.Elt, nil) + specifyType(store, n, lookup, ct.Elt, st.Elt, nil) case *NativeType: // NOTE: see note about type-checking. et := st.Elem() - specifyType(n, store, lookup, ct.Elt, et, nil) + specifyType(store, n, lookup, ct.Elt, et, nil) default: panic(fmt.Sprintf( "expected slice kind but got %s", @@ -2498,14 +2498,14 @@ func specifyType(n Node, store Store, lookup map[Name]Type, tmpl Type, spec Type case *MapType: switch mt := baseOf(spec).(type) { case *MapType: - specifyType(n, store, lookup, ct.Key, mt.Key, nil) - specifyType(n, store, lookup, ct.Value, mt.Value, nil) + specifyType(store, n, lookup, ct.Key, mt.Key, nil) + specifyType(store, n, lookup, ct.Value, mt.Value, nil) case *NativeType: // NOTE: see note about type-checking. kt := mt.Key() vt := mt.Elem() - specifyType(n, store, lookup, ct.Key, kt, nil) - specifyType(n, store, lookup, ct.Value, vt, nil) + specifyType(store, n, lookup, ct.Key, kt, nil) + specifyType(store, n, lookup, ct.Value, vt, nil) default: panic(fmt.Sprintf( "expected map kind but got %s", @@ -2576,9 +2576,9 @@ func specifyType(n Node, store Store, lookup map[Name]Type, tmpl Type, spec Type switch cbt := baseOf(spec).(type) { case *NativeType: gnoType := store.Go2GnoType(cbt.Type) - specifyType(n, store, lookup, ct.Type, gnoType, nil) + specifyType(store, n, lookup, ct.Type, gnoType, nil) default: - specifyType(n, store, lookup, ct.Type, cbt, nil) + specifyType(store, n, lookup, ct.Type, cbt, nil) } default: // ignore, no generics. From a23d2b504d2abbe88b4a89fa75b34cc5144c96eb Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Mon, 9 Dec 2024 20:55:04 +0100 Subject: [PATCH 11/11] refactor: reorder parameters in assignment parsing functions for consistency --- gnovm/pkg/gnolang/preprocess.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index cdc3a7cce36..6e749053d72 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -2400,9 +2400,9 @@ func defineOrDecl( tvs := make([]TypedValue, numNames) if numVals == 1 && numNames > 1 { - parseMultipleAssignFromOneExpr(sts, tvs, store, bn, n, nameExprs, typeExpr, valueExprs[0]) + parseMultipleAssignFromOneExpr(store, bn, n, sts, tvs, nameExprs, typeExpr, valueExprs[0]) } else { - parseAssignFromExprList(sts, tvs, store, bn, n, isConst, nameExprs, typeExpr, valueExprs) + parseAssignFromExprList(store, bn, n, sts, tvs, isConst, nameExprs, typeExpr, valueExprs) } node := skipFile(bn) @@ -2421,11 +2421,11 @@ func defineOrDecl( // parseAssignFromExprList parses assignment to multiple variables from a list of expressions. // This function will alter the value of sts, tvs. func parseAssignFromExprList( - sts []Type, - tvs []TypedValue, store Store, bn BlockNode, n Node, + sts []Type, + tvs []TypedValue, isConst bool, nameExprs []NameExpr, typeExpr Expr, @@ -2508,11 +2508,11 @@ func parseAssignFromExprList( // - a, b := n.(T) // - a, b := n[i], where n is a map func parseMultipleAssignFromOneExpr( - sts []Type, - tvs []TypedValue, store Store, bn BlockNode, n Node, + sts []Type, + tvs []TypedValue, nameExprs []NameExpr, typeExpr Expr, valueExpr Expr,