Skip to content

Commit

Permalink
fix(gnovm): prevent assignment to non-assignable expressions (gnolang…
Browse files Browse the repository at this point in the history
…#2896)

closes: gnolang#2889
<!-- please provide a detailed description of the changes made in this
pull request. -->

<details><summary>Contributors' checklist...</summary>

- [ ] Added new tests, or not needed, or not feasible
- [ ] Provided an example (e.g. screenshot) to aid review or the PR is
self-explanatory
- [ ] Updated the official documentation or not needed
- [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
- [ ] Added references to related issues and PRs
- [ ] Provided any useful hints for running manual tests
- [ ] Added new benchmarks to [generated
graphs](https://gnoland.github.io/benchmarks), if any. More info
[here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
</details>
  • Loading branch information
omarsy authored Nov 7, 2024
1 parent 9129e4e commit 7ef606c
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 0 deletions.
3 changes: 3 additions & 0 deletions gnovm/pkg/gnolang/preprocess.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down
35 changes: 35 additions & 0 deletions gnovm/pkg/gnolang/type_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Expand Down Expand Up @@ -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()
Expand Down
8 changes: 8 additions & 0 deletions gnovm/tests/files/assign29.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package main

func main() {
t := struct{}
}

// Error:
// main/files/assign29.gno:4:2: struct{} (type) is not an expression
8 changes: 8 additions & 0 deletions gnovm/tests/files/assign30.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package main

func main() {
t := *struct{}
}

// Error:
// main/files/assign30.gno:4:2: *struct{} (type) is not an expression
9 changes: 9 additions & 0 deletions gnovm/tests/files/assign31.gno
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions gnovm/tests/files/var31.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package main

func main() {
var t = struct{}
}

// Error:
// main/files/var31.gno:4:6: struct{} (type) is not an expression
8 changes: 8 additions & 0 deletions gnovm/tests/files/var32.gno
Original file line number Diff line number Diff line change
@@ -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
9 changes: 9 additions & 0 deletions gnovm/tests/files/var33.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package main

func main() {
var t *int = nil
println("pass")
}

// Output:
// pass

0 comments on commit 7ef606c

Please sign in to comment.