Skip to content

Commit a563401

Browse files
authored
Merge branch 'master' into decrease_max_methods_setting_for_various_interface_functions
2 parents 8b50a30 + 3396562 commit a563401

File tree

23 files changed

+380
-80
lines changed

23 files changed

+380
-80
lines changed

NEWS.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ New library features
6060
* `sort(keys(::Dict))` and `sort(values(::Dict))` now automatically collect, they previously threw ([#56978]).
6161
* `Base.AbstractOneTo` is added as a supertype of one-based axes, with `Base.OneTo` as its subtype ([#56902]).
6262
* `takestring!(::IOBuffer)` removes the content from the buffer, returning the content as a `String`.
63+
* The `macroexpand` (with default true) and the new `macroexpand!` (with default false)
64+
functions now support a `legacyscope` boolean keyword argument to control whether to run
65+
the legacy scope resolution pass over the result. The legacy scope resolution code has
66+
known design bugs and will be disabled by default in a future version. Users should
67+
migrate now by calling `legacyscope=false` or using `macroexpand!`. This may often require
68+
fixes to the code calling `macroexpand` with `Meta.unescape` and `Meta.reescape` or by
69+
updating tests to expect `hygienic-scope` or `escape` markers might appear in the result.
6370

6471
Standard library changes
6572
------------------------

base/c.jl

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -441,11 +441,18 @@ The string literal could also be used directly before the function
441441
name, if desired `"libglib-2.0".g_uri_escape_string(...`
442442
443443
It's possible to declare the ccall as `gc_safe` by using the `gc_safe = true` option:
444+
444445
@ccall gc_safe=true strlen(s::Cstring)::Csize_t
446+
445447
This allows the garbage collector to run concurrently with the ccall, which can be useful whenever
446448
the `ccall` may block outside of julia.
447-
WARNING: This option should be used with caution, as it can lead to undefined behavior if the ccall
448-
calls back into the julia runtime. (`@cfunction`/`@ccallables` are safe however)
449+
450+
!!! warning
451+
This option should be used with caution, as it can lead to undefined behavior if the ccall
452+
calls back into the julia runtime. (`@cfunction`/`@ccallables` are safe however)
453+
454+
!!! compat "Julia 1.12"
455+
The `gc_safe` argument requires Julia 1.12 or higher.
449456
"""
450457
macro ccall(exprs...)
451458
return ccall_macro_lower((:ccall), ccall_macro_parse(exprs)...)

base/exports.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,7 @@ export
837837
gensym,
838838
@kwdef,
839839
macroexpand,
840+
macroexpand!,
840841
@macroexpand1,
841842
@macroexpand,
842843
parse,

base/expr.jl

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,12 @@ function ==(x::DebugInfo, y::DebugInfo)
174174
end
175175

176176
"""
177-
macroexpand(m::Module, x; recursive=true)
177+
macroexpand(m::Module, x; recursive=true, legacyscope=true)
178178
179179
Take the expression `x` and return an equivalent expression with all macros removed (expanded)
180180
for executing in module `m`.
181181
The `recursive` keyword controls whether deeper levels of nested macros are also expanded.
182+
The `legacyscope` keyword controls whether legacy macroscope expansion is performed.
182183
This is demonstrated in the example below:
183184
```jldoctest; filter = r"#= .*:6 =#"
184185
julia> module M
@@ -198,12 +199,28 @@ julia> macroexpand(M, :(@m2()), recursive=false)
198199
:(#= REPL[1]:6 =# @m1)
199200
```
200201
"""
201-
function macroexpand(m::Module, @nospecialize(x); recursive=true)
202-
if recursive
203-
ccall(:jl_macroexpand, Any, (Any, Any), x, m)
204-
else
205-
ccall(:jl_macroexpand1, Any, (Any, Any), x, m)
206-
end
202+
function macroexpand(m::Module, @nospecialize(x); recursive=true, legacyscope=true)
203+
ccall(:jl_macroexpand, Any, (Any, Any, Cint, Cint, Cint), x, m, recursive, false, legacyscope)
204+
end
205+
206+
"""
207+
macroexpand!(m::Module, x; recursive=true, legacyscope=false)
208+
209+
Take the expression `x` and return an equivalent expression with all macros removed (expanded)
210+
for executing in module `m`, modifying `x` in place without copying.
211+
The `recursive` keyword controls whether deeper levels of nested macros are also expanded.
212+
The `legacyscope` keyword controls whether legacy macroscope expansion is performed.
213+
214+
This function performs macro expansion without the initial copy step, making it more efficient
215+
when the original expression is no longer needed. By default, macroscope expansion is disabled
216+
for in-place expansion as it can be called separately if needed.
217+
218+
!!! warning
219+
This function modifies the input expression `x` in place. Use `macroexpand` if you need
220+
to preserve the original expression.
221+
"""
222+
function macroexpand!(m::Module, @nospecialize(x); recursive=true, legacyscope=false)
223+
ccall(:jl_macroexpand, Any, (Any, Any, Cint, Cint, Cint), x, m, recursive, true, legacyscope)
207224
end
208225

209226
"""
@@ -250,10 +267,10 @@ With `macroexpand` the expression expands in the module given as the first argum
250267
The two-argument form requires at least Julia 1.11.
251268
"""
252269
macro macroexpand(code)
253-
return :(macroexpand($__module__, $(QuoteNode(code)), recursive=true))
270+
return :(macroexpand($__module__, $(QuoteNode(code)); recursive=true, legacyscope=true))
254271
end
255272
macro macroexpand(mod, code)
256-
return :(macroexpand($(esc(mod)), $(QuoteNode(code)), recursive=true))
273+
return :(macroexpand($(esc(mod)), $(QuoteNode(code)); recursive=true, legacyscope=true))
257274
end
258275

259276
"""
@@ -262,10 +279,10 @@ end
262279
Non recursive version of [`@macroexpand`](@ref).
263280
"""
264281
macro macroexpand1(code)
265-
return :(macroexpand($__module__, $(QuoteNode(code)), recursive=false))
282+
return :(macroexpand($__module__, $(QuoteNode(code)); recursive=false, legacyscope=true))
266283
end
267284
macro macroexpand1(mod, code)
268-
return :(macroexpand($(esc(mod)), $(QuoteNode(code)), recursive=false))
285+
return :(macroexpand($(esc(mod)), $(QuoteNode(code)); recursive=false, legacyscope=true))
269286
end
270287

271288
## misc syntax ##

base/meta.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -636,8 +636,11 @@ This is the inverse operation of [`unescape`](@ref) - if the original expression
636636
was escaped, the unescaped expression is wrapped in `:escape` again.
637637
"""
638638
function reescape(@nospecialize(unescaped_expr), @nospecialize(original_expr))
639-
if isexpr(original_expr, :escape) || isexpr(original_expr, :var"hygienic-scope")
639+
if isexpr(original_expr, :escape)
640640
return reescape(Expr(:escape, unescaped_expr), original_expr.args[1])
641+
elseif isexpr(original_expr, :var"hygienic-scope")
642+
next, ctx... = original_expr.args
643+
return reescape(Expr(:var"hygienic-scope", unescaped_expr, ctx...), next)
641644
else
642645
return unescaped_expr
643646
end

base/special/trig.jl

Lines changed: 120 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,10 @@ Return a `T(NaN)` if `isnan(x)`.
11041104
See also [`sinc`](@ref).
11051105
"""
11061106
cosc(x::Number) = _cosc(float(x))
1107+
function _cosc_generic(x)
1108+
pi_x = pi * x
1109+
(pi_x*cospi(x)-sinpi(x))/(pi_x*x)
1110+
end
11071111
function _cosc(x::Number)
11081112
# naive cosc formula is susceptible to catastrophic
11091113
# cancellation error near x=0, so we use the Taylor series
@@ -1124,17 +1128,128 @@ function _cosc(x::Number)
11241128
end
11251129
return π*s
11261130
else
1127-
return isinf_real(x) ? zero(x) : ((pi*x)*cospi(x)-sinpi(x))/((pi*x)*x)
1131+
return isinf_real(x) ? zero(x) : _cosc_generic(x)
1132+
end
1133+
end
1134+
1135+
#=
1136+
1137+
## `cosc(x)` for `x` around the first zero, at `x = 0`
1138+
1139+
`Float32`:
1140+
1141+
```sollya
1142+
prec = 500!;
1143+
accurate = ((pi * x) * cos(pi * x) - sin(pi * x)) / (pi * x * x);
1144+
b1 = 0.27001953125;
1145+
b2 = 0.449951171875;
1146+
domain_0 = [-b1/2, b1];
1147+
domain_1 = [b1, b2];
1148+
machinePrecision = 24;
1149+
freeMonomials = [|1, 3, 5, 7|];
1150+
freeMonomialPrecisions = [|machinePrecision, machinePrecision, machinePrecision, machinePrecision|];
1151+
polynomial_0 = fpminimax(accurate, freeMonomials, freeMonomialPrecisions, domain_0);
1152+
polynomial_1 = fpminimax(accurate, freeMonomials, freeMonomialPrecisions, domain_1);
1153+
polynomial_0;
1154+
polynomial_1;
1155+
```
1156+
1157+
`Float64`:
1158+
1159+
```sollya
1160+
prec = 500!;
1161+
accurate = ((pi * x) * cos(pi * x) - sin(pi * x)) / (pi * x * x);
1162+
b1 = 0.1700439453125;
1163+
b2 = 0.27001953125;
1164+
b3 = 0.340087890625;
1165+
b4 = 0.39990234375;
1166+
domain_0 = [-b1/2, b1];
1167+
domain_1 = [b1, b2];
1168+
domain_2 = [b2, b3];
1169+
domain_3 = [b3, b4];
1170+
machinePrecision = 53;
1171+
freeMonomials = [|1, 3, 5, 7, 9, 11|];
1172+
freeMonomialPrecisions = [|machinePrecision, machinePrecision, machinePrecision, machinePrecision, machinePrecision, machinePrecision|];
1173+
polynomial_0 = fpminimax(accurate, freeMonomials, freeMonomialPrecisions, domain_0);
1174+
polynomial_1 = fpminimax(accurate, freeMonomials, freeMonomialPrecisions, domain_1);
1175+
polynomial_2 = fpminimax(accurate, freeMonomials, freeMonomialPrecisions, domain_2);
1176+
polynomial_3 = fpminimax(accurate, freeMonomials, freeMonomialPrecisions, domain_3);
1177+
polynomial_0;
1178+
polynomial_1;
1179+
polynomial_2;
1180+
polynomial_3;
1181+
```
1182+
1183+
=#
1184+
1185+
function _cos_cardinal_eval(x::AbstractFloat, polynomials_close_to_origin::NTuple)
1186+
function choose_poly(a::AbstractFloat, polynomials_close_to_origin::NTuple{2})
1187+
((b1, p0), (_, p1)) = polynomials_close_to_origin
1188+
if a b1
1189+
p0
1190+
else
1191+
p1
1192+
end
1193+
end
1194+
function choose_poly(a::AbstractFloat, polynomials_close_to_origin::NTuple{4})
1195+
((b1, p0), (b2, p1), (b3, p2), (_, p3)) = polynomials_close_to_origin
1196+
if a b2 # hardcoded binary search
1197+
if a b1
1198+
p0
1199+
else
1200+
p1
1201+
end
1202+
else
1203+
if a b3
1204+
p2
1205+
else
1206+
p3
1207+
end
1208+
end
1209+
end
1210+
a = abs(x)
1211+
if (polynomials_close_to_origin !== ()) && (a polynomials_close_to_origin[end][1])
1212+
x * evalpoly(x * x, choose_poly(a, polynomials_close_to_origin))
1213+
elseif isinf(x)
1214+
typeof(x)(0)
1215+
else
1216+
_cosc_generic(x)
11281217
end
11291218
end
1219+
1220+
const _cosc_f32 = let b = Float32 Float16
1221+
(
1222+
(b(0.27), (-3.289868f0, 3.246966f0, -1.1443111f0, 0.20542027f0)),
1223+
(b(0.45), (-3.2898617f0, 3.2467577f0, -1.1420113f0, 0.1965574f0)),
1224+
)
1225+
end
1226+
1227+
const _cosc_f64 = let b = Float64 Float16
1228+
(
1229+
(b(0.17), (-3.289868133696453, 3.2469697011333203, -1.1445109446992934, 0.20918277797812262, -0.023460519561502552, 0.001772485141534688)),
1230+
(b(0.27), (-3.289868133695205, 3.246969700970421, -1.1445109360543062, 0.20918254132488637, -0.023457115021035743, 0.0017515112964895303)),
1231+
(b(0.34), (-3.289868133634355, 3.246969697075094, -1.1445108347839286, 0.209181201609773, -0.023448079433318045, 0.001726628430505518)),
1232+
(b(0.4), (-3.289868133074254, 3.2469696736659346, -1.1445104406286049, 0.20917785794416457, -0.02343378376047161, 0.0017019796223768677)),
1233+
)
1234+
end
1235+
1236+
function _cosc(x::Union{Float32, Float64})
1237+
if x isa Float32
1238+
pols = _cosc_f32
1239+
elseif x isa Float64
1240+
pols = _cosc_f64
1241+
end
1242+
_cos_cardinal_eval(x, pols)
1243+
end
1244+
11301245
# hard-code Float64/Float32 Taylor series, with coefficients
11311246
# Float64.([(-1)^n*big(pi)^(2n)/((2n+1)*factorial(2n-1)) for n = 1:6])
1132-
_cosc(x::Union{Float64,ComplexF64}) =
1247+
_cosc(x::ComplexF64) =
11331248
fastabs(x) < 0.14 ? x*evalpoly(x^2, (-3.289868133696453, 3.2469697011334144, -1.1445109447325053, 0.2091827825412384, -0.023460810354558236, 0.001781145516372852)) :
1134-
isinf_real(x) ? zero(x) : ((pi*x)*cospi(x)-sinpi(x))/((pi*x)*x)
1135-
_cosc(x::Union{Float32,ComplexF32}) =
1249+
isinf_real(x) ? zero(x) : _cosc_generic(x)
1250+
_cosc(x::ComplexF32) =
11361251
fastabs(x) < 0.26f0 ? x*evalpoly(x^2, (-3.289868f0, 3.2469697f0, -1.144511f0, 0.20918278f0)) :
1137-
isinf_real(x) ? zero(x) : ((pi*x)*cospi(x)-sinpi(x))/((pi*x)*x)
1252+
isinf_real(x) ? zero(x) : _cosc_generic(x)
11381253
_cosc(x::Float16) = Float16(_cosc(Float32(x)))
11391254
_cosc(x::ComplexF16) = ComplexF16(_cosc(ComplexF32(x)))
11401255

base/tuple.jl

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -269,10 +269,18 @@ first(t::Tuple) = t[1]
269269

270270
# eltype
271271

272-
eltype(::Type{Tuple{}}) = Bottom
273272
# the <: here makes the runtime a bit more complicated (needing to check isdefined), but really helps inference
274-
eltype(t::Type{<:Tuple{Vararg{E}}}) where {E} = @isdefined(E) ? (E isa Type ? E : Union{}) : _compute_eltype(t)
275-
eltype(t::Type{<:Tuple}) = _compute_eltype(t)
273+
_eltype_ntuple(t::Type{<:Tuple{Vararg{E}}}) where {E} = @isdefined(E) ? (E isa Type ? E : Union{}) : _compute_eltype(t)
274+
# We'd like to be able to infer eltype(::Tuple), so keep the number of eltype(::Type{<:Tuple}) methods at max_methods!
275+
function eltype(t::Type{<:Tuple})
276+
if t <: Tuple{}
277+
Bottom
278+
elseif t <: NTuple
279+
_eltype_ntuple(t)
280+
else
281+
_compute_eltype(t)
282+
end
283+
end
276284
function _compute_eltype(@nospecialize t)
277285
@_total_meta
278286
has_free_typevars(t) && return Any
@@ -297,21 +305,6 @@ function _compute_eltype(@nospecialize t)
297305
return r
298306
end
299307

300-
# We'd like to be able to infer eltype(::Tuple), which needs to be able to
301-
# look at these four methods:
302-
#
303-
# julia> methods(Base.eltype, Tuple{Type{<:Tuple}})
304-
# 4 methods for generic function "eltype" from Base:
305-
# [1] eltype(::Type{Union{}})
306-
# @ abstractarray.jl:234
307-
# [2] eltype(::Type{Tuple{}})
308-
# @ tuple.jl:199
309-
# [3] eltype(t::Type{<:Tuple{Vararg{E}}}) where E
310-
# @ tuple.jl:200
311-
# [4] eltype(t::Type{<:Tuple})
312-
# @ tuple.jl:209
313-
typeof(function eltype end).name.max_methods = UInt8(4)
314-
315308
# key/val types
316309
keytype(@nospecialize t::Tuple) = keytype(typeof(t))
317310
keytype(@nospecialize T::Type{<:Tuple}) = Int

doc/NEWS-update.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ NEWS = get(ARGS, 1, "NEWS.md")
55

66
s = read(NEWS, String)
77

8-
m = match(r"\[#[0-9]+\]:", s)
8+
m = match(r"^\[#[0-9]+\]:"m, s)
99
if m !== nothing
1010
s = s[1:m.offset-1]
1111
end

doc/src/base/base.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,7 @@ Meta.parse(::AbstractString)
538538
Meta.ParseError
539539
Core.QuoteNode
540540
Base.macroexpand
541+
Base.macroexpand!
541542
Base.@macroexpand
542543
Base.@macroexpand1
543544
Base.code_lowered

src/ast.c

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,24 +1252,15 @@ static jl_value_t *jl_expand_macros(jl_value_t *expr, jl_module_t *inmodule, str
12521252
return expr;
12531253
}
12541254

1255-
JL_DLLEXPORT jl_value_t *jl_macroexpand(jl_value_t *expr, jl_module_t *inmodule)
1255+
JL_DLLEXPORT jl_value_t *jl_macroexpand(jl_value_t *expr, jl_module_t *inmodule, int recursive, int inplace, int expand_scope)
12561256
{
12571257
JL_TIMING(LOWERING, LOWERING);
12581258
JL_GC_PUSH1(&expr);
1259-
expr = jl_copy_ast(expr);
1260-
expr = jl_expand_macros(expr, inmodule, NULL, 0, jl_atomic_load_acquire(&jl_world_counter), 0);
1261-
expr = jl_call_scm_on_ast("jl-expand-macroscope", expr, inmodule);
1262-
JL_GC_POP();
1263-
return expr;
1264-
}
1265-
1266-
JL_DLLEXPORT jl_value_t *jl_macroexpand1(jl_value_t *expr, jl_module_t *inmodule)
1267-
{
1268-
JL_TIMING(LOWERING, LOWERING);
1269-
JL_GC_PUSH1(&expr);
1270-
expr = jl_copy_ast(expr);
1271-
expr = jl_expand_macros(expr, inmodule, NULL, 1, jl_atomic_load_acquire(&jl_world_counter), 0);
1272-
expr = jl_call_scm_on_ast("jl-expand-macroscope", expr, inmodule);
1259+
if (!inplace)
1260+
expr = jl_copy_ast(expr);
1261+
expr = jl_expand_macros(expr, inmodule, NULL, !recursive, jl_atomic_load_acquire(&jl_world_counter), 0);
1262+
if (expand_scope)
1263+
expr = jl_call_scm_on_ast("jl-expand-macroscope", expr, inmodule);
12731264
JL_GC_POP();
12741265
return expr;
12751266
}

0 commit comments

Comments
 (0)