@@ -354,11 +354,76 @@ end
354354`true` if a call to method with signature `sig` is permitted to contain 
355355`Libtask.produce` statements. 
356356
357- This is an opt-in mechanism. the  fallback method of this function returns `false` indicating 
357+ This is an opt-in mechanism. The  fallback method of this function returns `false` indicating 
358358that, by default, we assume that calls do not contain `Libtask.produce` statements. 
359359""" 
360360might_produce (:: Type{<:Tuple} ) =  false 
361361
362+ """ 
363+     @might_produce(f) 
364+ 
365+ If `f` is a function that may call `Libtask.produce` inside it, then `@might_produce(f)` 
366+ will generate the appropriate methods needed to ensure that `Libtask.might_produce` returns 
367+ `true` for **all** relevant signatures of `f`. This works even if `f` has methods with 
368+ keyword arguments. 
369+ 
370+ !!! note 
371+     Because `@might_produce f` is applied to all possible signatures, there are performance 
372+     penalties associated with marking all methods of `f` as produceable if only one method 
373+     can actually call `produce`. If performance is critical, please use the 
374+     [`might_produce`](@ref) function directly. 
375+ 
376+ ```jldoctest might_produce_macro 
377+ julia> # For this demonstration we need to mark `g` as not being inlineable. 
378+        @noinline function g(x; y, z=0) 
379+            produce(x + y + z) 
380+        end 
381+ g (generic function with 1 method) 
382+ 
383+ julia> function f() 
384+            g(1; y=2, z=3) 
385+        end 
386+ f (generic function with 1 method) 
387+ 
388+ julia> # This returns nothing because `g` isn't yet marked as being able to `produce`. 
389+        consume(Libtask.TapedTask(nothing, f)) 
390+ 
391+ julia> Libtask.@might_produce(g) 
392+ 
393+ julia> # Now it works! 
394+        consume(Libtask.TapedTask(nothing, f)) 
395+ 6 
396+ """ 
397+ macro  might_produce (f)
398+     #  See https://github.com/TuringLang/Libtask.jl/issues/197 for discussion of this macro.
399+     quote 
400+         function  $ (Libtask). might_produce (:: Type{<:Tuple{typeof($(esc(f))),Vararg}} )
401+             return  true 
402+         end 
403+         possible_n_kwargs =  unique (map (length ∘  Base. kwarg_decl, methods ($ (esc (f)))))
404+         if  possible_n_kwargs !=  [0 ]
405+             #  Oddly we need to interpolate the module and not the function: either
406+             #  `$(might_produce)` or $(Libtask.might_produce) seem more natural but both of
407+             #  those cause the entire `Libtask.might_produce` to be treated as a single
408+             #  symbol. See https://discourse.julialang.org/t/128613
409+             function  $ (Libtask). might_produce (
410+                 :: Type{<:Tuple{typeof(Core.kwcall),<:NamedTuple,typeof($(esc(f))),Vararg}} 
411+             )
412+                 return  true 
413+             end 
414+             for  n in  possible_n_kwargs
415+                 #  We only need `Any` and not `<:Any` because tuples are covariant.
416+                 kwarg_types =  fill (Any, n)
417+                 function  $ (Libtask). might_produce (
418+                     :: Type{<:Tuple{<:Function,kwarg_types...,typeof($(esc(f))),Vararg}} 
419+                 )
420+                     return  true 
421+                 end 
422+             end 
423+         end 
424+     end 
425+ end 
426+ 
362427#  Helper struct used in `derive_copyable_task_ir`.
363428struct  TupleRef
364429    n:: Int 
0 commit comments