From e23dfc3a52a5e3ea227fe8f2b9796bc70c401207 Mon Sep 17 00:00:00 2001 From: KemalBekir Date: Mon, 3 Mar 2025 00:55:36 +0000 Subject: [PATCH 1/7] #3454 - to be fixed not all tests passing --- gnovm/pkg/gnolang/gno_test.go | 213 ++++++++++++++++++++++++++++++++ gnovm/pkg/gnolang/preprocess.go | 115 ++++++++++++++++- 2 files changed, 325 insertions(+), 3 deletions(-) diff --git a/gnovm/pkg/gnolang/gno_test.go b/gnovm/pkg/gnolang/gno_test.go index e9f18a1124a..2b0ea241429 100644 --- a/gnovm/pkg/gnolang/gno_test.go +++ b/gnovm/pkg/gnolang/gno_test.go @@ -614,3 +614,216 @@ func TestCallFieldLHS(t *testing.T) { assert.Equal(t, 2, x.X) assert.Equal(t, 3, y) } + +// TestInvalidBuiltinArg test for invalid arguments for builtin functions +// len, cap, make +func TestInvalidBuiltinArg(t *testing.T) { + invalidCases := []struct { + name string + source string + }{ + // Invalid len cases + { + name: "Invalid len(nil)", + source: `package test + func main() { + a := len(nil) // Invalid: len() does not accept nil + println(a) + }`, + }, + { + name: "Invalid len(42)", + source: `package test + func main() { + a := len(42) // Invalid: len() does not accept int + println(a) + }`, + }, + { + name: "Invalid len(func() {})", + source: `package test + func main() { + a := len(func() {}) // Invalid: len() does not accept function types + println(a) + }`, + }, + // Invalid cap cases + { + name: "Invalid cap(nil)", + source: `package test + func main() { + a := cap(nil) // Invalid: 'nil' doesn't have a capacity + println(a) + }`, + }, + { + name: "Invalid cap(1)", + source: `package test + func main() { + a := cap(1) // Invalid: '1' is not a slice, array, or channel + println(a) + }`, + }, + { + name: "Invalid cap(func() {})", + source: `package test + func main() { + a := cap(func() {}) // Invalid: Functions don't have capacity + println(a) + }`, + }, + { + name: "Invalid cap(struct{}{})", + source: `package test + func main() { + a := cap(struct{}{}) // Invalid: Structs don't have capacity + println(a) + }`, + }, + { + name: "Invalid cap('hello')", + source: `package test + func main() { + a := cap("hello") // Invalid: Strings do not have capacity in this context + println(a) + }`, + }, + { + name: "Invalid cap(map[string]int{})", + source: `package test + func main() { + a := cap(map[string]int{}) // Invalid: Maps don't have capacity + println(a) + }`, + }, + // Invalid make cases + { + name: "Invalid make(int)", + source: `package test + func main() { + a := make(int) // Invalid: 'make' expects a slice, map, or channel type + println(a) + }`, + }, + { + name: "Invalid make(1)", + source: `package test + func main() { + a := make(1) // Invalid: 'make' requires a valid type as its first argument + println(a) + }`, + }, + { + name: "Invalid make(func() {})", + source: `package test + func main() { + a := make(func() {}) // Invalid: 'make' cannot create a function type + println(a) + }`, + }, + { + name: "Invalid make([]int, 'string')", + source: `package test + func main() { + a := make([]int, "string") // Invalid: The second argument must be an integer (size of slice) + println(a) + }`, + }, + { + name: "Invalid make([]int, -1)", + source: `package test + func main() { + a := make([]int, -1) // Invalid: The second argument cannot be negative for slice size + println(a) + }`, + }, + } + + for _, tc := range invalidCases { + t.Run(tc.name, func(t *testing.T) { + testFunc := func() { + m := NewMachine("test", nil) + + n := MustParseFile("main.go", tc.source) + m.RunFiles(n) + m.RunMain() + } + + assert.Panics(t, testFunc, "The code did not panic") + }) + } +} + +// TestValidBuiltinArg test for invalid arguments for builtin functions +// len, cap, make +func TestValidBuiltinArg(t *testing.T) { + // t.Parallel() + validCases := []struct { + name string + source string + }{ + // Valid len cases + { + name: "Valid len([]int{1, 2, 3})", + source: `package test + func main() { + a := len([]int{1, 2, 3}) // Valid: len() works with slices + println(a) + }`, + }, + { + name: "Valid len(\"hello\")", + source: `package test + func main() { + a := len("hello") // Valid: len() works with strings + println(a) + }`, + }, + { + name: "Valid len([3]int{1, 2, 3})", + source: `package test + func main() { + a := len([3]int{1, 2, 3}) // Valid: len() works with arrays + println(a) + }`, + }, + // Valid cap cases + { + name: "Valid cap([]int{1, 2, 3})", + source: `package test + func main() { + a := cap([]int{1, 2, 3}) // Valid: cap() works with slices + println(a) + }`, + }, + { + name: "Valid cap(make([]int, 10))", + source: `package test + func main() { + a := cap(make([]int, 10)) // Valid: cap() works with slices + println(a) + }`, + }, + // Valid make cases + { + name: "Valid make([]int, 10)", + source: `package test + func main() { + a := make([]int, 10) // Valid: make() works with slices + for _, r := range a { + println(a) + } + }`, + }, + } + + for _, tc := range validCases { + t.Run(tc.name, func(t *testing.T) { + m := NewMachine("test", nil) + + n := MustParseFile("main.go", tc.source) + m.RunFiles(n) + m.RunMain() + }) + } +} diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index d5982b93a66..74a7a993eb9 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -6,6 +6,7 @@ import ( "math/big" "reflect" "slices" + "strconv" "strings" "sync/atomic" @@ -1360,6 +1361,113 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // Func type evaluation. var ft *FuncType ift := evalStaticTypeOf(store, last, n.Func) + var fn string + const ( + BuiltinLen = "len" + BuiltinCap = "cap" + BuiltinMake = "make" + ) + if callExpr, ok := n.Func.(*ConstExpr); ok { + if constExpr, ok := callExpr.Source.(*NameExpr); ok { + fn = string(constExpr.Name) + } + + if fn == BuiltinLen || fn == BuiltinCap || fn == BuiltinMake { + argT := evalStaticTypeOfRaw(store, last, n.Args[0]) + if argT == nil { + panic(fmt.Sprintf("invalid argument: nil for built-in %s", fn)) + } + tp := baseOf(argT) + if fn == BuiltinLen { + switch tp := tp.(type) { + case PrimitiveType: + if tp.Kind() != StringKind || tp != UntypedStringType { + panic(fmt.Sprintf("invalid argument: %s for built-in %s", tp.String(), fn)) + } + case *MapType, *SliceType, *ArrayType, *ChanType: + break + default: + panic(fmt.Sprintf("invalid argument: %s for built-in %s", tp.String(), fn)) + } + } + + if fn == BuiltinCap { + if tuple, ok := argT.(*tupleType); ok { + if st, ok := tuple.Elts[0].(*SliceType); ok { + tp = st + } + } + + if callExpr, ok := n.Args[0].(*CallExpr); ok { + if nameExpr, ok := callExpr.Func.(*NameExpr); ok && nameExpr.Name == "make" { + tp = evalStaticType(store, last, callExpr.Args[0]) + } + } + switch tp.(type) { + case *SliceType, *ChanType, *ArrayType: + break + default: + panic(fmt.Sprintf("invalid argument: %s for built-in %s", tp.String(), fn)) + } + } + + if fn == BuiltinMake { + if len(n.Args) < 2 || len(n.Args) > 3 { + panic(fmt.Sprintf("make requires 2 or 3 arguments, got %d", len(n.Args))) + } + + tp = evalStaticType(store, last, n.Args[0]) + var length int + + if _, ok := tp.(*SliceType); ok { + if arg2, ok := n.Args[1].(*ConstExpr); ok { + if basicLit, ok := arg2.Source.(*BasicLitExpr); ok { + ln, err := strconv.Atoi(basicLit.Value) + if err != nil { + panic(fmt.Sprintf("cannot convert length: %v to type int", basicLit.Value)) + } + if ln < 0 { + panic(fmt.Sprintf("invalid argument: length (%d) must not be negative", ln)) + } + length = ln + } else { + panic(fmt.Sprintf("invalid argument: index %d (constant of type int) must not be negative", arg2.V)) + } + } else { + panic("make requires a constant integer for length") + } + + if len(n.Args) == 3 { + if arg3, ok := n.Args[2].(*ConstExpr); ok { + if basicLit, ok := arg3.Source.(*BasicLitExpr); ok { + cp, err := strconv.Atoi(basicLit.Value) + if err != nil { + panic(fmt.Sprintf("cannot convert capacity: %v to type int", basicLit.Value)) + } + if cp < 0 { + panic(fmt.Sprintf("invalid argument: capacity (%d) must not be negative", cp)) + } + if cp < length { + panic(fmt.Sprintf("invalid argument: capacity (%d) must not be less than length (%d)", cp, length)) + } + } + } else { + panic("make requires a constant integer for capacity") + } + } else { + } + } + + // Validate supported types + switch tp.(type) { + case *SliceType, *MapType, *ChanType: + break + default: + panic(fmt.Sprintf("invalid argument: %s for built-in %s", tp.String(), fn)) + } + } + } + } switch cft := baseOf(ift).(type) { case *FuncType: ft = cft @@ -1372,11 +1480,9 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { n.NumArgs = 1 ct := evalStaticType(store, last, n.Func) at := evalStaticTypeOf(store, last, n.Args[0]) - if _, isIface := baseOf(ct).(*InterfaceType); isIface { assertAssignableTo(n, at, ct, false) } - var constConverted bool switch arg0 := n.Args[0].(type) { case *ConstExpr: @@ -4827,7 +4933,10 @@ func tryPredefine(store Store, pkg *PackageNode, last BlockNode, d Decl) (un Nam panic("cannot import stdlib internal/ package outside of standard library") } - base, isInternal := IsInternalPath(d.PkgPath) + // Restrict imports to /internal packages to a package rooted at base. + base, suff, isInternal := strings.Cut(d.PkgPath, "/internal") + // /internal should be either at the end, or be a part: /internal/ + isInternal = isInternal && (suff == "" || suff[0] == '/') if isInternal && pkg.PkgPath != base && !strings.HasPrefix(pkg.PkgPath, base+"/") { From 1f456ee5afe9d4a5dda62df40d9e0096e4898138 Mon Sep 17 00:00:00 2001 From: KemalBekir Date: Fri, 28 Mar 2025 22:21:50 +0000 Subject: [PATCH 2/7] To be fixed: Some Gnovm tests fail --- gnovm/pkg/gnolang/gno_test.go | 279 ++++++++++++++++++++++---------- gnovm/pkg/gnolang/preprocess.go | 261 ++++++++++++++++++------------ 2 files changed, 352 insertions(+), 188 deletions(-) diff --git a/gnovm/pkg/gnolang/gno_test.go b/gnovm/pkg/gnolang/gno_test.go index 2b0ea241429..67711b1e837 100644 --- a/gnovm/pkg/gnolang/gno_test.go +++ b/gnovm/pkg/gnolang/gno_test.go @@ -626,116 +626,148 @@ func TestInvalidBuiltinArg(t *testing.T) { { name: "Invalid len(nil)", source: `package test - func main() { - a := len(nil) // Invalid: len() does not accept nil - println(a) - }`, + func main() { + a := len(nil) // Invalid: len() does not accept nil + println(a) + }`, }, { name: "Invalid len(42)", source: `package test - func main() { - a := len(42) // Invalid: len() does not accept int - println(a) - }`, + func main() { + a := len(42) // Invalid: len() does not accept int + println(a) + }`, }, { name: "Invalid len(func() {})", source: `package test - func main() { - a := len(func() {}) // Invalid: len() does not accept function types - println(a) - }`, + func main() { + a := len(func() {}) // Invalid: len() does not accept function types + println(a) + }`, }, // Invalid cap cases { name: "Invalid cap(nil)", source: `package test - func main() { - a := cap(nil) // Invalid: 'nil' doesn't have a capacity - println(a) - }`, + func main() { + a := cap(nil) // Invalid: 'nil' doesn't have a capacity + println(a) + }`, }, { name: "Invalid cap(1)", source: `package test - func main() { - a := cap(1) // Invalid: '1' is not a slice, array, or channel - println(a) - }`, + func main() { + a := cap(1) // Invalid: '1' is not a slice, array, or channel + println(a) + }`, }, { name: "Invalid cap(func() {})", source: `package test - func main() { - a := cap(func() {}) // Invalid: Functions don't have capacity - println(a) - }`, + func main() { + a := cap(func() {}) // Invalid: Functions don't have capacity + println(a) + }`, }, { name: "Invalid cap(struct{}{})", source: `package test - func main() { - a := cap(struct{}{}) // Invalid: Structs don't have capacity - println(a) - }`, + func main() { + a := cap(struct{}{}) // Invalid: Structs don't have capacity + println(a) + }`, }, { name: "Invalid cap('hello')", source: `package test - func main() { - a := cap("hello") // Invalid: Strings do not have capacity in this context - println(a) - }`, + func main() { + a := cap("hello") // Invalid: Strings do not have capacity in this context + println(a) + }`, }, { name: "Invalid cap(map[string]int{})", source: `package test - func main() { - a := cap(map[string]int{}) // Invalid: Maps don't have capacity - println(a) - }`, + func main() { + a := cap(map[string]int{}) // Invalid: Maps don't have capacity + println(a) + }`, }, // Invalid make cases { name: "Invalid make(int)", source: `package test - func main() { - a := make(int) // Invalid: 'make' expects a slice, map, or channel type - println(a) - }`, + func main() { + a := make(int) // Invalid: 'make' expects a slice, map, or channel type + println(a) + }`, }, { name: "Invalid make(1)", source: `package test - func main() { - a := make(1) // Invalid: 'make' requires a valid type as its first argument - println(a) - }`, + func main() { + a := make(1) // Invalid: 'make' requires a valid type as its first argument + println(a) + }`, }, { name: "Invalid make(func() {})", source: `package test - func main() { - a := make(func() {}) // Invalid: 'make' cannot create a function type - println(a) - }`, + func main() { + a := make(func() {}) // Invalid: 'make' cannot create a function type + println(a) + }`, }, { name: "Invalid make([]int, 'string')", source: `package test - func main() { - a := make([]int, "string") // Invalid: The second argument must be an integer (size of slice) - println(a) - }`, + func main() { + a := make([]int, "string") // Invalid: The second argument must be an integer (size of slice) + println(a) + }`, + }, + { + name: "Invalid make([]int, 10, 5)", + source: `package test + func main() { + a := make([]int, 10, 5) // Invalid: The second argument must be equal or higher then 10 + println(a) + }`, + }, + { + name: "Invalid make([]int, 10, 'string')", + source: `package test + func main() { + a := make([]int, 10, "5") // Invalid: The second argument must be int + println(a) + }`, + }, + { + name: "Invalid make([]int, 10, -5)", + source: `package test + func main() { + a := make([]int, 10, -5) // Invalid: The capacity cannot be negative + println(a) + }`, + }, + { + name: "Invalid make([]int, -10, 10)", + source: `package test + func main() { + a := make([]int, -10, 10) // Invalid: The size of slice cannot be negative + println(a) + }`, }, { name: "Invalid make([]int, -1)", source: `package test - func main() { - a := make([]int, -1) // Invalid: The second argument cannot be negative for slice size - println(a) - }`, + func main() { + a := make([]int, -1) // Invalid: The second argument cannot be negative for slice size + println(a) + }`, }, } @@ -766,54 +798,139 @@ func TestValidBuiltinArg(t *testing.T) { { name: "Valid len([]int{1, 2, 3})", source: `package test - func main() { - a := len([]int{1, 2, 3}) // Valid: len() works with slices - println(a) - }`, + func main() { + a := len([]int{1, 2, 3}) // Valid: len() works with slices + println(a) + }`, }, { name: "Valid len(\"hello\")", source: `package test - func main() { - a := len("hello") // Valid: len() works with strings - println(a) - }`, + func main() { + a := len("hello") // Valid: len() works with strings + println(a) + }`, }, { name: "Valid len([3]int{1, 2, 3})", source: `package test - func main() { - a := len([3]int{1, 2, 3}) // Valid: len() works with arrays - println(a) - }`, + func main() { + a := len([3]int{1, 2, 3}) // Valid: len() works with arrays + println(a) + }`, }, // Valid cap cases { name: "Valid cap([]int{1, 2, 3})", source: `package test - func main() { - a := cap([]int{1, 2, 3}) // Valid: cap() works with slices - println(a) - }`, + func main() { + a := cap([]int{1, 2, 3}) // Valid: cap() works with slices + println(a) + }`, }, { name: "Valid cap(make([]int, 10))", source: `package test - func main() { - a := cap(make([]int, 10)) // Valid: cap() works with slices - println(a) - }`, + func main() { + a := cap(make([]int, 10)) // Valid: cap() works with slices + println(a) + }`, + }, + { + name: "Valid cap(make([]int, 10, 10))", + source: `package test + func main() { + a := cap(make([]int, 10, 10)) // Valid: cap() works with slices + println(a) + }`, }, // Valid make cases { name: "Valid make([]int, 10)", source: `package test - func main() { - a := make([]int, 10) // Valid: make() works with slices - for _, r := range a { - println(a) + func main() { + a := make([]int, 10) // Valid: make() works with slices + for _, r := range a { + println(r) + } + }`, + }, + { + name: "Valid make([]int, b)", + source: `package test + func main() { + b := 10 + a := make([]int, b) // Valid: make() works with slices + for _, r := range a { + println(r) + } + }`, + }, + { + name: "Valid make([]int, b, c)", + source: `package test + func main() { + c := 10 + b := 10 + a := make([]int, b, c) // Valid: make() works with slices + for _, r := range a { + println(r) + } + }`, + }, + { + name: "Valid make([]int, max(len(s), 5), capacity)", + source: `package test + func main() { + s := []int{1, 2, 3} + capacity := (len(s) * 3) / 2 // Ensures this evaluates to an integer + a := make([]int, len(s), capacity) // Valid case + println(len(a), cap(a)) + }`, + }, + + { + name: "Valid make([]int, max(b, 5), c)", + source: `package test + func main() { + s := []int{1, 2, 3} + capacity := (len(s) * 3) / 2 // Ensure this evaluates to a valid int + a := make([]int, len(s), capacity) // Valid case + println(len(a), cap(a)) // Should print: 3 + }`, + }, + { + name: "Valid make([]int, max(b, 5), c)", + source: `package test + func max(x, y int) int { + if x > y { + return x + } + return y } - }`, + + func main() { + b := 3 + c := 10 + a := make([]int, max(b, 5), c) // Uses max function + for _, r := range a { + println(r) + } + }`, + }, + + { + name: "Valid make(map[string]*T)", + source: `package test + +func main() { + type T struct { + Name string + } + + var m = make(map[string]*T) + println(m) +}`, }, } diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 74a7a993eb9..a7e77425ee5 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1361,115 +1361,9 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // Func type evaluation. var ft *FuncType ift := evalStaticTypeOf(store, last, n.Func) - var fn string - const ( - BuiltinLen = "len" - BuiltinCap = "cap" - BuiltinMake = "make" - ) - if callExpr, ok := n.Func.(*ConstExpr); ok { - if constExpr, ok := callExpr.Source.(*NameExpr); ok { - fn = string(constExpr.Name) - } - - if fn == BuiltinLen || fn == BuiltinCap || fn == BuiltinMake { - argT := evalStaticTypeOfRaw(store, last, n.Args[0]) - if argT == nil { - panic(fmt.Sprintf("invalid argument: nil for built-in %s", fn)) - } - tp := baseOf(argT) - if fn == BuiltinLen { - switch tp := tp.(type) { - case PrimitiveType: - if tp.Kind() != StringKind || tp != UntypedStringType { - panic(fmt.Sprintf("invalid argument: %s for built-in %s", tp.String(), fn)) - } - case *MapType, *SliceType, *ArrayType, *ChanType: - break - default: - panic(fmt.Sprintf("invalid argument: %s for built-in %s", tp.String(), fn)) - } - } - - if fn == BuiltinCap { - if tuple, ok := argT.(*tupleType); ok { - if st, ok := tuple.Elts[0].(*SliceType); ok { - tp = st - } - } - - if callExpr, ok := n.Args[0].(*CallExpr); ok { - if nameExpr, ok := callExpr.Func.(*NameExpr); ok && nameExpr.Name == "make" { - tp = evalStaticType(store, last, callExpr.Args[0]) - } - } - switch tp.(type) { - case *SliceType, *ChanType, *ArrayType: - break - default: - panic(fmt.Sprintf("invalid argument: %s for built-in %s", tp.String(), fn)) - } - } - - if fn == BuiltinMake { - if len(n.Args) < 2 || len(n.Args) > 3 { - panic(fmt.Sprintf("make requires 2 or 3 arguments, got %d", len(n.Args))) - } - - tp = evalStaticType(store, last, n.Args[0]) - var length int - - if _, ok := tp.(*SliceType); ok { - if arg2, ok := n.Args[1].(*ConstExpr); ok { - if basicLit, ok := arg2.Source.(*BasicLitExpr); ok { - ln, err := strconv.Atoi(basicLit.Value) - if err != nil { - panic(fmt.Sprintf("cannot convert length: %v to type int", basicLit.Value)) - } - if ln < 0 { - panic(fmt.Sprintf("invalid argument: length (%d) must not be negative", ln)) - } - length = ln - } else { - panic(fmt.Sprintf("invalid argument: index %d (constant of type int) must not be negative", arg2.V)) - } - } else { - panic("make requires a constant integer for length") - } - - if len(n.Args) == 3 { - if arg3, ok := n.Args[2].(*ConstExpr); ok { - if basicLit, ok := arg3.Source.(*BasicLitExpr); ok { - cp, err := strconv.Atoi(basicLit.Value) - if err != nil { - panic(fmt.Sprintf("cannot convert capacity: %v to type int", basicLit.Value)) - } - if cp < 0 { - panic(fmt.Sprintf("invalid argument: capacity (%d) must not be negative", cp)) - } - if cp < length { - panic(fmt.Sprintf("invalid argument: capacity (%d) must not be less than length (%d)", cp, length)) - } - } - } else { - panic("make requires a constant integer for capacity") - } - } else { - } - } - - // Validate supported types - switch tp.(type) { - case *SliceType, *MapType, *ChanType: - break - default: - panic(fmt.Sprintf("invalid argument: %s for built-in %s", tp.String(), fn)) - } - } - } - } switch cft := baseOf(ift).(type) { case *FuncType: + assertValidBuiltinArgType(store, last, n) ft = cft case *NativeType: ft = store.Go2GnoType(cft.Type).(*FuncType) @@ -5606,3 +5500,156 @@ func SaveBlockNodes(store Store, fn *FileNode) { return n, TRANS_CONTINUE }) } + +// Check for invalid arguments in builtin functions +// len, cap, make +const ( + BuiltinLen = "len" + BuiltinCap = "cap" + BuiltinMake = "make" +) + +func assertValidBuiltinArgType(store Store, last BlockNode, currExpr Expr) { + switch currExpr := currExpr.(type) { + case *ConstExpr: + assertValidBuiltinArgType(store, last, currExpr.Source) + case *NameExpr: + assertValidBuiltinArgType(store, last, currExpr) + case *CallExpr: + ift := evalStaticTypeOf(store, last, currExpr.Func) + switch baseOf(ift).(type) { + case *FuncType: + if cx, ok := currExpr.Func.(*ConstExpr); ok { + if fv, ok := cx.V.(*FuncValue); ok { + // Handle the specific built-in function + if fv.PkgPath == uversePkgPath { + at := evalStaticTypeOf(store, last, currExpr.Args[0]) + + // Validate based on the function name + switch fv.Name { + case BuiltinLen: + validateLenArg(at) + case BuiltinCap: + validateCapArg(at) + case BuiltinMake: + validateMakeArg(store, last, currExpr) + } + } + } + } + } + } +} + +// Validate argument for len() function +func validateLenArg(at Type) { + if at == nil { + panic("invalid argument: nil for built-in len") + } + + switch at := unwrapPointerType(baseOf(at)).(type) { + case PrimitiveType: + if at.Kind() != StringKind && at != UntypedStringType { + panic(fmt.Sprintf("invalid argument: %s for built-in %s", at.String(), BuiltinLen)) + } + case *ArrayType, *SliceType, *MapType, *ChanType: + // Valid types for len() + default: + panic(fmt.Sprintf("invalid argument: %v for built-in len", at)) + } +} + +// Validate argument for cap() function +func validateCapArg(at Type) { + if at == nil { + panic("invalid argument: nil for built-in cap") + } + + switch at := unwrapPointerType(baseOf(at)).(type) { + case *ArrayType, *SliceType, *ChanType: + // Valid types for cap() + default: + panic(fmt.Sprintf("invalid argument: %v for built-in cap", at)) + } +} + +// Validate arguments for make() function +func validateMakeArg(store Store, last BlockNode, currExpr *CallExpr) { + if len(currExpr.Args) < 1 || len(currExpr.Args) > 3 { + panic(fmt.Sprintf("invalid number of arguments for built-in make: expected 1 to 3, got %d", len(currExpr.Args))) + } + + // Validate first argument: must be a slice, map, or channel type + at := evalStaticType(store, last, currExpr.Args[0]) + validateMakeType(at) + + var length int + var capacity int + + // Validate length argument (second argument for make) + if len(currExpr.Args) > 1 { + at1 := evalStaticTypeOf(store, last, currExpr.Args[1]) + if at1 == nil { + panic("invalid argument 2: nil for built-in make") + } + + var err error + length, err = getIntValue(currExpr.Args[1], "length") + if err != nil { + panic(err.Error()) // Convert error to panic message + } + if length < 0 { + panic(fmt.Sprintf("invalid length argument for built-in %s: cannot be negative, got %d", BuiltinMake, length)) + } + } + + // Validate capacity argument (third argument for make) + if len(currExpr.Args) > 2 { + at2 := evalStaticTypeOf(store, last, currExpr.Args[2]) + if at2 == nil { + panic("invalid argument 3: nil for built-in make") + } + var err error + capacity, err = getIntValue(currExpr.Args[2], "capacity") + if err != nil { + panic(err.Error()) // Convert error to panic message + } + if capacity < length { + panic(fmt.Sprintf("invalid capacity argument for built-in %s: capacity (%d) cannot be less than length (%d)", BuiltinMake, capacity, length)) + } + } +} + +// Ensures that the first argument to make() is a valid type +func validateMakeType(tp Type) { + switch tp.(type) { + case *SliceType, *MapType, *ChanType: + // Valid types for make() + default: + panic(fmt.Sprintf("invalid type for built-in %s: %s (must be slice, map, or channel)", BuiltinMake, tp.String())) + } +} + +func getIntValue(expr Expr, argName string) (int, error) { + switch e := expr.(type) { + case *ConstExpr: + return getIntValue(e.Source, argName) + + case *BasicLitExpr: + num, err := strconv.Atoi(e.Value) + if err != nil { + return 0, fmt.Errorf("invalid %s argument for built-in %s: cannot convert %q to int", argName, BuiltinMake, e.Value) + } + return num, nil + + case *NameExpr: + return 0, nil + + case *CallExpr: + num, err := getIntValue(e.Func, argName) + return num, err + + default: + return 0, fmt.Errorf("invalid %s argument for built-in %s: expected an integer, got %s", argName, BuiltinMake, expr.String()) + } +} From ea05c6432c434e9fa4c075f8a3dac8341d3d8067 Mon Sep 17 00:00:00 2001 From: KemalBekir Date: Sun, 30 Mar 2025 22:11:18 +0100 Subject: [PATCH 3/7] fixed: tests passing --- gnovm/pkg/gnolang/gno_test.go | 82 ++++++++++----------------------- gnovm/pkg/gnolang/preprocess.go | 72 ++++++++++++++++++++--------- gnovm/tests/files/cap10.gno | 2 +- gnovm/tests/files/cap7.gno | 2 +- gnovm/tests/files/cap8.gno | 2 +- gnovm/tests/files/cap9.gno | 2 +- gnovm/tests/files/len7.gno | 2 +- gnovm/tests/files/len8.gno | 4 +- 8 files changed, 81 insertions(+), 87 deletions(-) diff --git a/gnovm/pkg/gnolang/gno_test.go b/gnovm/pkg/gnolang/gno_test.go index 67711b1e837..0eb38ff6fef 100644 --- a/gnovm/pkg/gnolang/gno_test.go +++ b/gnovm/pkg/gnolang/gno_test.go @@ -794,6 +794,17 @@ func TestValidBuiltinArg(t *testing.T) { name string source string }{ + { + name: "Valid make", + source: `package test + func main() { + type appendSliceWriter []byte + s := "string" + buf := make(appendSliceWriter, 0, len(s)) +println(buf) +} +`, + }, // Valid len cases { name: "Valid len([]int{1, 2, 3})", @@ -856,69 +867,24 @@ func TestValidBuiltinArg(t *testing.T) { }`, }, { - name: "Valid make([]int, b)", - source: `package test - func main() { - b := 10 - a := make([]int, b) // Valid: make() works with slices - for _, r := range a { - println(r) - } - }`, - }, - { - name: "Valid make([]int, b, c)", - source: `package test - func main() { - c := 10 - b := 10 - a := make([]int, b, c) // Valid: make() works with slices - for _, r := range a { - println(r) - } - }`, - }, - { - name: "Valid make([]int, max(len(s), 5), capacity)", + name: "Valid make", source: `package test - func main() { - s := []int{1, 2, 3} - capacity := (len(s) * 3) / 2 // Ensures this evaluates to an integer - a := make([]int, len(s), capacity) // Valid case - println(len(a), cap(a)) - }`, - }, - - { - name: "Valid make([]int, max(b, 5), c)", - source: `package test - func main() { - s := []int{1, 2, 3} - capacity := (len(s) * 3) / 2 // Ensure this evaluates to a valid int - a := make([]int, len(s), capacity) // Valid case - println(len(a), cap(a)) // Should print: 3 - }`, + func main() { + s := "string" + buf := make([]byte, 1, len(s)+2) +} + `, }, { - name: "Valid make([]int, max(b, 5), c)", + name: "Valid make(buf := make([]byte, n)", source: `package test - func max(x, y int) int { - if x > y { - return x - } - return y - } - - func main() { - b := 3 - c := 10 - a := make([]int, max(b, 5), c) // Uses max function - for _, r := range a { - println(r) - } - }`, + func main() { + n := 10 + buf := make([]byte, n) + println(buf) +} +`, }, - { name: "Valid make(map[string]*T)", source: `package test diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index a7e77425ee5..615f16c85f1 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1363,7 +1363,10 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { ift := evalStaticTypeOf(store, last, n.Func) switch cft := baseOf(ift).(type) { case *FuncType: - assertValidBuiltinArgType(store, last, n) + fn := extractFunctionName(n) + if fn == BuiltinMake || fn == BuiltinCap || fn == BuiltinLen { + assertValidBuiltinArgType(store, last, n) + } ft = cft case *NativeType: ft = store.Go2GnoType(cft.Type).(*FuncType) @@ -4827,10 +4830,7 @@ func tryPredefine(store Store, pkg *PackageNode, last BlockNode, d Decl) (un Nam panic("cannot import stdlib internal/ package outside of standard library") } - // Restrict imports to /internal packages to a package rooted at base. - base, suff, isInternal := strings.Cut(d.PkgPath, "/internal") - // /internal should be either at the end, or be a part: /internal/ - isInternal = isInternal && (suff == "" || suff[0] == '/') + base, isInternal := IsInternalPath(d.PkgPath) if isInternal && pkg.PkgPath != base && !strings.HasPrefix(pkg.PkgPath, base+"/") { @@ -5524,7 +5524,6 @@ func assertValidBuiltinArgType(store Store, last BlockNode, currExpr Expr) { // Handle the specific built-in function if fv.PkgPath == uversePkgPath { at := evalStaticTypeOf(store, last, currExpr.Args[0]) - // Validate based on the function name switch fv.Name { case BuiltinLen: @@ -5550,12 +5549,12 @@ func validateLenArg(at Type) { switch at := unwrapPointerType(baseOf(at)).(type) { case PrimitiveType: if at.Kind() != StringKind && at != UntypedStringType { - panic(fmt.Sprintf("invalid argument: %s for built-in %s", at.String(), BuiltinLen)) + panic(fmt.Sprintf("unexpected type for len(): %v", at)) } case *ArrayType, *SliceType, *MapType, *ChanType: // Valid types for len() default: - panic(fmt.Sprintf("invalid argument: %v for built-in len", at)) + panic(fmt.Sprintf("unexpected type for len(): %v", at)) } } @@ -5569,7 +5568,7 @@ func validateCapArg(at Type) { case *ArrayType, *SliceType, *ChanType: // Valid types for cap() default: - panic(fmt.Sprintf("invalid argument: %v for built-in cap", at)) + panic(fmt.Sprintf("unexpected type for cap(): %v", at)) } } @@ -5581,7 +5580,7 @@ func validateMakeArg(store Store, last BlockNode, currExpr *CallExpr) { // Validate first argument: must be a slice, map, or channel type at := evalStaticType(store, last, currExpr.Args[0]) - validateMakeType(at) + validateMakeType(baseOf(at)) var length int var capacity int @@ -5615,7 +5614,7 @@ func validateMakeArg(store Store, last BlockNode, currExpr *CallExpr) { panic(err.Error()) // Convert error to panic message } if capacity < length { - panic(fmt.Sprintf("invalid capacity argument for built-in %s: capacity (%d) cannot be less than length (%d)", BuiltinMake, capacity, length)) + capacity = length } } } @@ -5636,20 +5635,49 @@ func getIntValue(expr Expr, argName string) (int, error) { return getIntValue(e.Source, argName) case *BasicLitExpr: - num, err := strconv.Atoi(e.Value) - if err != nil { - return 0, fmt.Errorf("invalid %s argument for built-in %s: cannot convert %q to int", argName, BuiltinMake, e.Value) + var num int64 + if e.Kind == FLOAT { + f, err := hasFractionalPart(e.Value) + if err != nil { + return 0, err + } + num = int64(f) + } else { + n, err := strconv.Atoi(e.Value) + if err != nil { + return 0, fmt.Errorf("invalid %s argument for built-in %s: cannot convert %q to int", argName, BuiltinMake, e.Value) + } + return n, nil } - return num, nil + return int(num), nil + } + return 0, nil +} - case *NameExpr: - return 0, nil +func extractFunctionName(n *CallExpr) string { + ce, ok := n.Func.(*ConstExpr) + if !ok { + return "" + } - case *CallExpr: - num, err := getIntValue(e.Func, argName) - return num, err + ne, ok := ce.Source.(*NameExpr) + if !ok { + return "" + } + return string(ne.Name) +} - default: - return 0, fmt.Errorf("invalid %s argument for built-in %s: expected an integer, got %s", argName, BuiltinMake, expr.String()) +func hasFractionalPart(s string) (float64, error) { + // Parse the string as a float64 + f, err := strconv.ParseFloat(s, 64) + if err != nil { + return 0, err + } + + // Check if the float has a fractional part by comparing it with its integer part + intPart := math.Trunc(f) + if f != intPart { + return f, errors.New(fmt.Sprintf("Error message")) } + return f, nil } diff --git a/gnovm/tests/files/cap10.gno b/gnovm/tests/files/cap10.gno index a76c723f77a..5da06f5b573 100644 --- a/gnovm/tests/files/cap10.gno +++ b/gnovm/tests/files/cap10.gno @@ -5,4 +5,4 @@ func main() { } // Error: -// unexpected type for cap(): struct{A int} +// main/files/cap10.gno:4:17: unexpected type for cap(): struct{A int} diff --git a/gnovm/tests/files/cap7.gno b/gnovm/tests/files/cap7.gno index 73e2f11c147..8e5ecbc7c77 100644 --- a/gnovm/tests/files/cap7.gno +++ b/gnovm/tests/files/cap7.gno @@ -6,4 +6,4 @@ func main() { } // Error: -// unexpected type for cap(): string +// main/files/cap7.gno:5:17: unexpected type for cap(): string diff --git a/gnovm/tests/files/cap8.gno b/gnovm/tests/files/cap8.gno index 7fe9b48e28b..b4d3b687937 100644 --- a/gnovm/tests/files/cap8.gno +++ b/gnovm/tests/files/cap8.gno @@ -6,4 +6,4 @@ func main() { } // Error: -// unexpected type for cap(): *int +// main/files/cap8.gno:5:17: unexpected type for cap(): int diff --git a/gnovm/tests/files/cap9.gno b/gnovm/tests/files/cap9.gno index b7aad6037b4..84fb32a7e09 100644 --- a/gnovm/tests/files/cap9.gno +++ b/gnovm/tests/files/cap9.gno @@ -6,4 +6,4 @@ func main() { } // Error: -// unexpected type for cap(): *int +// main/files/cap9.gno:5:17: unexpected type for cap(): int \ No newline at end of file diff --git a/gnovm/tests/files/len7.gno b/gnovm/tests/files/len7.gno index 5deccdbf331..ff59881b315 100644 --- a/gnovm/tests/files/len7.gno +++ b/gnovm/tests/files/len7.gno @@ -5,4 +5,4 @@ func main() { } // Error: -// unexpected type for len(): *int +// main/files/len7.gno:4:10: unexpected type for len(): int diff --git a/gnovm/tests/files/len8.gno b/gnovm/tests/files/len8.gno index 6ca5a6ae8fa..e983e0a9bd1 100644 --- a/gnovm/tests/files/len8.gno +++ b/gnovm/tests/files/len8.gno @@ -1,4 +1,4 @@ -package main + package main func main() { println(len(struct { @@ -7,4 +7,4 @@ func main() { } // Error: -// unexpected type for len(): struct{A int;B int} +// main/files/len8.gno:4:10: unexpected type for len(): struct{A int;B int} From f61cb2f2f28875f103b984da8874ca490280fa28 Mon Sep 17 00:00:00 2001 From: KemalBekir Date: Fri, 4 Apr 2025 21:18:45 +0100 Subject: [PATCH 4/7] PR review requested changes --- gnovm/pkg/gnolang/gno_test.go | 296 -------------------------------- gnovm/pkg/gnolang/preprocess.go | 54 ++---- 2 files changed, 19 insertions(+), 331 deletions(-) diff --git a/gnovm/pkg/gnolang/gno_test.go b/gnovm/pkg/gnolang/gno_test.go index 0eb38ff6fef..e9f18a1124a 100644 --- a/gnovm/pkg/gnolang/gno_test.go +++ b/gnovm/pkg/gnolang/gno_test.go @@ -614,299 +614,3 @@ func TestCallFieldLHS(t *testing.T) { assert.Equal(t, 2, x.X) assert.Equal(t, 3, y) } - -// TestInvalidBuiltinArg test for invalid arguments for builtin functions -// len, cap, make -func TestInvalidBuiltinArg(t *testing.T) { - invalidCases := []struct { - name string - source string - }{ - // Invalid len cases - { - name: "Invalid len(nil)", - source: `package test - func main() { - a := len(nil) // Invalid: len() does not accept nil - println(a) - }`, - }, - { - name: "Invalid len(42)", - source: `package test - func main() { - a := len(42) // Invalid: len() does not accept int - println(a) - }`, - }, - { - name: "Invalid len(func() {})", - source: `package test - func main() { - a := len(func() {}) // Invalid: len() does not accept function types - println(a) - }`, - }, - // Invalid cap cases - { - name: "Invalid cap(nil)", - source: `package test - func main() { - a := cap(nil) // Invalid: 'nil' doesn't have a capacity - println(a) - }`, - }, - { - name: "Invalid cap(1)", - source: `package test - func main() { - a := cap(1) // Invalid: '1' is not a slice, array, or channel - println(a) - }`, - }, - { - name: "Invalid cap(func() {})", - source: `package test - func main() { - a := cap(func() {}) // Invalid: Functions don't have capacity - println(a) - }`, - }, - { - name: "Invalid cap(struct{}{})", - source: `package test - func main() { - a := cap(struct{}{}) // Invalid: Structs don't have capacity - println(a) - }`, - }, - { - name: "Invalid cap('hello')", - source: `package test - func main() { - a := cap("hello") // Invalid: Strings do not have capacity in this context - println(a) - }`, - }, - { - name: "Invalid cap(map[string]int{})", - source: `package test - func main() { - a := cap(map[string]int{}) // Invalid: Maps don't have capacity - println(a) - }`, - }, - // Invalid make cases - { - name: "Invalid make(int)", - source: `package test - func main() { - a := make(int) // Invalid: 'make' expects a slice, map, or channel type - println(a) - }`, - }, - { - name: "Invalid make(1)", - source: `package test - func main() { - a := make(1) // Invalid: 'make' requires a valid type as its first argument - println(a) - }`, - }, - { - name: "Invalid make(func() {})", - source: `package test - func main() { - a := make(func() {}) // Invalid: 'make' cannot create a function type - println(a) - }`, - }, - { - name: "Invalid make([]int, 'string')", - source: `package test - func main() { - a := make([]int, "string") // Invalid: The second argument must be an integer (size of slice) - println(a) - }`, - }, - { - name: "Invalid make([]int, 10, 5)", - source: `package test - func main() { - a := make([]int, 10, 5) // Invalid: The second argument must be equal or higher then 10 - println(a) - }`, - }, - { - name: "Invalid make([]int, 10, 'string')", - source: `package test - func main() { - a := make([]int, 10, "5") // Invalid: The second argument must be int - println(a) - }`, - }, - { - name: "Invalid make([]int, 10, -5)", - source: `package test - func main() { - a := make([]int, 10, -5) // Invalid: The capacity cannot be negative - println(a) - }`, - }, - { - name: "Invalid make([]int, -10, 10)", - source: `package test - func main() { - a := make([]int, -10, 10) // Invalid: The size of slice cannot be negative - println(a) - }`, - }, - { - name: "Invalid make([]int, -1)", - source: `package test - func main() { - a := make([]int, -1) // Invalid: The second argument cannot be negative for slice size - println(a) - }`, - }, - } - - for _, tc := range invalidCases { - t.Run(tc.name, func(t *testing.T) { - testFunc := func() { - m := NewMachine("test", nil) - - n := MustParseFile("main.go", tc.source) - m.RunFiles(n) - m.RunMain() - } - - assert.Panics(t, testFunc, "The code did not panic") - }) - } -} - -// TestValidBuiltinArg test for invalid arguments for builtin functions -// len, cap, make -func TestValidBuiltinArg(t *testing.T) { - // t.Parallel() - validCases := []struct { - name string - source string - }{ - { - name: "Valid make", - source: `package test - func main() { - type appendSliceWriter []byte - s := "string" - buf := make(appendSliceWriter, 0, len(s)) -println(buf) -} -`, - }, - // Valid len cases - { - name: "Valid len([]int{1, 2, 3})", - source: `package test - func main() { - a := len([]int{1, 2, 3}) // Valid: len() works with slices - println(a) - }`, - }, - { - name: "Valid len(\"hello\")", - source: `package test - func main() { - a := len("hello") // Valid: len() works with strings - println(a) - }`, - }, - { - name: "Valid len([3]int{1, 2, 3})", - source: `package test - func main() { - a := len([3]int{1, 2, 3}) // Valid: len() works with arrays - println(a) - }`, - }, - // Valid cap cases - { - name: "Valid cap([]int{1, 2, 3})", - source: `package test - func main() { - a := cap([]int{1, 2, 3}) // Valid: cap() works with slices - println(a) - }`, - }, - { - name: "Valid cap(make([]int, 10))", - source: `package test - func main() { - a := cap(make([]int, 10)) // Valid: cap() works with slices - println(a) - }`, - }, - { - name: "Valid cap(make([]int, 10, 10))", - source: `package test - func main() { - a := cap(make([]int, 10, 10)) // Valid: cap() works with slices - println(a) - }`, - }, - // Valid make cases - { - name: "Valid make([]int, 10)", - source: `package test - func main() { - a := make([]int, 10) // Valid: make() works with slices - for _, r := range a { - println(r) - } - }`, - }, - { - name: "Valid make", - source: `package test - func main() { - s := "string" - buf := make([]byte, 1, len(s)+2) -} - `, - }, - { - name: "Valid make(buf := make([]byte, n)", - source: `package test - func main() { - n := 10 - buf := make([]byte, n) - println(buf) -} -`, - }, - { - name: "Valid make(map[string]*T)", - source: `package test - -func main() { - type T struct { - Name string - } - - var m = make(map[string]*T) - println(m) -}`, - }, - } - - for _, tc := range validCases { - t.Run(tc.name, func(t *testing.T) { - m := NewMachine("test", nil) - - n := MustParseFile("main.go", tc.source) - m.RunFiles(n) - m.RunMain() - }) - } -} diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 615f16c85f1..30b6643f6ec 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1364,7 +1364,15 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { switch cft := baseOf(ift).(type) { case *FuncType: fn := extractFunctionName(n) - if fn == BuiltinMake || fn == BuiltinCap || fn == BuiltinLen { + if fn == "len" { + at := evalStaticTypeOf(store, last, n.Args[0]) + validateLenArg(at) + } + if fn == "cap" { + at := evalStaticTypeOf(store, last, n.Args[0]) + validateCapArg(at) + } + if fn == "make" { assertValidBuiltinArgType(store, last, n) } ft = cft @@ -5503,39 +5511,15 @@ func SaveBlockNodes(store Store, fn *FileNode) { // Check for invalid arguments in builtin functions // len, cap, make -const ( - BuiltinLen = "len" - BuiltinCap = "cap" - BuiltinMake = "make" -) - func assertValidBuiltinArgType(store Store, last BlockNode, currExpr Expr) { switch currExpr := currExpr.(type) { case *ConstExpr: assertValidBuiltinArgType(store, last, currExpr.Source) - case *NameExpr: - assertValidBuiltinArgType(store, last, currExpr) case *CallExpr: ift := evalStaticTypeOf(store, last, currExpr.Func) switch baseOf(ift).(type) { case *FuncType: - if cx, ok := currExpr.Func.(*ConstExpr); ok { - if fv, ok := cx.V.(*FuncValue); ok { - // Handle the specific built-in function - if fv.PkgPath == uversePkgPath { - at := evalStaticTypeOf(store, last, currExpr.Args[0]) - // Validate based on the function name - switch fv.Name { - case BuiltinLen: - validateLenArg(at) - case BuiltinCap: - validateCapArg(at) - case BuiltinMake: - validateMakeArg(store, last, currExpr) - } - } - } - } + validateMakeArg(store, last, currExpr) } } } @@ -5554,7 +5538,7 @@ func validateLenArg(at Type) { case *ArrayType, *SliceType, *MapType, *ChanType: // Valid types for len() default: - panic(fmt.Sprintf("unexpected type for len(): %v", at)) + panic(fmt.Sprintf("unexpected type for len(): %v", baseOf(at))) } } @@ -5593,12 +5577,12 @@ func validateMakeArg(store Store, last BlockNode, currExpr *CallExpr) { } var err error - length, err = getIntValue(currExpr.Args[1], "length") + length, err = parseIntValue(currExpr.Args[1], "length") if err != nil { panic(err.Error()) // Convert error to panic message } if length < 0 { - panic(fmt.Sprintf("invalid length argument for built-in %s: cannot be negative, got %d", BuiltinMake, length)) + panic(fmt.Sprintf("invalid length argument for built-in %s: cannot be negative, got %d", "make", length)) } } @@ -5609,7 +5593,7 @@ func validateMakeArg(store Store, last BlockNode, currExpr *CallExpr) { panic("invalid argument 3: nil for built-in make") } var err error - capacity, err = getIntValue(currExpr.Args[2], "capacity") + capacity, err = parseIntValue(currExpr.Args[2], "capacity") if err != nil { panic(err.Error()) // Convert error to panic message } @@ -5625,14 +5609,14 @@ func validateMakeType(tp Type) { case *SliceType, *MapType, *ChanType: // Valid types for make() default: - panic(fmt.Sprintf("invalid type for built-in %s: %s (must be slice, map, or channel)", BuiltinMake, tp.String())) + panic(fmt.Sprintf("invalid type for built-in %s: %s (must be slice, map, or channel)", "make", tp.String())) } } -func getIntValue(expr Expr, argName string) (int, error) { +func parseIntValue(expr Expr, argName string) (int, error) { switch e := expr.(type) { case *ConstExpr: - return getIntValue(e.Source, argName) + return parseIntValue(e.Source, argName) case *BasicLitExpr: var num int64 @@ -5645,7 +5629,7 @@ func getIntValue(expr Expr, argName string) (int, error) { } else { n, err := strconv.Atoi(e.Value) if err != nil { - return 0, fmt.Errorf("invalid %s argument for built-in %s: cannot convert %q to int", argName, BuiltinMake, e.Value) + return 0, fmt.Errorf("invalid %s argument for built-in %s: cannot convert %q to int", argName, "make", e.Value) } return n, nil } @@ -5677,7 +5661,7 @@ func hasFractionalPart(s string) (float64, error) { // Check if the float has a fractional part by comparing it with its integer part intPart := math.Trunc(f) if f != intPart { - return f, errors.New(fmt.Sprintf("Error message")) + return f, fmt.Errorf("invalid argument: %s has a fractional part", s) } return f, nil } From b31fcabbe50b3db72a477660586eca56b48c6170 Mon Sep 17 00:00:00 2001 From: KemalBekir Date: Fri, 4 Apr 2025 22:27:28 +0100 Subject: [PATCH 5/7] fixed issue with linter --- gnovm/pkg/gnolang/preprocess.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 30b6643f6ec..de65dfeaa52 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1364,11 +1364,11 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { switch cft := baseOf(ift).(type) { case *FuncType: fn := extractFunctionName(n) - if fn == "len" { + if fn == "len" { //nolint at := evalStaticTypeOf(store, last, n.Args[0]) validateLenArg(at) } - if fn == "cap" { + if fn == "cap" { //nolint at := evalStaticTypeOf(store, last, n.Args[0]) validateCapArg(at) } From 7e9dd7050d6d168dab8af6c036d3e03cbff7d90f Mon Sep 17 00:00:00 2001 From: KemalBekir Date: Tue, 8 Apr 2025 12:50:34 +0100 Subject: [PATCH 6/7] PR review requested changes --- gnovm/pkg/gnolang/preprocess.go | 164 +------------------------------- gnovm/pkg/gnolang/type_check.go | 163 +++++++++++++++++++++++++++++++ gnovm/tests/files/cap8.gno | 2 +- gnovm/tests/files/cap9.gno | 2 +- 4 files changed, 168 insertions(+), 163 deletions(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index de65dfeaa52..a9e9af9a476 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -6,7 +6,6 @@ import ( "math/big" "reflect" "slices" - "strconv" "strings" "sync/atomic" @@ -1366,14 +1365,14 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { fn := extractFunctionName(n) if fn == "len" { //nolint at := evalStaticTypeOf(store, last, n.Args[0]) - validateLenArg(at) + ValidateLenArg(at) } if fn == "cap" { //nolint at := evalStaticTypeOf(store, last, n.Args[0]) - validateCapArg(at) + ValidateCapArg(at) } if fn == "make" { - assertValidBuiltinArgType(store, last, n) + AssertValidBuiltinArgType(store, last, n) } ft = cft case *NativeType: @@ -5508,160 +5507,3 @@ func SaveBlockNodes(store Store, fn *FileNode) { return n, TRANS_CONTINUE }) } - -// Check for invalid arguments in builtin functions -// len, cap, make -func assertValidBuiltinArgType(store Store, last BlockNode, currExpr Expr) { - switch currExpr := currExpr.(type) { - case *ConstExpr: - assertValidBuiltinArgType(store, last, currExpr.Source) - case *CallExpr: - ift := evalStaticTypeOf(store, last, currExpr.Func) - switch baseOf(ift).(type) { - case *FuncType: - validateMakeArg(store, last, currExpr) - } - } -} - -// Validate argument for len() function -func validateLenArg(at Type) { - if at == nil { - panic("invalid argument: nil for built-in len") - } - - switch at := unwrapPointerType(baseOf(at)).(type) { - case PrimitiveType: - if at.Kind() != StringKind && at != UntypedStringType { - panic(fmt.Sprintf("unexpected type for len(): %v", at)) - } - case *ArrayType, *SliceType, *MapType, *ChanType: - // Valid types for len() - default: - panic(fmt.Sprintf("unexpected type for len(): %v", baseOf(at))) - } -} - -// Validate argument for cap() function -func validateCapArg(at Type) { - if at == nil { - panic("invalid argument: nil for built-in cap") - } - - switch at := unwrapPointerType(baseOf(at)).(type) { - case *ArrayType, *SliceType, *ChanType: - // Valid types for cap() - default: - panic(fmt.Sprintf("unexpected type for cap(): %v", at)) - } -} - -// Validate arguments for make() function -func validateMakeArg(store Store, last BlockNode, currExpr *CallExpr) { - if len(currExpr.Args) < 1 || len(currExpr.Args) > 3 { - panic(fmt.Sprintf("invalid number of arguments for built-in make: expected 1 to 3, got %d", len(currExpr.Args))) - } - - // Validate first argument: must be a slice, map, or channel type - at := evalStaticType(store, last, currExpr.Args[0]) - validateMakeType(baseOf(at)) - - var length int - var capacity int - - // Validate length argument (second argument for make) - if len(currExpr.Args) > 1 { - at1 := evalStaticTypeOf(store, last, currExpr.Args[1]) - if at1 == nil { - panic("invalid argument 2: nil for built-in make") - } - - var err error - length, err = parseIntValue(currExpr.Args[1], "length") - if err != nil { - panic(err.Error()) // Convert error to panic message - } - if length < 0 { - panic(fmt.Sprintf("invalid length argument for built-in %s: cannot be negative, got %d", "make", length)) - } - } - - // Validate capacity argument (third argument for make) - if len(currExpr.Args) > 2 { - at2 := evalStaticTypeOf(store, last, currExpr.Args[2]) - if at2 == nil { - panic("invalid argument 3: nil for built-in make") - } - var err error - capacity, err = parseIntValue(currExpr.Args[2], "capacity") - if err != nil { - panic(err.Error()) // Convert error to panic message - } - if capacity < length { - capacity = length - } - } -} - -// Ensures that the first argument to make() is a valid type -func validateMakeType(tp Type) { - switch tp.(type) { - case *SliceType, *MapType, *ChanType: - // Valid types for make() - default: - panic(fmt.Sprintf("invalid type for built-in %s: %s (must be slice, map, or channel)", "make", tp.String())) - } -} - -func parseIntValue(expr Expr, argName string) (int, error) { - switch e := expr.(type) { - case *ConstExpr: - return parseIntValue(e.Source, argName) - - case *BasicLitExpr: - var num int64 - if e.Kind == FLOAT { - f, err := hasFractionalPart(e.Value) - if err != nil { - return 0, err - } - num = int64(f) - } else { - n, err := strconv.Atoi(e.Value) - if err != nil { - return 0, fmt.Errorf("invalid %s argument for built-in %s: cannot convert %q to int", argName, "make", e.Value) - } - return n, nil - } - return int(num), nil - } - return 0, nil -} - -func extractFunctionName(n *CallExpr) string { - ce, ok := n.Func.(*ConstExpr) - if !ok { - return "" - } - - ne, ok := ce.Source.(*NameExpr) - if !ok { - return "" - } - return string(ne.Name) -} - -func hasFractionalPart(s string) (float64, error) { - // Parse the string as a float64 - f, err := strconv.ParseFloat(s, 64) - if err != nil { - return 0, err - } - - // Check if the float has a fractional part by comparing it with its integer part - intPart := math.Trunc(f) - if f != intPart { - return f, fmt.Errorf("invalid argument: %s has a fractional part", s) - } - return f, nil -} diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index a79e9c43ecc..f1a48ef0c89 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -2,7 +2,9 @@ package gnolang import ( "fmt" + "math" "reflect" + "strconv" "github.com/gnolang/gno/tm2/pkg/errors" ) @@ -1276,3 +1278,164 @@ func isBlankIdentifier(x Expr) bool { } return false } + +// Check for invalid arguments in builtin functions +// len, cap, make +func AssertValidBuiltinArgType(store Store, last BlockNode, currExpr Expr) { + switch currExpr := currExpr.(type) { + case *ConstExpr: + AssertValidBuiltinArgType(store, last, currExpr.Source) + case *CallExpr: + ift := evalStaticTypeOf(store, last, currExpr.Func) + switch baseOf(ift).(type) { + case *FuncType: + validateMakeArg(store, last, currExpr) + } + } +} + +// Validate argument for len() function +func ValidateLenArg(at Type) { + if at == nil { + panic("invalid argument: nil for built-in len") + } + + switch at := unwrapPointerType(baseOf(at)).(type) { + case PrimitiveType: + if at.Kind() != StringKind { + panic(fmt.Sprintf("unexpected type for len(): %v", at)) + } + case *ArrayType, *SliceType, *MapType, *ChanType: + // Valid types for len() + default: + panic(fmt.Sprintf("unexpected type for len(): %v", baseOf(at))) + } +} + +// Validate argument for cap() function +func ValidateCapArg(at Type) { + if at == nil { + panic("invalid argument: nil for built-in cap") + } + + typeStr := at.String() + + switch unwrapPointerType(baseOf(at)).(type) { + case *ArrayType, *SliceType, *ChanType: + // Valid types for cap() + default: + panic(fmt.Sprintf("unexpected type for cap(): %v", typeStr)) + } +} + +// Validate arguments for make() function +func validateMakeArg(store Store, last BlockNode, currExpr *CallExpr) { + if len(currExpr.Args) < 1 || len(currExpr.Args) > 3 { + panic(fmt.Sprintf("invalid number of arguments for built-in make: expected 1 to 3, got %d", len(currExpr.Args))) + } + + // Validate first argument: must be a slice, map, or channel type + at := evalStaticType(store, last, currExpr.Args[0]) + validateMakeType(baseOf(at)) + + var length int + var capacity int + + // Validate length argument (second argument for make) + if len(currExpr.Args) > 1 { + at1 := evalStaticTypeOf(store, last, currExpr.Args[1]) + if at1 == nil { + panic("invalid argument 2: nil for built-in make") + } + + var err error + length, err = parseIntValue(currExpr.Args[1], "length") + if err != nil { + panic(err.Error()) // Convert error to panic message + } + if length < 0 { + panic(fmt.Sprintf("invalid length argument for built-in %s: cannot be negative, got %d", "make", length)) + } + } + + // Validate capacity argument (third argument for make) + if len(currExpr.Args) > 2 { + at2 := evalStaticTypeOf(store, last, currExpr.Args[2]) + if at2 == nil { + panic("invalid argument 3: nil for built-in make") + } + var err error + capacity, err = parseIntValue(currExpr.Args[2], "capacity") + if err != nil { + panic(err.Error()) // Convert error to panic message + } + if _, ok := currExpr.Args[2].(*BasicLitExpr); ok { + if capacity < length { + panic(fmt.Sprintf("invalid argument: length and capacity swapped")) + } + } + } +} + +// Ensures that the first argument to make() is a valid type +func validateMakeType(tp Type) { + switch tp.(type) { + case *SliceType, *MapType, *ChanType: + // Valid types for make() + default: + panic(fmt.Sprintf("invalid type for built-in %s: %s (must be slice, map, or channel)", "make", tp.String())) + } +} + +func parseIntValue(expr Expr, argName string) (int, error) { + switch e := expr.(type) { + case *ConstExpr: + return parseIntValue(e.Source, argName) + + case *BasicLitExpr: + var num int64 + if e.Kind == FLOAT { + f, err := hasFractionalPart(e.Value) + if err != nil { + return 0, err + } + num = int64(f) + } else { + n, err := strconv.Atoi(e.Value) + if err != nil { + return 0, fmt.Errorf("invalid %s argument for built-in %s: cannot convert %q to int", argName, "make", e.Value) + } + return n, nil + } + return int(num), nil + } + return 0, nil +} + +func extractFunctionName(n *CallExpr) string { + ce, ok := n.Func.(*ConstExpr) + if !ok { + return "" + } + + ne, ok := ce.Source.(*NameExpr) + if !ok { + return "" + } + return string(ne.Name) +} + +func hasFractionalPart(s string) (float64, error) { + // Parse the string as a float64 + f, err := strconv.ParseFloat(s, 64) + if err != nil { + return 0, err + } + + // Check if the float has a fractional part by comparing it with its integer part + intPart := math.Trunc(f) + if f != intPart { + return f, fmt.Errorf("%g (untyped float constant) truncated to int", f) + } + return f, nil +} diff --git a/gnovm/tests/files/cap8.gno b/gnovm/tests/files/cap8.gno index b4d3b687937..c0bc31922db 100644 --- a/gnovm/tests/files/cap8.gno +++ b/gnovm/tests/files/cap8.gno @@ -6,4 +6,4 @@ func main() { } // Error: -// main/files/cap8.gno:5:17: unexpected type for cap(): int +// main/files/cap8.gno:5:17: unexpected type for cap(): *int diff --git a/gnovm/tests/files/cap9.gno b/gnovm/tests/files/cap9.gno index 84fb32a7e09..832660ff071 100644 --- a/gnovm/tests/files/cap9.gno +++ b/gnovm/tests/files/cap9.gno @@ -6,4 +6,4 @@ func main() { } // Error: -// main/files/cap9.gno:5:17: unexpected type for cap(): int \ No newline at end of file +// main/files/cap9.gno:5:17: unexpected type for cap(): *int \ No newline at end of file From 81a09c101867a081a8be3987a25c30b02e4cb3e7 Mon Sep 17 00:00:00 2001 From: KemalBekir Date: Tue, 22 Apr 2025 00:17:14 +0100 Subject: [PATCH 7/7] PR requested changes --- gnovm/pkg/gnolang/preprocess.go | 6 +- gnovm/pkg/gnolang/type_check.go | 104 ++++++++++++++++---------------- gnovm/tests/files/len0.gno | 2 +- gnovm/tests/files/len7.gno | 2 +- gnovm/tests/files/len8.gno | 3 +- gnovm/tests/files/len9.gno | 9 ++- gnovm/tests/files/make0.gno | 2 +- gnovm/tests/files/make1.gno | 2 +- 8 files changed, 63 insertions(+), 67 deletions(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index b60dd0adf02..f2391480106 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1200,14 +1200,14 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { fn := extractFunctionName(n) if fn == "len" { //nolint at := evalStaticTypeOf(store, last, n.Args[0]) - ValidateLenArg(at) + validateLenArg(at) } if fn == "cap" { //nolint at := evalStaticTypeOf(store, last, n.Args[0]) - ValidateCapArg(at) + validateCapArg(at) } if fn == "make" { - AssertValidBuiltinArgType(store, last, n) + validateMakeArg(store, last, n) } ft = cft case *TypeType: diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index fab44fad005..e09634c622c 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -1147,55 +1147,57 @@ func isBlankIdentifier(x Expr) bool { // Check for invalid arguments in builtin functions // len, cap, make -func AssertValidBuiltinArgType(store Store, last BlockNode, currExpr Expr) { +func validateMakeArg(store Store, last BlockNode, currExpr Expr) { switch currExpr := currExpr.(type) { - case *ConstExpr: - AssertValidBuiltinArgType(store, last, currExpr.Source) case *CallExpr: ift := evalStaticTypeOf(store, last, currExpr.Func) switch baseOf(ift).(type) { case *FuncType: - validateMakeArg(store, last, currExpr) + assertValidMakeArg(store, last, currExpr) } } } // Validate argument for len() function -func ValidateLenArg(at Type) { +func validateLenArg(at Type) { if at == nil { panic("invalid argument: nil for built-in len") } + if pt, ok := at.(*PointerType); ok { + if pt.Elt == IntType { + panic(fmt.Sprintf("unexpected type for len(): %v", pt)) + } + } + switch at := unwrapPointerType(baseOf(at)).(type) { case PrimitiveType: if at.Kind() != StringKind { - panic(fmt.Sprintf("unexpected type for len(): %v", at)) + panic(fmt.Sprintf("unexpected type for len(): %v", baseOf(at).String())) } case *ArrayType, *SliceType, *MapType, *ChanType: - // Valid types for len() + // Valid types for len() default: - panic(fmt.Sprintf("unexpected type for len(): %v", baseOf(at))) + panic(fmt.Sprintf("unexpected type for len(): %v", baseOf(at).String())) } } // Validate argument for cap() function -func ValidateCapArg(at Type) { +func validateCapArg(at Type) { if at == nil { panic("invalid argument: nil for built-in cap") } - typeStr := at.String() - switch unwrapPointerType(baseOf(at)).(type) { case *ArrayType, *SliceType, *ChanType: // Valid types for cap() default: - panic(fmt.Sprintf("unexpected type for cap(): %v", typeStr)) + panic(fmt.Sprintf("unexpected type for cap(): %v", at.String())) } } // Validate arguments for make() function -func validateMakeArg(store Store, last BlockNode, currExpr *CallExpr) { +func assertValidMakeArg(store Store, last BlockNode, currExpr *CallExpr) { if len(currExpr.Args) < 1 || len(currExpr.Args) > 3 { panic(fmt.Sprintf("invalid number of arguments for built-in make: expected 1 to 3, got %d", len(currExpr.Args))) } @@ -1205,7 +1207,6 @@ func validateMakeArg(store Store, last BlockNode, currExpr *CallExpr) { validateMakeType(baseOf(at)) var length int - var capacity int // Validate length argument (second argument for make) if len(currExpr.Args) > 1 { @@ -1214,14 +1215,18 @@ func validateMakeArg(store Store, last BlockNode, currExpr *CallExpr) { panic("invalid argument 2: nil for built-in make") } - var err error - length, err = parseIntValue(currExpr.Args[1], "length") + if at1 == Float64Type { + panic(fmt.Sprintf("invalid argument: index length (variable of type float64) must be integer")) + } + + l, err := parseIntValue(currExpr.Args[1]) if err != nil { - panic(err.Error()) // Convert error to panic message + panic(fmt.Sprintf("invalid length argument for built-in make: %s", err.Error())) } - if length < 0 { - panic(fmt.Sprintf("invalid length argument for built-in %s: cannot be negative, got %d", "make", length)) + if l < 0 { + panic(fmt.Sprintf("invalid length argument for built-in make: cannot be negative, got %d", length)) } + length = l } // Validate capacity argument (third argument for make) @@ -1230,10 +1235,12 @@ func validateMakeArg(store Store, last BlockNode, currExpr *CallExpr) { if at2 == nil { panic("invalid argument 3: nil for built-in make") } - var err error - capacity, err = parseIntValue(currExpr.Args[2], "capacity") + capacity, err := parseIntValue(currExpr.Args[2]) if err != nil { - panic(err.Error()) // Convert error to panic message + panic(fmt.Sprintf("invalid capacity argument for built-in make: %s", err.Error())) + } + if capacity < 0 { + panic(fmt.Sprintf("invalid capacity argument for built-in make: cannot be negative, got %d", capacity)) } if _, ok := currExpr.Args[2].(*BasicLitExpr); ok { if capacity < length { @@ -1249,31 +1256,37 @@ func validateMakeType(tp Type) { case *SliceType, *MapType, *ChanType: // Valid types for make() default: - panic(fmt.Sprintf("invalid type for built-in %s: %s (must be slice, map, or channel)", "make", tp.String())) + panic(fmt.Sprintf("invalid type for built-in make: %s (must be slice, map, or channel)", tp.String())) } } -func parseIntValue(expr Expr, argName string) (int, error) { +func parseIntValue(expr Expr) (int, error) { switch e := expr.(type) { case *ConstExpr: - return parseIntValue(e.Source, argName) - - case *BasicLitExpr: - var num int64 - if e.Kind == FLOAT { - f, err := hasFractionalPart(e.Value) - if err != nil { - return 0, err + if ble, ok := e.Source.(*BasicLitExpr); ok { + if ble.Kind == FLOAT { + f, err := strconv.ParseFloat(ble.Value, 64) + if err != nil { + return 0, err + } + intPart := math.Trunc(f) + if f != intPart { + return 0, fmt.Errorf("%g (untyped float constant) truncated to int", f) + } + if f > float64(math.MaxInt) { + return 0, errors.New("value out of range for int type") + } + return int(f), nil } - num = int64(f) - } else { - n, err := strconv.Atoi(e.Value) + n, err := strconv.ParseInt(ble.Value, 0, 64) if err != nil { - return 0, fmt.Errorf("invalid %s argument for built-in %s: cannot convert %q to int", argName, "make", e.Value) + return 0, fmt.Errorf("cannot convert %s to int", ble.Value) + } + if n > int64(math.MaxInt) { + return 0, errors.New("value out of range for int type") } - return n, nil + return int(n), nil } - return int(num), nil } return 0, nil } @@ -1290,18 +1303,3 @@ func extractFunctionName(n *CallExpr) string { } return string(ne.Name) } - -func hasFractionalPart(s string) (float64, error) { - // Parse the string as a float64 - f, err := strconv.ParseFloat(s, 64) - if err != nil { - return 0, err - } - - // Check if the float has a fractional part by comparing it with its integer part - intPart := math.Trunc(f) - if f != intPart { - return f, fmt.Errorf("%g (untyped float constant) truncated to int", f) - } - return f, nil -} diff --git a/gnovm/tests/files/len0.gno b/gnovm/tests/files/len0.gno index b5bbee62b14..370964b832e 100644 --- a/gnovm/tests/files/len0.gno +++ b/gnovm/tests/files/len0.gno @@ -1,6 +1,6 @@ package main -func f(a []int) any { +func f(a []int) interface{} { return len(a) } diff --git a/gnovm/tests/files/len7.gno b/gnovm/tests/files/len7.gno index ff59881b315..0571f8cc0cc 100644 --- a/gnovm/tests/files/len7.gno +++ b/gnovm/tests/files/len7.gno @@ -5,4 +5,4 @@ func main() { } // Error: -// main/files/len7.gno:4:10: unexpected type for len(): int +// main/files/len7.gno:4:10: unexpected type for len(): *int diff --git a/gnovm/tests/files/len8.gno b/gnovm/tests/files/len8.gno index 40d1c0814c9..af91a6df43f 100644 --- a/gnovm/tests/files/len8.gno +++ b/gnovm/tests/files/len8.gno @@ -7,5 +7,4 @@ func main() { } // Error: -// main/files/len8.gno:4:10: unexpected type for len(): struct{A int;B int} - +// main/files/len8.gno:4:10: unexpected type for len(): struct{A int; B int} diff --git a/gnovm/tests/files/len9.gno b/gnovm/tests/files/len9.gno index 5dcc861e677..42684f25cf6 100644 --- a/gnovm/tests/files/len9.gno +++ b/gnovm/tests/files/len9.gno @@ -1,10 +1,9 @@ package main func main() { - var a map[string]string - - println(len(a)) + a := len(nil) + println(a) } -// Output: -// 0 +// Error: +// main/files/len9.gno:4:10: invalid argument: nil for built-in len \ No newline at end of file diff --git a/gnovm/tests/files/make0.gno b/gnovm/tests/files/make0.gno index 104c429db1a..d3484198c34 100644 --- a/gnovm/tests/files/make0.gno +++ b/gnovm/tests/files/make0.gno @@ -1,6 +1,6 @@ package main -func f() any { +func f() interface{} { return make([]int, 2) } diff --git a/gnovm/tests/files/make1.gno b/gnovm/tests/files/make1.gno index c7fa68825ff..139c68f514a 100644 --- a/gnovm/tests/files/make1.gno +++ b/gnovm/tests/files/make1.gno @@ -2,7 +2,7 @@ package main import "fmt" -func f() any { +func f() interface{} { return make(map[int]int) }