@@ -1468,7 +1468,7 @@ export class Compiler extends DiagnosticEmitter {
1468
1468
let stmt = this . compileStatement ( statements [ i ] ) ;
1469
1469
if ( getExpressionId ( stmt ) != ExpressionId . Nop ) {
1470
1470
stmts [ count ++ ] = stmt ;
1471
- if ( flow . isAny ( FlowFlags . BREAKS | FlowFlags . CONTINUES | FlowFlags . RETURNS ) ) break ;
1471
+ if ( flow . isAny ( FlowFlags . TERMINATED ) ) break ;
1472
1472
}
1473
1473
}
1474
1474
stmts . length = count ;
@@ -1774,14 +1774,18 @@ export class Compiler extends DiagnosticEmitter {
1774
1774
var module = this . module ;
1775
1775
var currentFunction = this . currentFunction ;
1776
1776
1777
+ var cases = statement . cases ;
1778
+ var numCases = cases . length ;
1779
+ if ( ! numCases ) {
1780
+ return this . compileExpression ( statement . condition , Type . void , ConversionKind . IMPLICIT , WrapMode . NONE ) ;
1781
+ }
1782
+
1777
1783
// Everything within a switch uses the same break context
1778
1784
var context = currentFunction . enterBreakContext ( ) ;
1779
1785
1780
1786
// introduce a local for evaluating the condition (exactly once)
1781
1787
var tempLocal = currentFunction . getTempLocal ( Type . u32 , false ) ;
1782
1788
var tempLocalIndex = tempLocal . index ;
1783
- var cases = statement . cases ;
1784
- var numCases = cases . length ;
1785
1789
1786
1790
// Prepend initializer to inner block. Does not initiate a new branch, yet.
1787
1791
var breaks = new Array < ExpressionRef > ( 1 + numCases ) ;
@@ -1833,31 +1837,37 @@ export class Compiler extends DiagnosticEmitter {
1833
1837
let breakLabel = "break|" + context ;
1834
1838
flow . breakLabel = breakLabel ;
1835
1839
1836
- let fallsThrough = i ! = numCases - 1 ;
1837
- let nextLabel = ! fallsThrough ? breakLabel : "case" + ( i + 1 ) . toString ( 10 ) + "|" + context ;
1840
+ let isLast = i = = numCases - 1 ;
1841
+ let nextLabel = isLast ? breakLabel : "case" + ( i + 1 ) . toString ( 10 ) + "|" + context ;
1838
1842
let stmts = new Array < ExpressionRef > ( 1 + numStatements ) ;
1839
1843
stmts [ 0 ] = currentBlock ;
1840
1844
let count = 1 ;
1845
+ let terminated = false ;
1841
1846
for ( let j = 0 ; j < numStatements ; ++ j ) {
1842
1847
let stmt = this . compileStatement ( statements [ j ] ) ;
1843
1848
if ( getExpressionId ( stmt ) != ExpressionId . Nop ) {
1844
1849
stmts [ count ++ ] = stmt ;
1845
- if ( flow . is ( FlowFlags . BREAKS | FlowFlags . CONTINUES | FlowFlags . RETURNS ) ) break ;
1850
+ if ( flow . isAny ( FlowFlags . TERMINATED ) ) {
1851
+ terminated = true ;
1852
+ break ;
1853
+ }
1846
1854
}
1847
1855
}
1848
1856
stmts . length = count ;
1849
- if ( ! ( fallsThrough || flow . is ( FlowFlags . RETURNS ) ) ) alwaysReturns = false ; // ignore fall-throughs
1850
- if ( ! ( fallsThrough || flow . is ( FlowFlags . RETURNS_WRAPPED ) ) ) alwaysReturnsWrapped = false ; // ignore fall-throughs
1851
- if ( ! ( fallsThrough || flow . is ( FlowFlags . THROWS ) ) ) alwaysThrows = false ;
1852
- if ( ! ( fallsThrough || flow . is ( FlowFlags . ALLOCATES ) ) ) alwaysAllocates = false ;
1857
+ if ( terminated || isLast ) {
1858
+ if ( ! flow . is ( FlowFlags . RETURNS ) ) alwaysReturns = false ;
1859
+ if ( ! flow . is ( FlowFlags . RETURNS_WRAPPED ) ) alwaysReturnsWrapped = false ;
1860
+ if ( ! flow . is ( FlowFlags . THROWS ) ) alwaysThrows = false ;
1861
+ if ( ! flow . is ( FlowFlags . ALLOCATES ) ) alwaysAllocates = false ;
1862
+ }
1853
1863
1854
1864
// Switch back to the parent flow
1855
- currentFunction . flow = flow . leaveBranchOrScope ( ) ;
1865
+ currentFunction . flow = flow . leaveBranchOrScope ( false ) ;
1856
1866
currentBlock = module . createBlock ( nextLabel , stmts , NativeType . None ) ; // must be a labeled block
1857
1867
}
1858
1868
currentFunction . leaveBreakContext ( ) ;
1859
1869
1860
- // If the switch has a default and always returns , propagate that
1870
+ // If the switch has a default (guaranteed to handle any value) , propagate common flags
1861
1871
if ( defaultIndex >= 0 ) {
1862
1872
let flow = currentFunction . flow ;
1863
1873
if ( alwaysReturns ) flow . set ( FlowFlags . RETURNS ) ;
@@ -5101,7 +5111,7 @@ export class Compiler extends DiagnosticEmitter {
5101
5111
let stmt = this . compileStatement ( statements [ i ] ) ;
5102
5112
if ( getExpressionId ( stmt ) != ExpressionId . Nop ) {
5103
5113
body . push ( stmt ) ;
5104
- if ( flow . is ( FlowFlags . RETURNS ) ) break ;
5114
+ if ( flow . isAny ( FlowFlags . TERMINATED ) ) break ;
5105
5115
}
5106
5116
}
5107
5117
} else {
@@ -5122,8 +5132,8 @@ export class Compiler extends DiagnosticEmitter {
5122
5132
this . currentFunction . flow = previousFlow ;
5123
5133
this . currentType = returnType ;
5124
5134
5125
- // Check that all branches return
5126
- if ( returnType != Type . void && ! flow . is ( FlowFlags . RETURNS ) ) {
5135
+ // Check that all branches are terminated
5136
+ if ( returnType != Type . void && ! flow . isAny ( FlowFlags . TERMINATED ) ) {
5127
5137
this . error (
5128
5138
DiagnosticCode . A_function_whose_declared_type_is_not_void_must_return_a_value ,
5129
5139
declaration . signature . returnType . range
@@ -5224,16 +5234,28 @@ export class Compiler extends DiagnosticEmitter {
5224
5234
] ) ;
5225
5235
for ( let i = 0 ; i < numOptional ; ++ i , ++ operandIndex ) {
5226
5236
let type = originalParameterTypes [ minArguments + i ] ;
5227
- body = module . createBlock ( names [ i + 1 ] , [
5228
- body ,
5229
- module . createSetLocal ( operandIndex ,
5237
+ let declaration = originalParameterDeclarations [ minArguments + i ] ;
5238
+ let initializer = declaration . initializer ;
5239
+ let initExpr : ExpressionRef ;
5240
+ if ( initializer ) {
5241
+ initExpr = module . createSetLocal ( operandIndex ,
5230
5242
this . compileExpression (
5231
- assert ( originalParameterDeclarations [ minArguments + i ] . initializer ) ,
5243
+ initializer ,
5232
5244
type ,
5233
5245
ConversionKind . IMPLICIT ,
5234
5246
WrapMode . WRAP
5235
5247
)
5236
- )
5248
+ ) ;
5249
+ } else {
5250
+ this . error (
5251
+ DiagnosticCode . Optional_parameter_must_have_an_initializer ,
5252
+ declaration . range
5253
+ ) ;
5254
+ initExpr = module . createUnreachable ( ) ;
5255
+ }
5256
+ body = module . createBlock ( names [ i + 1 ] , [
5257
+ body ,
5258
+ initExpr ,
5237
5259
] ) ;
5238
5260
forwardedOperands [ operandIndex ] = module . createGetLocal ( operandIndex , type . toNativeType ( ) ) ;
5239
5261
}
@@ -5326,9 +5348,10 @@ export class Compiler extends DiagnosticEmitter {
5326
5348
let parameterNodes = instance . prototype . declaration . signature . parameterTypes ;
5327
5349
let allOptionalsAreConstant = true ;
5328
5350
for ( let i = numArguments ; i < maxArguments ; ++ i ) {
5329
- let initializer = assert ( parameterNodes [ i ] . initializer ) ;
5330
- if ( initializer . kind != NodeKind . LITERAL ) {
5351
+ let initializer = parameterNodes [ i ] . initializer ;
5352
+ if ( ! ( initializer && initializer . kind == NodeKind . LITERAL ) ) {
5331
5353
// TODO: other kinds might be constant as well
5354
+ // NOTE: if the initializer is missing this is reported in ensureTrampoline below
5332
5355
allOptionalsAreConstant = false ;
5333
5356
break ;
5334
5357
}
0 commit comments