Skip to content

Commit 8307dbf

Browse files
committed
improve effects of typejoin and simplify implementation of Tuple _compute_eltype
- Repair definition of Core._foldable_meta - Handle Vararg better in eltype - Mark has_free_typevars ccall as total - Mark tailjoin foldable explicitly also, since it calls typejoin recursively - Handle non-constructable Tuple{:a,2} and Tuple{T} types - Prepare special code for inlining (improves generic effects too) - Only optimize typejoin in concrete eval, not needlessly generating specializations otherwise on specific types The compiler seems better able to deal with this version of _compute_eltype, and it does not seem that necessary to form the IdSet.
1 parent 479743e commit 8307dbf

File tree

6 files changed

+67
-62
lines changed

6 files changed

+67
-62
lines changed

base/boot.jl

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -249,11 +249,25 @@ macro nospecialize(x)
249249
_expr(:meta, :nospecialize, x)
250250
end
251251

252-
TypeVar(n::Symbol) = _typevar(n, Union{}, Any)
253-
TypeVar(n::Symbol, @nospecialize(ub)) = _typevar(n, Union{}, ub)
254-
TypeVar(n::Symbol, @nospecialize(lb), @nospecialize(ub)) = _typevar(n, lb, ub)
252+
_is_internal(__module__) = __module__ === Core
253+
# can be used in place of `@assume_effects :foldable` (supposed to be used for bootstrapping)
254+
macro _foldable_meta()
255+
return _is_internal(__module__) && _expr(:meta, _expr(:purity,
256+
#=:consistent=#true,
257+
#=:effect_free=#true,
258+
#=:nothrow=#false,
259+
#=:terminates_globally=#true,
260+
#=:terminates_locally=#false,
261+
#=:notaskstate=#true,
262+
#=:inaccessiblememonly=#true,
263+
#=:noub=#true))
264+
end
255265

256-
UnionAll(v::TypeVar, @nospecialize(t)) = ccall(:jl_type_unionall, Any, (Any, Any), v, t)
266+
# n.b. the effects and model of these is refined in inference abstractinterpretation.jl
267+
TypeVar(@nospecialize(n)) = _typevar(n::Symbol, Union{}, Any)
268+
TypeVar(@nospecialize(n), @nospecialize(ub)) = _typevar(n::Symbol, Union{}, ub)
269+
TypeVar(@nospecialize(n), @nospecialize(lb), @nospecialize(ub)) = _typevar(n::Symbol, lb, ub)
270+
UnionAll(@nospecialize(v), @nospecialize(t)) = ccall(:jl_type_unionall, Any, (Any, Any), v::TypeVar, t)
257271

258272
# simple convert for use by constructors of types in Core
259273
# note that there is no actual conversion defined here,
@@ -453,20 +467,6 @@ function _Task(@nospecialize(f), reserved_stack::Int, completion_future)
453467
return ccall(:jl_new_task, Ref{Task}, (Any, Any, Int), f, completion_future, reserved_stack)
454468
end
455469

456-
_is_internal(__module__) = __module__ === Core
457-
# can be used in place of `@assume_effects :foldable` (supposed to be used for bootstrapping)
458-
macro _foldable_meta()
459-
return _is_internal(__module__) && Expr(:meta, Expr(:purity,
460-
#=:consistent=#true,
461-
#=:effect_free=#true,
462-
#=:nothrow=#false,
463-
#=:terminates_globally=#true,
464-
#=:terminates_locally=#false,
465-
#=:notaskstate=#false,
466-
#=:inaccessiblememonly=#false,
467-
#=:noub=#true))
468-
end
469-
470470
const NTuple{N,T} = Tuple{Vararg{T,N}}
471471

472472
## primitive Array constructors

base/compiler/abstractinterpretation.jl

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,7 @@ function concrete_eval_eligible(interp::AbstractInterpreter,
847847
end
848848
# disable concrete-evaluation if this function call is tainted by some overlayed
849849
# method since currently there is no easy way to execute overlayed methods
850-
add_remark!(interp, sv, "[constprop] Concrete evel disabled for overlayed methods")
850+
add_remark!(interp, sv, "[constprop] Concrete eval disabled for overlayed methods")
851851
end
852852
if !any_conditional(arginfo)
853853
return :semi_concrete_eval
@@ -1852,7 +1852,7 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs
18521852
return rt
18531853
end
18541854

1855-
function abstract_call_unionall(interp::AbstractInterpreter, argtypes::Vector{Any})
1855+
function abstract_call_unionall(interp::AbstractInterpreter, argtypes::Vector{Any}, call::CallMeta)
18561856
if length(argtypes) == 3
18571857
canconst = true
18581858
a2 = argtypes[2]
@@ -1865,10 +1865,10 @@ function abstract_call_unionall(interp::AbstractInterpreter, argtypes::Vector{An
18651865
body = a3.parameters[1]
18661866
canconst = false
18671867
else
1868-
return CallMeta(Any, Effects(EFFECTS_TOTAL; nothrow), NoCallInfo())
1868+
return CallMeta(Any, Effects(EFFECTS_TOTAL; nothrow), call.info)
18691869
end
18701870
if !(isa(body, Type) || isa(body, TypeVar))
1871-
return CallMeta(Any, EFFECTS_THROWS, NoCallInfo())
1871+
return CallMeta(Any, EFFECTS_THROWS, call.info)
18721872
end
18731873
if has_free_typevars(body)
18741874
if isa(a2, Const)
@@ -1877,13 +1877,13 @@ function abstract_call_unionall(interp::AbstractInterpreter, argtypes::Vector{An
18771877
tv = a2.tv
18781878
canconst = false
18791879
else
1880-
return CallMeta(Any, EFFECTS_THROWS, NoCallInfo())
1880+
return CallMeta(Any, EFFECTS_THROWS, call.info)
18811881
end
1882-
isa(tv, TypeVar) || return CallMeta(Any, EFFECTS_THROWS, NoCallInfo())
1882+
isa(tv, TypeVar) || return CallMeta(Any, EFFECTS_THROWS, call.info)
18831883
body = UnionAll(tv, body)
18841884
end
18851885
ret = canconst ? Const(body) : Type{body}
1886-
return CallMeta(ret, Effects(EFFECTS_TOTAL; nothrow), NoCallInfo())
1886+
return CallMeta(ret, Effects(EFFECTS_TOTAL; nothrow), call.info)
18871887
end
18881888
return CallMeta(Bottom, EFFECTS_THROWS, NoCallInfo())
18891889
end
@@ -1998,12 +1998,20 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
19981998
elseif la == 3
19991999
ub_var = argtypes[3]
20002000
end
2001+
# make sure generic code is prepared for inlining if needed later
2002+
call = let T = Any[Type{TypeVar}, Any, Any, Any]
2003+
resize!(T, la)
2004+
atype = Tuple{T...}
2005+
T[1] = Const(TypeVar)
2006+
abstract_call_gf_by_type(interp, f, ArgInfo(nothing, T), si, atype, sv, max_methods)
2007+
end
20012008
pT = typevar_tfunc(𝕃ᵢ, n, lb_var, ub_var)
20022009
effects = builtin_effects(𝕃ᵢ, Core._typevar, ArgInfo(nothing,
20032010
Any[Const(Core._typevar), n, lb_var, ub_var]), pT)
2004-
return CallMeta(pT, effects, NoCallInfo())
2011+
return CallMeta(pT, effects, call.info)
20052012
elseif f === UnionAll
2006-
return abstract_call_unionall(interp, argtypes)
2013+
call = abstract_call_gf_by_type(interp, f, ArgInfo(nothing, Any[Const(UnionAll), Any, Any]), si, Tuple{Type{UnionAll}, Any, Any}, sv, max_methods)
2014+
return abstract_call_unionall(interp, argtypes, call)
20072015
elseif f === Tuple && la == 2
20082016
aty = argtypes[2]
20092017
ty = isvarargtype(aty) ? unwrapva(aty) : widenconst(aty)
@@ -2021,13 +2029,14 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
20212029
end
20222030
elseif la == 3 && istopfunction(f, :!==)
20232031
# mark !== as exactly a negated call to ===
2032+
call = abstract_call_gf_by_type(interp, f, ArgInfo(fargs, Any[Const(f), Any, Any]), si, Tuple{typeof(f), Any, Any}, sv, max_methods)
20242033
rty = abstract_call_known(interp, (===), arginfo, si, sv, max_methods).rt
20252034
if isa(rty, Conditional)
20262035
return CallMeta(Conditional(rty.slot, rty.elsetype, rty.thentype), EFFECTS_TOTAL, NoCallInfo()) # swap if-else
20272036
elseif isa(rty, Const)
20282037
return CallMeta(Const(rty.val === false), EFFECTS_TOTAL, MethodResultPure())
20292038
end
2030-
return CallMeta(rty, EFFECTS_TOTAL, NoCallInfo())
2039+
return call
20312040
elseif la == 3 && istopfunction(f, :(>:))
20322041
# mark issupertype as a exact alias for issubtype
20332042
# swap T1 and T2 arguments and call <:

base/essentials.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ macro _foldable_meta()
219219
#=:nothrow=#false,
220220
#=:terminates_globally=#true,
221221
#=:terminates_locally=#false,
222-
#=:notaskstate=#false,
222+
#=:notaskstate=#true,
223223
#=:inaccessiblememonly=#true,
224224
#=:noub=#true))
225225
end
@@ -264,6 +264,9 @@ end
264264
macro _propagate_inbounds_meta()
265265
return Expr(:meta, :inline, :propagate_inbounds)
266266
end
267+
macro _nospecializeinfer_meta()
268+
return Expr(:meta, :nospecializeinfer)
269+
end
267270

268271
function iterate end
269272

base/promotion.jl

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ Number
1818
```
1919
"""
2020
typejoin() = Bottom
21-
typejoin(@nospecialize(t)) = t
22-
typejoin(@nospecialize(t), ts...) = (@_foldable_meta; typejoin(t, typejoin(ts...)))
21+
typejoin(@nospecialize(t)) = (@_nospecializeinfer_meta; t)
22+
typejoin(@nospecialize(t), ts...) = (@_foldable_meta; @_nospecializeinfer_meta; typejoin(t, typejoin(ts...)))
2323
function typejoin(@nospecialize(a), @nospecialize(b))
2424
@_foldable_meta
25+
@_nospecializeinfer_meta
2526
if isa(a, TypeVar)
2627
return typejoin(a.ub, b)
2728
elseif isa(b, TypeVar)
@@ -90,9 +91,9 @@ function typejoin(@nospecialize(a), @nospecialize(b))
9091
elseif b <: Tuple
9192
return Any
9293
end
93-
while b !== Any
94+
while !(b === Any)
9495
if a <: b.name.wrapper
95-
while a.name !== b.name
96+
while !(a.name === b.name)
9697
a = supertype(a)::DataType
9798
end
9899
if a.name === Type.body.name
@@ -139,6 +140,7 @@ end
139140
# (Core.Compiler.isnotbrokensubtype), use only simple types for `b`
140141
function typesplit(@nospecialize(a), @nospecialize(b))
141142
@_foldable_meta
143+
@_nospecializeinfer_meta
142144
if a <: b
143145
return Bottom
144146
end
@@ -239,7 +241,8 @@ function full_va_len(p::Core.SimpleVector)
239241
end
240242

241243
# reduce typejoin over A[i:end]
242-
function tailjoin(A, i)
244+
function tailjoin(A::SimpleVector, i::Int)
245+
@_foldable_meta
243246
if i > length(A)
244247
return unwrapva(A[end])
245248
end

base/reflection.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,7 @@ end
674674

675675
iskindtype(@nospecialize t) = (t === DataType || t === UnionAll || t === Union || t === typeof(Bottom))
676676
isconcretedispatch(@nospecialize t) = isconcretetype(t) && !iskindtype(t)
677-
has_free_typevars(@nospecialize(t)) = ccall(:jl_has_free_typevars, Cint, (Any,), t) != 0
677+
has_free_typevars(@nospecialize(t)) = (@_total_meta; ccall(:jl_has_free_typevars, Cint, (Any,), t) != 0)
678678

679679
# equivalent to isa(v, Type) && isdispatchtuple(Tuple{v}) || v === Union{}
680680
# and is thus perhaps most similar to the old (pre-1.0) `isleaftype` query

base/tuple.jl

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -199,41 +199,31 @@ first(t::Tuple) = t[1]
199199
# eltype
200200

201201
eltype(::Type{Tuple{}}) = Bottom
202-
function eltype(t::Type{<:Tuple{Vararg{E}}}) where {E}
203-
if @isdefined(E)
204-
return E
205-
else
206-
# TODO: need to guard against E being miscomputed by subtyping (ref #23017)
207-
# and compute the result manually in this case
208-
return _compute_eltype(t)
209-
end
210-
end
202+
# the <: here makes the runtime a bit more complicated (needing to check isdefined), but really helps inference
203+
eltype(t::Type{<:Tuple{Vararg{E}}}) where {E} = @isdefined(E) ? (E isa Type ? E : Union{}) : _compute_eltype(t)
211204
eltype(t::Type{<:Tuple}) = _compute_eltype(t)
212-
function _tuple_unique_fieldtypes(@nospecialize t)
205+
function _compute_eltype(@nospecialize t)
213206
@_total_meta
214-
types = IdSet()
207+
has_free_typevars(t) && return Any
215208
= unwrap_unionall(t)
216209
# Given t = Tuple{Vararg{S}} where S<:Real, the various
217210
# unwrapping/wrapping/va-handling here will return Real
218211
ifisa Union
219-
union!(types, _tuple_unique_fieldtypes(rewrap_unionall(t´.a, t)))
220-
union!(types, _tuple_unique_fieldtypes(rewrap_unionall(t´.b, t)))
221-
else
222-
for ti in (t´::DataType).parameters
223-
push!(types, rewrap_unionall(unwrapva(ti), t))
224-
end
212+
return promote_typejoin(_compute_eltype(rewrap_unionall(t´.a, t)),
213+
_compute_eltype(rewrap_unionall(t´.b, t)))
225214
end
226-
return Core.svec(types...)
227-
end
228-
function _compute_eltype(@nospecialize t)
229-
@_total_meta # TODO: the compiler shouldn't need this
230-
types = _tuple_unique_fieldtypes(t)
231-
return afoldl(types...) do a, b
232-
# if we've already reached Any, it can't widen any more
233-
a === Any && return Any
234-
b === Any && return Any
235-
return promote_typejoin(a, b)
215+
p = (t´::DataType).parameters
216+
length(p) == 0 && return Union{}
217+
elt = rewrap_unionall(unwrapva(p[1]), t)
218+
elt isa Type || return Union{} # Tuple{2} is legal as a Type, but the eltype is Union{} since it is uninhabited
219+
r = elt
220+
for i in 2:length(p)
221+
r === Any && return r # if we've already reached Any, it can't widen any more
222+
elt = rewrap_unionall(unwrapva(p[i]), t)
223+
elt isa Type || return Union{} # Tuple{2} is legal as a Type, but the eltype is Union{} since it is uninhabited
224+
r = promote_typejoin(elt, r)
236225
end
226+
return r
237227
end
238228

239229
# We'd like to be able to infer eltype(::Tuple), which needs to be able to

0 commit comments

Comments
 (0)