From 407155549363ae68751cce53141e4f78a6ffbbec Mon Sep 17 00:00:00 2001 From: Sukera Date: Sun, 3 Dec 2023 17:10:43 +0100 Subject: [PATCH] Improve type inference for `@something` `@something` eagerly unwraps any `Some` given to it, while keeping the variable between its arguments the same. This can be an issue if a previously unpacked value is used as input to `@something`, leading to a type instability on more than two arguments (e.g. because of a fallback to `Some(nothing)`). By using different variables for each argument, type inference has an easier time handling these cases that are isolated to single branches anyway. --- base/some.jl | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/base/some.jl b/base/some.jl index 0d538cbed6c23..46b912243f859 100644 --- a/base/some.jl +++ b/base/some.jl @@ -138,10 +138,31 @@ true This macro is available as of Julia 1.7. """ macro something(args...) - expr = :(nothing) + noth = GlobalRef(Base, :nothing) + something = GlobalRef(Base, :something) + + # This preserves existing semantics of throwing on `nothing` + expr = :($something($noth)) + + #= + We go through the arguments in reverse + because we're building a nested if/else + expression from the inside out. + The innermost thing to check is the last argument, + which is why we need the last argument first + when building the final expression. + =# for arg in reverse(args) - expr = :(val = $(esc(arg)); val !== nothing ? val : ($expr)) + val = gensym() + expr = quote + $val = $(esc(arg)) + if !isnothing($val) + # unwrap eagerly to help type inference + $something($val) + else + $expr + end + end end - something = GlobalRef(Base, :something) - return :($something($expr)) + return expr end