-
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 3 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,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 | ||
ltzmaxwell marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
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 | ||
petar-dambovaliev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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)) | ||
ltzmaxwell marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
case *TypeType: | ||
for _, arg := range vx.Args { | ||
checkConstantExpr(store, last, arg) | ||
} | ||
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
|
||
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) | ||
ltzmaxwell marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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)) | ||
omarsy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
case *PointerType, *DeclaredType, *StructType, *InterfaceType: | ||
ltzmaxwell marked this conversation as resolved.
Show resolved
Hide resolved
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. this is a counter case: package main
type MyStruct struct {
arr [2]int
}
const a = len(MyStruct{arr: [2]int{1, 2}}.arr)
func main() {
println("ok")
} 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. :o I miss this case. Thank you ^^ |
||
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: | ||
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 |
||
checkConstantExpr(store, last, vx.Type) | ||
default: | ||
ift := evalStaticType(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 ( | ||
|
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 |
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 |
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) (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 |
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 |
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) | ||
} | ||
|
||
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. I test this one, seems not caught by your new added logic package main
import "fmt"
func main() {
const t = 1 + 2 + len([2]string{})
fmt.Println(t)
} 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. This will work because an array is permitted in a constant expression, whereas a slice is not. |
||
// Error: | ||
// main/files/const32.gno:6:8: [](const-type string) (variable of type []string) is not constant |
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.
doubt about this, only with len(arr)?
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.
Yes array is only variable allowed in a constant.
this should work on go:
this should not work:
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.
consider this:
and
these should not work, but works on the current branch.
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.
nice catch I did a bad merge ^^
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.
hmmm, this logic seems incorrect:
Due to this logic, the error in
const43.gno
was not caught.I guess your intention is to check len(arr), right? I think that needs additional context to know if the outer expr is a built func call:
len
ormax
?Not so sure, but we need to focus on ensuring that the logic of this function is correct(not limited to the above-mentioned).