@@ -1673,6 +1673,7 @@ def private finalize_emission_stmts(top : Expression?; srcName : string; at : Li
16731673
16741674// The per-element bind name emit fns peel where/select/key/value lambdas against. Drives build_decs_inner_for_pruned's
16751675// pattern matching, so the decs side MUST use `decs_tup`. DecsJoin carries the per-pair result-lam bind name in its payload.
1676+ [macro_function ]
16761677def private adapter_bind_name (adapter : SourceAdapter ; at : LineInfo ) : string {
16771678 return (adapter as DecsJoin ).resBindName if (adapter is DecsJoin )
16781679 return qn ("decs_tup" , at ) if (adapter is Decs )
@@ -4156,88 +4157,6 @@ def private expr_uses_var(var e : Expression?; name : string) : bool {
41564157
41574158// ── group_by core (shared between array and decs sources) ──────────────────────
41584159
4159- // Source-loop adapter: dispatches the per-source-shape divergence (initial bind name + element type, per-element source loop emission, final invoke wrap). Used by plan_group_by_core to unify the ~350 LOC shared recognizer/splice machinery between plan_group_by (array source via for(it in src)) and plan_decs_group_by (decs bridge via for_each_archetype + inner multi-iter for + tupBind).
4160- struct private GroupBySourceAdapter {
4161- // The bind name the upstream segment walk and reducer recognition see as "the element" (srcItName for array, tupName for decs — both alias the post-iterator-bind variable).
4162- initialBindName : string
4163- // The element type at that bind point (peel_each-derived from `top._type.firstType` for array, `bridge.elementType` for decs).
4164- initialElemType : TypeDeclPtr
4165- // Prefix for all hoisted names ("" for array → tab/dummy/k/uk/…; "decs_" for decs → decs_tab/decs_dummy/…). Lets the same core function generate distinct-but-recognizable names per source.
4166- namePrefix : string
4167- // Discriminator for the three emit modes below (isDecsJoin > isDecs > array fallthrough).
4168- isDecs : bool
4169- isDecsJoin : bool
4170- // Array side: the source expression bound by finalize_emission_stmts into srcName.
4171- arrayTop : Expression?
4172- arraySrcName : string
4173- // Decs side: bridge metadata used by build_decs_tup_bind / build_decs_inner_for + the outer for_each_archetype call.
4174- decsBridge : DecsBridgeShape?
4175- // Decs-join side (Theme 3 — audit probe C3): hashB collect + srcA probe + per-pair result-lam bind feeds plan_group_by_core's bucket update directly. initialBindName aliases the result-lam output (one bind per (a,b) pair).
4176- decsJoinBridgeA : DecsBridgeShape?
4177- decsJoinBridgeB : DecsBridgeShape?
4178- decsJoinKeyaLam : Expression?
4179- decsJoinKeybLam : Expression?
4180- decsJoinResultLam : Expression?
4181- decsJoinKeyType : TypeDeclPtr
4182- decsJoinTupBType : TypeDeclPtr
4183- decsJoinResultType : TypeDeclPtr
4184- }
4185-
4186- // decs-join → Decs(null, …) is safe at finalize: adapter_wrap_invoke's Decs branch reads only the variant tag.
4187- [macro_function ]
4188- def private to_source_adapter (adapter : GroupBySourceAdapter ) : SourceAdapter {
4189- return SourceAdapter (Decs = (adapter .decsBridge , adapter .initialBindName )) if (adapter .isDecs || adapter .isDecsJoin )
4190- return SourceAdapter (Array = (adapter .arrayTop , adapter .arraySrcName ))
4191- }
4192-
4193- [macro_function ]
4194- def private adapter_emit_source_loop (adapter : GroupBySourceAdapter ; var body : Expression ?; at : LineInfo ) : Expression ? {
4195- // decs-join (Theme 3 C3) hashes srcB, walks srcA, binds result-lam output once per pair — GroupBy-specific.
4196- if (adapter .isDecsJoin ) {
4197- let jhashName = qn ("{ adapter .namePrefix } jhash" , at )
4198- let arrName = qn ("{ adapter .namePrefix } jarr" , at )
4199- let tupAName = qn ("{ adapter .namePrefix } jta" , at )
4200- let tupBName = qn ("{ adapter .namePrefix } jtb" , at )
4201- let resName = adapter .initialBindName
4202- var keyaBody = peel_lambda_rename_var (adapter .decsJoinKeyaLam , tupAName )
4203- var keybBody = peel_lambda_rename_var (adapter .decsJoinKeybLam , tupBName )
4204- var resultBody = peel_lambda_rename_2vars (adapter .decsJoinResultLam , tupAName , tupBName )
4205- if (keyaBody == null || keybBody == null || resultBody == null ) return null
4206- var tupBindA = build_decs_tup_bind (adapter .decsJoinBridgeA , tupAName , at )
4207- var tupBindB = build_decs_tup_bind (adapter .decsJoinBridgeB , tupBName , at )
4208- if (tupBindA == null || tupBindB == null ) return null
4209- var collectBody < - qmacro_block_to_array () {
4210- // nolint:PERF006 per-key bucket size unknown ahead of time
4211- $ i(jhashName)[$e(keybBody)] |> push_clone($i(tupBName))
4212- }
4213- var collectInner = build_decs_inner_for (adapter .decsJoinBridgeB , tupBindB , stmts_to_expr (collectBody ), at )
4214- var probeBody < - qmacro_block_to_array () {
4215- get ($ i (jhashName ), $ e (keyaBody ), $ (var $ i (arrName ) : array < $ t (adapter .decsJoinTupBType )> ) {
4216- for ($ i(tupBName) in $i(arrName)) {
4217- let $ i(resName) : $t(adapter.decsJoinResultType) = $e(resultBody)
4218- $ e(body)
4219- }
4220- })
4221- }
4222- var probeInner = build_decs_inner_for (adapter .decsJoinBridgeA , tupBindA , stmts_to_expr (probeBody ), at )
4223- return qmacro_block () {
4224- var $ i(jhashName) : table<$t(adapter.decsJoinKeyType); array < $ t(adapter.decsJoinTupBType)>>
4225- for_each_archetype ($ e (adapter .decsJoinBridgeB .reqHashExpr ), $ e (adapter .decsJoinBridgeB .erqExpr ), $ ($ i (adapter .decsJoinBridgeB .archName ) : Archetype ) {
4226- $ e(collectInner)
4227- })
4228- for_each_archetype ($ e (adapter .decsJoinBridgeA .reqHashExpr ), $ e (adapter .decsJoinBridgeA .erqExpr ), $ ($ i (adapter .decsJoinBridgeA .archName ) : Archetype ) {
4229- $ e(probeInner)
4230- })
4231- }
4232- }
4233- return adapter_wrap_source_loop (to_source_adapter (adapter ), body , at )
4234- }
4235-
4236- [macro_function ]
4237- def private adapter_finalize_emission (adapter : GroupBySourceAdapter ; var stmts : array < Expression ?> ; retType : TypeDeclPtr ; at : LineInfo ) : Expression ? {
4238- return adapter_wrap_invoke (to_source_adapter (adapter ), stmts , retType , false , at )
4239- }
4240-
42414160[macro_function ]
42424161def private plan_group_by_core (var calls : array < tuple < ExprCall ?; LinqCall ?>> ;
42434162 var keyBlock : Expression ?;
@@ -4249,9 +4168,9 @@ def private plan_group_by_core(var calls : array<tuple<ExprCall?; LinqCall?>>;
42494168 var orderKey : Expression ?;
42504169 exprIsIterator : bool ;
42514170 at : LineInfo ;
4252- var adapter : GroupBySourceAdapter ) : Expression ? {
4253- // Shared group_by splice machinery. Caller pops terminator/select/having/group_by_lazy + extracts keyBlock + sets up the source adapter; this function does the rest (upstream chain walk, reducer recognition, state-table hoisting, tab?[uk]??dummy splice, output emission, source-loop wrap, terminator emission, final invoke wrap ).
4254- let prefix = adapter . namePrefix
4171+ var adapter : SourceAdapter ) : Expression ? {
4172+ // Prefix keeps hoisted names per- source distinct ("" array / "decs_" decs / "djoin_" decs-join ).
4173+ let prefix = ( adapter is Array ) ? "" : (( adapter is DecsJoin ) ? "djoin_" : "decs_" )
42554174 let tabName = qn ("{ prefix } tab" , at )
42564175 let kName = qn ("{ prefix } k" , at )
42574176 let ukName = qn ("{ prefix } uk" , at )
@@ -4268,8 +4187,8 @@ def private plan_group_by_core(var calls : array<tuple<ExprCall?; LinqCall?>>;
42684187 var currentBinds : array < Expression ?>
42694188 var currentWhere : Expression ?
42704189 var projection : Expression ?
4271- var lastBindName = adapter . initialBindName
4272- var elemType = strip_const_ref (clone_type (adapter . initialElemType ))
4190+ var lastBindName = adapter_bind_name ( adapter , at )
4191+ var elemType = strip_const_ref (adapter_element_type (adapter ))
42734192 var totalBindCount = 0
42744193 for (i in 0 .. (calls | > length )) {
42754194 let opName = calls [i ]._1 .name
@@ -4451,7 +4370,7 @@ def private plan_group_by_core(var calls : array<tuple<ExprCall?; LinqCall?>>;
44514370 idx --
44524371 }
44534372 // Adapter-specific source loop emission (for(it in src) for array, for_each_archetype + inner for for decs).
4454- stmts | > push (adapter_emit_source_loop (adapter , body , at ))
4373+ stmts | > push (adapter_wrap_source_loop (adapter , body , at ))
44554374 // Compute output tuple + bufElemType — needed by to_array always, and by count when trailingWherePred is present (predicate is bound against the constructed output).
44564375 let needOutput = terminatorName ! = "count" || trailingWherePred ! = null
44574376 var outputExpr : Expression ?
@@ -4602,7 +4521,7 @@ def private plan_group_by_core(var calls : array<tuple<ExprCall?; LinqCall?>>;
46024521 }
46034522 stmts | > push (buffer_return (bufName , exprIsIterator ))
46044523 }
4605- return adapter_finalize_emission (adapter , stmts , retType , at )
4524+ return adapter_wrap_invoke (adapter , stmts , retType , false , at )
46064525}
46074526
46084527// Shared array/decs/decs-join group_by emitter. Reconstructs head calls (mirrors emit_loop_or_count_lane_decs),
@@ -4640,7 +4559,7 @@ def private emit_group_by(var c : Captures; var ctx : EmitCtx; at : LineInfo) :
46404559 var lc & = unsafe (linqCalls ?[nm ] ?? default < LinqCall > )
46414560 headCalls | > push ((call , unsafe (addr (lc ))))
46424561 }
4643- var adapter : GroupBySourceAdapter
4562+ var adapter : SourceAdapter
46444563 if (ctx .src is Decs ) {
46454564 var bridge = (ctx .src as Decs )._0
46464565 if (c .single | > key_exists ("upstream_join" )) {
@@ -4657,47 +4576,23 @@ def private emit_group_by(var c : Captures; var ctx : EmitCtx; at : LineInfo) :
46574576 if (! is_primitive_join_key_type (keyType )) return null
46584577 var resultType = strip_const_ref (clone_type (resultLam ._type .firstType ))
46594578 var tupBType = strip_const_ref (clone_type (bridgeB .elementType ))
4660- adapter = GroupBySourceAdapter (
4661- initialBindName := qn ("djoin_jres" , at ),
4662- initialElemType = strip_const_ref (clone_type (resultLam ._type .firstType )),
4663- namePrefix := "djoin_" ,
4664- isDecs = false ,
4665- isDecsJoin = true ,
4666- arrayTop = null ,
4667- arraySrcName := "" ,
4668- decsBridge = null ,
4669- decsJoinBridgeA = bridge ,
4670- decsJoinBridgeB = bridgeB ,
4671- decsJoinKeyaLam = keyaLam ,
4672- decsJoinKeybLam = keybLam ,
4673- decsJoinResultLam = resultLam ,
4674- decsJoinKeyType = keyType ,
4675- decsJoinTupBType = tupBType ,
4676- decsJoinResultType = resultType
4677- )
4579+ adapter = SourceAdapter (DecsJoin = DecsJoinShape (
4580+ bridgeA = bridge ,
4581+ bridgeB = bridgeB ,
4582+ keyaLam = keyaLam ,
4583+ keybLam = keybLam ,
4584+ resultLam = resultLam ,
4585+ keyType = keyType ,
4586+ tupBType = tupBType ,
4587+ resultType = resultType ,
4588+ resBindName = qn ("djoin_jres" , at )))
46784589 } else {
4679- adapter = GroupBySourceAdapter (
4680- initialBindName := qn ("decs_tup" , at ),
4681- initialElemType = strip_const_ref (clone_type (bridge .elementType )),
4682- namePrefix := "decs_" ,
4683- isDecs = true ,
4684- arrayTop = null ,
4685- arraySrcName := "" ,
4686- decsBridge = bridge
4687- )
4590+ adapter = SourceAdapter (Decs = (bridge , qn ("decs_tup" , at )))
46884591 }
46894592 } else {
46904593 var top = (ctx .src as Array )._0
46914594 if (top ._type == null ) return null
4692- adapter = GroupBySourceAdapter (
4693- initialBindName := qn ("it" , at ),
4694- initialElemType = strip_const_ref (clone_type (top ._type .firstType )),
4695- namePrefix := "" ,
4696- isDecs = false ,
4697- arrayTop = top ,
4698- arraySrcName := qn ("source" , at ),
4699- decsBridge = null
4700- )
4595+ adapter = SourceAdapter (Array = (top , qn ("source" , at )))
47014596 }
47024597 return plan_group_by_core (headCalls , keyBlock , groupProjCall , havingCall , trailingWhereCall ,
47034598 terminatorName , orderName , orderKey , ctx .expr_is_iterator , at , adapter )
0 commit comments