Skip to content

Commit 0bcc64c

Browse files
committed
Handle switch statement
1 parent 7497862 commit 0bcc64c

File tree

4 files changed

+139
-5
lines changed

4 files changed

+139
-5
lines changed

src/compiler.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2673,13 +2673,17 @@ export class Compiler extends DiagnosticEmitter {
26732673
// stmts.push(module.createUnreachable());
26742674
return module.flatten(stmts);
26752675
}
2676-
26772676
// Otherwise emit a normal return
26782677
if (!stmts.length) return module.return(expr);
26792678
stmts.push(module.return(expr));
26802679
return module.flatten(stmts);
26812680
}
26822681

2682+
// For strict field init:
2683+
// Need to sequentially check for each of the branches
2684+
// if no default assume not init
2685+
// if default try and check all statements
2686+
26832687
private compileSwitchStatement(
26842688
statement: SwitchStatement
26852689
): ExpressionRef {
@@ -2742,6 +2746,9 @@ export class Compiler extends DiagnosticEmitter {
27422746
var currentBlock = module.block("case0|" + context, breaks, NativeType.None);
27432747
var commonCategorical = FlowFlags.ANY_CATEGORICAL;
27442748
var commonConditional = 0;
2749+
// Track qualifying flows for field flag analysis
2750+
// A qualifying flow is a breaking flow or the last flow
2751+
var flows: Flow[] = [];
27452752
for (let i = 0; i < numCases; ++i) {
27462753
let case_ = cases[i];
27472754
let statements = case_.statements;
@@ -2775,6 +2782,10 @@ export class Compiler extends DiagnosticEmitter {
27752782
}
27762783
commonConditional |= innerFlow.flags & FlowFlags.ANY_CONDITIONAL;
27772784

2785+
if (innerFlow.isAny(FlowFlags.BREAKS | FlowFlags.RETURNS) || isLast) {
2786+
flows.push(innerFlow);
2787+
}
2788+
27782789
// Switch back to the parent flow
27792790
if (!terminates) this.performAutoreleases(innerFlow, stmts);
27802791
innerFlow.unset(
@@ -2788,7 +2799,28 @@ export class Compiler extends DiagnosticEmitter {
27882799
outerFlow.popBreakLabel();
27892800

27902801
// If the switch has a default (guaranteed to handle any value), propagate common flags
2791-
if (defaultIndex >= 0) outerFlow.flags |= commonCategorical & ~FlowFlags.BREAKS;
2802+
if (defaultIndex >= 0){
2803+
outerFlow.flags |= commonCategorical & ~FlowFlags.BREAKS;
2804+
2805+
// If the switch:
2806+
// - has a default label
2807+
// - is located in the constructor
2808+
// - and has one or more qualifying flows
2809+
// analyze field flags
2810+
if (flows.length > 0 && this.currentFlow.actualFunction.is(CommonFlags.CONSTRUCTOR)) {
2811+
if (flows.length == 1) {
2812+
this.currentFlow.inheritFieldFlags(flows[0]);
2813+
} else {
2814+
for(let i = 0; i < flows.length; ++i) {
2815+
if (i < (flows.length - 1)) {
2816+
const left = flows[i];
2817+
const right = flows[i + 1];
2818+
this.currentFlow.mergeFieldFlags(left, right);
2819+
}
2820+
}
2821+
}
2822+
}
2823+
}
27922824
outerFlow.flags |= commonConditional & ~FlowFlags.CONDITIONALLY_BREAKS;
27932825
// TODO: what about local states?
27942826
return currentBlock;

src/flow.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -842,9 +842,12 @@ export class Flow {
842842
}
843843
}
844844

845+
this.mergeFieldFlags(left, right);
846+
}
845847

846-
if ((left.fieldFlags !== null) &&
847-
(right.fieldFlags !== null) &&
848+
mergeFieldFlags(left: Flow, right: Flow): void {
849+
if (left.fieldFlags !== null &&
850+
right.fieldFlags !== null &&
848851
right.fieldFlags.size > 0
849852
) {
850853
const rightFieldFlags = right.fieldFlags;
@@ -867,6 +870,26 @@ export class Flow {
867870
}
868871
}
869872

873+
inheritFieldFlags(other: Flow): void {
874+
if (
875+
this.fieldFlags !== null &&
876+
other.fieldFlags !== null
877+
) {
878+
const otherFieldFlags = other.fieldFlags;
879+
const otherKeys = Map_keys(otherFieldFlags!);
880+
const otherValues = Map_values(otherFieldFlags!);
881+
const currentFieldFlags = this.fieldFlags;
882+
883+
for (let i = 0, k = otherValues.length; i < k; ++i) {
884+
const key = otherKeys[i];
885+
const otherValue = otherValues[i];
886+
if (otherValue & FieldFlags.INITIALIZED) {
887+
currentFieldFlags.set(key, FieldFlags.INITIALIZED)
888+
}
889+
}
890+
}
891+
}
892+
870893
/** Tests if the specified flows have differing local states. */
871894
static hasIncompatibleLocalStates(before: Flow, after: Flow): bool {
872895
var numThisLocalFlags = before.localFlags.length;

tests/compiler/strict-init.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
"TS2564: Property b has no initializer and is not definitely assigned in constructor",
88
"TS2564: Property fieldLeft has no initializer and is not definitely assigned in constructor",
99
"TS2564: Property fieldRight has no initializer and is not definitely assigned in constructor",
10-
"TS2564: Property p has no initializer and is not definitely assigned in constructor"
10+
"TS2564: Property p has no initializer and is not definitely assigned in constructor",
11+
"TS2564: Property switchNotInitInDefault has no initializer and is not definitely assigned in constructor",
12+
"TS2564: Property switchNoDefault has no initializer and is not definitely assigned in constructor"
1113
]
1214
}

tests/compiler/strict-init.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,80 @@ export abstract class X extends Y {
104104
}
105105
}
106106
}
107+
108+
// OK
109+
export class SwitchFallback {
110+
switchFallback: i32;
111+
constructor(some: i32) {
112+
switch (some) {
113+
case 1:
114+
case 2:
115+
case 3:
116+
default:
117+
this.switchFallback = 6;
118+
break;
119+
}
120+
}
121+
}
122+
123+
// ERR
124+
export class SwitchWithNonInitInDefault {
125+
switchNotInitInDefault: i32;
126+
127+
constructor(some: i32) {
128+
switch (some) {
129+
case 0:
130+
case 1:
131+
this.switchNotInitInDefault = 1;
132+
break;
133+
case 3:
134+
this.switchNotInitInDefault = 5;
135+
default:
136+
const x = 6;
137+
break;
138+
}
139+
}
140+
}
141+
142+
// OK
143+
export class SwitchDefinitelyInit {
144+
switchDefinitelyInit: i32;
145+
constructor(some: i32) {
146+
switch(some) {
147+
case 0:
148+
this.switchDefinitelyInit = 0;
149+
break;
150+
case 1:
151+
this.switchDefinitelyInit = 1;
152+
break;
153+
case 3:
154+
this.switchDefinitelyInit = 5;
155+
default:
156+
this.switchDefinitelyInit = 10;
157+
break;
158+
}
159+
}
160+
}
161+
162+
// ERR
163+
export class SwitchNoDefault {
164+
switchNoDefault: i32;
165+
166+
constructor(some: i32) {
167+
switch(some) {
168+
case 1:
169+
this.switchNoDefault = 4;
170+
}
171+
}
172+
}
173+
174+
export class SwitchOnlyDefault {
175+
switchOnlyDefault: i32;
176+
177+
constructor(some: i32) {
178+
switch (some) {
179+
default:
180+
this.switchOnlyDefault = 0;
181+
}
182+
}
183+
}

0 commit comments

Comments
 (0)