Skip to content

Conversation

@NHDaly
Copy link
Member

@NHDaly NHDaly commented Aug 18, 2025

Fixes #59326.

Change the logic that decides not to specialize a function parameter based on whether or not the supplied argument is a Function, and that function is not used, so that it will still work if the SpecType is a Union{Function,Nothing} or any other union that contains a Function.

The logic is changed from a hardcoded rule of type_i == Function || type_i == Any || type_i == Base.Callable to type_i >: Function.

This covers all of the above cases, but also includes custom Union{Function, T} such as Union{Function, Nothing}.

NHDaly added 2 commits August 18, 2025 15:00
Change the logic that decides not to specialize a function parameter
based on whether or not the supplied argument is a Function, and that
function is not used, so that it will still work if the SpecType is a
Union{Function,Nothing} or any other union that contains a Function.

The logic is changed from a hardcoded rule of `type_i == Function ||
type_i == Any` to `type_i >: Function`.
@NHDaly NHDaly force-pushed the nhd-function-nospecialize-59326 branch from 1cc730c to 8fdcee9 Compare August 18, 2025 22:29
@NHDaly NHDaly force-pushed the nhd-function-nospecialize-59326 branch from 8fdcee9 to 19250e0 Compare August 19, 2025 14:04
@NHDaly NHDaly added backport 1.10 Change should be backported to the 1.10 release backport 1.11 Change should be backported to release-1.11 backport 1.12 Change should be backported to release-1.12 labels Aug 19, 2025
@NHDaly
Copy link
Member Author

NHDaly commented Aug 19, 2025

I've added backport labels since I believe this is a bug-fix. If that's wrong we can remove them.

Copy link
Member

@vtjnash vtjnash left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. You also need to update the reverse function:

diff --git a/src/gf.c b/src/gf.c
index 39dd47d64e..4ec67d3ca7 100644
--- a/src/gf.c
+++ b/src/gf.c
@@ -1442,15 +1442,9 @@ JL_DLLEXPORT int jl_isa_compileable_sig(
         int notcalled_func = (i_arg > 0 && i_arg <= 8 && !(definition->called & (1 << (i_arg - 1))) &&
                               !jl_has_free_typevars(decl_i) &&
                               jl_subtype(elt, (jl_value_t*)jl_function_type));
-        if (notcalled_func && (type_i == (jl_value_t*)jl_any_type ||
-                               type_i == (jl_value_t*)jl_function_type ||
-                               (jl_is_uniontype(type_i) && // Base.Callable
-                                ((((jl_uniontype_t*)type_i)->a == (jl_value_t*)jl_function_type &&
-                                  ((jl_uniontype_t*)type_i)->b == (jl_value_t*)jl_type_type) ||
-                                 (((jl_uniontype_t*)type_i)->b == (jl_value_t*)jl_function_type &&
-                                  ((jl_uniontype_t*)type_i)->a == (jl_value_t*)jl_type_type))))) {
-            // and attempt to despecialize types marked Function, Callable, or Any
-            // when called with a subtype of Function but is not called
+        if (notcalled_func && jl_subtype((jl_value_t*)jl_function_type, type_i)) {
+            // and attempt to despecialize types marked as a supertype of Function (i.e.
+            // Function, Callable, Any, or a Union{Function, T})
             if (elt == (jl_value_t*)jl_function_type)
                 continue;
             JL_GC_POP();

@vtjnash vtjnash removed backport 1.10 Change should be backported to the 1.10 release backport 1.11 Change should be backported to release-1.11 labels Aug 19, 2025
@vtjnash
Copy link
Member

vtjnash commented Aug 19, 2025

It is a bit too much of a change to alter heuristics for older versions, but it can apply to v1.12

@KristofferC KristofferC mentioned this pull request Aug 19, 2025
27 tasks
@JeffBezanson
Copy link
Member

This is fine but I think, looking forward, we should consider actually removing this heuristic. I just tried a build with that change for fun, and so far I don't see serious problems. The sysimage is not even much bigger.

NHDaly and others added 3 commits August 21, 2025 09:30
Co-authored-by: Nick Robinson <npr251@gmail.com>
Co-authored-by: Jameson Nash <vtjnash@gmail.com>
@NHDaly
Copy link
Member Author

NHDaly commented Aug 21, 2025

oh nice, thanks Jameson! 👍 I've pushed up that change as well. Should be good to merge once tests pass.

@NHDaly
Copy link
Member Author

NHDaly commented Aug 21, 2025

This is fine but I think, looking forward, we should consider actually removing this heuristic. I just tried a build with that change for fun, and so far I don't see serious problems. The sysimage is not even much bigger.

Interesting, @JeffBezanson! 🤔 What would be the benefit of that specialization? I think this heuristic does make sense to me: if you're not going to call the function, it's really just data you're passing through, so why specialize on it?

@NHDaly NHDaly requested a review from vtjnash August 21, 2025 17:28
@JeffBezanson
Copy link
Member

Usually the problem is that the function argument is passed along to another function that does call it, but the intermediate function inserts a dispatch barrier.

When this heuristic was introduced we did not even have @nospecialize (I was hoping we wouldn't need it but oh well!!), and sprinkling that around plus other latency improvements may have made this obsolete.

@NHDaly NHDaly merged commit 9612115 into master Aug 21, 2025
7 of 8 checks passed
@NHDaly NHDaly deleted the nhd-function-nospecialize-59326 branch August 21, 2025 22:04
NHDaly added a commit to RelationalAI/julia that referenced this pull request Aug 21, 2025
…iaLang#59327)

Fixes JuliaLang#59326.

Change the logic that decides not to specialize a function parameter
based on whether or not the supplied argument is a Function, and that
function is not used, so that it will still work if the SpecType is a
Union{Function,Nothing} or any other union that contains a Function.

The logic is changed from a hardcoded rule of `type_i == Function ||
type_i == Any || type_i == Base.Callable` to `type_i >: Function`.

This covers all of the above cases, but also includes custom
`Union{Function, T}` such as `Union{Function, Nothing}`.

---------

Co-authored-by: Nick Robinson <npr251@gmail.com>
Co-authored-by: Jameson Nash <vtjnash@gmail.com>
NHDaly added a commit to RelationalAI/julia that referenced this pull request Aug 26, 2025
…iaLang#59327) (#251)

Fixes JuliaLang#59326.

Change the logic that decides not to specialize a function parameter
based on whether or not the supplied argument is a Function, and that
function is not used, so that it will still work if the SpecType is a
Union{Function,Nothing} or any other union that contains a Function.

The logic is changed from a hardcoded rule of `type_i == Function ||
type_i == Any || type_i == Base.Callable` to `type_i >: Function`.

This covers all of the above cases, but also includes custom
`Union{Function, T}` such as `Union{Function, Nothing}`.

---------

Co-authored-by: Nick Robinson <npr251@gmail.com>
Co-authored-by: Jameson Nash <vtjnash@gmail.com>
KristofferC pushed a commit that referenced this pull request Sep 1, 2025
)

Fixes #59326.

Change the logic that decides not to specialize a function parameter
based on whether or not the supplied argument is a Function, and that
function is not used, so that it will still work if the SpecType is a
Union{Function,Nothing} or any other union that contains a Function.

The logic is changed from a hardcoded rule of `type_i == Function ||
type_i == Any || type_i == Base.Callable` to `type_i >: Function`.

This covers all of the above cases, but also includes custom
`Union{Function, T}` such as `Union{Function, Nothing}`.

---------

Co-authored-by: Nick Robinson <npr251@gmail.com>
Co-authored-by: Jameson Nash <vtjnash@gmail.com>
(cherry picked from commit 9612115)
KristofferC pushed a commit that referenced this pull request Sep 2, 2025
)

Fixes #59326.

Change the logic that decides not to specialize a function parameter
based on whether or not the supplied argument is a Function, and that
function is not used, so that it will still work if the SpecType is a
Union{Function,Nothing} or any other union that contains a Function.

The logic is changed from a hardcoded rule of `type_i == Function ||
type_i == Any || type_i == Base.Callable` to `type_i >: Function`.

This covers all of the above cases, but also includes custom
`Union{Function, T}` such as `Union{Function, Nothing}`.

---------

Co-authored-by: Nick Robinson <npr251@gmail.com>
Co-authored-by: Jameson Nash <vtjnash@gmail.com>
(cherry picked from commit 9612115)
@KristofferC KristofferC removed the backport 1.12 Change should be backported to release-1.12 label Sep 11, 2025
KristofferC pushed a commit that referenced this pull request Sep 15, 2025
)

Fixes #59326.

Change the logic that decides not to specialize a function parameter
based on whether or not the supplied argument is a Function, and that
function is not used, so that it will still work if the SpecType is a
Union{Function,Nothing} or any other union that contains a Function.

The logic is changed from a hardcoded rule of `type_i == Function ||
type_i == Any || type_i == Base.Callable` to `type_i >: Function`.

This covers all of the above cases, but also includes custom
`Union{Function, T}` such as `Union{Function, Nothing}`.

---------

Co-authored-by: Nick Robinson <npr251@gmail.com>
Co-authored-by: Jameson Nash <vtjnash@gmail.com>
(cherry picked from commit 9612115)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Method parameter type annotation Union{Function, Nothing} causes incorrect specialization on concrete Function type

6 participants