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

Issue:
[Bug][Compiler-v2] Types not inferred properly when using receiver function style calls
# 🐛 Bug

Instances where types were inferred are failing when replaced with receiver style functions.

## To reproduce

**Code snippet to reproduce**

#### Original code
Can be found within `/move-examples/post_mint_reveal_nft/sources/big_vector.move`, [here](https://github.com/aptos-labs/aptos-core/blob/7228496c1a7b0e7b465a4c8d9bf20972bfc59fff/aptos-move/move-examples/post_mint_reveal_nft/sources/big_vector.move#L268).

#### Updated to receiver function style
```rust
    #[test]
    fun big_vector_append_edge_case_test() {
        let v1 = empty(5);
        let v2 = singleton(1u64, 7);
        let v3 = empty(6);
        let v4 = empty(8);
        v3.append(v4);
        assert!(v3.length() == 0, 0);
        v2.append(v3);
        assert!(v2.length() == 1, 0);
        v1.append(v2);
        assert!(v1.length() == 1, 0);
        v1.destroy();
    }
```

**Stack trace/error message**
```bash
bug: unexpected type: _
    ┌─ /Users/alexzanderstone/Documents/aptos/aptos-core/aptos-move/move-examples/post_mint_reveal_nft/sources/big_vector.move:268:9
    │
268 │     fun big_vector_append_edge_case_test() {
    │         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
```

## Expected Behavior

Type should be properly inferred, like original implementation.

## System information

**Please complete the following information:**
- Aptos CLI Version
  - aptos 3.5.0
- Rust Version
  - rustc 1.79.0
- Computer OS
  - MacOS




Discussion / hints:

Fix patch:
diff --git a/third_party/move/move-model/src/builder/exp_builder.rs b/third_party/move/move-model/src/builder/exp_builder.rs
index 41a1dfea61295..8f5df47065031 100644
--- a/third_party/move/move-model/src/builder/exp_builder.rs
+++ b/third_party/move/move-model/src/builder/exp_builder.rs
@@ -2044,7 +2044,7 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
         receiver_arg_ty: &Type,
         inst: ReceiverFunctionInstance,
     ) -> RewriteResult {
-        let receiver_param_type = inst.arg_types.first().expect("argument").clone();
+        let mut receiver_param_type = inst.arg_types.first().expect("argument").clone();
         // Determine whether an automatic borrow needs to be inserted
         // and it's kind.
         let borrow_kind_opt = inst.receiver_needs_borrow(receiver_arg_ty);
@@ -2099,6 +2099,7 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo
             // to avoid follow-up errors, only do if unification
             // succeeded
             if ok {
+                receiver_param_type = subs.specialize(&receiver_param_type);
                 self.subs = subs;
                 let inst = inst
                     .type_inst


Test patch:
diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14471_receiver_inference.exp b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14471_receiver_inference.exp
new file mode 100644
index 0000000000000..c8a13847d8cae
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14471_receiver_inference.exp
@@ -0,0 +1,79 @@
+// -- Model dump before bytecode pipeline
+module 0x815::m {
+    struct MyMap {
+        table: m::Table<address, m::ValueWrap>,
+    }
+    struct Table {
+        x: #0,
+        y: #1,
+    }
+    struct ValueWrap {
+        val: u64,
+    }
+    private fun contains<T1,T2>(self: &m::Table<#0, #1>,_key: #0): bool {
+        true
+    }
+    private fun add<T1,T2>(self: &mut m::Table<#0, #1>,_key: #0,_val: #1) {
+        Tuple()
+    }
+    public fun add_when_missing(key: address,val: u64)
+        acquires m::MyMap(*)
+     {
+        {
+          let my_map: &mut m::MyMap = BorrowGlobal(Mutable)<m::MyMap>(0x815);
+          if Not(m::contains<address, m::ValueWrap>(Borrow(Immutable)(select m::MyMap.table<&mut m::MyMap>(my_map)), key)) {
+            {
+              let wrap: m::ValueWrap = pack m::ValueWrap(val);
+              m::add<address, m::ValueWrap>(Borrow(Mutable)(select m::MyMap.table<&mut m::MyMap>(my_map)), key, wrap);
+              Tuple()
+            }
+          } else {
+            Tuple()
+          }
+        }
+    }
+} // end 0x815::m
+
+============ initial bytecode ================
+
+[variant baseline]
+fun m::contains<#0, #1>($t0: &m::Table<#0, #1>, $t1: #0): bool {
+     var $t2: bool
+  0: $t2 := true
+  1: return $t2
+}
+
+
+[variant baseline]
+fun m::add<#0, #1>($t0: &mut m::Table<#0, #1>, $t1: #0, $t2: #1) {
+  0: return ()
+}
+
+
+[variant baseline]
+public fun m::add_when_missing($t0: address, $t1: u64) {
+     var $t2: &mut m::MyMap
+     var $t3: address
+     var $t4: bool
+     var $t5: bool
+     var $t6: &m::Table<address, m::ValueWrap>
+     var $t7: m::ValueWrap
+     var $t8: &mut m::Table<address, m::ValueWrap>
+  0: $t3 := 0x815
+  1: $t2 := borrow_global<m::MyMap>($t3)
+  2: $t6 := borrow_field<m::MyMap>.table($t2)
+  3: $t5 := m::contains<address, m::ValueWrap>($t6, $t0)
+  4: $t4 := !($t5)
+  5: if ($t4) goto 6 else goto 11
+  6: label L0
+  7: $t7 := pack m::ValueWrap($t1)
+  8: $t8 := borrow_field<m::MyMap>.table($t2)
+  9: m::add<address, m::ValueWrap>($t8, $t0, $t7)
+ 10: goto 12
+ 11: label L1
+ 12: label L2
+ 13: return ()
+}
+
+
+============ bytecode verification succeeded ========
diff --git a/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14471_receiver_inference.move b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14471_receiver_inference.move
new file mode 100644
index 0000000000000..853a75a998384
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/bytecode-generator/bug_14471_receiver_inference.move
@@ -0,0 +1,28 @@
+module 0x815::m {
+
+    struct ValueWrap has store, drop {
+        val: u64,
+    }
+    struct Table<T1, T2> has store {
+        x: T1,
+        y: T2
+    }
+    fun add<T1:drop, T2:drop>(self: &mut Table<T1, T2>, _key: T1, _val: T2) {
+    }
+    fun contains<T1:drop, T2:drop>(self: &Table<T1, T2>, _key: T1): bool {
+        true
+    }
+
+    struct MyMap has key {
+        table: Table<address, ValueWrap>,
+    }
+
+    public fun add_when_missing(key: address, val:u64) acquires MyMap {
+        let my_map = borrow_global_mut<MyMap>(@0x815);
+        if (!contains(&my_map.table, key))
+        {
+            let wrap = ValueWrap { val };
+            my_map.table.add(key, wrap);
+        }
+    }
+}