@@ -58,6 +58,7 @@ import {
5858 isConstExpressionNaN ,
5959 ensureType ,
6060 createType ,
61+ expandType ,
6162 getConstValueInteger
6263} from "./module" ;
6364
@@ -473,6 +474,10 @@ export class Compiler extends DiagnosticEmitter {
473474 overrideStubs : Set < Function > = new Set ( ) ;
474475 /** Elements currently undergoing compilation. */
475476 pendingElements : Set < Element > = new Set ( ) ;
477+ /** Component locals for tuple-capturing locals (first component at index 0). */
478+ private tupleLocalComponents : Map < Local , Local [ ] > = new Map ( ) ;
479+ /** TypeRef overrides for hidden locals that use tuple-typed Binaryen IR locals. */
480+ private localTypeRefOverrides : Map < Local , TypeRef > = new Map ( ) ;
476481 /** Elements, that are module exports, already processed */
477482 doneModuleExports : Set < Element > = new Set ( ) ;
478483 /** Shadow stack reference. */
@@ -739,7 +744,7 @@ export class Compiler extends DiagnosticEmitter {
739744 startFunctionInstance . internalName ,
740745 signature . paramRefs ,
741746 signature . resultRefs ,
742- typesToRefs ( startFunctionInstance . getNonParameterLocalTypes ( ) ) ,
747+ typesToRefs ( startFunctionInstance . getNonParameterLocalTypes ( ) , this . getNonParameterLocalTypeRefOverrides ( startFunctionInstance ) ) ,
743748 module . flatten ( startFunctionBody )
744749 ) ;
745750 startFunctionInstance . finalize ( module , funcRef ) ;
@@ -1681,7 +1686,7 @@ export class Compiler extends DiagnosticEmitter {
16811686 instance . internalName ,
16821687 signature . paramRefs ,
16831688 signature . resultRefs ,
1684- typesToRefs ( instance . getNonParameterLocalTypes ( ) ) ,
1689+ typesToRefs ( instance . getNonParameterLocalTypes ( ) , this . getNonParameterLocalTypeRefOverrides ( instance ) ) ,
16851690 module . flatten ( stmts , signature . resultRefs )
16861691 ) ;
16871692
@@ -1922,7 +1927,7 @@ export class Compiler extends DiagnosticEmitter {
19221927 getterInstance . internalName ,
19231928 thisTypeRef ,
19241929 valueTypeRef ,
1925- typesToRefs ( getterInstance . getNonParameterLocalTypes ( ) ) ,
1930+ typesToRefs ( getterInstance . getNonParameterLocalTypes ( ) , this . getNonParameterLocalTypeRefOverrides ( getterInstance ) ) ,
19261931 body
19271932 ) ;
19281933 }
@@ -3174,7 +3179,7 @@ export class Compiler extends DiagnosticEmitter {
31743179 let pendingElements = this . pendingElements ;
31753180 let temp = flow . addScopedDummyLocal ( name , Type . auto , statement ) ; // pending dummy
31763181 pendingElements . add ( temp ) ;
3177- initExpr = this . compileExpression ( initializerNode , Type . auto ) ; // reports
3182+ initExpr = this . compileExpression ( initializerNode , Type . auto , Constraints . AllowMultiValueResult ) ;
31783183 initType = this . currentType ;
31793184 pendingElements . delete ( temp ) ;
31803185 flow . freeScopedDummyLocal ( name ) ;
@@ -3309,9 +3314,43 @@ export class Compiler extends DiagnosticEmitter {
33093314 if ( isConst ) flow . setLocalFlag ( local . index , LocalFlags . Constant ) ;
33103315 }
33113316 if ( initExpr ) {
3312- initializers . push (
3313- this . makeLocalAssignment ( local , initExpr , initType ? initType : type , false )
3314- ) ;
3317+ let tupleReturnTypes : Type [ ] | null = null ;
3318+ let tupleCaptureNode = assert ( initializerNode ) ;
3319+ while ( tupleCaptureNode . kind == NodeKind . Parenthesized )
3320+ tupleCaptureNode = ( < ParenthesizedExpression > tupleCaptureNode ) . expression ;
3321+ if ( tupleCaptureNode . kind == NodeKind . Call && this . options . hasFeature ( Feature . MultiValue ) )
3322+ tupleReturnTypes = this . resolveTupleReturnTypesFromExpressionRef ( initExpr ) ;
3323+ if ( tupleReturnTypes && tupleReturnTypes . length > 1 ) {
3324+ let tupleLocals = new Array < Local > ( tupleReturnTypes . length ) ;
3325+ tupleLocals [ 0 ] = local ;
3326+ for ( let j = 1 , k = tupleReturnTypes . length ; j < k ; ++ j ) {
3327+ let tupleLocal = flow . targetFunction . addLocal ( tupleReturnTypes [ j ] ) ;
3328+ flow . unsetLocalFlag ( tupleLocal . index , ~ 0 ) ;
3329+ tupleLocals [ j ] = tupleLocal ;
3330+ }
3331+ this . tupleLocalComponents . set ( local , tupleLocals ) ;
3332+ this . resolver . tupleLocalReturnTypes . set ( local , tupleReturnTypes ) ;
3333+
3334+ let tupleTypeRef = createType ( typesToRefs ( tupleReturnTypes ) ) ;
3335+ let tupleStorage = flow . targetFunction . addLocal ( tupleReturnTypes [ 0 ] ) ;
3336+ flow . unsetLocalFlag ( tupleStorage . index , ~ 0 ) ;
3337+ this . localTypeRefOverrides . set ( tupleStorage , tupleTypeRef ) ;
3338+
3339+ let tupleInit = new Array < ExpressionRef > ( 1 + tupleReturnTypes . length ) ;
3340+ tupleInit [ 0 ] = module . local_set ( tupleStorage . index , initExpr , false ) ;
3341+ for ( let j = 0 , k = tupleReturnTypes . length ; j < k ; ++ j ) {
3342+ let tupleType = tupleReturnTypes [ j ] ;
3343+ tupleInit [ 1 + j ] = this . makeLocalAssignment (
3344+ tupleLocals [ j ] ,
3345+ module . tuple_extract ( module . local_get ( tupleStorage . index , tupleTypeRef ) , j ) ,
3346+ tupleType ,
3347+ false
3348+ ) ;
3349+ }
3350+ initializers . push ( module . flatten ( tupleInit ) ) ;
3351+ } else {
3352+ initializers . push ( this . makeLocalAssignment ( local , initExpr , initType ? initType : type , false ) ) ;
3353+ }
33153354 } else {
33163355 // no need to assign zero
33173356 if ( local . type . isShortIntegerValue ) {
@@ -3326,6 +3365,48 @@ export class Compiler extends DiagnosticEmitter {
33263365 : module . flatten ( initializers ) ;
33273366 }
33283367
3368+ /** Resolves tuple return types from a compiled expression, if it has a tuple Binaryen type. */
3369+ private resolveTupleReturnTypesFromExpressionRef ( expression : ExpressionRef ) : Type [ ] | null {
3370+ let typeRefs = expandType ( getExpressionType ( expression ) ) ;
3371+ let numTypes = typeRefs . length ;
3372+ if ( numTypes <= 1 ) return null ;
3373+ let returnTypes = new Array < Type > ( numTypes ) ;
3374+ for ( let i = 0 ; i < numTypes ; ++ i ) {
3375+ switch ( unchecked ( typeRefs [ i ] ) ) {
3376+ case TypeRef . I32 : unchecked ( returnTypes [ i ] = Type . i32 ) ; break ;
3377+ case TypeRef . I64 : unchecked ( returnTypes [ i ] = Type . i64 ) ; break ;
3378+ case TypeRef . F32 : unchecked ( returnTypes [ i ] = Type . f32 ) ; break ;
3379+ case TypeRef . F64 : unchecked ( returnTypes [ i ] = Type . f64 ) ; break ;
3380+ case TypeRef . V128 : unchecked ( returnTypes [ i ] = Type . v128 ) ; break ;
3381+ default : return null ;
3382+ }
3383+ }
3384+ return returnTypes ;
3385+ }
3386+
3387+ /** Gets optional non-parameter local type ref overrides for a function, if any are present. */
3388+ private getNonParameterLocalTypeRefOverrides ( instance : Function ) : TypeRef [ ] | null {
3389+ let localTypeRefOverrides = this . localTypeRefOverrides ;
3390+ if ( localTypeRefOverrides . size == 0 ) return null ;
3391+ let localsByIndex = instance . localsByIndex ;
3392+ let signature = instance . signature ;
3393+ let numFixed = signature . parameterTypes . length ;
3394+ if ( signature . thisType ) ++ numFixed ;
3395+ let numAdditional = localsByIndex . length - numFixed ;
3396+ let refs = new Array < TypeRef > ( numAdditional ) ;
3397+ let hasOverride = false ;
3398+ for ( let i = 0 ; i < numAdditional ; ++ i ) {
3399+ let local = localsByIndex [ numFixed + i ] ;
3400+ let ref = local . type . toRef ( ) ;
3401+ if ( localTypeRefOverrides . has ( local ) ) {
3402+ ref = assert ( localTypeRefOverrides . get ( local ) ) ;
3403+ hasOverride = true ;
3404+ }
3405+ refs [ i ] = ref ;
3406+ }
3407+ return hasOverride ? refs : null ;
3408+ }
3409+
33293410 private compileVoidStatement (
33303411 statement : VoidStatement
33313412 ) : ExpressionRef {
@@ -5911,6 +5992,15 @@ export class Compiler extends DiagnosticEmitter {
59115992 switch ( target . kind ) {
59125993 case ElementKind . Local : {
59135994 let local = < Local > target ;
5995+ if ( this . tupleLocalComponents . has ( local ) ) {
5996+ this . error (
5997+ DiagnosticCode . Not_implemented_0 ,
5998+ valueExpression . range ,
5999+ "Assigning to locals that capture multi-value call results is not supported yet"
6000+ ) ;
6001+ this . currentType = tee ? local . type : Type . void ;
6002+ return module . unreachable ( ) ;
6003+ }
59146004 if ( flow . isLocalFlag ( local . index , LocalFlags . Constant , true ) ) {
59156005 this . error (
59166006 DiagnosticCode . Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property ,
@@ -6759,7 +6849,10 @@ export class Compiler extends DiagnosticEmitter {
67596849 stub . internalName ,
67606850 stub . signature . paramRefs ,
67616851 stub . signature . resultRefs ,
6762- typesToRefs ( stub . getNonParameterLocalTypes ( ) ) ,
6852+ typesToRefs (
6853+ stub . getNonParameterLocalTypes ( ) ,
6854+ this . getNonParameterLocalTypeRefOverrides ( stub )
6855+ ) ,
67636856 module . flatten ( stmts , stub . signature . resultRefs )
67646857 ) ;
67656858 stub . set ( CommonFlags . Compiled ) ;
@@ -7267,7 +7360,30 @@ export class Compiler extends DiagnosticEmitter {
72677360 let module = this . module ;
72687361 let targetExpression = expression . expression ;
72697362 let resolver = this . resolver ;
7270- let targetElement = resolver . lookupExpression ( targetExpression , this . currentFlow , Type . auto , ReportMode . Swallow ) ;
7363+ let target = resolver . lookupExpression ( targetExpression , this . currentFlow , Type . auto , ReportMode . Swallow ) ;
7364+ if ( target && target . kind == ElementKind . Local ) {
7365+ let tupleLocals = this . tupleLocalComponents . get ( < Local > target ) ;
7366+ if ( tupleLocals ) {
7367+ let index = resolver . resolveTupleElementLiteralIndex (
7368+ expression . elementExpression ,
7369+ tupleLocals . length ,
7370+ ReportMode . Report
7371+ ) ;
7372+ if ( index < 0 ) return module . unreachable ( ) ;
7373+ let tupleLocal = tupleLocals [ index ] ;
7374+ if ( ! this . currentFlow . isLocalFlag ( tupleLocal . index , LocalFlags . Initialized ) ) {
7375+ this . error (
7376+ DiagnosticCode . Variable_0_is_used_before_being_assigned ,
7377+ expression . range , tupleLocal . name
7378+ ) ;
7379+ }
7380+ let tupleElementType = tupleLocal . type ;
7381+ this . currentType = tupleElementType ;
7382+ return module . local_get ( tupleLocal . index , tupleElementType . toRef ( ) ) ;
7383+ }
7384+ }
7385+
7386+ let targetElement = target ;
72717387 if ( targetElement && targetElement . kind == ElementKind . Enum ) {
72727388 const elementExpr = this . compileExpression ( expression . elementExpression , Type . i32 , Constraints . ConvImplicit ) ;
72737389 const toStringFunctionName = this . ensureEnumToString ( < Enum > targetElement , expression ) ;
0 commit comments