@@ -1141,12 +1141,13 @@ export class Compiler extends DiagnosticEmitter {
1141
1141
// none of the following can be an arrow function
1142
1142
assert ( ! instance . isAny ( CommonFlags . CONSTRUCTOR | CommonFlags . GET | CommonFlags . SET ) ) ;
1143
1143
1144
- let expr = this . compileExpression ( ( < ExpressionStatement > bodyNode ) . expression , returnType ,
1145
- Constraints . CONV_IMPLICIT
1146
- ) ;
1144
+ // take special care of properly retaining the returned value
1145
+ let expr = this . compileReturnedExpression ( ( < ExpressionStatement > bodyNode ) . expression , returnType , Constraints . CONV_IMPLICIT ) ;
1146
+
1147
1147
if ( ! stmts ) stmts = [ expr ] ;
1148
1148
else stmts . push ( expr ) ;
1149
- if ( ! flow . is ( FlowFlags . TERMINATES ) ) { // TODO: detect if returning an autorelease local?
1149
+
1150
+ if ( ! flow . is ( FlowFlags . TERMINATES ) ) {
1150
1151
let indexBefore = stmts . length ;
1151
1152
this . performAutoreleases ( flow , stmts ) ;
1152
1153
this . finishAutoreleases ( flow , stmts ) ;
@@ -2215,6 +2216,32 @@ export class Compiler extends DiagnosticEmitter {
2215
2216
// foo // is possibly null
2216
2217
}
2217
2218
2219
+ /** Compiles an expression that is about to be returned, taking special care of retaining and setting flow states. */
2220
+ compileReturnedExpression (
2221
+ /** Expression to compile. */
2222
+ expression : Expression ,
2223
+ /** Return type of the function. */
2224
+ returnType : Type ,
2225
+ /** Constraints indicating contextual conditions. */
2226
+ constraints : Constraints = Constraints . NONE
2227
+ ) : ExpressionRef {
2228
+ // pretend to retain the expression immediately so the autorelease, if any, is skipped
2229
+ var expr = this . compileExpression ( expression , returnType , constraints | Constraints . WILL_RETAIN ) ;
2230
+ var flow = this . currentFlow ;
2231
+ if ( returnType . isManaged ) {
2232
+ // check if that worked, and if it didn't, keep the reference alive
2233
+ if ( ! this . skippedAutoreleases . has ( expr ) ) {
2234
+ let index = this . tryUndoAutorelease ( expr , flow ) ;
2235
+ if ( index == - 1 ) expr = this . makeRetain ( expr ) ;
2236
+ this . skippedAutoreleases . add ( expr ) ;
2237
+ }
2238
+ }
2239
+ // remember return states
2240
+ if ( ! flow . canOverflow ( expr , returnType ) ) flow . set ( FlowFlags . RETURNS_WRAPPED ) ;
2241
+ if ( flow . isNonnull ( expr , returnType ) ) flow . set ( FlowFlags . RETURNS_NONNULL ) ;
2242
+ return expr ;
2243
+ }
2244
+
2218
2245
compileReturnStatement (
2219
2246
statement : ReturnStatement ,
2220
2247
isLastInBody : bool
@@ -2239,27 +2266,9 @@ export class Compiler extends DiagnosticEmitter {
2239
2266
}
2240
2267
let constraints = Constraints . CONV_IMPLICIT ;
2241
2268
if ( flow . actualFunction . is ( CommonFlags . MODULE_EXPORT ) ) constraints |= Constraints . MUST_WRAP ;
2242
- expr = this . compileExpression ( valueExpression , returnType , constraints | Constraints . WILL_RETAIN ) ;
2243
-
2244
- // when returning a local, and it is already retained, skip the final set
2245
- // of retaining it as the return value and releasing it as a variable
2246
- if ( ! this . skippedAutoreleases . has ( expr ) ) {
2247
- if ( returnType . isManaged ) {
2248
- if ( getExpressionId ( expr ) == ExpressionId . LocalGet ) {
2249
- let index = getLocalGetIndex ( expr ) ;
2250
- if ( flow . isAnyLocalFlag ( index , LocalFlags . ANY_RETAINED ) ) {
2251
- flow . unsetLocalFlag ( index , LocalFlags . ANY_RETAINED ) ;
2252
- flow . setLocalFlag ( index , LocalFlags . RETURNED ) ;
2253
- this . skippedAutoreleases . add ( expr ) ;
2254
- }
2255
- }
2256
- }
2257
- }
2258
-
2259
- // remember return states
2260
- if ( ! flow . canOverflow ( expr , returnType ) ) flow . set ( FlowFlags . RETURNS_WRAPPED ) ;
2261
- if ( flow . isNonnull ( expr , returnType ) ) flow . set ( FlowFlags . RETURNS_NONNULL ) ;
2262
2269
2270
+ // take special care of properly retaining the returned value
2271
+ expr = this . compileReturnedExpression ( valueExpression , returnType , constraints ) ;
2263
2272
} else if ( returnType != Type . void ) {
2264
2273
this . error (
2265
2274
DiagnosticCode . Type_0_is_not_assignable_to_type_1 ,
@@ -2272,9 +2281,6 @@ export class Compiler extends DiagnosticEmitter {
2272
2281
this . performAutoreleases ( flow , stmts ) ;
2273
2282
this . finishAutoreleases ( flow , stmts ) ;
2274
2283
2275
- // Make sure that the return value is retained for the caller
2276
- if ( returnType . isManaged && ! this . skippedAutoreleases . has ( expr ) ) expr = this . makeRetain ( expr ) ;
2277
-
2278
2284
if ( returnType != Type . void && stmts . length ) {
2279
2285
let temp = flow . getTempLocal ( returnType ) ;
2280
2286
if ( flow . isNonnull ( expr , returnType ) ) flow . setLocalFlag ( temp . index , LocalFlags . NONNULL ) ;
@@ -6506,24 +6512,32 @@ export class Compiler extends DiagnosticEmitter {
6506
6512
/** Flow that would autorelease. */
6507
6513
flow : Flow
6508
6514
) : i32 {
6509
- // NOTE: Can't remove the local.tee completely because it's already compiled
6510
- // and a child of something else. Preventing the final release however makes
6511
- // it optimize away .
6515
+ // The following assumes that the expression actually belongs to the flow and that
6516
+ // top-level autoreleases are never undone. While that's true, it's not necessary
6517
+ // to check presence in scopedLocals .
6512
6518
switch ( getExpressionId ( expr ) ) {
6513
- case ExpressionId . LocalSet : { // local.tee(__retain(expr))
6519
+ case ExpressionId . LocalGet : { // local.get(idx)
6520
+ let index = getLocalGetIndex ( expr ) ;
6521
+ if ( flow . isAnyLocalFlag ( index , LocalFlags . ANY_RETAINED ) ) {
6522
+ flow . unsetLocalFlag ( index , LocalFlags . ANY_RETAINED ) ;
6523
+ return index ;
6524
+ }
6525
+ break ;
6526
+ }
6527
+ case ExpressionId . LocalSet : { // local.tee(idx, expr)
6514
6528
if ( isLocalTee ( expr ) ) {
6529
+ // NOTE: Can't remove the local.tee completely because it's already compiled
6530
+ // and a child of something else. Preventing the final release however makes
6531
+ // it optimize away.
6515
6532
let index = getLocalSetIndex ( expr ) ;
6516
6533
if ( flow . isAnyLocalFlag ( index , LocalFlags . ANY_RETAINED ) ) {
6517
- // Assumes that the expression actually belongs to the flow and that
6518
- // top-level autoreleases are never undone. While that's true, it's
6519
- // not necessary to check presence in scopedLocals.
6520
6534
flow . unsetLocalFlag ( index , LocalFlags . ANY_RETAINED ) ;
6521
6535
return index ;
6522
6536
}
6523
6537
}
6524
6538
break ;
6525
6539
}
6526
- case ExpressionId . Block : { // { ..., local.tee(__retain(expr) ) }
6540
+ case ExpressionId . Block : { // { ..., local.get| tee(... ) }
6527
6541
if ( getBlockName ( expr ) === null ) { // must not be a break target
6528
6542
let count = getBlockChildCount ( expr ) ;
6529
6543
if ( count ) {
0 commit comments