diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index a7a1e15bbf3..78e4488b2a0 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1961,6 +1961,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // TRANS_LEAVE ----------------------- case *AssignStmt: n.AssertCompatible(store, last) + // NOTE: keep DEFINE and ASSIGN in sync. if n.Op == DEFINE { // Rhs consts become default *ConstExprs. @@ -2291,6 +2292,8 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // TRANS_LEAVE ----------------------- case *ValueDecl: + assertValidAssignRhs(store, last, n) + // evaluate value if const expr. if n.Const { // NOTE: may or may not be a *ConstExpr, diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index f86c44e7921..c62d67375ee 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -837,6 +837,7 @@ func (x *RangeStmt) AssertCompatible(store Store, last BlockNode) { func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) { if x.Op == ASSIGN || x.Op == DEFINE { + assertValidAssignRhs(store, last, x) if len(x.Lhs) > len(x.Rhs) { if len(x.Rhs) != 1 { panic(fmt.Sprintf("assignment mismatch: %d variables but %d values", len(x.Lhs), len(x.Rhs))) @@ -997,6 +998,40 @@ func assertValidAssignLhs(store Store, last BlockNode, lx Expr) { } } +func assertValidAssignRhs(store Store, last BlockNode, n Node) { + var exps []Expr + switch x := n.(type) { + case *ValueDecl: + exps = x.Values + case *AssignStmt: + exps = x.Rhs + default: + panic(fmt.Sprintf("unexpected node type %T", n)) + } + + for _, exp := range exps { + tt := evalStaticTypeOfRaw(store, last, exp) + if tt == nil { + switch x := n.(type) { + case *ValueDecl: + if x.Type != nil { + continue + } + panic("use of untyped nil in variable declaration") + case *AssignStmt: + if x.Op != DEFINE { + continue + } + panic("use of untyped nil in assignment") + } + } + if _, ok := tt.(*TypeType); ok { + tt = evalStaticType(store, last, exp) + panic(fmt.Sprintf("%s (type) is not an expression", tt.String())) + } + } +} + func kindString(xt Type) string { if xt != nil { return xt.Kind().String() diff --git a/gnovm/tests/files/assign29.gno b/gnovm/tests/files/assign29.gno new file mode 100644 index 00000000000..ca605f5ecbe --- /dev/null +++ b/gnovm/tests/files/assign29.gno @@ -0,0 +1,8 @@ +package main + +func main() { + t := struct{} +} + +// Error: +// main/files/assign29.gno:4:2: struct{} (type) is not an expression diff --git a/gnovm/tests/files/assign30.gno b/gnovm/tests/files/assign30.gno new file mode 100644 index 00000000000..ad72f880f27 --- /dev/null +++ b/gnovm/tests/files/assign30.gno @@ -0,0 +1,8 @@ +package main + +func main() { + t := *struct{} +} + +// Error: +// main/files/assign30.gno:4:2: *struct{} (type) is not an expression diff --git a/gnovm/tests/files/assign31.gno b/gnovm/tests/files/assign31.gno new file mode 100644 index 00000000000..8c96c04501e --- /dev/null +++ b/gnovm/tests/files/assign31.gno @@ -0,0 +1,9 @@ +package main + +func main() { + a := nil + println(a) +} + +// Error: +// main/files/assign31.gno:4:2: use of untyped nil in assignment diff --git a/gnovm/tests/files/var31.gno b/gnovm/tests/files/var31.gno new file mode 100644 index 00000000000..813e6ff9e22 --- /dev/null +++ b/gnovm/tests/files/var31.gno @@ -0,0 +1,8 @@ +package main + +func main() { + var t = struct{} +} + +// Error: +// main/files/var31.gno:4:6: struct{} (type) is not an expression diff --git a/gnovm/tests/files/var32.gno b/gnovm/tests/files/var32.gno new file mode 100644 index 00000000000..827c3951f94 --- /dev/null +++ b/gnovm/tests/files/var32.gno @@ -0,0 +1,8 @@ +package main + +func main() { + var t = nil +} + +// Error: +// main/files/var32.gno:4:6: use of untyped nil in variable declaration diff --git a/gnovm/tests/files/var33.gno b/gnovm/tests/files/var33.gno new file mode 100644 index 00000000000..ce883dce47c --- /dev/null +++ b/gnovm/tests/files/var33.gno @@ -0,0 +1,9 @@ +package main + +func main() { + var t *int = nil + println("pass") +} + +// Output: +// pass