-
Notifications
You must be signed in to change notification settings - Fork 385
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
base: master
Are you sure you want to change the base?
Changes from all commits
3d5030a
2543294
bb2d9fd
c7643eb
b80a890
ff74523
761ef1a
5fa41e9
90c8c5b
e74e6e9
3a52e14
f53a19f
194883a
13136c5
27e69b4
5dc615a
6fb3cf7
743962c
35d6ae1
459f0da
1ba4872
54878f0
72b8910
88dae32
59a7d85
9001059
c45f63e
7083830
4da7934
6ef45d4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -215,6 +215,180 @@ | |
} | ||
} | ||
|
||
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())) | ||
} | ||
} | ||
|
||
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) | ||
} | ||
|
||
func assertValidConstValue(store Store, last BlockNode, currExpr, parentExpr 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 | ||
ltzmaxwell marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.Name, t)) | ||
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) { | ||
case *FuncType: | ||
tup := evalStaticTypeOfRaw(store, last, currExpr).(*tupleType) | ||
|
||
// check for built-in functions | ||
petar-dambovaliev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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": | ||
assertValidConstValue(store, last, currExpr.Args[0], currExpr) | ||
break Main | ||
case fv.Name == "cap": | ||
assertValidConstValue(store, last, currExpr.Args[0], currExpr) | ||
break Main | ||
} | ||
} | ||
} | ||
} | ||
|
||
switch { | ||
case len(tup.Elts) == 0: | ||
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", currExpr.String(), tup.Elts[0])) | ||
default: | ||
panic(fmt.Sprintf("multiple-value %s (value of type %s) in single-value context", currExpr.String(), tup.Elts)) | ||
} | ||
case *TypeType: | ||
for _, arg := range currExpr.Args { | ||
assertValidConstValue(store, last, arg, currExpr) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please check this: package main
const a = interface{}(nil)
func main() {
println("ok")
} seems also need to check the type, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My bad break on my last refactoring when I move the check type Nice catch ^^ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so the above clause: |
||
case *NativeType: | ||
ltzmaxwell marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Todo: should add a test after the fix of https://github.com/gnolang/gno/issues/3006 | ||
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: | ||
assertValidConstValue(store, last, currExpr.Left, parentExpr) | ||
assertValidConstValue(store, last, currExpr.Right, parentExpr) | ||
case *SelectorExpr: | ||
xt := evalStaticTypeOf(store, last, currExpr.X) | ||
switch xt := xt.(type) { | ||
case *PackageType: | ||
var pv *PackageValue | ||
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, currExpr.X) | ||
pv_, ok := pvc.V.(*PackageValue) | ||
if !ok { | ||
panic(fmt.Sprintf( | ||
"missing package in selector expr %s", | ||
currExpr.String())) | ||
} | ||
pv = pv_ | ||
} | ||
if pv.GetBlock(store).Source.GetIsConst(store, currExpr.Sel) { | ||
break 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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. seems |
||
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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we just check parent and panic here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we verify the expression type before calling this function, this case should not occur. Removed for array: 4da7934 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will it be more straightforward that just check if |
||
assertValidConstValue(store, last, currExpr.Type, parentExpr) | ||
default: | ||
ift := evalStaticTypeOf(store, last, currExpr) | ||
if _, ok := ift.(*TypeType); ok { | ||
ift = evalStaticType(store, last, currExpr) | ||
} | ||
panic(fmt.Sprintf("%s (variable of type %s) is not constant", currExpr.String(), ift)) | ||
} | ||
} | ||
|
||
func isParentCallExprWithArrayArg(currType Type, parentExpr Expr) bool { | ||
_, okArray := baseOf(currType).(*ArrayType) | ||
_, okCallExpr := parentExpr.(*CallExpr) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please add a comment. |
||
|
||
return okArray && okCallExpr | ||
} | ||
|
||
// checkValDefineMismatch checks for mismatch between the number of variables and values in a ValueDecl or AssignStmt. | ||
func checkValDefineMismatch(n Node) { | ||
var ( | ||
|
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
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) | ||
const x = time.Second | ||
|
||
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) | ||
println(x) | ||
} | ||
|
||
// 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 | ||
// 1s |
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 |
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 |
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 |
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 |
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 |
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the same time, I’m wondering—many of the checks here are for functions like len and cap. So, is this recursive call really necessary? Could most of the cases be handled under callExpr instead? Please consider this.
Apologies for the continuous iterations in reviewing this PR. Thank you for understanding.