Skip to content

Commit

Permalink
feat(gnovm): align Gno constant handling with Go specifications
Browse files Browse the repository at this point in the history
  • Loading branch information
omarsy committed Sep 26, 2024
1 parent 5503cca commit e9a9be7
Show file tree
Hide file tree
Showing 14 changed files with 323 additions and 2 deletions.
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 @@ 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 {
Expand Down Expand Up @@ -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 {

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

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

0 comments on commit e9a9be7

Please sign in to comment.