Repository: rune-rs/rune (language: rust)

Issue:
Unary expressions clobber deferred slots
run the following in the Playground, and got Output "false \n true". What would be the reason?

```rust
pub fn main() {
    let a = 2;
    let b = -5;
    dbg!(a > -b);
    dbg!(a > -b);
}
```
Unary expressions clobber deferred slots
run the following in the Playground, and got Output "false \n true". What would be the reason?

```rust
pub fn main() {
    let a = 2;
    let b = -5;
    dbg!(a > -b);
    dbg!(a > -b);
}
```


Discussion / hints:
Probably a miscompilation with the new assembler like #830 

Thanks for the report!
Enabling instructions reveals that the variable slot is being clobbered when bending instead of using a temporary: 

```
# instruction fn main() (0x1c69d5964e831fc1):
  0000 = allocate size=3
  0001 = store value=2, out=keep(0)
  entry:2:13-14: let a = 2;
  0002 = store value=-5, out=keep(1) 
  entry:3:13-15: let b = -5;
  0003 = neg addr=1, out=keep(1) // this writes to `b` instead of its own slot.
```
Probably a miscompilation with the new assembler like #830 

Thanks for the report!
Enabling instructions reveals that the variable slot is being clobbered when bending instead of using a temporary: 

```
# instruction fn main() (0x1c69d5964e831fc1):
  0000 = allocate size=3
  0001 = store value=2, out=keep(0)
  entry:2:13-14: let a = 2;
  0002 = store value=-5, out=keep(1) 
  entry:3:13-15: let b = -5;
  0003 = neg addr=1, out=keep(1) // this writes to `b` instead of its own slot.
```

Fix patch:
diff --git a/crates/rune/src/compile/v1/assemble.rs b/crates/rune/src/compile/v1/assemble.rs
index 26e07f5a4..0d351028a 100644
--- a/crates/rune/src/compile/v1/assemble.rs
+++ b/crates/rune/src/compile/v1/assemble.rs
@@ -2920,18 +2920,16 @@ fn expr_unary<'a, 'hir>(
     span: &'hir dyn Spanned,
     needs: &mut dyn Needs<'a, 'hir>,
 ) -> compile::Result<Asm<'hir>> {
-    converge!(expr(cx, &hir.expr, needs)?);
-
-    let Some(addr) = needs.try_as_addr()? else {
-        return Ok(Asm::new(span, ()));
-    };
+    let mut addr = cx.scopes.defer(span);
+    converge!(expr(cx, &hir.expr, &mut addr)?, free(addr));
+    let addr = addr.into_addr()?;
 
     match hir.op {
         ast::UnOp::Not(..) => {
             cx.asm.push(
                 Inst::Not {
                     addr: addr.addr(),
-                    out: addr.output(),
+                    out: needs.alloc_output()?,
                 },
                 span,
             )?;
@@ -2940,7 +2938,7 @@ fn expr_unary<'a, 'hir>(
             cx.asm.push(
                 Inst::Neg {
                     addr: addr.addr(),
-                    out: addr.output(),
+                    out: needs.alloc_output()?,
                 },
                 span,
             )?;
@@ -2953,6 +2951,7 @@ fn expr_unary<'a, 'hir>(
         }
     }
 
+    addr.free()?;
     Ok(Asm::new(span, ()))
 }
 


Test patch:
diff --git a/crates/rune/tests/bug_838.rn b/crates/rune/tests/bug_838.rn
new file mode 100644
index 000000000..e745d80c9
--- /dev/null
+++ b/crates/rune/tests/bug_838.rn
@@ -0,0 +1,36 @@
+// This ensures that unary operations do not clobber a slot.
+// https://github.com/rune-rs/rune/issues/838
+
+#[test]
+fn negation_simple() {
+    let b = -1;
+    let d = 0 + -b;
+    assert_eq!(d, 1);
+    assert_eq!(b, -1);
+}
+
+#[test]
+fn negation_simple_chain() {
+    let b = -1;
+    let d = 0 + -b;
+    d = 0 + -b;
+    d = 0 + -b;
+    d = 0 + -b;
+    assert_eq!(d, 1);
+    assert_eq!(b, -1);
+}
+
+#[test]
+fn negation() {
+    let a = 1;
+    let b = -2;
+    let c = -b;
+    let d = 1 < -b;
+    let e = 1 < -b;
+
+    assert!(d);
+    assert!(e);
+    assert_eq!(a, 1);
+    assert_eq!(b, -2);
+    assert_eq!(c, 2);
+}
diff --git a/crates/rune/tests/matching.rn b/crates/rune/tests/matching.rn
index f87daec15..d81a765e5 100644
--- a/crates/rune/tests/matching.rn
+++ b/crates/rune/tests/matching.rn
@@ -168,3 +168,49 @@ fn match_enum2() {
 
     assert_eq!(out, Enum::Right);
 }
+
+#[test]
+fn match_extraction_vec() {
+    let v = [42];
+
+    fn inner(v) {
+        match v {
+            [a] => a,
+            _ => 0,
+        }
+    }
+
+    assert_eq!(inner(v), 42);
+}
+
+#[test]
+fn match_extraction_tuple() {
+    let v = (42,);
+
+    fn inner(v) {
+        match v {
+            (a,) => a,
+            _ => 0,
+        }
+    }
+
+    assert_eq!(inner(v), 42);
+}
+
+#[test]
+fn match_extraction_struct() {
+    struct Struct {
+        a,
+    }
+
+    fn inner(v) {
+        match v {
+            Struct { a, .. } => a,
+            _ => 0,
+        }
+    }
+
+    let v = Struct { a: 42 };
+
+    assert_eq!(inner(v), 42);
+}