Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(gnovm): align Gno constant handling with Go specifications #2828

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/keystore/keystore_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

func TestRender(t *testing.T) {
const (
var (
author1 std.Address = testutils.TestAddress("author1")
author2 std.Address = testutils.TestAddress("author2")
)
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/microblog/microblog_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

func TestMicroblog(t *testing.T) {
const (
var (
author1 std.Address = testutils.TestAddress("author1")
author2 std.Address = testutils.TestAddress("author2")
)
Expand Down
18 changes: 18 additions & 0 deletions gnovm/pkg/gnolang/preprocess.go
Original file line number Diff line number Diff line change
Expand Up @@ -2188,6 +2188,7 @@
// 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 {
Expand Down Expand Up @@ -2268,6 +2269,16 @@
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 {

Check warning on line 2273 in gnovm/pkg/gnolang/preprocess.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L2273

Added line #L2273 was not covered by tests
nt = go2GnoBaseType(xnt.Type)
}

if _, ok := baseOf(nt).(PrimitiveType); !ok {

Check warning on line 2277 in gnovm/pkg/gnolang/preprocess.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L2277

Added line #L2277 was not covered by tests
panic(fmt.Sprintf("invalid constant type %s", nt.String()))
}

Check warning on line 2279 in gnovm/pkg/gnolang/preprocess.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/preprocess.go#L2279

Added line #L2279 was not covered by tests
}

for i := 0; i < numNames; i++ {
sts[i] = nt
}
Expand All @@ -2279,6 +2290,13 @@
// 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 {
Expand Down
109 changes: 109 additions & 0 deletions gnovm/pkg/gnolang/type_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,115 @@
}
}

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])

Check warning on line 244 in gnovm/pkg/gnolang/type_check.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/type_check.go#L243-L244

Added lines #L243 - L244 were not covered by tests
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

Check warning on line 287 in gnovm/pkg/gnolang/type_check.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/type_check.go#L287

Added line #L287 was not covered by tests
// // *NameExprs, and cannot be copied.
// pvc := evalConst(store, last, vx.X)
// pv_, ok := pvc.V.(*PackageValue)
// if !ok {
// panic(fmt.Sprintf(

Check warning on line 292 in gnovm/pkg/gnolang/type_check.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/type_check.go#L292

Added line #L292 was not covered by tests
// "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))
}
}

Check warning on line 326 in gnovm/pkg/gnolang/type_check.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/type_check.go#L324-L326

Added lines #L324 - L326 were not covered by tests
// checkValDefineMismatch checks for mismatch between the number of variables and values in a ValueDecl or AssignStmt.
func checkValDefineMismatch(n Node) {
var (
Expand Down
11 changes: 11 additions & 0 deletions gnovm/tests/files/const23.gno
Original file line number Diff line number Diff line change
@@ -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
76 changes: 76 additions & 0 deletions gnovm/tests/files/const24.gno
Original file line number Diff line number Diff line change
@@ -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
11 changes: 11 additions & 0 deletions gnovm/tests/files/const25.gno
Original file line number Diff line number Diff line change
@@ -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
15 changes: 15 additions & 0 deletions gnovm/tests/files/const26.gno
Original file line number Diff line number Diff line change
@@ -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<VPBlock(3,0)>() (value of type string) is not constant
16 changes: 16 additions & 0 deletions gnovm/tests/files/const27.gno
Original file line number Diff line number Diff line change
@@ -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<VPBlock(1,0)>.((const-type int)) (comma, ok expression of type (const-type int)) is not constant
12 changes: 12 additions & 0 deletions gnovm/tests/files/const28.gno
Original file line number Diff line number Diff line change
@@ -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<VPBlock(1,0)>[(const (0 int))] (variable of type s<VPBlock(1,0)>) is not constant
12 changes: 12 additions & 0 deletions gnovm/tests/files/const29.gno
Original file line number Diff line number Diff line change
@@ -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
15 changes: 15 additions & 0 deletions gnovm/tests/files/const30.gno
Original file line number Diff line number Diff line change
@@ -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<VPBlock(3,0)>() (no value) used as value
15 changes: 15 additions & 0 deletions gnovm/tests/files/const31.gno
Original file line number Diff line number Diff line change
@@ -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
11 changes: 11 additions & 0 deletions gnovm/tests/files/const32.gno
Original file line number Diff line number Diff line change
@@ -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
Loading