@@ -4479,10 +4479,12 @@ export class Compiler extends DiagnosticEmitter {
44794479 compound = true ;
44804480 let assignmentLeft = left ;
44814481 let setupExprs : ExpressionRef [ ] | null = null ;
4482- if ( this . needsCompoundAssignmentSideEffectCache ( left ) ) {
4482+ let cacheTarget = this . getCompoundAssignmentSideEffectCacheTarget ( left ) ;
4483+ if ( cacheTarget && this . needsCompoundAssignmentSideEffectCache ( left ) ) {
44834484 let flow = this . currentFlow ;
4484- if ( left . kind == NodeKind . PropertyAccess ) {
4485- let access = < PropertyAccessExpression > left ;
4485+ let rewrittenTarget : Expression ;
4486+ if ( cacheTarget . kind == NodeKind . PropertyAccess ) {
4487+ let access = < PropertyAccessExpression > cacheTarget ;
44864488 let receiverExpression = access . expression ;
44874489 if ( this . expressionHasSideEffects ( receiverExpression ) ) {
44884490 let receiverExpr = this . compileExpression ( receiverExpression , Type . auto ) ;
@@ -4492,14 +4494,16 @@ export class Compiler extends DiagnosticEmitter {
44924494 flow . addScopedAlias ( receiverName , receiverType , receiverTemp . index ) ;
44934495 flow . setLocalFlag ( receiverTemp . index , LocalFlags . Initialized ) ;
44944496 setupExprs = [ module . local_set ( receiverTemp . index , receiverExpr , receiverType . isManaged ) ] ;
4495- assignmentLeft = Node . createPropertyAccessExpression (
4497+ rewrittenTarget = Node . createPropertyAccessExpression (
44964498 Node . createIdentifierExpression ( receiverName , receiverExpression . range ) ,
44974499 access . property ,
44984500 access . range
44994501 ) ;
4502+ } else {
4503+ rewrittenTarget = cacheTarget ;
45004504 }
45014505 } else {
4502- let access = < ElementAccessExpression > left ;
4506+ let access = < ElementAccessExpression > cacheTarget ;
45034507 let receiverExpression = access . expression ;
45044508 let elementExpression = access . elementExpression ;
45054509 let receiverName : string | null = null ;
@@ -4527,7 +4531,7 @@ export class Compiler extends DiagnosticEmitter {
45274531 setupExprs . push ( module . local_set ( elementTemp . index , elementExpr , elementType . isManaged ) ) ;
45284532 }
45294533
4530- assignmentLeft = Node . createElementAccessExpression (
4534+ rewrittenTarget = Node . createElementAccessExpression (
45314535 receiverName
45324536 ? Node . createIdentifierExpression ( receiverName , receiverExpression . range )
45334537 : receiverExpression ,
@@ -4537,15 +4541,14 @@ export class Compiler extends DiagnosticEmitter {
45374541 access . range
45384542 ) ;
45394543 }
4544+ assignmentLeft = this . replaceCompoundAssignmentSideEffectCacheTarget (
4545+ left ,
4546+ this . wrapCompoundAssignmentCacheSetup ( setupExprs , rewrittenTarget )
4547+ ) ;
45404548 }
45414549
45424550 leftExpr = this . compileExpression ( assignmentLeft , contextualType . intType ) ;
45434551 leftType = this . currentType ;
4544- if ( setupExprs ) {
4545- let wrappedExprs = setupExprs ;
4546- wrappedExprs . push ( leftExpr ) ;
4547- leftExpr = module . block ( null , wrappedExprs , leftType . toRef ( ) ) ;
4548- }
45494552
45504553 // check operator overload
45514554 let classReference = leftType . getClassOrWrapper ( this . program ) ;
@@ -4916,17 +4919,93 @@ export class Compiler extends DiagnosticEmitter {
49164919 }
49174920
49184921 private needsCompoundAssignmentSideEffectCache ( target : Expression ) : bool {
4919- if ( target . kind == NodeKind . PropertyAccess ) {
4920- return this . expressionHasSideEffects ( ( < PropertyAccessExpression > target ) . expression ) ;
4922+ let cacheTarget = this . getCompoundAssignmentSideEffectCacheTarget ( target ) ;
4923+ if ( ! cacheTarget ) return false ;
4924+ if ( cacheTarget . kind == NodeKind . PropertyAccess ) {
4925+ return this . expressionHasSideEffects ( ( < PropertyAccessExpression > cacheTarget ) . expression ) ;
49214926 }
4922- if ( target . kind == NodeKind . ElementAccess ) {
4923- let access = < ElementAccessExpression > target ;
4927+ if ( cacheTarget . kind == NodeKind . ElementAccess ) {
4928+ let access = < ElementAccessExpression > cacheTarget ;
49244929 return this . expressionHasSideEffects ( access . expression )
49254930 || this . expressionHasSideEffects ( access . elementExpression ) ;
49264931 }
49274932 return false ;
49284933 }
49294934
4935+ private getCompoundAssignmentSideEffectCacheTarget ( target : Expression ) : Expression | null {
4936+ while ( target . kind == NodeKind . Parenthesized ) {
4937+ target = ( < ParenthesizedExpression > target ) . expression ;
4938+ }
4939+ switch ( target . kind ) {
4940+ case NodeKind . PropertyAccess :
4941+ case NodeKind . ElementAccess :
4942+ return target ;
4943+ case NodeKind . Assertion : {
4944+ let assertion = < AssertionExpression > target ;
4945+ if ( assertion . assertionKind == AssertionKind . NonNull ) {
4946+ return this . getCompoundAssignmentSideEffectCacheTarget ( assertion . expression ) ;
4947+ }
4948+ return null ;
4949+ }
4950+ case NodeKind . Comma : {
4951+ let expressions = ( < CommaExpression > target ) . expressions ;
4952+ return this . getCompoundAssignmentSideEffectCacheTarget ( expressions [ assert ( expressions . length ) - 1 ] ) ;
4953+ }
4954+ default :
4955+ return null ;
4956+ }
4957+ }
4958+
4959+ private replaceCompoundAssignmentSideEffectCacheTarget ( target : Expression , replacement : Expression ) : Expression {
4960+ while ( target . kind == NodeKind . Parenthesized ) {
4961+ target = ( < ParenthesizedExpression > target ) . expression ;
4962+ replacement = Node . createParenthesizedExpression ( replacement , target . range ) ;
4963+ }
4964+ switch ( target . kind ) {
4965+ case NodeKind . PropertyAccess :
4966+ case NodeKind . ElementAccess :
4967+ return replacement ;
4968+ case NodeKind . Assertion : {
4969+ let assertion = < AssertionExpression > target ;
4970+ if ( assertion . assertionKind == AssertionKind . NonNull ) {
4971+ return Node . createAssertionExpression (
4972+ AssertionKind . NonNull ,
4973+ this . replaceCompoundAssignmentSideEffectCacheTarget ( assertion . expression , replacement ) ,
4974+ null ,
4975+ assertion . range
4976+ ) ;
4977+ }
4978+ return target ;
4979+ }
4980+ case NodeKind . Comma : {
4981+ let comma = < CommaExpression > target ;
4982+ let expressions = comma . expressions ;
4983+ let cloned = new Array < Expression > ( expressions . length ) ;
4984+ for ( let i = 0 , k = expressions . length - 1 ; i < k ; ++ i ) {
4985+ cloned [ i ] = unchecked ( expressions [ i ] ) ;
4986+ }
4987+ cloned [ assert ( expressions . length ) - 1 ] = this . replaceCompoundAssignmentSideEffectCacheTarget (
4988+ expressions [ assert ( expressions . length ) - 1 ] ,
4989+ replacement
4990+ ) ;
4991+ return Node . createCommaExpression ( cloned , comma . range ) ;
4992+ }
4993+ default :
4994+ return target ;
4995+ }
4996+ }
4997+
4998+ private wrapCompoundAssignmentCacheSetup ( setupExprs : ExpressionRef [ ] | null , target : Expression ) : Expression {
4999+ if ( ! setupExprs || ! setupExprs . length ) return target ;
5000+ let range = target . range ;
5001+ let expressions = new Array < Expression > ( setupExprs . length + 1 ) ;
5002+ for ( let i = 0 , k = setupExprs . length ; i < k ; ++ i ) {
5003+ expressions [ i ] = Node . createCompiledExpression ( unchecked ( setupExprs [ i ] ) , Type . void , range ) ;
5004+ }
5005+ expressions [ setupExprs . length ] = target ;
5006+ return Node . createCommaExpression ( expressions , range ) ;
5007+ }
5008+
49305009 private expressionHasSideEffects ( expression : Expression ) : bool {
49315010 while ( expression . kind == NodeKind . Parenthesized ) {
49325011 expression = ( < ParenthesizedExpression > expression ) . expression ;
0 commit comments