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 21, 2024
1 parent 5503cca commit 78ec19f
Show file tree
Hide file tree
Showing 11 changed files with 250 additions and 0 deletions.
10 changes: 10 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,12 @@ 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 _, ok := baseOf(nt).(PrimitiveType); !ok {
panic(fmt.Sprintf("invalid constant type %s", nt.String()))
}
}

for i := 0; i < numNames; i++ {
sts[i] = nt
}
Expand All @@ -2279,6 +2286,9 @@ 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 _, ok := baseOf(vt).(PrimitiveType); !ok {
panic(fmt.Sprintf("%s (value of type %s) is not constant", vx.String(), vt.String()))
}
sts[i] = vt
}
} else {
Expand Down
66 changes: 66 additions & 0 deletions gnovm/pkg/gnolang/type_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,72 @@ func assertAssignableTo(xt, dt Type, autoNative bool) {
}
}

func checkConstantExpr(store Store, last BlockNode, vx Expr) {
Main:
switch vx := vx.(type) {
case *NameExpr:
panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.Name, evalStaticTypeOf(store, last, vx)))
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)
tup := evalStaticTypeOfRaw(store, last, vx).(*tupleType)
switch baseOf(ift).(type) {
case *FuncType:

// check for built-in functions
if cx, ok := vx.Func.(*ConstExpr); ok {
if fv, ok := cx.V.(*FuncValue); ok {
if fv.PkgPath == uversePkgPath {
switch {
case fv.Name == "len":
checkConstantExpr(store, last, vx.Args[0])
break Main
case fv.Name == "min":
checkConstantExpr(store, last, vx.Args[0])
checkConstantExpr(store, last, vx.Args[1])
break Main
case fv.Name == "max":
checkConstantExpr(store, last, vx.Args[0])
checkConstantExpr(store, last, vx.Args[1])
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:
fmt.Printf("TypeType\n")
case *NativeType:
fmt.Printf("NativeType\n")
default:
fmt.Printf("ValueDecl: %T\n", vx)
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 *ConstExpr:
case *BasicLitExpr:
default:
ift := evalStaticTypeOf(store, last, vx)
panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), ift))

}

Check failure on line 281 in gnovm/pkg/gnolang/type_check.go

View workflow job for this annotation

GitHub Actions / Run Main / Go Linter / lint

unnecessary trailing newline (whitespace)
}

// 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: invalid constant type []string
67 changes: 67 additions & 0 deletions gnovm/tests/files/const24.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package main

import "fmt"

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 = min(i, min(1, 3))
const v = max(i, max(1, 3))
const w = min(i, max(1, 3)) + max(i, min(1, 3)) + 3
const x+=w

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

// Output:
// 1000000
// 1
// 1e+06
// Hello, World!
// 97
// true
// 1000
// 1
// 1
// 1
// 1
// 1
// 1
// 1
// 1
// 1e+06
// 1e+06
// 1
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 (slice[("1" string)] []string)) (value 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/const25.gno:6:8: (const (slice[("1" string)] []string)) (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<VPUverse(0)>.(int<VPUverse(0)>) (comma, ok expression of type int<VPUverse(0)>) 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/const25.gno:6:8: (const (slice[("1" string)] []string)) (value of type []string) 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/const29.gno:7:8: s (variable of type string) is not constant
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/const29.gno:7:8: s (variable of type string) is not constant

0 comments on commit 78ec19f

Please sign in to comment.