Skip to content

Commit 94fa207

Browse files
authoredNov 8, 2023
fix: return the assigned value in assignments (#2773)
Fixes #2770.
1 parent a7e168b commit 94fa207

5 files changed

+4602
-70
lines changed
 

‎src/compiler.ts

+20-17
Original file line numberDiff line numberDiff line change
@@ -5703,10 +5703,11 @@ export class Compiler extends DiagnosticEmitter {
57035703
assert(targetType != Type.void);
57045704
let valueExpr = this.compileExpression(valueExpression, targetType);
57055705
let valueType = this.currentType;
5706+
if (targetType.isNullableReference && this.currentFlow.isNonnull(valueExpr, valueType)) targetType = targetType.nonNullableType;
57065707
return this.makeAssignment(
57075708
target,
57085709
this.convertExpression(valueExpr, valueType, targetType, false, valueExpression),
5709-
valueType,
5710+
targetType,
57105711
valueExpression,
57115712
thisExpression,
57125713
elementExpression,
@@ -5799,6 +5800,7 @@ export class Compiler extends DiagnosticEmitter {
57995800
return module.unreachable();
58005801
}
58015802
assert(setterInstance.signature.parameterTypes.length == 1);
5803+
assert(setterInstance.signature.returnType == Type.void);
58025804
if (propertyInstance.is(CommonFlags.Instance)) {
58035805
let thisType = assert(setterInstance.signature.thisType);
58045806
let thisExpr = this.compileExpression(
@@ -5807,28 +5809,29 @@ export class Compiler extends DiagnosticEmitter {
58075809
Constraints.ConvImplicit | Constraints.IsThis
58085810
);
58095811
if (!tee) return this.makeCallDirect(setterInstance, [ thisExpr, valueExpr ], valueExpression);
5810-
let getterInstance = assert((<Property>target).getterInstance);
5811-
assert(getterInstance.signature.thisType == thisType);
5812-
let returnType = getterInstance.signature.returnType;
5813-
let returnTypeRef = returnType.toRef();
5814-
let tempThis = flow.getTempLocal(thisType);
5812+
let tempLocal = flow.getTempLocal(valueType);
5813+
let valueTypeRef = valueType.toRef();
58155814
let ret = module.block(null, [
58165815
this.makeCallDirect(setterInstance, [
5817-
module.local_tee(tempThis.index, thisExpr, /* isManaged=*/false, thisType.toRef()), // thisType is managed but here it must be alive
5818-
valueExpr
5816+
thisExpr,
5817+
module.local_tee(tempLocal.index, valueExpr, valueType.isManaged, valueTypeRef)
58195818
], valueExpression),
5820-
this.makeCallDirect(getterInstance, [
5821-
module.local_get(tempThis.index, thisType.toRef())
5822-
], valueExpression)
5823-
], returnTypeRef);
5819+
module.local_get(tempLocal.index, valueTypeRef),
5820+
], valueTypeRef);
5821+
this.currentType = valueType;
58245822
return ret;
58255823
} else {
58265824
if (!tee) return this.makeCallDirect(setterInstance, [ valueExpr ], valueExpression);
5827-
let getterInstance = assert((<Property>target).getterInstance);
5828-
return module.block(null, [
5829-
this.makeCallDirect(setterInstance, [ valueExpr ], valueExpression),
5830-
this.makeCallDirect(getterInstance, null, valueExpression)
5831-
], getterInstance.signature.returnType.toRef());
5825+
let tempLocal = flow.getTempLocal(valueType);
5826+
let valueTypeRef = valueType.toRef();
5827+
let ret = module.block(null, [
5828+
this.makeCallDirect(setterInstance, [
5829+
module.local_tee(tempLocal.index, valueExpr, valueType.isManaged, valueTypeRef),
5830+
], valueExpression),
5831+
module.local_get(tempLocal.index, valueTypeRef),
5832+
], valueTypeRef);
5833+
this.currentType = valueType;
5834+
return ret;
58325835
}
58335836
}
58345837
case ElementKind.IndexSignature: {

‎tests/compiler/assignment-chain.debug.wat

+2,665-30
Large diffs are not rendered by default.

‎tests/compiler/assignment-chain.release.wat

+1,873-20
Large diffs are not rendered by default.

‎tests/compiler/assignment-chain.ts

+41-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,45 @@ class A {
33
y: i64 = 0;
44
}
55

6-
export function foo(a: A): void {
7-
a.x = a.y = 1;
6+
export function normal_assignment_chain(): void {
7+
let x = new A();
8+
let cnt = 0;
9+
x.x = x.y = cnt++;
10+
assert(cnt == 1); // `cnt++` should be executed only once
811
}
12+
normal_assignment_chain();
13+
14+
class B {
15+
_setter_cnt: i32 = 0;
16+
_getter_cnt: i32 = 0;
17+
_y: f64 = 0.0;
18+
set y(z: f64) {
19+
this._setter_cnt += 1;
20+
this._y = z;
21+
}
22+
get y(): f64 {
23+
this._getter_cnt += 1;
24+
return this._y;
25+
}
26+
}
27+
export function setter_assignment_chain(): void {
28+
let x = new B();
29+
x.y = x.y = 1;
30+
assert(x._setter_cnt == 2);
31+
assert(x._getter_cnt == 0); // should not use getter method
32+
}
33+
setter_assignment_chain();
34+
35+
class C {
36+
static _setter_cnt: i32 = 0;
37+
static _y: f64 = 0.0;
38+
static set y(z: f64) {
39+
C._setter_cnt += 1;
40+
C._y = z;
41+
}
42+
}
43+
export function static_setter_assignment_chain(): void {
44+
C.y = C.y = 1;
45+
assert(C._setter_cnt == 2);
46+
}
47+
static_setter_assignment_chain();

‎tests/compiler/getter-setter.debug.wat

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
global.set $getter-setter/Foo._bar
2424
)
2525
(func $start:getter-setter
26+
(local $0 i32)
2627
call $getter-setter/Foo.get:bar
2728
i32.const 0
2829
i32.eq
@@ -50,8 +51,9 @@
5051
unreachable
5152
end
5253
i32.const 2
54+
local.tee $0
5355
call $getter-setter/Foo.set:bar
54-
call $getter-setter/Foo.get:bar
56+
local.get $0
5557
i32.const 2
5658
i32.eq
5759
i32.eqz

0 commit comments

Comments
 (0)
Please sign in to comment.