From 3d5030ae7afd3d1c2bdfcd2a1b1f0acb9dc9a3a6 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Sat, 21 Sep 2024 16:08:23 +0200 Subject: [PATCH 01/28] feat(gnovm): align Gno constant handling with Go specifications --- .../r/demo/keystore/keystore_test.gno | 2 +- .../r/demo/microblog/microblog_test.gno | 2 +- gnovm/pkg/gnolang/preprocess.go | 18 +++ gnovm/pkg/gnolang/type_check.go | 109 ++++++++++++++++++ gnovm/tests/files/const23.gno | 11 ++ gnovm/tests/files/const24.gno | 76 ++++++++++++ gnovm/tests/files/const25.gno | 11 ++ gnovm/tests/files/const26.gno | 15 +++ gnovm/tests/files/const27.gno | 16 +++ gnovm/tests/files/const28.gno | 12 ++ gnovm/tests/files/const29.gno | 12 ++ gnovm/tests/files/const30.gno | 15 +++ gnovm/tests/files/const31.gno | 15 +++ gnovm/tests/files/const32.gno | 11 ++ 14 files changed, 323 insertions(+), 2 deletions(-) create mode 100644 gnovm/tests/files/const23.gno create mode 100644 gnovm/tests/files/const24.gno create mode 100644 gnovm/tests/files/const25.gno create mode 100644 gnovm/tests/files/const26.gno create mode 100644 gnovm/tests/files/const27.gno create mode 100644 gnovm/tests/files/const28.gno create mode 100644 gnovm/tests/files/const29.gno create mode 100644 gnovm/tests/files/const30.gno create mode 100644 gnovm/tests/files/const31.gno create mode 100644 gnovm/tests/files/const32.gno diff --git a/examples/gno.land/r/demo/keystore/keystore_test.gno b/examples/gno.land/r/demo/keystore/keystore_test.gno index ffd8e60936f..9b5fafa2f95 100644 --- a/examples/gno.land/r/demo/keystore/keystore_test.gno +++ b/examples/gno.land/r/demo/keystore/keystore_test.gno @@ -11,7 +11,7 @@ import ( ) func TestRender(t *testing.T) { - const ( + var ( author1 std.Address = testutils.TestAddress("author1") author2 std.Address = testutils.TestAddress("author2") ) diff --git a/examples/gno.land/r/demo/microblog/microblog_test.gno b/examples/gno.land/r/demo/microblog/microblog_test.gno index a3c8f04ee7f..9ad98d3cbfe 100644 --- a/examples/gno.land/r/demo/microblog/microblog_test.gno +++ b/examples/gno.land/r/demo/microblog/microblog_test.gno @@ -10,7 +10,7 @@ import ( ) func TestMicroblog(t *testing.T) { - const ( + var ( author1 std.Address = testutils.TestAddress("author1") author2 std.Address = testutils.TestAddress("author2") ) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 9168fc6f7c1..8a3ad82d9f6 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -2188,6 +2188,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // NOTE: may or may not be a *ConstExpr, // but if not, make one now. for i, vx := range n.Values { + checkConstantExpr(store, last, vx) n.Values[i] = evalConst(store, last, vx) } } else { @@ -2268,6 +2269,16 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { if n.Type != nil { // only a single type can be specified. nt := evalStaticType(store, last, n.Type) + if n.Const { + if xnt, ok := nt.(*NativeType); ok { + nt = go2GnoBaseType(xnt.Type) + } + + if _, ok := baseOf(nt).(PrimitiveType); !ok { + panic(fmt.Sprintf("invalid constant type %s", nt.String())) + } + } + for i := 0; i < numNames; i++ { sts[i] = nt } @@ -2279,6 +2290,13 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // derive static type from values. for i, vx := range n.Values { vt := evalStaticTypeOf(store, last, vx) + if xnt, ok := vt.(*NativeType); ok { + vt = go2GnoBaseType(xnt.Type) + } + + if _, ok := baseOf(vt).(PrimitiveType); !ok { + panic(fmt.Sprintf("invalid constant type %s", vt.String())) + } sts[i] = vt } } else { diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index 31025fef152..8b2ea344d74 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -215,6 +215,115 @@ func assertAssignableTo(xt, dt Type, autoNative bool) { } } +func checkConstantExpr(store Store, last BlockNode, vx Expr) { +Main: + switch vx := vx.(type) { + case *NameExpr: + t := evalStaticTypeOf(store, last, vx) + if _, ok := t.(*ArrayType); ok { + break Main + } + panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.Name, t)) + case *TypeAssertExpr: + panic(fmt.Sprintf("%s (comma, ok expression of type %s) is not constant", vx.String(), vx.Type)) + case *IndexExpr: + panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), vx.X)) + case *CallExpr: + ift := evalStaticTypeOf(store, last, vx.Func) + switch baseOf(ift).(type) { + case *FuncType: + tup := evalStaticTypeOfRaw(store, last, vx).(*tupleType) + + // check for built-in functions + if cx, ok := vx.Func.(*ConstExpr); ok { + if fv, ok := cx.V.(*FuncValue); ok { + if fv.PkgPath == uversePkgPath { + // TODO: should support min, max + switch { + case fv.Name == "len": + checkConstantExpr(store, last, vx.Args[0]) + break Main + case fv.Name == "cap": + checkConstantExpr(store, last, vx.Args[0]) + break Main + } + } + } + } + + switch { + case len(tup.Elts) == 0: + panic(fmt.Sprintf("%s (no value) used as value", vx.String())) + case len(tup.Elts) == 1: + panic(fmt.Sprintf("%s (value of type %s) is not constant", vx.String(), tup.Elts[0])) + default: + panic(fmt.Sprintf("multiple-value %s (value of type %s) in single-value context", vx.String(), tup.Elts)) + } + case *TypeType: + for _, arg := range vx.Args { + checkConstantExpr(store, last, arg) + } + case *NativeType: + panic("NativeType\n") + default: + panic(fmt.Sprintf( + "unexpected func type %v (%v)", + ift, reflect.TypeOf(ift))) + } + case *BinaryExpr: + checkConstantExpr(store, last, vx.Left) + checkConstantExpr(store, last, vx.Right) + case *SelectorExpr: + xt := evalStaticTypeOf(store, last, vx.X) + switch xt := xt.(type) { + case *PackageType: + // Todo: check if the package is const after the fix of https://github.com/gnolang/gno/issues/2836 + // var pv *PackageValue + // if cx, ok := vx.X.(*ConstExpr); ok { + // // NOTE: *Machine.TestMemPackage() needs this + // // to pass in an imported package as *ConstEzpr. + // pv = cx.V.(*PackageValue) + // } else { + // // otherwise, packages can only be referred to by + // // *NameExprs, and cannot be copied. + // pvc := evalConst(store, last, vx.X) + // pv_, ok := pvc.V.(*PackageValue) + // if !ok { + // panic(fmt.Sprintf( + // "missing package in selector expr %s", + // vx.String())) + // } + // pv = pv_ + // } + // if pv.GetBlock(store).Source.GetIsConst(store, vx.Sel) { + // break Main + // } + // panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), xt)) + case *PointerType, *DeclaredType, *StructType, *InterfaceType: + ty := evalStaticTypeOf(store, last, vx.X) + panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), ty)) + case *TypeType: + ty := evalStaticType(store, last, vx.X) + panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), ty)) + case *NativeType: + panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), xt)) + default: + panic(fmt.Sprintf( + "unexpected selector expression type %v", + reflect.TypeOf(xt))) + } + + case *ArrayTypeExpr: + case *ConstExpr: + case *BasicLitExpr: + case *CompositeLitExpr: + checkConstantExpr(store, last, vx.Type) + default: + ift := evalStaticTypeOf(store, last, vx) + panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), ift)) + } +} + // checkValDefineMismatch checks for mismatch between the number of variables and values in a ValueDecl or AssignStmt. func checkValDefineMismatch(n Node) { var ( diff --git a/gnovm/tests/files/const23.gno b/gnovm/tests/files/const23.gno new file mode 100644 index 00000000000..bb6464fd88a --- /dev/null +++ b/gnovm/tests/files/const23.gno @@ -0,0 +1,11 @@ +package main + +import "fmt" + +func main() { + const t []string = []string{} + fmt.Println(t) +} + +// Error: +// main/files/const23.gno:6:8: [](const-type string){} (variable of type []string) is not constant diff --git a/gnovm/tests/files/const24.gno b/gnovm/tests/files/const24.gno new file mode 100644 index 00000000000..58901fe301b --- /dev/null +++ b/gnovm/tests/files/const24.gno @@ -0,0 +1,76 @@ +package main + +import ( + "fmt" + "time" +) + +func main() { + const a int = 1_000_000 + const b byte = byte(1) + const c float64 = 1_000_000.000 + const d string = "Hello, World!" + const e rune = 'a' + const g bool = true + const h uint = 1_000 + const i int8 = 1 + const j int16 = 1 + const k int32 = 1 + const l int64 = 1 + const m uint8 = 1 + const n uint16 = 1 + const o uint32 = 1 + const p uint64 = 1 + const r float32 = 1_000_000.000 + const s = r + const t = len("s") + const u = 1 + len("s") + 3 + ars := [10]string{} + const v = len(ars) + const w = cap(ars) + + fmt.Println(a) + fmt.Println(b) + fmt.Println(c) + fmt.Println(d) + fmt.Println(e) + fmt.Println(g) + fmt.Println(h) + fmt.Println(i) + fmt.Println(j) + fmt.Println(k) + fmt.Println(l) + fmt.Println(m) + fmt.Println(n) + fmt.Println(o) + fmt.Println(p) + fmt.Println(r) + fmt.Println(s) + fmt.Println(t) + fmt.Println(u) + fmt.Println(v) + fmt.Println(w) +} + +// Output: +// 1000000 +// 1 +// 1e+06 +// Hello, World! +// 97 +// true +// 1000 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1e+06 +// 1e+06 +// 1 +// 5 +// 10 +// 10 diff --git a/gnovm/tests/files/const25.gno b/gnovm/tests/files/const25.gno new file mode 100644 index 00000000000..64e0358bdef --- /dev/null +++ b/gnovm/tests/files/const25.gno @@ -0,0 +1,11 @@ +package main + +import "fmt" + +func main() { + const t = []string{"1"} + fmt.Println(t) +} + +// Error: +// main/files/const25.gno:6:8: [](const-type string){(const ("1" string))} (variable of type []string) is not constant diff --git a/gnovm/tests/files/const26.gno b/gnovm/tests/files/const26.gno new file mode 100644 index 00000000000..a1533e98c57 --- /dev/null +++ b/gnovm/tests/files/const26.gno @@ -0,0 +1,15 @@ +package main + +import "fmt" + +func v() string { + return "" +} + +func main() { + const t = v() + fmt.Println(t) +} + +// Error: +// main/files/const26.gno:10:8: v() (value of type string) is not constant diff --git a/gnovm/tests/files/const27.gno b/gnovm/tests/files/const27.gno new file mode 100644 index 00000000000..4be731e16a7 --- /dev/null +++ b/gnovm/tests/files/const27.gno @@ -0,0 +1,16 @@ +package main + +import "fmt" + +func v() string { + return "" +} + +func main() { + var i interface{} = 1 + const t, ok = i.(int) + fmt.Println(t, ok) +} + +// Error: +// main/files/const27.gno:11:8: i.((const-type int)) (comma, ok expression of type (const-type int)) is not constant diff --git a/gnovm/tests/files/const28.gno b/gnovm/tests/files/const28.gno new file mode 100644 index 00000000000..7b6b5648bf1 --- /dev/null +++ b/gnovm/tests/files/const28.gno @@ -0,0 +1,12 @@ +package main + +import "fmt" + +func main() { + var s []string = []string{"1"} + const t, ok = s[0] + fmt.Println(t, ok) +} + +// Error: +// main/files/const28.gno:7:8: s[(const (0 int))] (variable of type s) is not constant diff --git a/gnovm/tests/files/const29.gno b/gnovm/tests/files/const29.gno new file mode 100644 index 00000000000..0ebd8bcab6c --- /dev/null +++ b/gnovm/tests/files/const29.gno @@ -0,0 +1,12 @@ +package main + +import "fmt" + +func main() { + s := "1" + const t = s + fmt.Println(t) +} + +// Error: +// main/files/const29.gno:7:8: s (variable of type string) is not constant diff --git a/gnovm/tests/files/const30.gno b/gnovm/tests/files/const30.gno new file mode 100644 index 00000000000..3908ee26ee3 --- /dev/null +++ b/gnovm/tests/files/const30.gno @@ -0,0 +1,15 @@ +package main + +import "fmt" + +func v() { + return +} + +func main() { + const t = v() + fmt.Println(t) +} + +// Error: +// main/files/const30.gno:10:8: v() (no value) used as value diff --git a/gnovm/tests/files/const31.gno b/gnovm/tests/files/const31.gno new file mode 100644 index 00000000000..a192bd8ab86 --- /dev/null +++ b/gnovm/tests/files/const31.gno @@ -0,0 +1,15 @@ +package main + +import "fmt" + +func v() (string, string) { + return "", "" +} + +func main() { + const t, v = v() + fmt.Println(t) +} + +// Error: +// main/files/const31.gno:10:8: multiple-value (const (v func()( string, string)))() (value of type [string string]) in single-value context diff --git a/gnovm/tests/files/const32.gno b/gnovm/tests/files/const32.gno new file mode 100644 index 00000000000..83d3ae5e73c --- /dev/null +++ b/gnovm/tests/files/const32.gno @@ -0,0 +1,11 @@ +package main + +import "fmt" + +func main() { + const t = 1 + 2 + len([]string{}) + fmt.Println(t) +} + +// Error: +// main/files/const32.gno:6:8: [](const-type string){} (variable of type []string) is not constant From bb2d9fd4799e845acdaa46214bbb8f9a0c1c8cc2 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Sat, 19 Oct 2024 00:53:24 +0200 Subject: [PATCH 02/28] feat: correct test --- gnovm/pkg/gnolang/type_check.go | 2 +- gnovm/tests/files/const23.gno | 2 +- gnovm/tests/files/const25.gno | 2 +- gnovm/tests/files/const32.gno | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index bd34070ae95..22445a720e7 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -319,7 +319,7 @@ Main: case *CompositeLitExpr: checkConstantExpr(store, last, vx.Type) default: - ift := evalStaticTypeOf(store, last, vx) + ift := evalStaticType(store, last, vx) panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), ift)) } } diff --git a/gnovm/tests/files/const23.gno b/gnovm/tests/files/const23.gno index bb6464fd88a..7282c959b02 100644 --- a/gnovm/tests/files/const23.gno +++ b/gnovm/tests/files/const23.gno @@ -8,4 +8,4 @@ func main() { } // Error: -// main/files/const23.gno:6:8: [](const-type string){} (variable of type []string) is not constant +// main/files/const23.gno:6:8: [](const-type string) (variable of type []string) is not constant diff --git a/gnovm/tests/files/const25.gno b/gnovm/tests/files/const25.gno index 64e0358bdef..c83495a4d20 100644 --- a/gnovm/tests/files/const25.gno +++ b/gnovm/tests/files/const25.gno @@ -8,4 +8,4 @@ func main() { } // Error: -// main/files/const25.gno:6:8: [](const-type string){(const ("1" string))} (variable of type []string) is not constant +// main/files/const25.gno:6:8: [](const-type string) (variable of type []string) is not constant diff --git a/gnovm/tests/files/const32.gno b/gnovm/tests/files/const32.gno index 83d3ae5e73c..75acf5fb446 100644 --- a/gnovm/tests/files/const32.gno +++ b/gnovm/tests/files/const32.gno @@ -8,4 +8,4 @@ func main() { } // Error: -// main/files/const32.gno:6:8: [](const-type string){} (variable of type []string) is not constant +// main/files/const32.gno:6:8: [](const-type string) (variable of type []string) is not constant From b80a890e4641dce6846370b8c26c87a80958a69e Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Wed, 23 Oct 2024 20:55:18 +0200 Subject: [PATCH 03/28] feat: add a todo for native type conversion --- gnovm/pkg/gnolang/type_check.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index cac86ea0d51..243cfed6ef3 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -264,7 +264,9 @@ Main: checkConstantExpr(store, last, arg) } case *NativeType: - panic("NativeType\n") + // Todo: should add a test after the fix of https://github.com/gnolang/gno/issues/3006 + ty := evalStaticType(store, last, vx.Func) + panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), ty)) default: panic(fmt.Sprintf( "unexpected func type %v (%v)", From ff74523155566b0f3c6dc364ac03818afd0a920a Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Wed, 23 Oct 2024 20:59:25 +0200 Subject: [PATCH 04/28] feat: uncomment package branch --- gnovm/pkg/gnolang/type_check.go | 43 ++++++++++++++++----------------- gnovm/tests/files/const24.gno | 3 +++ 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index 243cfed6ef3..f2cd7d73c67 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -279,28 +279,27 @@ Main: xt := evalStaticTypeOf(store, last, vx.X) switch xt := xt.(type) { case *PackageType: - // Todo: check if the package is const after the fix of https://github.com/gnolang/gno/issues/2836 - // var pv *PackageValue - // if cx, ok := vx.X.(*ConstExpr); ok { - // // NOTE: *Machine.TestMemPackage() needs this - // // to pass in an imported package as *ConstEzpr. - // pv = cx.V.(*PackageValue) - // } else { - // // otherwise, packages can only be referred to by - // // *NameExprs, and cannot be copied. - // pvc := evalConst(store, last, vx.X) - // pv_, ok := pvc.V.(*PackageValue) - // if !ok { - // panic(fmt.Sprintf( - // "missing package in selector expr %s", - // vx.String())) - // } - // pv = pv_ - // } - // if pv.GetBlock(store).Source.GetIsConst(store, vx.Sel) { - // break Main - // } - // panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), xt)) + var pv *PackageValue + if cx, ok := vx.X.(*ConstExpr); ok { + // NOTE: *Machine.TestMemPackage() needs this + // to pass in an imported package as *ConstEzpr. + pv = cx.V.(*PackageValue) + } else { + // otherwise, packages can only be referred to by + // *NameExprs, and cannot be copied. + pvc := evalConst(store, last, vx.X) + pv_, ok := pvc.V.(*PackageValue) + if !ok { + panic(fmt.Sprintf( + "missing package in selector expr %s", + vx.String())) + } + pv = pv_ + } + if pv.GetBlock(store).Source.GetIsConst(store, vx.Sel) { + break Main + } + panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), xt)) case *PointerType, *DeclaredType, *StructType, *InterfaceType: ty := evalStaticTypeOf(store, last, vx.X) panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), ty)) diff --git a/gnovm/tests/files/const24.gno b/gnovm/tests/files/const24.gno index 58901fe301b..cc2187b9ac4 100644 --- a/gnovm/tests/files/const24.gno +++ b/gnovm/tests/files/const24.gno @@ -28,6 +28,7 @@ func main() { ars := [10]string{} const v = len(ars) const w = cap(ars) + const x = time.Second fmt.Println(a) fmt.Println(b) @@ -50,6 +51,7 @@ func main() { fmt.Println(u) fmt.Println(v) fmt.Println(w) + fmt.Println(x) } // Output: @@ -74,3 +76,4 @@ func main() { // 5 // 10 // 10 +// 1s From 761ef1afa7ba27ff1d94acf3ba82156217cd2ad5 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Wed, 23 Oct 2024 21:09:44 +0200 Subject: [PATCH 05/28] feat: use println --- gnovm/tests/files/const24.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/tests/files/const24.gno b/gnovm/tests/files/const24.gno index cc2187b9ac4..c9cb14ef369 100644 --- a/gnovm/tests/files/const24.gno +++ b/gnovm/tests/files/const24.gno @@ -51,7 +51,7 @@ func main() { fmt.Println(u) fmt.Println(v) fmt.Println(w) - fmt.Println(x) + println(x) } // Output: From 5fa41e9284dbe2d5490b23aae7caf8b35b237f8d Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Wed, 23 Oct 2024 22:13:45 +0200 Subject: [PATCH 06/28] feat: add test --- gnovm/tests/files/const33.gno | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 gnovm/tests/files/const33.gno diff --git a/gnovm/tests/files/const33.gno b/gnovm/tests/files/const33.gno new file mode 100644 index 00000000000..989fe7dda5b --- /dev/null +++ b/gnovm/tests/files/const33.gno @@ -0,0 +1,12 @@ +package main + +var x = 1 +var y = 1 + +const b = x == y + +func main() { + println("ok") +} +// Error: +// main/files/const33.gno:6:7: x (variable of type int) is not constant From 90c8c5bdb4b1eee893f32c77ccff627b63fe050f Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Wed, 23 Oct 2024 22:21:33 +0200 Subject: [PATCH 07/28] fix: use evalStaticType only for type --- gnovm/pkg/gnolang/type_check.go | 5 ++++- gnovm/tests/files/const34.gno | 10 ++++++++++ gnovm/tests/files/const35.gno | 12 ++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 gnovm/tests/files/const34.gno create mode 100644 gnovm/tests/files/const35.gno diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index f2cd7d73c67..0763e7ae3ee 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -320,7 +320,10 @@ Main: case *CompositeLitExpr: checkConstantExpr(store, last, vx.Type) default: - ift := evalStaticType(store, last, vx) + ift := evalStaticTypeOf(store, last, vx) + if _, ok := ift.(*TypeType); ok { + ift = evalStaticType(store, last, vx) + } panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), ift)) } } diff --git a/gnovm/tests/files/const34.gno b/gnovm/tests/files/const34.gno new file mode 100644 index 00000000000..9c79cf86b88 --- /dev/null +++ b/gnovm/tests/files/const34.gno @@ -0,0 +1,10 @@ +package main + +const a = func() { println("hey") } + +func main() { + println("ok") +} + +// Error: +// main/files/const34.gno:3:7: func func(){ (const (println func(xs ...interface{})()))((const ("hey" string))) } (variable of type func()()) is not constant diff --git a/gnovm/tests/files/const35.gno b/gnovm/tests/files/const35.gno new file mode 100644 index 00000000000..e684edb7263 --- /dev/null +++ b/gnovm/tests/files/const35.gno @@ -0,0 +1,12 @@ +package main + +var x = 1 + +const ff = +(1 << x) + +func main() { + println("ok") +} + +// Error: +// main/files/const35.gno:5:7: +(const (1 bigint)) << (const-type uint)(x) (variable of type bigint) is not constant From e74e6e99bd3137e644efa8ca5a36f88874797708 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Thu, 24 Oct 2024 00:50:48 +0200 Subject: [PATCH 08/28] feat: add more test --- gnovm/pkg/gnolang/preprocess.go | 2 +- gnovm/pkg/gnolang/type_check.go | 7 +++++-- gnovm/tests/files/const36.gno | 13 +++++++++++++ gnovm/tests/files/const37_native.gno | 10 ++++++++++ gnovm/tests/files/const37_stdlibs.gno | 10 ++++++++++ gnovm/tests/files/const38.gno | 11 +++++++++++ gnovm/tests/files/const39.gno | 14 ++++++++++++++ gnovm/tests/files/const40.gno | 8 ++++++++ gnovm/tests/files/const41.gno | 8 ++++++++ 9 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 gnovm/tests/files/const36.gno create mode 100644 gnovm/tests/files/const37_native.gno create mode 100644 gnovm/tests/files/const37_stdlibs.gno create mode 100644 gnovm/tests/files/const38.gno create mode 100644 gnovm/tests/files/const39.gno create mode 100644 gnovm/tests/files/const40.gno create mode 100644 gnovm/tests/files/const41.gno diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 21d562bb8d5..1b06dd3b489 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -2404,7 +2404,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { } if _, ok := baseOf(vt).(PrimitiveType); !ok { - panic(fmt.Sprintf("invalid constant type %s", vt.String())) + panic(fmt.Sprintf("%s (value of type %s) is not constant", vx.String(), vt.String())) } sts[i] = vt } diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index 0763e7ae3ee..d4ed40ab03b 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -299,7 +299,9 @@ Main: if pv.GetBlock(store).Source.GetIsConst(store, vx.Sel) { break Main } - panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), xt)) + + tt := pv.GetBlock(store).Source.GetStaticTypeOf(store, vx.Sel) + panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), tt)) case *PointerType, *DeclaredType, *StructType, *InterfaceType: ty := evalStaticTypeOf(store, last, vx.X) panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), ty)) @@ -307,7 +309,8 @@ Main: ty := evalStaticType(store, last, vx.X) panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), ty)) case *NativeType: - panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), xt)) + ty := evalStaticTypeOf(store, last, vx.X) + panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), ty)) default: panic(fmt.Sprintf( "unexpected selector expression type %v", diff --git a/gnovm/tests/files/const36.gno b/gnovm/tests/files/const36.gno new file mode 100644 index 00000000000..c6cf82c6f6b --- /dev/null +++ b/gnovm/tests/files/const36.gno @@ -0,0 +1,13 @@ +package main + +type s struct { + x int +} + +func main() { + s := s{1} + const v = s.x +} + +// Error: +// main/files/const36.gno:9:8: s.x (variable of type main.s) is not constant diff --git a/gnovm/tests/files/const37_native.gno b/gnovm/tests/files/const37_native.gno new file mode 100644 index 00000000000..413fa011151 --- /dev/null +++ b/gnovm/tests/files/const37_native.gno @@ -0,0 +1,10 @@ +package main + +import "time" + +func main() { + const v = time.UTC +} + +// Error: +// main/files/const37_native.gno:6:8: time.UTC (variable of type gonative{*time.Location}) is not constant \ No newline at end of file diff --git a/gnovm/tests/files/const37_stdlibs.gno b/gnovm/tests/files/const37_stdlibs.gno new file mode 100644 index 00000000000..9ec614f2538 --- /dev/null +++ b/gnovm/tests/files/const37_stdlibs.gno @@ -0,0 +1,10 @@ +package main + +import "time" + +func main() { + const v = time.UTC +} + +// Error: +// main/files/const37_stdlibs.gno:6:8: time.UTC (variable of type *time.Location) is not constant \ No newline at end of file diff --git a/gnovm/tests/files/const38.gno b/gnovm/tests/files/const38.gno new file mode 100644 index 00000000000..1aee945d434 --- /dev/null +++ b/gnovm/tests/files/const38.gno @@ -0,0 +1,11 @@ +package main + +import "net" + +func main() { + v := net.TCPAddr{} + const c = v.IP +} + +// Error: +// main/files/const38.gno:7:8: v.IP (variable of type gonative{net.TCPAddr}) is not constant \ No newline at end of file diff --git a/gnovm/tests/files/const39.gno b/gnovm/tests/files/const39.gno new file mode 100644 index 00000000000..fa3cdb5f517 --- /dev/null +++ b/gnovm/tests/files/const39.gno @@ -0,0 +1,14 @@ +package main + +type T struct { + a int +} + +func (tv T) Mv(a int) int { return 0 } + +func main() { + const t = T.Mv +} + +// Error: +// main/files/const39.gno:10:8: T.Mv (variable of type main.T) is not constant \ No newline at end of file diff --git a/gnovm/tests/files/const40.gno b/gnovm/tests/files/const40.gno new file mode 100644 index 00000000000..f0eeedeaeb9 --- /dev/null +++ b/gnovm/tests/files/const40.gno @@ -0,0 +1,8 @@ +package main + +func main() { + const t = [0]string{} +} + +// Error: +// main/files/const40.gno:4:8: (const (array[] [0]string)) (value of type [0]string) is not constant \ No newline at end of file diff --git a/gnovm/tests/files/const41.gno b/gnovm/tests/files/const41.gno new file mode 100644 index 00000000000..b4424dcef94 --- /dev/null +++ b/gnovm/tests/files/const41.gno @@ -0,0 +1,8 @@ +package main + +func main() { + const t [0]string = [0]string{} +} + +// Error: +// main/files/const41.gno:4:8: invalid constant type [0]string \ No newline at end of file From 3a52e1452130155342ba43090b9eb833b66b23ee Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Thu, 24 Oct 2024 23:12:51 +0200 Subject: [PATCH 09/28] feat: add missing init expr --- gnovm/pkg/gnolang/preprocess.go | 4 ++++ gnovm/tests/files/const42.gno | 8 ++++++++ 2 files changed, 12 insertions(+) create mode 100644 gnovm/tests/files/const42.gno diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 1b06dd3b489..0cec8e25475 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -2379,6 +2379,10 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // only a single type can be specified. nt := evalStaticType(store, last, n.Type) if n.Const { + if len(n.Values) == 0 { + panic(fmt.Sprintf("missing init expr for %s", n.NameExprs[0].Name)) + } + if xnt, ok := nt.(*NativeType); ok { nt = go2GnoBaseType(xnt.Type) } diff --git a/gnovm/tests/files/const42.gno b/gnovm/tests/files/const42.gno new file mode 100644 index 00000000000..5763a2fc121 --- /dev/null +++ b/gnovm/tests/files/const42.gno @@ -0,0 +1,8 @@ +package main + +func main() { + const t int +} + +// Error: +// main/files/const42.gno:4:8: missing init expr for t \ No newline at end of file From f53a19f5a9c243c0828c1fb2be4ddb0820308ba9 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Thu, 7 Nov 2024 22:07:58 +0100 Subject: [PATCH 10/28] feat: add real and imag on todo --- gnovm/pkg/gnolang/type_check.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index d4ed40ab03b..4b0a2490a0b 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -238,7 +238,7 @@ Main: if cx, ok := vx.Func.(*ConstExpr); ok { if fv, ok := cx.V.(*FuncValue); ok { if fv.PkgPath == uversePkgPath { - // TODO: should support min, max + // TODO: should support min, max, real, imag switch { case fv.Name == "len": checkConstantExpr(store, last, vx.Args[0]) From 13136c5e4bc7674260905ae699d8f76c69f017cb Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Tue, 26 Nov 2024 21:00:00 +0100 Subject: [PATCH 11/28] fix: test --- gnovm/pkg/gnolang/preprocess.go | 9 +++++++++ gnovm/tests/files/const30.gno | 2 +- gnovm/tests/files/const37_native.gno | 2 +- gnovm/tests/files/const38.gno | 8 ++++---- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 9cacf7b60fa..a29e96cec02 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -2436,6 +2436,15 @@ func parseAssignFromExprList( for i := 0; i < numNames; i++ { sts[i] = nt } + if isConst { + if xnt, ok := nt.(*NativeType); ok { + nt = go2GnoBaseType(xnt.Type) + } + + if _, ok := baseOf(nt).(PrimitiveType); !ok { + panic(fmt.Sprintf("invalid constant type %s", nt.String())) + } + } // Convert if const to nt. for i := range valueExprs { checkOrConvertType(store, bn, &valueExprs[i], nt, false) diff --git a/gnovm/tests/files/const30.gno b/gnovm/tests/files/const30.gno index 3908ee26ee3..4a166013cdc 100644 --- a/gnovm/tests/files/const30.gno +++ b/gnovm/tests/files/const30.gno @@ -12,4 +12,4 @@ func main() { } // Error: -// main/files/const30.gno:10:8: v() (no value) used as value +// main/files/const30.gno:10:8: v (no value) used as value diff --git a/gnovm/tests/files/const37_native.gno b/gnovm/tests/files/const37_native.gno index 413fa011151..6a164328a3b 100644 --- a/gnovm/tests/files/const37_native.gno +++ b/gnovm/tests/files/const37_native.gno @@ -7,4 +7,4 @@ func main() { } // Error: -// main/files/const37_native.gno:6:8: time.UTC (variable of type gonative{*time.Location}) is not constant \ No newline at end of file +// main/files/const37_native.gno:6:8: time.UTC (variable of type *time.Location) is not constant diff --git a/gnovm/tests/files/const38.gno b/gnovm/tests/files/const38.gno index 1aee945d434..57f0b64f74f 100644 --- a/gnovm/tests/files/const38.gno +++ b/gnovm/tests/files/const38.gno @@ -1,11 +1,11 @@ package main -import "net" +import "std" func main() { - v := net.TCPAddr{} - const c = v.IP + v := std.Coin{} + const c = v.Denom } // Error: -// main/files/const38.gno:7:8: v.IP (variable of type gonative{net.TCPAddr}) is not constant \ No newline at end of file +// main/files/const38.gno:7:8: v.Denom (variable of type std.Coin) is not constant From 27e69b4c903492535092539a89011e6c6939930f Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Wed, 27 Nov 2024 09:52:44 +0100 Subject: [PATCH 12/28] feat: correct merge --- gnovm/pkg/gnolang/preprocess.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index a29e96cec02..3e36b8442f6 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -2454,6 +2454,13 @@ func parseAssignFromExprList( for i, vx := range valueExprs { vt := evalStaticTypeOf(store, bn, vx) sts[i] = vt + if xnt, ok := vt.(*NativeType); ok { + vt = go2GnoBaseType(xnt.Type) + } + + if _, ok := baseOf(vt).(PrimitiveType); !ok { + panic(fmt.Sprintf("invalid constant type %s", vt.String())) + } } } else { // T is nil, n not const => same as AssignStmt DEFINE // Convert n.Value to default type. From 5dc615ab6eab440d1d9da04c30dff5eef5157e6c Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Wed, 27 Nov 2024 09:58:24 +0100 Subject: [PATCH 13/28] feat: add test --- gnovm/tests/files/const43.gno | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 gnovm/tests/files/const43.gno diff --git a/gnovm/tests/files/const43.gno b/gnovm/tests/files/const43.gno new file mode 100644 index 00000000000..733338d056f --- /dev/null +++ b/gnovm/tests/files/const43.gno @@ -0,0 +1,10 @@ +package main + +func main() { + a := [2]int{1, 2} + + const b = a +} + +// Error: +// main/files/const43.gno:6:8: invalid constant type [2]int From 743962c161c3c8d76d66e806100110ef7b09ec8d Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Wed, 27 Nov 2024 15:14:46 +0100 Subject: [PATCH 14/28] fix: test --- gnovm/tests/files/const40.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/tests/files/const40.gno b/gnovm/tests/files/const40.gno index f0eeedeaeb9..718a67d38a5 100644 --- a/gnovm/tests/files/const40.gno +++ b/gnovm/tests/files/const40.gno @@ -5,4 +5,4 @@ func main() { } // Error: -// main/files/const40.gno:4:8: (const (array[] [0]string)) (value of type [0]string) is not constant \ No newline at end of file +// main/files/const40.gno:4:8: invalid constant type [0]string From 1ba4872c2badb651a8212a730245e1d86083181d Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Sat, 7 Dec 2024 22:19:40 +0100 Subject: [PATCH 15/28] refactor: replace checkConstantExpr with assertValidConstExpr for improved validation --- gnovm/pkg/gnolang/preprocess.go | 18 +------ gnovm/pkg/gnolang/type_check.go | 93 ++++++++++++++++++++------------- gnovm/tests/files/const23.gno | 2 +- gnovm/tests/files/const40.gno | 2 +- 4 files changed, 59 insertions(+), 56 deletions(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 23edab70e95..fa61a378c81 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -2292,7 +2292,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // NOTE: may or may not be a *ConstExpr, // but if not, make one now. for i, vx := range n.Values { - checkConstantExpr(store, last, vx) + assertValidConstExpr(store, last, n, vx) n.Values[i] = evalConst(store, last, vx) } } else { @@ -2449,15 +2449,6 @@ func parseAssignFromExprList( for i := 0; i < numNames; i++ { sts[i] = nt } - if isConst { - if xnt, ok := nt.(*NativeType); ok { - nt = go2GnoBaseType(xnt.Type) - } - - if _, ok := baseOf(nt).(PrimitiveType); !ok { - panic(fmt.Sprintf("invalid constant type %s", nt.String())) - } - } // Convert if const to nt. for i := range valueExprs { checkOrConvertType(store, bn, &valueExprs[i], nt, false) @@ -2467,13 +2458,6 @@ func parseAssignFromExprList( for i, vx := range valueExprs { vt := evalStaticTypeOf(store, bn, vx) sts[i] = vt - if xnt, ok := vt.(*NativeType); ok { - vt = go2GnoBaseType(xnt.Type) - } - - if _, ok := baseOf(vt).(PrimitiveType); !ok { - panic(fmt.Sprintf("invalid constant type %s", vt.String())) - } } } else { // T is nil, n not const => same as AssignStmt DEFINE // Convert n.Value to default type. diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index 336d7070681..921cfe7b36e 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -215,36 +215,51 @@ func assertAssignableTo(xt, dt Type, autoNative bool) { } } -func checkConstantExpr(store Store, last BlockNode, vx Expr) { +func assertValidConstExpr(store Store, last BlockNode, n *ValueDecl, expr Expr) { + if n.Type != nil { + nt := evalStaticType(store, last, n.Type) + if xnt, ok := nt.(*NativeType); ok { + nt = go2GnoBaseType(xnt.Type) + } + + if _, ok := baseOf(nt).(PrimitiveType); !ok { + panic(fmt.Sprintf("invalid constant type %s", nt.String())) + } + } + + assertValidConstantExprRecursively(store, last, expr, nil) +} + +func assertValidConstantExprRecursively(store Store, last BlockNode, currExpr, parentExpr Expr) { Main: - switch vx := vx.(type) { + switch currExpr := currExpr.(type) { case *NameExpr: - t := evalStaticTypeOf(store, last, vx) + t := evalStaticTypeOf(store, last, currExpr) if _, ok := t.(*ArrayType); ok { break Main } - panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.Name, t)) + panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.Name, t)) case *TypeAssertExpr: - panic(fmt.Sprintf("%s (comma, ok expression of type %s) is not constant", vx.String(), vx.Type)) + panic(fmt.Sprintf("%s (comma, ok expression of type %s) is not constant", currExpr.String(), currExpr.Type)) case *IndexExpr: - panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), vx.X)) + panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), currExpr.X)) case *CallExpr: - ift := evalStaticTypeOf(store, last, vx.Func) + ift := evalStaticTypeOf(store, last, currExpr.Func) switch baseOf(ift).(type) { case *FuncType: - tup := evalStaticTypeOfRaw(store, last, vx).(*tupleType) + tup := evalStaticTypeOfRaw(store, last, currExpr).(*tupleType) // check for built-in functions - if cx, ok := vx.Func.(*ConstExpr); ok { + if cx, ok := currExpr.Func.(*ConstExpr); ok { if fv, ok := cx.V.(*FuncValue); ok { if fv.PkgPath == uversePkgPath { // TODO: should support min, max, real, imag switch { case fv.Name == "len": - checkConstantExpr(store, last, vx.Args[0]) + assertValidConstantExprRecursively(store, last, currExpr.Args[0], currExpr) break Main case fv.Name == "cap": - checkConstantExpr(store, last, vx.Args[0]) + assertValidConstantExprRecursively(store, last, currExpr.Args[0], currExpr) break Main } } @@ -253,64 +268,64 @@ Main: switch { case len(tup.Elts) == 0: - panic(fmt.Sprintf("%s (no value) used as value", vx.String())) + panic(fmt.Sprintf("%s (no value) used as value", currExpr.String())) case len(tup.Elts) == 1: - panic(fmt.Sprintf("%s (value of type %s) is not constant", vx.String(), tup.Elts[0])) + panic(fmt.Sprintf("%s (value of type %s) is not constant", currExpr.String(), tup.Elts[0])) default: - panic(fmt.Sprintf("multiple-value %s (value of type %s) in single-value context", vx.String(), tup.Elts)) + panic(fmt.Sprintf("multiple-value %s (value of type %s) in single-value context", currExpr.String(), tup.Elts)) } case *TypeType: - for _, arg := range vx.Args { - checkConstantExpr(store, last, arg) + for _, arg := range currExpr.Args { + assertValidConstantExprRecursively(store, last, arg, currExpr) } case *NativeType: // Todo: should add a test after the fix of https://github.com/gnolang/gno/issues/3006 - ty := evalStaticType(store, last, vx.Func) - panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), ty)) + ty := evalStaticType(store, last, currExpr.Func) + panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), ty)) default: panic(fmt.Sprintf( "unexpected func type %v (%v)", ift, reflect.TypeOf(ift))) } case *BinaryExpr: - checkConstantExpr(store, last, vx.Left) - checkConstantExpr(store, last, vx.Right) + assertValidConstantExprRecursively(store, last, currExpr.Left, parentExpr) + assertValidConstantExprRecursively(store, last, currExpr.Right, parentExpr) case *SelectorExpr: - xt := evalStaticTypeOf(store, last, vx.X) + xt := evalStaticTypeOf(store, last, currExpr.X) switch xt := xt.(type) { case *PackageType: var pv *PackageValue - if cx, ok := vx.X.(*ConstExpr); ok { + if cx, ok := currExpr.X.(*ConstExpr); ok { // NOTE: *Machine.TestMemPackage() needs this // to pass in an imported package as *ConstEzpr. pv = cx.V.(*PackageValue) } else { // otherwise, packages can only be referred to by // *NameExprs, and cannot be copied. - pvc := evalConst(store, last, vx.X) + pvc := evalConst(store, last, currExpr.X) pv_, ok := pvc.V.(*PackageValue) if !ok { panic(fmt.Sprintf( "missing package in selector expr %s", - vx.String())) + currExpr.String())) } pv = pv_ } - if pv.GetBlock(store).Source.GetIsConst(store, vx.Sel) { + if pv.GetBlock(store).Source.GetIsConst(store, currExpr.Sel) { break Main } - tt := pv.GetBlock(store).Source.GetStaticTypeOf(store, vx.Sel) - panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), tt)) + tt := pv.GetBlock(store).Source.GetStaticTypeOf(store, currExpr.Sel) + panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), tt)) case *PointerType, *DeclaredType, *StructType, *InterfaceType: - ty := evalStaticTypeOf(store, last, vx.X) - panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), ty)) + ty := evalStaticTypeOf(store, last, currExpr.X) + panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), ty)) case *TypeType: - ty := evalStaticType(store, last, vx.X) - panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), ty)) + ty := evalStaticType(store, last, currExpr.X) + panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), ty)) case *NativeType: - ty := evalStaticTypeOf(store, last, vx.X) - panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), ty)) + ty := evalStaticTypeOf(store, last, currExpr.X) + panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), ty)) default: panic(fmt.Sprintf( "unexpected selector expression type %v", @@ -318,16 +333,20 @@ Main: } case *ArrayTypeExpr: + if parentExpr == nil { + ty := evalStaticTypeOf(store, last, currExpr) + panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), ty)) + } case *ConstExpr: case *BasicLitExpr: case *CompositeLitExpr: - checkConstantExpr(store, last, vx.Type) + assertValidConstantExprRecursively(store, last, currExpr.Type, parentExpr) default: - ift := evalStaticTypeOf(store, last, vx) + ift := evalStaticTypeOf(store, last, currExpr) if _, ok := ift.(*TypeType); ok { - ift = evalStaticType(store, last, vx) + ift = evalStaticType(store, last, currExpr) } - panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), ift)) + panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), ift)) } } diff --git a/gnovm/tests/files/const23.gno b/gnovm/tests/files/const23.gno index 7282c959b02..f445c3e8eb2 100644 --- a/gnovm/tests/files/const23.gno +++ b/gnovm/tests/files/const23.gno @@ -8,4 +8,4 @@ func main() { } // Error: -// main/files/const23.gno:6:8: [](const-type string) (variable of type []string) is not constant +// main/files/const23.gno:6:8: invalid constant type []string diff --git a/gnovm/tests/files/const40.gno b/gnovm/tests/files/const40.gno index 718a67d38a5..0b40b953011 100644 --- a/gnovm/tests/files/const40.gno +++ b/gnovm/tests/files/const40.gno @@ -5,4 +5,4 @@ func main() { } // Error: -// main/files/const40.gno:4:8: invalid constant type [0]string +// main/files/const40.gno:4:8: [(const (0 int))](const-type string) (variable of type type{}) is not constant From 54878f00b8eb36747d884e86cd5df86f7be76f1b Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Mon, 9 Dec 2024 20:40:59 +0100 Subject: [PATCH 16/28] refactor: rename assertValidConstantExprRecursively to assertValidConstValue for clarity --- gnovm/pkg/gnolang/type_check.go | 22 +++++++++++++--------- gnovm/tests/files/const43.gno | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index 921cfe7b36e..4e0a3037446 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -227,15 +227,19 @@ func assertValidConstExpr(store Store, last BlockNode, n *ValueDecl, expr Expr) } } - assertValidConstantExprRecursively(store, last, expr, nil) + assertValidConstValue(store, last, expr, nil) } -func assertValidConstantExprRecursively(store Store, last BlockNode, currExpr, parentExpr Expr) { +func assertValidConstValue(store Store, last BlockNode, currExpr, parentExpr Expr) { Main: switch currExpr := currExpr.(type) { case *NameExpr: t := evalStaticTypeOf(store, last, currExpr) - if _, ok := t.(*ArrayType); ok { + _, okArray := t.(*ArrayType) + _, okCallExpr := parentExpr.(*CallExpr) + + // special case for len, cap + if okArray && okCallExpr { break Main } panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.Name, t)) @@ -256,10 +260,10 @@ Main: // TODO: should support min, max, real, imag switch { case fv.Name == "len": - assertValidConstantExprRecursively(store, last, currExpr.Args[0], currExpr) + assertValidConstValue(store, last, currExpr.Args[0], currExpr) break Main case fv.Name == "cap": - assertValidConstantExprRecursively(store, last, currExpr.Args[0], currExpr) + assertValidConstValue(store, last, currExpr.Args[0], currExpr) break Main } } @@ -276,7 +280,7 @@ Main: } case *TypeType: for _, arg := range currExpr.Args { - assertValidConstantExprRecursively(store, last, arg, currExpr) + assertValidConstValue(store, last, arg, currExpr) } case *NativeType: // Todo: should add a test after the fix of https://github.com/gnolang/gno/issues/3006 @@ -288,8 +292,8 @@ Main: ift, reflect.TypeOf(ift))) } case *BinaryExpr: - assertValidConstantExprRecursively(store, last, currExpr.Left, parentExpr) - assertValidConstantExprRecursively(store, last, currExpr.Right, parentExpr) + assertValidConstValue(store, last, currExpr.Left, parentExpr) + assertValidConstValue(store, last, currExpr.Right, parentExpr) case *SelectorExpr: xt := evalStaticTypeOf(store, last, currExpr.X) switch xt := xt.(type) { @@ -340,7 +344,7 @@ Main: case *ConstExpr: case *BasicLitExpr: case *CompositeLitExpr: - assertValidConstantExprRecursively(store, last, currExpr.Type, parentExpr) + assertValidConstValue(store, last, currExpr.Type, parentExpr) default: ift := evalStaticTypeOf(store, last, currExpr) if _, ok := ift.(*TypeType); ok { diff --git a/gnovm/tests/files/const43.gno b/gnovm/tests/files/const43.gno index 733338d056f..8aefc791170 100644 --- a/gnovm/tests/files/const43.gno +++ b/gnovm/tests/files/const43.gno @@ -7,4 +7,4 @@ func main() { } // Error: -// main/files/const43.gno:6:8: invalid constant type [2]int +// main/files/const43.gno:6:8: a (variable of type [2]int) is not constant From 9001059f8f0cf5850c48e1221ff7751b41dc471f Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Thu, 26 Dec 2024 13:39:18 +0100 Subject: [PATCH 17/28] fix(gnovm): check type --- gnovm/pkg/gnolang/type_check.go | 13 +++++++++++++ gnovm/tests/files/const25.gno | 2 +- gnovm/tests/files/const31.gno | 2 +- gnovm/tests/files/const39.gno | 2 +- gnovm/tests/files/const40.gno | 2 +- gnovm/tests/files/const43.gno | 2 +- gnovm/tests/files/const44.gno | 10 ++++++++++ 7 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 gnovm/tests/files/const44.gno diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index 4362cc9921a..978ab1d0187 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -227,6 +227,19 @@ func assertValidConstExpr(store Store, last BlockNode, n *ValueDecl, expr Expr) } } + nt := evalStaticTypeOf(store, last, expr) + if xnt, ok := nt.(*NativeType); ok { + nt = go2GnoBaseType(xnt.Type) + } + + if nt == nil { + panic(fmt.Sprintf("%s (variable of type nil) is not constant", expr)) + } + + if _, ok := baseOf(nt).(PrimitiveType); !ok { + panic(fmt.Sprintf("%s (variable of type %s) is not constant", expr, nt)) + } + assertValidConstValue(store, last, expr, nil) } diff --git a/gnovm/tests/files/const25.gno b/gnovm/tests/files/const25.gno index c83495a4d20..64e0358bdef 100644 --- a/gnovm/tests/files/const25.gno +++ b/gnovm/tests/files/const25.gno @@ -8,4 +8,4 @@ func main() { } // Error: -// main/files/const25.gno:6:8: [](const-type string) (variable of type []string) is not constant +// main/files/const25.gno:6:8: [](const-type string){(const ("1" string))} (variable of type []string) is not constant diff --git a/gnovm/tests/files/const31.gno b/gnovm/tests/files/const31.gno index a192bd8ab86..e37577789e6 100644 --- a/gnovm/tests/files/const31.gno +++ b/gnovm/tests/files/const31.gno @@ -12,4 +12,4 @@ func main() { } // Error: -// main/files/const31.gno:10:8: multiple-value (const (v func()( string, string)))() (value of type [string string]) in single-value context +// main/files/const31.gno:10:8: (const (v func()( string, string)))() (variable of type (string,string)) is not constant diff --git a/gnovm/tests/files/const39.gno b/gnovm/tests/files/const39.gno index fa3cdb5f517..68ff7dd5630 100644 --- a/gnovm/tests/files/const39.gno +++ b/gnovm/tests/files/const39.gno @@ -11,4 +11,4 @@ func main() { } // Error: -// main/files/const39.gno:10:8: T.Mv (variable of type main.T) is not constant \ No newline at end of file +// main/files/const39.gno:10:8: T.Mv (variable of type func(tv main.T,a int)( int)) is not constant diff --git a/gnovm/tests/files/const40.gno b/gnovm/tests/files/const40.gno index 0b40b953011..d1dedc382e6 100644 --- a/gnovm/tests/files/const40.gno +++ b/gnovm/tests/files/const40.gno @@ -5,4 +5,4 @@ func main() { } // Error: -// main/files/const40.gno:4:8: [(const (0 int))](const-type string) (variable of type type{}) is not constant +// main/files/const40.gno:4:8: [(const (0 int))](const-type string){} (variable of type [0]string) is not constant diff --git a/gnovm/tests/files/const43.gno b/gnovm/tests/files/const43.gno index 8aefc791170..d929e526579 100644 --- a/gnovm/tests/files/const43.gno +++ b/gnovm/tests/files/const43.gno @@ -7,4 +7,4 @@ func main() { } // Error: -// main/files/const43.gno:6:8: a (variable of type [2]int) is not constant +// main/files/const43.gno:6:8: a (variable of type [2]int) is not constant diff --git a/gnovm/tests/files/const44.gno b/gnovm/tests/files/const44.gno new file mode 100644 index 00000000000..69924b8ea9d --- /dev/null +++ b/gnovm/tests/files/const44.gno @@ -0,0 +1,10 @@ +package main + +const a = interface{}(nil) + +func main() { + println("ok") +} + +// Error: +// main/files/const44.gno:3:7: (const (undefined)) (variable of type interface{}) is not constant From c45f63e91890294894d982d283300ae016af9c45 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Thu, 26 Dec 2024 15:43:39 +0100 Subject: [PATCH 18/28] fix(gnovm): enhance constant evaluation for array and map types --- gnovm/pkg/gnolang/type_check.go | 33 +++++++++++++++++++++++---------- gnovm/tests/files/const32.gno | 2 +- gnovm/tests/files/const36.gno | 2 +- gnovm/tests/files/const38.gno | 2 +- gnovm/tests/files/const45_a.gno | 14 ++++++++++++++ gnovm/tests/files/const45_b.gno | 14 ++++++++++++++ gnovm/tests/files/const46.gno | 10 ++++++++++ gnovm/tests/files/const47.gno | 10 ++++++++++ gnovm/tests/files/const48.gno | 9 +++++++++ 9 files changed, 83 insertions(+), 13 deletions(-) create mode 100644 gnovm/tests/files/const45_a.gno create mode 100644 gnovm/tests/files/const45_b.gno create mode 100644 gnovm/tests/files/const46.gno create mode 100644 gnovm/tests/files/const47.gno create mode 100644 gnovm/tests/files/const48.gno diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index 978ab1d0187..7676534ad26 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -257,8 +257,22 @@ Main: } panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.Name, t)) case *TypeAssertExpr: + ty := evalStaticTypeOf(store, last, currExpr) + _, okCallExpr := parentExpr.(*CallExpr) + _, okArray := ty.(*ArrayType) + if okCallExpr && okArray { + break Main + } panic(fmt.Sprintf("%s (comma, ok expression of type %s) is not constant", currExpr.String(), currExpr.Type)) case *IndexExpr: + ty := evalStaticTypeOf(store, last, currExpr) + _, okCallExpr := parentExpr.(*CallExpr) + _, okArray := ty.(*ArrayType) + if okCallExpr && okArray { + // TODO: should add a test after the fix of https://github.com/gnolang/gno/issues/3409 + break Main + } + panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), currExpr.X)) case *CallExpr: ift := evalStaticTypeOf(store, last, currExpr.Func) @@ -334,14 +348,15 @@ Main: tt := pv.GetBlock(store).Source.GetStaticTypeOf(store, currExpr.Sel) panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), tt)) - case *PointerType, *DeclaredType, *StructType, *InterfaceType: - ty := evalStaticTypeOf(store, last, currExpr.X) - panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), ty)) - case *TypeType: - ty := evalStaticType(store, last, currExpr.X) - panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), ty)) - case *NativeType: - ty := evalStaticTypeOf(store, last, currExpr.X) + case *PointerType, *DeclaredType, *StructType, *InterfaceType, *TypeType, *NativeType: + ty := evalStaticTypeOf(store, last, currExpr) + _, okCallExpr := parentExpr.(*CallExpr) + _, okArray := ty.(*ArrayType) + + // special case for len, cap + if okCallExpr && okArray { + break Main + } panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), ty)) default: panic(fmt.Sprintf( @@ -356,8 +371,6 @@ Main: } case *ConstExpr: case *BasicLitExpr: - case *CompositeLitExpr: - assertValidConstValue(store, last, currExpr.Type, parentExpr) default: ift := evalStaticTypeOf(store, last, currExpr) if _, ok := ift.(*TypeType); ok { diff --git a/gnovm/tests/files/const32.gno b/gnovm/tests/files/const32.gno index 75acf5fb446..83d3ae5e73c 100644 --- a/gnovm/tests/files/const32.gno +++ b/gnovm/tests/files/const32.gno @@ -8,4 +8,4 @@ func main() { } // Error: -// main/files/const32.gno:6:8: [](const-type string) (variable of type []string) is not constant +// main/files/const32.gno:6:8: [](const-type string){} (variable of type []string) is not constant diff --git a/gnovm/tests/files/const36.gno b/gnovm/tests/files/const36.gno index c6cf82c6f6b..8a677a35be9 100644 --- a/gnovm/tests/files/const36.gno +++ b/gnovm/tests/files/const36.gno @@ -10,4 +10,4 @@ func main() { } // Error: -// main/files/const36.gno:9:8: s.x (variable of type main.s) is not constant +// main/files/const36.gno:9:8: s.x (variable of type int) is not constant diff --git a/gnovm/tests/files/const38.gno b/gnovm/tests/files/const38.gno index 57f0b64f74f..815e74fa76a 100644 --- a/gnovm/tests/files/const38.gno +++ b/gnovm/tests/files/const38.gno @@ -8,4 +8,4 @@ func main() { } // Error: -// main/files/const38.gno:7:8: v.Denom (variable of type std.Coin) is not constant +// main/files/const38.gno:7:8: v.Denom (variable of type string) is not constant diff --git a/gnovm/tests/files/const45_a.gno b/gnovm/tests/files/const45_a.gno new file mode 100644 index 00000000000..fef13e2fc68 --- /dev/null +++ b/gnovm/tests/files/const45_a.gno @@ -0,0 +1,14 @@ +package main + +type MyStruct struct { + arr [2]int +} + +const a = len(MyStruct{arr: [2]int{1, 2}}.arr) + +func main() { + println("ok") +} + +// Output: +// ok diff --git a/gnovm/tests/files/const45_b.gno b/gnovm/tests/files/const45_b.gno new file mode 100644 index 00000000000..7da25d3268c --- /dev/null +++ b/gnovm/tests/files/const45_b.gno @@ -0,0 +1,14 @@ +package main + +type MyStruct struct { + arr []int +} + +const a = len(MyStruct{arr: []int{1, 2}}.arr) + +func main() { + println("ok") +} + +// Error: +// main/files/const45_b.gno:7:7: MyStruct{arr: [](const-type int){(const (1 int)), (const (2 int))}}.arr (variable of type []int) is not constant diff --git a/gnovm/tests/files/const46.gno b/gnovm/tests/files/const46.gno new file mode 100644 index 00000000000..4722cba294e --- /dev/null +++ b/gnovm/tests/files/const46.gno @@ -0,0 +1,10 @@ +package main + +const a = len(map[string][2]int{"arr": {1, 2}}["arr"]) + +func main() { + println("ok") +} + +// Output: +// ok diff --git a/gnovm/tests/files/const47.gno b/gnovm/tests/files/const47.gno new file mode 100644 index 00000000000..528ba469562 --- /dev/null +++ b/gnovm/tests/files/const47.gno @@ -0,0 +1,10 @@ +package main + +const a = len(map[string][2]interface{}{"arr": {1, 2}}["arr"]) + +func main() { + println("ok") +} + +// Output: +// ok diff --git a/gnovm/tests/files/const48.gno b/gnovm/tests/files/const48.gno new file mode 100644 index 00000000000..001fd911fa5 --- /dev/null +++ b/gnovm/tests/files/const48.gno @@ -0,0 +1,9 @@ +package main + +func main() { + const a = len(map[string][]int{"arr": {1, 2}}) + println("ok", a) +} + +// Error: +// main/files/const48.gno:4:8: map[(const-type string)] [](const-type int){(const ("arr" string)): (const-type []int){(const (1 int)), (const (2 int))}} (variable of type map[string][]int) is not constant From 7083830082561b415b1f071d947fb8614c56d728 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Thu, 26 Dec 2024 17:23:09 +0100 Subject: [PATCH 19/28] refactore(gnovm): some refactor --- gnovm/pkg/gnolang/type_check.go | 42 ++++++++++++++++++++++----------- gnovm/tests/files/const32.gno | 2 +- gnovm/tests/files/const48.gno | 2 +- gnovm/tests/files/const49.gno | 16 +++++++++++++ 4 files changed, 46 insertions(+), 16 deletions(-) create mode 100644 gnovm/tests/files/const49.gno diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index 7676534ad26..c5019b68022 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -248,28 +248,32 @@ Main: switch currExpr := currExpr.(type) { case *NameExpr: t := evalStaticTypeOf(store, last, currExpr) - _, okArray := t.(*ArrayType) - _, okCallExpr := parentExpr.(*CallExpr) - + if _, ok := t.(*TypeType); ok { + t = evalStaticType(store, last, currExpr) + } // special case for len, cap - if okArray && okCallExpr { + if isParentCallExprWithArrayArg(t, parentExpr) { break Main } panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.Name, t)) case *TypeAssertExpr: ty := evalStaticTypeOf(store, last, currExpr) - _, okCallExpr := parentExpr.(*CallExpr) - _, okArray := ty.(*ArrayType) - if okCallExpr && okArray { + if _, ok := ty.(*TypeType); ok { + ty = evalStaticType(store, last, currExpr) + } + // special case for len, cap + if isParentCallExprWithArrayArg(ty, parentExpr) { break Main } panic(fmt.Sprintf("%s (comma, ok expression of type %s) is not constant", currExpr.String(), currExpr.Type)) case *IndexExpr: ty := evalStaticTypeOf(store, last, currExpr) - _, okCallExpr := parentExpr.(*CallExpr) - _, okArray := ty.(*ArrayType) - if okCallExpr && okArray { - // TODO: should add a test after the fix of https://github.com/gnolang/gno/issues/3409 + if _, ok := ty.(*TypeType); ok { + ty = evalStaticType(store, last, currExpr) + } + // TODO: should add a test after the fix of https://github.com/gnolang/gno/issues/3409 + // special case for len, cap + if isParentCallExprWithArrayArg(ty, parentExpr) { break Main } @@ -350,11 +354,12 @@ Main: panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), tt)) case *PointerType, *DeclaredType, *StructType, *InterfaceType, *TypeType, *NativeType: ty := evalStaticTypeOf(store, last, currExpr) - _, okCallExpr := parentExpr.(*CallExpr) - _, okArray := ty.(*ArrayType) + if _, ok := ty.(*TypeType); ok { + ty = evalStaticType(store, last, currExpr) + } // special case for len, cap - if okCallExpr && okArray { + if isParentCallExprWithArrayArg(ty, parentExpr) { break Main } panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), ty)) @@ -371,6 +376,8 @@ Main: } case *ConstExpr: case *BasicLitExpr: + case *CompositeLitExpr: + assertValidConstValue(store, last, currExpr.Type, parentExpr) default: ift := evalStaticTypeOf(store, last, currExpr) if _, ok := ift.(*TypeType); ok { @@ -380,6 +387,13 @@ Main: } } +func isParentCallExprWithArrayArg(currType Type, parentExpr Expr) bool { + _, okArray := baseOf(currType).(*ArrayType) + _, okCallExpr := parentExpr.(*CallExpr) + + return okArray && okCallExpr +} + // checkValDefineMismatch checks for mismatch between the number of variables and values in a ValueDecl or AssignStmt. func checkValDefineMismatch(n Node) { var ( diff --git a/gnovm/tests/files/const32.gno b/gnovm/tests/files/const32.gno index 83d3ae5e73c..75acf5fb446 100644 --- a/gnovm/tests/files/const32.gno +++ b/gnovm/tests/files/const32.gno @@ -8,4 +8,4 @@ func main() { } // Error: -// main/files/const32.gno:6:8: [](const-type string){} (variable of type []string) is not constant +// main/files/const32.gno:6:8: [](const-type string) (variable of type []string) is not constant diff --git a/gnovm/tests/files/const48.gno b/gnovm/tests/files/const48.gno index 001fd911fa5..4712a3929c1 100644 --- a/gnovm/tests/files/const48.gno +++ b/gnovm/tests/files/const48.gno @@ -6,4 +6,4 @@ func main() { } // Error: -// main/files/const48.gno:4:8: map[(const-type string)] [](const-type int){(const ("arr" string)): (const-type []int){(const (1 int)), (const (2 int))}} (variable of type map[string][]int) is not constant +// main/files/const48.gno:4:8: map[(const-type string)] [](const-type int) (variable of type map[string][]int) is not constant diff --git a/gnovm/tests/files/const49.gno b/gnovm/tests/files/const49.gno new file mode 100644 index 00000000000..4b3f38e4d5e --- /dev/null +++ b/gnovm/tests/files/const49.gno @@ -0,0 +1,16 @@ +package main + +import "io" + +type ( + s [2]int + m s +) + +func main() { + const v = len(m{1, 2}) + println(v) +} + +// Output: +// 2 From 4da7934658d4e73d642cc3ceb554c4f6b0283f47 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Thu, 26 Dec 2024 17:37:39 +0100 Subject: [PATCH 20/28] fix(gnovm): remove unnecessary panic for non-constant array type expressions --- gnovm/pkg/gnolang/type_check.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index c5019b68022..ad517f07a5b 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -369,11 +369,6 @@ Main: reflect.TypeOf(xt))) } - case *ArrayTypeExpr: - if parentExpr == nil { - ty := evalStaticTypeOf(store, last, currExpr) - panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), ty)) - } case *ConstExpr: case *BasicLitExpr: case *CompositeLitExpr: From 29b6ab00ebe43111f2bcbbf376d5400c49818414 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Wed, 1 Jan 2025 17:00:11 +0100 Subject: [PATCH 21/28] feat(gnolang): add comment --- gnovm/pkg/gnolang/type_check.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index ad517f07a5b..2d193c7f969 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -382,6 +382,9 @@ Main: } } +// isParentCallExprWithArrayArg checks if the parent expression is a call expression with an array argument. +// This is used to determine whether to skip the constant value check. +// This is because the parent expression may be a call to the len or cap built-in functions. func isParentCallExprWithArrayArg(currType Type, parentExpr Expr) bool { _, okArray := baseOf(currType).(*ArrayType) _, okCallExpr := parentExpr.(*CallExpr) From 700c1da5ea709f96cd56a2e17b07b9f6a755a6fe Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Thu, 9 Jan 2025 20:49:01 +0100 Subject: [PATCH 22/28] refactor: simplify constant expression validation logic and update error messages --- gnovm/pkg/gnolang/type_check.go | 78 ++++++++------------------------- gnovm/tests/files/const28.gno | 2 +- gnovm/tests/files/const29.gno | 2 +- gnovm/tests/files/const32.gno | 2 +- gnovm/tests/files/const33.gno | 2 +- gnovm/tests/files/const36.gno | 2 +- gnovm/tests/files/const38.gno | 2 +- gnovm/tests/files/const45_b.gno | 2 +- gnovm/tests/files/const48.gno | 2 +- gnovm/tests/files/const50.gno | 15 +++++++ 10 files changed, 41 insertions(+), 68 deletions(-) create mode 100644 gnovm/tests/files/const50.gno diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index 2d193c7f969..beb3f5ed4b9 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -240,44 +240,20 @@ func assertValidConstExpr(store Store, last BlockNode, n *ValueDecl, expr Expr) panic(fmt.Sprintf("%s (variable of type %s) is not constant", expr, nt)) } - assertValidConstValue(store, last, expr, nil) + assertValidConstValue(store, last, expr) } -func assertValidConstValue(store Store, last BlockNode, currExpr, parentExpr Expr) { +func assertValidConstValue(store Store, last BlockNode, currExpr Expr) { Main: switch currExpr := currExpr.(type) { - case *NameExpr: - t := evalStaticTypeOf(store, last, currExpr) - if _, ok := t.(*TypeType); ok { - t = evalStaticType(store, last, currExpr) - } - // special case for len, cap - if isParentCallExprWithArrayArg(t, parentExpr) { - break Main - } - panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.Name, t)) + case *ConstExpr: + case *BasicLitExpr: case *TypeAssertExpr: ty := evalStaticTypeOf(store, last, currExpr) if _, ok := ty.(*TypeType); ok { ty = evalStaticType(store, last, currExpr) } - // special case for len, cap - if isParentCallExprWithArrayArg(ty, parentExpr) { - break Main - } panic(fmt.Sprintf("%s (comma, ok expression of type %s) is not constant", currExpr.String(), currExpr.Type)) - case *IndexExpr: - ty := evalStaticTypeOf(store, last, currExpr) - if _, ok := ty.(*TypeType); ok { - ty = evalStaticType(store, last, currExpr) - } - // TODO: should add a test after the fix of https://github.com/gnolang/gno/issues/3409 - // special case for len, cap - if isParentCallExprWithArrayArg(ty, parentExpr) { - break Main - } - - panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), currExpr.X)) case *CallExpr: ift := evalStaticTypeOf(store, last, currExpr.Func) switch baseOf(ift).(type) { @@ -291,11 +267,19 @@ Main: // TODO: should support min, max, real, imag switch { case fv.Name == "len": - assertValidConstValue(store, last, currExpr.Args[0], currExpr) + at := evalStaticTypeOf(store, last, currExpr.Args[0]) + if _, ok := baseOf(at).(*ArrayType); ok { + // ok + break Main + } + assertValidConstValue(store, last, currExpr.Args[0]) break Main case fv.Name == "cap": - assertValidConstValue(store, last, currExpr.Args[0], currExpr) - break Main + at := evalStaticTypeOf(store, last, currExpr.Args[0]) + if _, ok := baseOf(at).(*ArrayType); ok { + // ok + break Main + } } } } @@ -311,7 +295,7 @@ Main: } case *TypeType: for _, arg := range currExpr.Args { - assertValidConstValue(store, last, arg, currExpr) + assertValidConstValue(store, last, arg) } case *NativeType: // Todo: should add a test after the fix of https://github.com/gnolang/gno/issues/3006 @@ -323,8 +307,8 @@ Main: ift, reflect.TypeOf(ift))) } case *BinaryExpr: - assertValidConstValue(store, last, currExpr.Left, parentExpr) - assertValidConstValue(store, last, currExpr.Right, parentExpr) + assertValidConstValue(store, last, currExpr.Left) + assertValidConstValue(store, last, currExpr.Right) case *SelectorExpr: xt := evalStaticTypeOf(store, last, currExpr.X) switch xt := xt.(type) { @@ -352,27 +336,11 @@ Main: tt := pv.GetBlock(store).Source.GetStaticTypeOf(store, currExpr.Sel) panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), tt)) - case *PointerType, *DeclaredType, *StructType, *InterfaceType, *TypeType, *NativeType: - ty := evalStaticTypeOf(store, last, currExpr) - if _, ok := ty.(*TypeType); ok { - ty = evalStaticType(store, last, currExpr) - } - - // special case for len, cap - if isParentCallExprWithArrayArg(ty, parentExpr) { - break Main - } - panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), ty)) default: panic(fmt.Sprintf( "unexpected selector expression type %v", reflect.TypeOf(xt))) } - - case *ConstExpr: - case *BasicLitExpr: - case *CompositeLitExpr: - assertValidConstValue(store, last, currExpr.Type, parentExpr) default: ift := evalStaticTypeOf(store, last, currExpr) if _, ok := ift.(*TypeType); ok { @@ -382,16 +350,6 @@ Main: } } -// isParentCallExprWithArrayArg checks if the parent expression is a call expression with an array argument. -// This is used to determine whether to skip the constant value check. -// This is because the parent expression may be a call to the len or cap built-in functions. -func isParentCallExprWithArrayArg(currType Type, parentExpr Expr) bool { - _, okArray := baseOf(currType).(*ArrayType) - _, okCallExpr := parentExpr.(*CallExpr) - - return okArray && okCallExpr -} - // checkValDefineMismatch checks for mismatch between the number of variables and values in a ValueDecl or AssignStmt. func checkValDefineMismatch(n Node) { var ( diff --git a/gnovm/tests/files/const28.gno b/gnovm/tests/files/const28.gno index 7b6b5648bf1..e4762c3205c 100644 --- a/gnovm/tests/files/const28.gno +++ b/gnovm/tests/files/const28.gno @@ -9,4 +9,4 @@ func main() { } // Error: -// main/files/const28.gno:7:8: s[(const (0 int))] (variable of type s) is not constant +// main/files/const28.gno:7:8: s[(const (0 int))] (variable of type string) is not constant diff --git a/gnovm/tests/files/const29.gno b/gnovm/tests/files/const29.gno index 0ebd8bcab6c..41d8d0a816d 100644 --- a/gnovm/tests/files/const29.gno +++ b/gnovm/tests/files/const29.gno @@ -9,4 +9,4 @@ func main() { } // Error: -// main/files/const29.gno:7:8: s (variable of type string) is not constant +// main/files/const29.gno:7:8: s (variable of type string) is not constant diff --git a/gnovm/tests/files/const32.gno b/gnovm/tests/files/const32.gno index 75acf5fb446..83d3ae5e73c 100644 --- a/gnovm/tests/files/const32.gno +++ b/gnovm/tests/files/const32.gno @@ -8,4 +8,4 @@ func main() { } // Error: -// main/files/const32.gno:6:8: [](const-type string) (variable of type []string) is not constant +// main/files/const32.gno:6:8: [](const-type string){} (variable of type []string) is not constant diff --git a/gnovm/tests/files/const33.gno b/gnovm/tests/files/const33.gno index 989fe7dda5b..42652d3b458 100644 --- a/gnovm/tests/files/const33.gno +++ b/gnovm/tests/files/const33.gno @@ -9,4 +9,4 @@ func main() { println("ok") } // Error: -// main/files/const33.gno:6:7: x (variable of type int) is not constant +// main/files/const33.gno:6:7: x (variable of type int) is not constant diff --git a/gnovm/tests/files/const36.gno b/gnovm/tests/files/const36.gno index 8a677a35be9..d31d20ad17b 100644 --- a/gnovm/tests/files/const36.gno +++ b/gnovm/tests/files/const36.gno @@ -10,4 +10,4 @@ func main() { } // Error: -// main/files/const36.gno:9:8: s.x (variable of type int) is not constant +// main/files/const36.gno:9:8: unexpected selector expression type *gnolang.DeclaredType diff --git a/gnovm/tests/files/const38.gno b/gnovm/tests/files/const38.gno index 815e74fa76a..9a2c4be55e9 100644 --- a/gnovm/tests/files/const38.gno +++ b/gnovm/tests/files/const38.gno @@ -8,4 +8,4 @@ func main() { } // Error: -// main/files/const38.gno:7:8: v.Denom (variable of type string) is not constant +// main/files/const38.gno:7:8: unexpected selector expression type *gnolang.DeclaredType diff --git a/gnovm/tests/files/const45_b.gno b/gnovm/tests/files/const45_b.gno index 7da25d3268c..15581790556 100644 --- a/gnovm/tests/files/const45_b.gno +++ b/gnovm/tests/files/const45_b.gno @@ -11,4 +11,4 @@ func main() { } // Error: -// main/files/const45_b.gno:7:7: MyStruct{arr: [](const-type int){(const (1 int)), (const (2 int))}}.arr (variable of type []int) is not constant +// main/files/const45_b.gno:7:7: unexpected selector expression type *gnolang.DeclaredType diff --git a/gnovm/tests/files/const48.gno b/gnovm/tests/files/const48.gno index 4712a3929c1..001fd911fa5 100644 --- a/gnovm/tests/files/const48.gno +++ b/gnovm/tests/files/const48.gno @@ -6,4 +6,4 @@ func main() { } // Error: -// main/files/const48.gno:4:8: map[(const-type string)] [](const-type int) (variable of type map[string][]int) is not constant +// main/files/const48.gno:4:8: map[(const-type string)] [](const-type int){(const ("arr" string)): (const-type []int){(const (1 int)), (const (2 int))}} (variable of type map[string][]int) is not constant diff --git a/gnovm/tests/files/const50.gno b/gnovm/tests/files/const50.gno new file mode 100644 index 00000000000..056d59a7f2b --- /dev/null +++ b/gnovm/tests/files/const50.gno @@ -0,0 +1,15 @@ +package main + +var ( + x = "a" + y = "b" +) + +const v = len(x) + +func main() { + println("ok") +} + +// Error: +// main/files/const50.gno:8:7: x (variable of type string) is not constant From 0cbd6f0f827dc2500679ed045a50935c066dbd57 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Fri, 10 Jan 2025 11:48:01 +0100 Subject: [PATCH 23/28] feat(gnolang): enhance constant value validation in type checking --- gnovm/pkg/gnolang/type_check.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index beb3f5ed4b9..ed10778e909 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -280,6 +280,8 @@ Main: // ok break Main } + assertValidConstValue(store, last, currExpr.Args[0]) + break Main } } } @@ -336,6 +338,12 @@ Main: tt := pv.GetBlock(store).Source.GetStaticTypeOf(store, currExpr.Sel) panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), tt)) + case *PointerType, *DeclaredType, *StructType, *InterfaceType, *TypeType, *NativeType: + ty := evalStaticTypeOf(store, last, currExpr) + if _, ok := ty.(*TypeType); ok { + ty = evalStaticType(store, last, currExpr) + } + panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), ty)) default: panic(fmt.Sprintf( "unexpected selector expression type %v", From f81280579e0995d8ae17419b3ac37d701d36a1c1 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Sun, 12 Jan 2025 23:53:33 +0100 Subject: [PATCH 24/28] fix(gnolang): add support for unary expressions in constant value validation --- gnovm/pkg/gnolang/type_check.go | 3 +++ gnovm/tests/files/const24.gno | 3 +++ 2 files changed, 6 insertions(+) diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index ed10778e909..863f6f75e72 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -248,6 +248,9 @@ Main: switch currExpr := currExpr.(type) { case *ConstExpr: case *BasicLitExpr: + case *UnaryExpr: + // *, & is filter out previously since they are not primitive + assertValidConstValue(store, last, currExpr.X) case *TypeAssertExpr: ty := evalStaticTypeOf(store, last, currExpr) if _, ok := ty.(*TypeType); ok { diff --git a/gnovm/tests/files/const24.gno b/gnovm/tests/files/const24.gno index c9cb14ef369..d82c50c86aa 100644 --- a/gnovm/tests/files/const24.gno +++ b/gnovm/tests/files/const24.gno @@ -29,6 +29,7 @@ func main() { const v = len(ars) const w = cap(ars) const x = time.Second + const y = +len("ay") fmt.Println(a) fmt.Println(b) @@ -52,6 +53,7 @@ func main() { fmt.Println(v) fmt.Println(w) println(x) + fmt.Println(y) } // Output: @@ -77,3 +79,4 @@ func main() { // 10 // 10 // 1s +// 2 From 09e32a0795a45f78509c472b0744afb9aa20a4ee Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Mon, 13 Jan 2025 00:17:05 +0100 Subject: [PATCH 25/28] fix(gnolang): correct test --- gnovm/tests/files/const35.gno | 2 +- gnovm/tests/files/const36.gno | 2 +- gnovm/tests/files/const38.gno | 2 +- gnovm/tests/files/const45_b.gno | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gnovm/tests/files/const35.gno b/gnovm/tests/files/const35.gno index e684edb7263..e85d68eb8db 100644 --- a/gnovm/tests/files/const35.gno +++ b/gnovm/tests/files/const35.gno @@ -9,4 +9,4 @@ func main() { } // Error: -// main/files/const35.gno:5:7: +(const (1 bigint)) << (const-type uint)(x) (variable of type bigint) is not constant +// main/files/const35.gno:5:7: x (variable of type int) is not constant diff --git a/gnovm/tests/files/const36.gno b/gnovm/tests/files/const36.gno index d31d20ad17b..8a677a35be9 100644 --- a/gnovm/tests/files/const36.gno +++ b/gnovm/tests/files/const36.gno @@ -10,4 +10,4 @@ func main() { } // Error: -// main/files/const36.gno:9:8: unexpected selector expression type *gnolang.DeclaredType +// main/files/const36.gno:9:8: s.x (variable of type int) is not constant diff --git a/gnovm/tests/files/const38.gno b/gnovm/tests/files/const38.gno index 9a2c4be55e9..815e74fa76a 100644 --- a/gnovm/tests/files/const38.gno +++ b/gnovm/tests/files/const38.gno @@ -8,4 +8,4 @@ func main() { } // Error: -// main/files/const38.gno:7:8: unexpected selector expression type *gnolang.DeclaredType +// main/files/const38.gno:7:8: v.Denom (variable of type string) is not constant diff --git a/gnovm/tests/files/const45_b.gno b/gnovm/tests/files/const45_b.gno index 15581790556..7da25d3268c 100644 --- a/gnovm/tests/files/const45_b.gno +++ b/gnovm/tests/files/const45_b.gno @@ -11,4 +11,4 @@ func main() { } // Error: -// main/files/const45_b.gno:7:7: unexpected selector expression type *gnolang.DeclaredType +// main/files/const45_b.gno:7:7: MyStruct{arr: [](const-type int){(const (1 int)), (const (2 int))}}.arr (variable of type []int) is not constant From a068cd126b24215bb2a04fa2f33aa9c41bee2180 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Mon, 13 Jan 2025 00:19:53 +0100 Subject: [PATCH 26/28] fix(gnolang): simplify variable declaration in const50.gno --- gnovm/tests/files/const50.gno | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/gnovm/tests/files/const50.gno b/gnovm/tests/files/const50.gno index 056d59a7f2b..b21b9c8b3bc 100644 --- a/gnovm/tests/files/const50.gno +++ b/gnovm/tests/files/const50.gno @@ -1,9 +1,6 @@ package main -var ( - x = "a" - y = "b" -) +var x = "a" const v = len(x) From e63f16dbba96bda52fc3e64f3e18c25f4c394d8b Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Mon, 13 Jan 2025 11:38:01 +0100 Subject: [PATCH 27/28] fix(gnolang): update error message location in const50.gno --- gnovm/tests/files/const50.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/tests/files/const50.gno b/gnovm/tests/files/const50.gno index b21b9c8b3bc..5144173bf74 100644 --- a/gnovm/tests/files/const50.gno +++ b/gnovm/tests/files/const50.gno @@ -9,4 +9,4 @@ func main() { } // Error: -// main/files/const50.gno:8:7: x (variable of type string) is not constant +// main/files/const50.gno:5:7: x (variable of type string) is not constant From 81ed3c91d42cfdb33adea48a4b8cd0ca894c8b66 Mon Sep 17 00:00:00 2001 From: Omar Sy Date: Mon, 13 Jan 2025 12:51:09 +0100 Subject: [PATCH 28/28] fix(gnolang): remove unused case for BasicLitExpr in type_check --- gnovm/pkg/gnolang/type_check.go | 1 - 1 file changed, 1 deletion(-) diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index 863f6f75e72..f96cb71e4b6 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -247,7 +247,6 @@ func assertValidConstValue(store Store, last BlockNode, currExpr Expr) { Main: switch currExpr := currExpr.(type) { case *ConstExpr: - case *BasicLitExpr: case *UnaryExpr: // *, & is filter out previously since they are not primitive assertValidConstValue(store, last, currExpr.X)