From ecd7ad19f2e914707efc858f993a75f959ead2ca Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcong.cai@bmw.com>
Date: Mon, 16 Oct 2023 16:18:01 +0800
Subject: [PATCH 1/2] fix: crash on assignment chain

Fixes: #2674
---
 src/compiler.ts                             |  6 +-
 tests/compiler/assignment-chain.debug.wat   | 98 +++++++++++++++++++++
 tests/compiler/assignment-chain.json        |  4 +
 tests/compiler/assignment-chain.release.wat | 69 +++++++++++++++
 tests/compiler/assignment-chain.ts          |  8 ++
 5 files changed, 182 insertions(+), 3 deletions(-)
 create mode 100644 tests/compiler/assignment-chain.debug.wat
 create mode 100644 tests/compiler/assignment-chain.json
 create mode 100644 tests/compiler/assignment-chain.release.wat
 create mode 100644 tests/compiler/assignment-chain.ts

diff --git a/src/compiler.ts b/src/compiler.ts
index 2567805aff..ef0b314174 100644
--- a/src/compiler.ts
+++ b/src/compiler.ts
@@ -5811,14 +5811,14 @@ export class Compiler extends DiagnosticEmitter {
           assert(getterInstance.signature.thisType == thisType);
           let returnType = getterInstance.signature.returnType;
           let returnTypeRef = returnType.toRef();
-          let tempThis = flow.getTempLocal(returnType);
+          let tempThis = flow.getTempLocal(thisType);
           let ret = module.block(null, [
             this.makeCallDirect(setterInstance, [
-              module.local_tee(tempThis.index, thisExpr, returnType.isManaged),
+              module.local_tee(tempThis.index, thisExpr, /*isManaged=*/false, thisType.toRef()), // thisType is managed but here it must be alive
               valueExpr
             ], valueExpression),
             this.makeCallDirect(getterInstance, [
-              module.local_get(tempThis.index, returnTypeRef)
+              module.local_get(tempThis.index, thisType.toRef())
             ], valueExpression)
           ], returnTypeRef);
           return ret;
diff --git a/tests/compiler/assignment-chain.debug.wat b/tests/compiler/assignment-chain.debug.wat
new file mode 100644
index 0000000000..e4415d8a18
--- /dev/null
+++ b/tests/compiler/assignment-chain.debug.wat
@@ -0,0 +1,98 @@
+(module
+ (type $i32_i64_=>_none (func (param i32 i64)))
+ (type $i32_=>_none (func (param i32)))
+ (type $i32_=>_i64 (func (param i32) (result i64)))
+ (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32)))
+ (type $none_=>_none (func))
+ (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32)))
+ (global $~lib/memory/__data_end i32 (i32.const 8))
+ (global $~lib/memory/__stack_pointer (mut i32) (i32.const 32776))
+ (global $~lib/memory/__heap_base i32 (i32.const 32776))
+ (memory $0 0)
+ (table $0 1 1 funcref)
+ (elem $0 (i32.const 1))
+ (export "memory" (memory $0))
+ (export "foo" (func $export:assignment-chain/foo))
+ (func $assignment-chain/A#set:y (param $this i32) (param $y i64)
+  local.get $this
+  local.get $y
+  i64.store $0 offset=8
+ )
+ (func $assignment-chain/A#get:y (param $this i32) (result i64)
+  local.get $this
+  i64.load $0 offset=8
+ )
+ (func $assignment-chain/A#set:x (param $this i32) (param $x i64)
+  local.get $this
+  local.get $x
+  i64.store $0
+ )
+ (func $~stack_check
+  global.get $~lib/memory/__stack_pointer
+  global.get $~lib/memory/__data_end
+  i32.lt_s
+  if
+   i32.const 32800
+   i32.const 32848
+   i32.const 1
+   i32.const 1
+   call $~lib/builtins/abort
+   unreachable
+  end
+ )
+ (func $assignment-chain/foo (param $a i32)
+  (local $1 i32)
+  (local $2 i32)
+  global.get $~lib/memory/__stack_pointer
+  i32.const 8
+  i32.sub
+  global.set $~lib/memory/__stack_pointer
+  call $~stack_check
+  global.get $~lib/memory/__stack_pointer
+  i64.const 0
+  i64.store $0
+  local.get $a
+  local.set $2
+  global.get $~lib/memory/__stack_pointer
+  local.get $2
+  i32.store $0
+  local.get $2
+  local.get $a
+  local.tee $1
+  local.set $2
+  global.get $~lib/memory/__stack_pointer
+  local.get $2
+  i32.store $0 offset=4
+  local.get $2
+  i64.const 1
+  call $assignment-chain/A#set:y
+  local.get $1
+  local.set $2
+  global.get $~lib/memory/__stack_pointer
+  local.get $2
+  i32.store $0 offset=4
+  local.get $2
+  call $assignment-chain/A#get:y
+  call $assignment-chain/A#set:x
+  global.get $~lib/memory/__stack_pointer
+  i32.const 8
+  i32.add
+  global.set $~lib/memory/__stack_pointer
+ )
+ (func $export:assignment-chain/foo (param $0 i32)
+  global.get $~lib/memory/__stack_pointer
+  i32.const 4
+  i32.sub
+  global.set $~lib/memory/__stack_pointer
+  call $~stack_check
+  global.get $~lib/memory/__stack_pointer
+  local.get $0
+  i32.store $0
+  local.get $0
+  call $assignment-chain/foo
+  global.get $~lib/memory/__stack_pointer
+  i32.const 4
+  i32.add
+  global.set $~lib/memory/__stack_pointer
+ )
+)
diff --git a/tests/compiler/assignment-chain.json b/tests/compiler/assignment-chain.json
new file mode 100644
index 0000000000..1bdd02b1be
--- /dev/null
+++ b/tests/compiler/assignment-chain.json
@@ -0,0 +1,4 @@
+{
+  "asc_flags": [
+  ]
+}
diff --git a/tests/compiler/assignment-chain.release.wat b/tests/compiler/assignment-chain.release.wat
new file mode 100644
index 0000000000..1203755091
--- /dev/null
+++ b/tests/compiler/assignment-chain.release.wat
@@ -0,0 +1,69 @@
+(module
+ (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32)))
+ (type $i32_=>_none (func (param i32)))
+ (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32)))
+ (global $~lib/memory/__stack_pointer (mut i32) (i32.const 33792))
+ (memory $0 0)
+ (export "memory" (memory $0))
+ (export "foo" (func $export:assignment-chain/foo))
+ (func $export:assignment-chain/foo (param $0 i32)
+  (local $1 i32)
+  global.get $~lib/memory/__stack_pointer
+  i32.const 4
+  i32.sub
+  global.set $~lib/memory/__stack_pointer
+  block $folding-inner0
+   global.get $~lib/memory/__stack_pointer
+   i32.const 1024
+   i32.lt_s
+   br_if $folding-inner0
+   global.get $~lib/memory/__stack_pointer
+   local.tee $1
+   local.get $0
+   i32.store $0
+   local.get $1
+   i32.const 8
+   i32.sub
+   global.set $~lib/memory/__stack_pointer
+   global.get $~lib/memory/__stack_pointer
+   i32.const 1024
+   i32.lt_s
+   br_if $folding-inner0
+   global.get $~lib/memory/__stack_pointer
+   local.tee $1
+   i64.const 0
+   i64.store $0
+   local.get $1
+   local.get $0
+   i32.store $0
+   local.get $1
+   local.get $0
+   i32.store $0 offset=4
+   local.get $0
+   i64.const 1
+   i64.store $0 offset=8
+   local.get $1
+   local.get $0
+   i32.store $0 offset=4
+   local.get $0
+   local.get $0
+   i64.load $0 offset=8
+   i64.store $0
+   local.get $1
+   i32.const 8
+   i32.add
+   global.set $~lib/memory/__stack_pointer
+   global.get $~lib/memory/__stack_pointer
+   i32.const 4
+   i32.add
+   global.set $~lib/memory/__stack_pointer
+   return
+  end
+  i32.const 33824
+  i32.const 33872
+  i32.const 1
+  i32.const 1
+  call $~lib/builtins/abort
+  unreachable
+ )
+)
diff --git a/tests/compiler/assignment-chain.ts b/tests/compiler/assignment-chain.ts
new file mode 100644
index 0000000000..f22186a7bc
--- /dev/null
+++ b/tests/compiler/assignment-chain.ts
@@ -0,0 +1,8 @@
+class A {
+  x: i64 = 0;
+  y: i64 = 0;
+}
+
+export function foo(a: A): void {
+  a.x = a.y = 1;
+}

From 38821c1f920800debeaecef1e29884286b4eceb4 Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcong.cai@bmw.com>
Date: Mon, 16 Oct 2023 16:43:45 +0800
Subject: [PATCH 2/2] fix lint

---
 src/compiler.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/compiler.ts b/src/compiler.ts
index ef0b314174..cda945784b 100644
--- a/src/compiler.ts
+++ b/src/compiler.ts
@@ -5814,7 +5814,7 @@ export class Compiler extends DiagnosticEmitter {
           let tempThis = flow.getTempLocal(thisType);
           let ret = module.block(null, [
             this.makeCallDirect(setterInstance, [
-              module.local_tee(tempThis.index, thisExpr, /*isManaged=*/false, thisType.toRef()), // thisType is managed but here it must be alive
+              module.local_tee(tempThis.index, thisExpr, /* isManaged=*/false, thisType.toRef()), // thisType is managed but here it must be alive
               valueExpr
             ], valueExpression),
             this.makeCallDirect(getterInstance, [