From aabf7c27644ba81ce5ec9c667223a983569b5212 Mon Sep 17 00:00:00 2001 From: Marc Vertes Date: Wed, 27 Sep 2023 09:49:33 +0200 Subject: [PATCH 1/6] fix: append nil to an array was causing a panic This is a particular case of Go append builtin where the array type is inferred from the first argument. We detect using untyped nil as argument, then we convert it to the array type prior to proceeed further. It fixes further consistency checks and value generation. Fixes #1136. --- gnovm/pkg/gnolang/preprocess.go | 12 ++++++++++++ gnovm/tests/files/append5.gno | 10 ++++++++++ 2 files changed, 22 insertions(+) create mode 100644 gnovm/tests/files/append5.gno diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index e02a158fcf1..ca2ebfba6f0 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1006,6 +1006,18 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { n.Args[1] = args1 } } + // Another special case for append: adding nil to an array. + // Untyped nil must be converted to the array type for consistency. + for i, arg := range n.Args[1:] { + if cx, ok := arg.(*ConstExpr); ok && isNilExpr(cx.Source) { + // We append an untyped nil value. Get the array type from + // the first argument and convert nil to it. + s0 := evalStaticTypeOf(store, last, n.Args[0]) + tx := constType(nil, s0.Elem()) + var arg1 Expr = Call(tx, arg) + n.Args[i+1] = Preprocess(nil, last, arg1).(Expr) + } + } } else if fv.PkgPath == uversePkgPath && fv.Name == "copy" { if len(n.Args) == 2 { // If the second argument is a string, diff --git a/gnovm/tests/files/append5.gno b/gnovm/tests/files/append5.gno new file mode 100644 index 00000000000..85783724205 --- /dev/null +++ b/gnovm/tests/files/append5.gno @@ -0,0 +1,10 @@ +package main + +func main() { + var errors []error + errors = append(errors, nil, nil) + println(len(errors)) +} + +// Output: +// 2 From 0432faf52fbd5a488b8747873be0ac73856a9c94 Mon Sep 17 00:00:00 2001 From: Marc Vertes Date: Wed, 27 Sep 2023 12:26:19 +0200 Subject: [PATCH 2/6] fix: generalize previous to handle any untyped const expression Fixes also #1149. --- gnovm/pkg/gnolang/preprocess.go | 24 +++++++++++++++--------- gnovm/tests/files/append6.gno | 12 ++++++++++++ 2 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 gnovm/tests/files/append6.gno diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index ca2ebfba6f0..0ef1a5ea939 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1006,17 +1006,23 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { n.Args[1] = args1 } } - // Another special case for append: adding nil to an array. - // Untyped nil must be converted to the array type for consistency. + // Another special case for append: adding untyped constants to an array. + // They must be converted to the array type for consistency. for i, arg := range n.Args[1:] { - if cx, ok := arg.(*ConstExpr); ok && isNilExpr(cx.Source) { - // We append an untyped nil value. Get the array type from - // the first argument and convert nil to it. - s0 := evalStaticTypeOf(store, last, n.Args[0]) - tx := constType(nil, s0.Elem()) - var arg1 Expr = Call(tx, arg) - n.Args[i+1] = Preprocess(nil, last, arg1).(Expr) + if _, ok := arg.(*ConstExpr); !ok { + // Consider only constant expressions. + continue } + t1 := evalStaticTypeOf(store, last, arg) + if t1 != nil && !isUntyped(t1) { + // Consider only untyped values. + continue + } + // Get the array type from the first argument and convert to it. + s0 := evalStaticTypeOf(store, last, n.Args[0]) + tx := constType(arg, s0.Elem()) + var arg1 Expr = Call(tx, arg) + n.Args[i+1] = Preprocess(nil, last, arg1).(Expr) } } else if fv.PkgPath == uversePkgPath && fv.Name == "copy" { if len(n.Args) == 2 { diff --git a/gnovm/tests/files/append6.gno b/gnovm/tests/files/append6.gno new file mode 100644 index 00000000000..523c57bf044 --- /dev/null +++ b/gnovm/tests/files/append6.gno @@ -0,0 +1,12 @@ +package main + +func main() { + const x = 118999 + y := 11 + p := []int{} + p = append(p, x, y) + println(p[0] + p[1]) +} + +// Output: +// 119010 From 1455aa7fa1dfba29a7058187b97d676a167cdcf0 Mon Sep 17 00:00:00 2001 From: Marc Vertes Date: Wed, 27 Sep 2023 12:31:25 +0200 Subject: [PATCH 3/6] cosmetic --- gnovm/pkg/gnolang/preprocess.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 0ef1a5ea939..1b122e3133e 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1006,16 +1006,15 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { n.Args[1] = args1 } } - // Another special case for append: adding untyped constants to an array. + // Another special case for append: adding untyped constants. // They must be converted to the array type for consistency. for i, arg := range n.Args[1:] { if _, ok := arg.(*ConstExpr); !ok { // Consider only constant expressions. continue } - t1 := evalStaticTypeOf(store, last, arg) - if t1 != nil && !isUntyped(t1) { - // Consider only untyped values. + if t1 := evalStaticTypeOf(store, last, arg); t1 != nil && !isUntyped(t1) { + // Consider only untyped values (including nil). continue } // Get the array type from the first argument and convert to it. From 196c6fbc1737e0f89a75a9fd3ded2c7c49bffd81 Mon Sep 17 00:00:00 2001 From: Marc Vertes Date: Wed, 29 Nov 2023 16:25:46 +0100 Subject: [PATCH 4/6] Update gnovm/pkg/gnolang/preprocess.go Co-authored-by: Morgan --- gnovm/pkg/gnolang/preprocess.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 23868b55dd0..0ba059af515 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1020,7 +1020,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // Get the array type from the first argument and convert to it. s0 := evalStaticTypeOf(store, last, n.Args[0]) tx := constType(arg, s0.Elem()) - var arg1 Expr = Call(tx, arg) + arg1 := Call(tx, arg) n.Args[i+1] = Preprocess(nil, last, arg1).(Expr) } } else if fv.PkgPath == uversePkgPath && fv.Name == "copy" { From 42c657ebe54e0291fb1495a83fd3d17494e56fe4 Mon Sep 17 00:00:00 2001 From: Marc Vertes Date: Thu, 30 Nov 2023 15:07:22 +0100 Subject: [PATCH 5/6] fix: catch forbidden conversions of undefined nil to scalar types This is done in ConverTo, so hopefully it works both at parsing and during execution, and for composite types. Some additional test cases have been added too. --- gnovm/pkg/gnolang/values_conversions.go | 4 ++++ gnovm/tests/files/append7.gno | 10 ++++++++++ gnovm/tests/files/convert4.gno | 7 +++++++ gnovm/tests/files/convert5.gno | 9 +++++++++ 4 files changed, 30 insertions(+) create mode 100644 gnovm/tests/files/append7.gno create mode 100644 gnovm/tests/files/convert4.gno create mode 100644 gnovm/tests/files/convert5.gno diff --git a/gnovm/pkg/gnolang/values_conversions.go b/gnovm/pkg/gnolang/values_conversions.go index cc7c0de9f09..2c111446c58 100644 --- a/gnovm/pkg/gnolang/values_conversions.go +++ b/gnovm/pkg/gnolang/values_conversions.go @@ -77,6 +77,10 @@ GNO_CASE: } // special case for undefined/nil source if tv.IsUndefined() { + switch t.Kind() { + case BoolKind, StringKind, IntKind, Int8Kind, Int16Kind, Int32Kind, Int64Kind, UintKind, Uint8Kind, Uint16Kind, Uint32Kind, Uint64Kind, Float32Kind, Float64Kind, BigintKind, BigdecKind: + panic(fmt.Sprintf("cannot convert %v to %v", tv, t)) + } tv.T = t return } diff --git a/gnovm/tests/files/append7.gno b/gnovm/tests/files/append7.gno new file mode 100644 index 00000000000..85783724205 --- /dev/null +++ b/gnovm/tests/files/append7.gno @@ -0,0 +1,10 @@ +package main + +func main() { + var errors []error + errors = append(errors, nil, nil) + println(len(errors)) +} + +// Output: +// 2 diff --git a/gnovm/tests/files/convert4.gno b/gnovm/tests/files/convert4.gno new file mode 100644 index 00000000000..6ad748499ed --- /dev/null +++ b/gnovm/tests/files/convert4.gno @@ -0,0 +1,7 @@ +package main + +func main() { + println(int(nil)) +} + +// error: cannot convert (undefined) to int diff --git a/gnovm/tests/files/convert5.gno b/gnovm/tests/files/convert5.gno new file mode 100644 index 00000000000..acd33889ff0 --- /dev/null +++ b/gnovm/tests/files/convert5.gno @@ -0,0 +1,9 @@ +package main + +func main() { + var ints []int + ints = append(ints, nil, nil) + println(ints) +} + +// error: cannot convert (undefined) to int From 04614ecea4ba0b02e0ea4d6349c81315b776bde2 Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Wed, 10 Jan 2024 13:38:00 +0100 Subject: [PATCH 6/6] fix failing tests, apply suggestion from jae --- .../gno.land/r/gnoland/faucet/z2_filetest.gno | 4 +- .../gno.land/r/gnoland/faucet/z3_filetest.gno | 8 ++-- gnovm/pkg/gnolang/preprocess.go | 38 +++++++++++-------- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/examples/gno.land/r/gnoland/faucet/z2_filetest.gno b/examples/gno.land/r/gnoland/faucet/z2_filetest.gno index 1791cd91989..9b59d7c07d3 100644 --- a/examples/gno.land/r/gnoland/faucet/z2_filetest.gno +++ b/examples/gno.land/r/gnoland/faucet/z2_filetest.gno @@ -16,11 +16,11 @@ func main() { ) std.TestSetOrigCaller(adminaddr) err := faucet.AdminAddController(controlleraddr1) - if err != nil { + if err != "" { panic(err) } err = faucet.AdminAddController(controlleraddr2) - if err != nil { + if err != "" { panic(err) } println(faucet.Render("")) diff --git a/examples/gno.land/r/gnoland/faucet/z3_filetest.gno b/examples/gno.land/r/gnoland/faucet/z3_filetest.gno index 1dca56811a9..682a619256a 100644 --- a/examples/gno.land/r/gnoland/faucet/z3_filetest.gno +++ b/examples/gno.land/r/gnoland/faucet/z3_filetest.gno @@ -18,21 +18,21 @@ func main() { ) std.TestSetOrigCaller(adminaddr) err := faucet.AdminAddController(controlleraddr1) - if err != nil { + if err != "" { panic(err) } err = faucet.AdminAddController(controlleraddr2) - if err != nil { + if err != "" { panic(err) } std.TestSetOrigCaller(controlleraddr1) err = faucet.Transfer(testaddr1, 1000000) - if err != nil { + if err != "" { panic(err) } std.TestSetOrigCaller(controlleraddr2) err = faucet.Transfer(testaddr1, 2000000) - if err != nil { + if err != "" { panic(err) } println(faucet.Render("")) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 3b483dedb37..6b77f5562a0 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1012,23 +1012,29 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { args1 = Preprocess(nil, last, args1).(Expr) n.Args[1] = args1 } - } - // Another special case for append: adding untyped constants. - // They must be converted to the array type for consistency. - for i, arg := range n.Args[1:] { - if _, ok := arg.(*ConstExpr); !ok { - // Consider only constant expressions. - continue - } - if t1 := evalStaticTypeOf(store, last, arg); t1 != nil && !isUntyped(t1) { - // Consider only untyped values (including nil). - continue + } else { + var tx *constTypeExpr // array type expr, lazily initialized + // Another special case for append: adding untyped constants. + // They must be converted to the array type for consistency. + for i, arg := range n.Args[1:] { + if _, ok := arg.(*ConstExpr); !ok { + // Consider only constant expressions. + continue + } + if t1 := evalStaticTypeOf(store, last, arg); t1 != nil && !isUntyped(t1) { + // Consider only untyped values (including nil). + continue + } + + if tx == nil { + // Get the array type from the first argument. + s0 := evalStaticTypeOf(store, last, n.Args[0]) + tx = constType(arg, s0.Elem()) + } + // Convert to the array type. + arg1 := Call(tx, arg) + n.Args[i+1] = Preprocess(nil, last, arg1).(Expr) } - // Get the array type from the first argument and convert to it. - s0 := evalStaticTypeOf(store, last, n.Args[0]) - tx := constType(arg, s0.Elem()) - arg1 := Call(tx, arg) - n.Args[i+1] = Preprocess(nil, last, arg1).(Expr) } } else if fv.PkgPath == uversePkgPath && fv.Name == "copy" { if len(n.Args) == 2 {