-
Notifications
You must be signed in to change notification settings - Fork 374
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
6272ad9
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,122 @@ | |
} | ||
} | ||
|
||
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, real, imag | ||
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)) | ||
} | ||
case *TypeType: | ||
for _, arg := range vx.Args { | ||
checkConstantExpr(store, last, arg) | ||
} | ||
case *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. a better msg? a test? 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. |
||
// Todo: should add a test after the fix of https://github.com/gnolang/gno/issues/3006 | ||
ty := evalStaticType(store, last, vx.Func) | ||
panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), ty)) | ||
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) | ||
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. consider this one as a special case: package main
var x = 1
var y = 1
const b = x == y
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. this one return an error in my PR, I think. Do you want I added as a test ? 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 see this: https://go.dev/play/p/dhxEqNA7p60 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 think that when you run it, you'll encounter an error like 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. IMO, it would be more natural that the RHS is not const, rather than its child node. 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 find the current error message a bit unclear. For example, consider the following code: package main
const x = 1
var y = 1
const b = x == y
func main() {
println("ok")
} In Go, this will produce the error: This difference suggests that we should change our code to make it work, like so: package main
const x = 1
const y = 1
const b = x == y
func main() {
println("ok")
} The Go error message is more informative, indicating that the expression itself is not considered constant rather than simply pointing out that 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. But I can the logic of the code if you think it is a blocker (to be more go like) |
||
case *SelectorExpr: | ||
xt := evalStaticTypeOf(store, last, vx.X) | ||
switch xt := xt.(type) { | ||
case *PackageType: | ||
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 | ||
} | ||
|
||
tt := pv.GetBlock(store).Source.GetStaticTypeOf(store, vx.Sel) | ||
panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), tt)) | ||
case *PointerType, *DeclaredType, *StructType, *InterfaceType: | ||
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. more tests on this? 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. right, done here: e74e6e9 |
||
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: | ||
ty := evalStaticTypeOf(store, last, vx.X) | ||
panic(fmt.Sprintf("%s (variable of type %s) is not constant", vx.String(), ty)) | ||
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) | ||
if _, ok := ift.(*TypeType); ok { | ||
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,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) (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) | ||
} | ||
|
||
// 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.
If
real
andimag
are added later, it would need to be handled here as well.Can you add a comment for this?
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.
done here: 6272ad9