Repository: aptos-labs/aptos-core (language: python)

Issue:
[Bug][Compiler-v2] STLOC_TYPE_MISMATCH_ERROR when trying to updating the field
# 🐛 Bug

When compiling the following code:

```
    enum Positional has drop {
        A(u8),
        B(u8),
    }
    fun test_common_access(): u8 {
        let x = Positional::A(42);
        x.0 = 19; 
        20
    }
```

Bytecode verification error `STLOC_TYPE_MISMATCH_ERROR` will be raised.



Discussion / hints:

Fix patch:
diff --git a/third_party/move/move-compiler-v2/src/bytecode_generator.rs b/third_party/move/move-compiler-v2/src/bytecode_generator.rs
index f09f82a1ae9f7..e20f3b09bf19e 100644
--- a/third_party/move/move-compiler-v2/src/bytecode_generator.rs
+++ b/third_party/move/move-compiler-v2/src/bytecode_generator.rs
@@ -1089,7 +1089,9 @@ impl<'env> Generator<'env> {
         match exp.as_ref() {
             ExpData::Temporary(_, temp) if !with_forced_temp => *temp,
             ExpData::LocalVar(id, sym) if !with_forced_temp => self.find_local(*id, *sym),
-            ExpData::Call(id, Operation::Select(..), _) if self.reference_mode() => {
+            ExpData::Call(id, Operation::Select(..) | Operation::SelectVariants(..), _)
+                if self.reference_mode() =>
+            {
                 // In reference mode, a selection is interpreted as selecting a reference to the
                 // field.
                 let ty =


Test patch:
diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_update_variant_select.exp b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_update_variant_select.exp
new file mode 100644
index 0000000000000..0a25e2fb1e722
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_update_variant_select.exp
@@ -0,0 +1,149 @@
+// -- Model dump before bytecode pipeline
+module 0x815::m {
+    enum CommonFields {
+        Foo {
+            x: u64,
+            y: u8,
+        }
+        Bar {
+            x: u64,
+            y: u8,
+            z: u32,
+        }
+        Baz {
+            y: u8,
+        }
+    }
+    private fun update_common_field(): u64 {
+        {
+          let common: m::CommonFields = pack m::CommonFields::Bar(30, 40, 50);
+          select_variants m::CommonFields.Foo.x|m::CommonFields.Bar.x<m::CommonFields>(common) = 15;
+          select_variants m::CommonFields.Foo.x|m::CommonFields.Bar.x<m::CommonFields>(common)
+        }
+    }
+    private fun update_common_field_different_offset(): u8 {
+        {
+          let common: m::CommonFields = pack m::CommonFields::Bar(30, 40, 50);
+          select_variants m::CommonFields.Foo.y|m::CommonFields.Bar.y|m::CommonFields.Baz.y<m::CommonFields>(common) = 15;
+          select_variants m::CommonFields.Foo.y|m::CommonFields.Bar.y|m::CommonFields.Baz.y<m::CommonFields>(common)
+        }
+    }
+    private fun update_non_common_field(): u32 {
+        {
+          let common: m::CommonFields = pack m::CommonFields::Bar(30, 40, 50);
+          select_variants m::CommonFields.Bar.z<m::CommonFields>(common) = 15;
+          select_variants m::CommonFields.Bar.z<m::CommonFields>(common)
+        }
+    }
+} // end 0x815::m
+
+============ initial bytecode ================
+
+[variant baseline]
+fun m::update_common_field(): u64 {
+     var $t0: u64
+     var $t1: m::CommonFields
+     var $t2: u64
+     var $t3: u8
+     var $t4: u32
+     var $t5: u64
+     var $t6: &mut u64
+     var $t7: &mut m::CommonFields
+     var $t8: &m::CommonFields
+     var $t9: &u64
+  0: $t2 := 30
+  1: $t3 := 40
+  2: $t4 := 50
+  3: $t1 := pack_variant m::CommonFields::Bar($t2, $t3, $t4)
+  4: $t5 := 15
+  5: $t7 := borrow_local($t1)
+  6: $t6 := borrow_variant_field<m::CommonFields::Foo|Bar>.x($t7)
+  7: write_ref($t6, $t5)
+  8: $t8 := borrow_local($t1)
+  9: $t9 := borrow_variant_field<m::CommonFields::Foo|Bar>.x($t8)
+ 10: $t0 := read_ref($t9)
+ 11: return $t0
+}
+
+
+[variant baseline]
+fun m::update_common_field_different_offset(): u8 {
+     var $t0: u8
+     var $t1: m::CommonFields
+     var $t2: u64
+     var $t3: u8
+     var $t4: u32
+     var $t5: u8
+     var $t6: &mut u8
+     var $t7: &mut m::CommonFields
+     var $t8: bool
+     var $t9: &m::CommonFields
+     var $t10: &u8
+     var $t11: bool
+  0: $t2 := 30
+  1: $t3 := 40
+  2: $t4 := 50
+  3: $t1 := pack_variant m::CommonFields::Bar($t2, $t3, $t4)
+  4: $t5 := 15
+  5: $t7 := borrow_local($t1)
+  6: $t8 := test_variant m::CommonFields::Foo($t7)
+  7: if ($t8) goto 13 else goto 8
+  8: label L3
+  9: $t8 := test_variant m::CommonFields::Bar($t7)
+ 10: if ($t8) goto 13 else goto 11
+ 11: label L4
+ 12: goto 16
+ 13: label L2
+ 14: $t6 := borrow_variant_field<m::CommonFields::Foo|Bar>.y($t7)
+ 15: goto 18
+ 16: label L1
+ 17: $t6 := borrow_variant_field<m::CommonFields::Baz>.y($t7)
+ 18: label L0
+ 19: write_ref($t6, $t5)
+ 20: $t9 := borrow_local($t1)
+ 21: $t11 := test_variant m::CommonFields::Foo($t9)
+ 22: if ($t11) goto 28 else goto 23
+ 23: label L8
+ 24: $t11 := test_variant m::CommonFields::Bar($t9)
+ 25: if ($t11) goto 28 else goto 26
+ 26: label L9
+ 27: goto 31
+ 28: label L7
+ 29: $t10 := borrow_variant_field<m::CommonFields::Foo|Bar>.y($t9)
+ 30: goto 33
+ 31: label L6
+ 32: $t10 := borrow_variant_field<m::CommonFields::Baz>.y($t9)
+ 33: label L5
+ 34: $t0 := read_ref($t10)
+ 35: return $t0
+}
+
+
+[variant baseline]
+fun m::update_non_common_field(): u32 {
+     var $t0: u32
+     var $t1: m::CommonFields
+     var $t2: u64
+     var $t3: u8
+     var $t4: u32
+     var $t5: u32
+     var $t6: &mut u32
+     var $t7: &mut m::CommonFields
+     var $t8: &m::CommonFields
+     var $t9: &u32
+  0: $t2 := 30
+  1: $t3 := 40
+  2: $t4 := 50
+  3: $t1 := pack_variant m::CommonFields::Bar($t2, $t3, $t4)
+  4: $t5 := 15
+  5: $t7 := borrow_local($t1)
+  6: $t6 := borrow_variant_field<m::CommonFields::Bar>.z($t7)
+  7: write_ref($t6, $t5)
+  8: $t8 := borrow_local($t1)
+  9: $t9 := borrow_variant_field<m::CommonFields::Bar>.z($t8)
+ 10: $t0 := read_ref($t9)
+ 11: return $t0
+}
+
+
+============ bytecode verification succeeded ========
diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_update_variant_select.move b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_update_variant_select.move
new file mode 100644
index 0000000000000..d8da35316f75a
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_update_variant_select.move
@@ -0,0 +1,38 @@
+module 0x815::m {
+
+    enum CommonFields has drop {
+      Foo{x: u64, y: u8},
+      Bar{x: u64, y: u8, z: u32}
+      Baz{y: u8}
+    }
+
+    fun update_common_field(): u64 {
+        let common = CommonFields::Bar {
+            x: 30,
+            y: 40,
+            z: 50
+        };
+        common.x = 15;
+        common.x
+    }
+
+    fun update_non_common_field(): u32 {
+        let common = CommonFields::Bar {
+            x: 30,
+            y: 40,
+            z: 50
+        };
+        common.z = 15;
+        common.z
+    }
+
+    fun update_common_field_different_offset(): u8 {
+        let common = CommonFields::Bar {
+            x: 30,
+            y: 40,
+            z: 50
+        };
+        common.y = 15;
+        common.y
+    }
+}
diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_variant_select_autoref.exp b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_variant_select_autoref.exp
new file mode 100644
index 0000000000000..285f4a28191fe
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_variant_select_autoref.exp
@@ -0,0 +1,41 @@
+// -- Model dump before bytecode pipeline
+module 0x815::m {
+    enum Positional {
+        A {
+            0: u8,
+        }
+        B {
+            0: u8,
+        }
+    }
+    private fun test_common_access(): u8 {
+        {
+          let x: m::Positional = pack m::Positional::A(42);
+          select_variants m::Positional.A.0|m::Positional.B.0<m::Positional>(x) = 19;
+          20
+        }
+    }
+} // end 0x815::m
+
+============ initial bytecode ================
+
+[variant baseline]
+fun m::test_common_access(): u8 {
+     var $t0: u8
+     var $t1: m::Positional
+     var $t2: u8
+     var $t3: u8
+     var $t4: &mut u8
+     var $t5: &mut m::Positional
+  0: $t2 := 42
+  1: $t1 := pack_variant m::Positional::A($t2)
+  2: $t3 := 19
+  3: $t5 := borrow_local($t1)
+  4: $t4 := borrow_variant_field<m::Positional::A|B>.0($t5)
+  5: write_ref($t4, $t3)
+  6: $t0 := 20
+  7: return $t0
+}
+
+
+============ bytecode verification succeeded ========
diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_variant_select_autoref.move b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_variant_select_autoref.move
new file mode 100644
index 0000000000000..3f90b104926e5
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14300_variant_select_autoref.move
@@ -0,0 +1,13 @@
+module 0x815::m {
+
+    enum Positional has drop {
+        A(u8),
+        B(u8),
+    }
+    fun test_common_access(): u8 {
+        let x = Positional::A(42);
+        x.0 = 19;
+        20
+    }
+
+}
diff --git a/third_party/move/move-compiler-v2/tests/reference-safety/immu_borrow_bug.exp b/third_party/move/move-compiler-v2/tests/reference-safety/immu_borrow_bug.exp
deleted file mode 100644
index acfff9c2a486e..0000000000000
--- a/third_party/move/move-compiler-v2/tests/reference-safety/immu_borrow_bug.exp
+++ /dev/null
@@ -1,9 +0,0 @@
-
-============ bytecode verification failed ========
-
-Diagnostics:
-bug: bytecode verification failed with unexpected status code `STLOC_TYPE_MISMATCH_ERROR`. This is a compiler bug, consider reporting it.
-   ┌─ tests/reference-safety/immu_borrow_bug.move:14:9
-   │
-14 │         common.x = 15;
-   │         ^^^^^^^^
diff --git a/third_party/move/move-compiler-v2/tests/reference-safety/immu_borrow_bug.move b/third_party/move/move-compiler-v2/tests/reference-safety/immu_borrow_bug.move
deleted file mode 100644
index a1ff97f387ae0..0000000000000
--- a/third_party/move/move-compiler-v2/tests/reference-safety/immu_borrow_bug.move
+++ /dev/null
@@ -1,17 +0,0 @@
-module 0x815::m {
-
-    enum CommonFields has drop {
-    Foo{x: u64, y: u8},
-    Bar{x: u64, y: u8, z: u32}
-    }
-
-    fun t9_common_field(): u64 {
-        let common = CommonFields::Bar {
-            x: 30,
-            y: 40,
-            z: 50
-        };
-        common.x = 15;
-        common.x
-    }
-}
diff --git a/third_party/move/move-compiler-v2/tests/reference-safety/immu_borrow_bug.no-opt.exp b/third_party/move/move-compiler-v2/tests/reference-safety/immu_borrow_bug.no-opt.exp
deleted file mode 100644
index acfff9c2a486e..0000000000000
--- a/third_party/move/move-compiler-v2/tests/reference-safety/immu_borrow_bug.no-opt.exp
+++ /dev/null
@@ -1,9 +0,0 @@
-
-============ bytecode verification failed ========
-
-Diagnostics:
-bug: bytecode verification failed with unexpected status code `STLOC_TYPE_MISMATCH_ERROR`. This is a compiler bug, consider reporting it.
-   ┌─ tests/reference-safety/immu_borrow_bug.move:14:9
-   │
-14 │         common.x = 15;
-   │         ^^^^^^^^
diff --git a/third_party/move/move-compiler-v2/tests/reference-safety/immu_borrow_bug.old.exp b/third_party/move/move-compiler-v2/tests/reference-safety/immu_borrow_bug.old.exp
deleted file mode 100644
index cbe6b5a9470a2..0000000000000
--- a/third_party/move/move-compiler-v2/tests/reference-safety/immu_borrow_bug.old.exp
+++ /dev/null
@@ -1,15 +0,0 @@
-
-Diagnostics:
-error: cannot immutably borrow since mutable references exist
-   ┌─ tests/reference-safety/immu_borrow_bug.move:15:9
-   │
-14 │         common.x = 15;
-   │         --------
-   │         │
-   │         used by field borrow
-   │         previous mutable local borrow
-15 │         common.x
-   │         ^^^^^^--
-   │         │
-   │         requirement enforced here
-   │         immutable borrow attempted here
diff --git a/third_party/move/move-compiler-v2/transactional-tests/tests/no-v1-comparison/enum/bug_14300_update_variant_field.exp b/third_party/move/move-compiler-v2/transactional-tests/tests/no-v1-comparison/enum/bug_14300_update_variant_field.exp
new file mode 100644
index 0000000000000..eb9aa2aa18d35
--- /dev/null
+++ b/third_party/move/move-compiler-v2/transactional-tests/tests/no-v1-comparison/enum/bug_14300_update_variant_field.exp
@@ -0,0 +1,10 @@
+processed 4 tasks
+
+task 1 'run'. lines 41-41:
+return values: 15
+
+task 2 'run'. lines 43-43:
+return values: 15
+
+task 3 'run'. lines 45-45:
+return values: 15
diff --git a/third_party/move/move-compiler-v2/transactional-tests/tests/no-v1-comparison/enum/bug_14300_update_variant_field.move b/third_party/move/move-compiler-v2/transactional-tests/tests/no-v1-comparison/enum/bug_14300_update_variant_field.move
new file mode 100644
index 0000000000000..4cade575e4bd2
--- /dev/null
+++ b/third_party/move/move-compiler-v2/transactional-tests/tests/no-v1-comparison/enum/bug_14300_update_variant_field.move
@@ -0,0 +1,45 @@
+//# publish
+module 0x815::m {
+
+    enum CommonFields has drop {
+      Foo{x: u64, y: u8},
+      Bar{x: u64, y: u8, z: u32}
+      Baz{y: u8}
+    }
+
+    fun update_common_field(): u64 {
+        let common = CommonFields::Bar {
+            x: 30,
+            y: 40,
+            z: 50
+        };
+        common.x = 15;
+        common.x
+    }
+
+    fun update_non_common_field(): u32 {
+        let common = CommonFields::Bar {
+            x: 30,
+            y: 40,
+            z: 50
+        };
+        common.z = 15;
+        common.z
+    }
+
+    fun update_common_field_different_offset(): u8 {
+        let common = CommonFields::Bar {
+            x: 30,
+            y: 40,
+            z: 50
+        };
+        common.y = 15;
+        common.y
+    }
+}
+
+//# run 0x815::m::update_common_field
+
+//# run 0x815::m::update_non_common_field
+
+//# run 0x815::m::update_common_field_different_offset