@@ -2,6 +2,7 @@ use core::alloc::Allocator;
22
33use hashql_core:: {
44 id:: { Id as _, IdVec } ,
5+ span:: Spanned ,
56 symbol:: sym,
67 r#type:: { TypeBuilder , Typed , builder} ,
78 value:: Primitive ,
@@ -17,6 +18,7 @@ use hashql_hir::node::{
1718 BinaryOperation , InputOperation , Operation , TypeConstructor , TypeOperation , UnaryOperation ,
1819 } ,
1920 thunk:: Thunk ,
21+ variable:: Variable ,
2022} ;
2123
2224use super :: {
@@ -200,6 +202,10 @@ impl<'mir, 'heap, A: Allocator, S: Allocator> Reifier<'_, 'mir, '_, '_, 'heap, A
200202 function : Node < ' heap > ,
201203 call_arguments : & [ CallArgument < ' heap > ] ,
202204 ) -> RValue < ' heap > {
205+ if let Some ( value) = self . rvalue_call_thin_specialize ( function, call_arguments) {
206+ return value;
207+ }
208+
203209 let mut arguments = IdVec :: with_capacity_in ( call_arguments. len ( ) , self . context . mir . heap ) ;
204210
205211 for CallArgument { span : _, value } in call_arguments {
@@ -214,6 +220,57 @@ impl<'mir, 'heap, A: Allocator, S: Allocator> Reifier<'_, 'mir, '_, '_, 'heap, A
214220 } )
215221 }
216222
223+ /// Attempts to beta-reduce a thin call to a qualified intrinsic into a closure aggregate.
224+ ///
225+ /// The thunking phase wraps every qualified variable in `Call(Thin, Qualified(...), [])`.
226+ /// When the target is a known intrinsic, this produces the closure aggregate directly
227+ /// instead of going through a thunk body that later passes would eliminate.
228+ ///
229+ /// Returns [`None`] if the call has arguments, the target is not a qualified variable,
230+ /// or the path is not a known intrinsic.
231+ fn rvalue_call_thin_specialize (
232+ & mut self ,
233+ function : Node < ' heap > ,
234+ call_arguments : & [ CallArgument < ' heap > ] ,
235+ ) -> Option < RValue < ' heap > > {
236+ if !call_arguments. is_empty ( ) {
237+ return None ;
238+ }
239+
240+ let NodeKind :: Variable ( Variable :: Qualified ( variable) ) = function. kind else {
241+ return None ;
242+ } ;
243+
244+ let diagnostics = self . state . diagnostics . critical ( ) ;
245+
246+ let Some ( synthetic) = self . state . synthetics . find_or_insert (
247+ self . context ,
248+ & mut self . state . diagnostics ,
249+ Spanned {
250+ span : function. span ,
251+ value : function. id ,
252+ } ,
253+ variable. path ,
254+ ) else {
255+ // A diagnostic was already emitted (e.g. non-first-classable intrinsic).
256+ // Return a bogus value to prevent the fallback from emitting a duplicate.
257+ if diagnostics != self . state . diagnostics . critical ( ) {
258+ return Some ( RValue :: Load ( Operand :: Constant ( Constant :: Unit ) ) ) ;
259+ }
260+
261+ return None ;
262+ } ;
263+
264+ let mut operands = IdVec :: with_capacity_in ( 2 , self . context . mir . heap ) ;
265+ operands. push ( Operand :: Constant ( Constant :: FnPtr ( synthetic. body ) ) ) ;
266+ operands. push ( Operand :: Constant ( Constant :: Unit ) ) ;
267+
268+ Some ( RValue :: Aggregate ( Aggregate {
269+ kind : AggregateKind :: Closure ,
270+ operands,
271+ } ) )
272+ }
273+
217274 fn rvalue_call_fat (
218275 & mut self ,
219276 function : Node < ' heap > ,
0 commit comments