Skip to content

Commit d765b0f

Browse files
KenoElOceanografo
authored andcommitted
Make Vararg not a DataType (JuliaLang#38136)
Currently `Vararg` is a DataType, but is special cased in a bunch of places to give it special behavior (e.g. in subtyping and of course in tuple construction). However, unlike all other DataTypes, it cannot appear as a type parameter, which caused trouble in PR JuliaLang#38071. Having it be a DataType is a bit of a pun of convenience in the first place - it's a lot more similar to a tvar (which can be considered an implementation detail of UnionAll in the same way Vararg is an implementation detail of Tuple), which has its own non-type object. This PR does the same to Vararg, and moves it from being an abstract DataType with special cased behavior to its own custom type (called `Core.TypeofVararg`). There are a few small behavior differences, but they are mostly internal. In particular, we no longer have `Vararg <: Type` and Vararg objects no longer have the .parameters field. Also, things like `Vararg{T} where T` are technically illegal now since Vararg is not a type. However, since a lot of people are using that pattern, I've brought it back with a deprecation (which is of course off by default). The only things that's disallowed is `Vararg{N, N} where N`, but I haven't seen anybody use that.
1 parent b8eaecc commit d765b0f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+662
-452
lines changed

base/boot.jl

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -242,14 +242,23 @@ ccall(:jl_toplevel_eval_in, Any, (Any, Any),
242242
(f::typeof(Typeof))(x) = ($(_expr(:meta,:nospecialize,:x)); isa(x,Type) ? Type{x} : typeof(x))
243243
end)
244244

245-
# let the compiler assume that calling Union{} as a constructor does not need
246-
# to be considered ever (which comes up often as Type{<:T})
247-
Union{}(a...) = throw(MethodError(Union{}, a))
248245

249246
macro nospecialize(x)
250247
_expr(:meta, :nospecialize, x)
251248
end
252249

250+
TypeVar(n::Symbol) = _typevar(n, Union{}, Any)
251+
TypeVar(n::Symbol, @nospecialize(ub)) = _typevar(n, Union{}, ub)
252+
TypeVar(n::Symbol, @nospecialize(lb), @nospecialize(ub)) = _typevar(n, lb, ub)
253+
254+
UnionAll(v::TypeVar, @nospecialize(t)) = ccall(:jl_type_unionall, Any, (Any, Any), v, t)
255+
256+
const Vararg = ccall(:jl_toplevel_eval_in, Any, (Any, Any), Core, _expr(:new, TypeofVararg))
257+
258+
# let the compiler assume that calling Union{} as a constructor does not need
259+
# to be considered ever (which comes up often as Type{<:T})
260+
Union{}(a...) = throw(MethodError(Union{}, a))
261+
253262
Expr(@nospecialize args...) = _expr(args...)
254263

255264
abstract type Exception end
@@ -378,12 +387,6 @@ mutable struct WeakRef
378387
(Ptr{Cvoid}, Any), getptls(), v)
379388
end
380389

381-
TypeVar(n::Symbol) = _typevar(n, Union{}, Any)
382-
TypeVar(n::Symbol, @nospecialize(ub)) = _typevar(n, Union{}, ub)
383-
TypeVar(n::Symbol, @nospecialize(lb), @nospecialize(ub)) = _typevar(n, lb, ub)
384-
385-
UnionAll(v::TypeVar, @nospecialize(t)) = ccall(:jl_type_unionall, Any, (Any, Any), v, t)
386-
387390
Tuple{}() = ()
388391

389392
struct VecElement{T}

base/broadcast.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -727,8 +727,7 @@ end
727727
ci = U
728728
end
729729
if i == lr && Core.Compiler.isvarargtype(pi)
730-
N = (Base.unwrap_unionall(pi)::DataType).parameters[2]
731-
c[i] = Base.rewrap_unionall(Vararg{ci, N}, pi)
730+
c[i] = isdefined(pi, :N) ? Vararg{ci, pi.N} : Vararg{ci}
732731
else
733732
c[i] = ci
734733
end

base/combinatorics.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ julia> 2^2 * 3^3
307307
!!! compat "Julia 1.6"
308308
The method that accepts a tuple requires Julia 1.6 or later.
309309
"""
310-
function nextprod(a::Union{Tuple{Vararg{<:Integer}},AbstractVector{<:Integer}}, x::Real)
310+
function nextprod(a::Union{Tuple{Vararg{Integer}},AbstractVector{<:Integer}}, x::Real)
311311
if x > typemax(Int)
312312
throw(ArgumentError("unsafe for x > typemax(Int), got $x"))
313313
end

base/compiler/abstractinterpretation.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,10 +1057,11 @@ function sp_type_rewrap(@nospecialize(T), linfo::MethodInstance, isreturn::Bool)
10571057
spsig = linfo.def.sig
10581058
if isa(spsig, UnionAll)
10591059
if !isempty(linfo.sparam_vals)
1060-
env = pointer_from_objref(linfo.sparam_vals) + sizeof(Ptr{Cvoid})
1061-
T = ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), T, spsig, env)
1060+
sparam_vals = Any[isa(v, Core.TypeofVararg) ? TypeVar(:N, Union{}, Any) :
1061+
v for v in linfo.sparam_vals]
1062+
T = ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), T, spsig, sparam_vals)
10621063
isref && isreturn && T === Any && return Bottom # catch invalid return Ref{T} where T = Any
1063-
for v in linfo.sparam_vals
1064+
for v in sparam_vals
10641065
if isa(v, TypeVar)
10651066
T = UnionAll(v, T)
10661067
end

base/compiler/inferencestate.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ function sptypes_from_meth_instance(linfo::MethodInstance)
192192
ty = UnionAll(tv, Type{tv})
193193
end
194194
end
195+
elseif isa(v, Core.TypeofVararg)
196+
ty = Int
195197
else
196198
ty = Const(v)
197199
end

base/compiler/ssair/inlining.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ function analyze_method!(match::MethodMatch, atypes::Vector{Any},
738738
# Bail out if any static parameters are left as TypeVar
739739
ok = true
740740
for i = 1:length(match.sparams)
741-
isa(match.sparams[i], TypeVar) && return nothing
741+
(isa(match.sparams[i], TypeVar) || isa(match.sparams[i], Core.TypeofVararg)) && return nothing
742742
end
743743

744744
if !params.inlining

base/compiler/tfuncs.jl

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -447,9 +447,7 @@ function typebound_nothrow(b)
447447
b = widenconst(b)
448448
(b TypeVar) && return true
449449
if isType(b)
450-
b = unwrap_unionall(b.parameters[1])
451-
b === Union{} && return true
452-
return !isa(b, DataType) || b.name != _va_typename
450+
return true
453451
end
454452
return false
455453
end
@@ -489,9 +487,8 @@ function typeof_concrete_vararg(t::DataType)
489487
for i = 1:np
490488
p = t.parameters[i]
491489
if i == np && isvarargtype(p)
492-
pp = unwrap_unionall(p)
493-
if isconcretetype(pp.parameters[1]) && pp.parameters[2] isa TypeVar
494-
return rewrap_unionall(Type{Tuple{t.parameters[1:np-1]..., pp}}, p)
490+
if isdefined(p, :T) && !isdefined(p, :N) && isconcretetype(p.T)
491+
return Type{Tuple{t.parameters[1:np-1]..., Vararg{p.T, N}}} where N
495492
end
496493
elseif !isconcretetype(p)
497494
break
@@ -555,15 +552,21 @@ function typeassert_type_instance(@nospecialize(v), @nospecialize(t))
555552
widev = widenconst(v)
556553
if widev <: t
557554
return v
558-
elseif typeintersect(widev, t) === Bottom
555+
end
556+
ti = typeintersect(widev, t)
557+
if ti === Bottom
559558
return Bottom
560559
end
561560
@assert widev <: Tuple
562561
new_fields = Vector{Any}(undef, length(v.fields))
563562
for i = 1:length(new_fields)
564-
new_fields[i] = typeassert_type_instance(v.fields[i], getfield_tfunc(t, Const(i)))
565-
if new_fields[i] === Bottom
566-
return Bottom
563+
if isa(v.fields[i], Core.TypeofVararg)
564+
new_fields[i] = v.fields[i]
565+
else
566+
new_fields[i] = typeassert_type_instance(v.fields[i], getfield_tfunc(t, Const(i)))
567+
if new_fields[i] === Bottom
568+
return Bottom
569+
end
567570
end
568571
end
569572
return tuple_tfunc(new_fields)
@@ -1130,10 +1133,10 @@ function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...)
11301133
elseif isconstType(headtypetype)
11311134
headtype = headtypetype.parameters[1]
11321135
else
1133-
return Type
1136+
return Any
11341137
end
11351138
if !isempty(args) && isvarargtype(args[end])
1136-
return Type
1139+
return isvarargtype(headtype) ? Core.TypeofVararg : Type
11371140
end
11381141
largs = length(args)
11391142
if headtype === Union
@@ -1175,8 +1178,8 @@ function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...)
11751178
end
11761179
return allconst ? Const(ty) : Type{ty}
11771180
end
1178-
istuple = (headtype == Tuple)
1179-
if !istuple && !isa(headtype, UnionAll)
1181+
istuple = isa(headtype, Type) && (headtype == Tuple)
1182+
if !istuple && !isa(headtype, UnionAll) && !isvarargtype(headtype)
11801183
return Union{}
11811184
end
11821185
uw = unwrap_unionall(headtype)
@@ -1193,7 +1196,8 @@ function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...)
11931196
aip1 = ai.parameters[1]
11941197
canconst &= !has_free_typevars(aip1)
11951198
push!(tparams, aip1)
1196-
elseif isa(ai, Const) && (isa(ai.val, Type) || isa(ai.val, TypeVar) || valid_tparam(ai.val))
1199+
elseif isa(ai, Const) && (isa(ai.val, Type) || isa(ai.val, TypeVar) ||
1200+
valid_tparam(ai.val) || (istuple && isa(ai.val, Core.TypeofVararg)))
11971201
push!(tparams, ai.val)
11981202
elseif isa(ai, PartialTypeVar)
11991203
canconst = false
@@ -1259,11 +1263,11 @@ function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...)
12591263
catch ex
12601264
# type instantiation might fail if one of the type parameters
12611265
# doesn't match, which could happen if a type estimate is too coarse
1262-
return Type{<:headtype}
1266+
return isvarargtype(headtype) ? Core.TypeofVararg : Type{<:headtype}
12631267
end
12641268
!uncertain && canconst && return Const(appl)
1265-
if isvarargtype(headtype)
1266-
return Type
1269+
if isvarargtype(appl)
1270+
return Core.TypeofVararg
12671271
end
12681272
if istuple
12691273
return Type{<:appl}
@@ -1315,7 +1319,7 @@ function tuple_tfunc(atypes::Vector{Any})
13151319
x = atypes[i]
13161320
# TODO ignore singleton Const (don't forget to update cache logic if you implement this)
13171321
if !anyinfo
1318-
anyinfo = !isa(x, Type) || isType(x)
1322+
anyinfo = (!isa(x, Type) && !isvarargtype(x)) || isType(x)
13191323
end
13201324
if isa(x, Const)
13211325
params[i] = typeof(x.val)

base/compiler/typelattice.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ widenconst(c::PartialTypeVar) = TypeVar
220220
widenconst(t::PartialStruct) = t.typ
221221
widenconst(t::Type) = t
222222
widenconst(t::TypeVar) = t
223+
widenconst(t::Core.TypeofVararg) = t
223224

224225
issubstate(a::VarState, b::VarState) = (a.typ b.typ && a.undef <= b.undef)
225226

base/compiler/typelimits.jl

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ function is_derived_type(@nospecialize(t), @nospecialize(c), mindepth::Int)
4646
# see if it is derived from the body
4747
# also handle the var here, since this construct bounds the mindepth to the smallest possible value
4848
return is_derived_type(t, c.var.ub, mindepth) || is_derived_type(t, c.body, mindepth)
49+
elseif isa(c, Core.TypeofVararg)
50+
return is_derived_type(t, unwrapva(c), mindepth)
4951
elseif isa(c, DataType)
5052
if mindepth > 0
5153
mindepth -= 1
@@ -85,7 +87,7 @@ function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVec
8587
return t # fast path: unparameterized are always simple
8688
else
8789
ut = unwrap_unionall(t)
88-
if isa(ut, DataType) && ut.name !== _va_typename && isa(c, Type) && c !== Union{} && c <: t
90+
if isa(ut, DataType) && isa(c, Type) && c !== Union{} && c <: t
8991
# TODO: need to check that the UnionAll bounds on t are limited enough too
9092
return t # t is already wider than the comparison in the type lattice
9193
elseif is_derived_type_from_any(ut, sources, depth)
@@ -118,19 +120,19 @@ function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVec
118120
b = _limit_type_size(t.b, c.b, sources, depth, allowed_tuplelen)
119121
return Union{a, b}
120122
end
123+
elseif isa(t, Core.TypeofVararg)
124+
isa(c, Core.TypeofVararg) || return Vararg
125+
VaT = _limit_type_size(unwrapva(t), unwrapva(c), sources, depth + 1, 0)
126+
if isdefined(t, :N) && (isa(t.N, TypeVar) || (isdefined(c, :N) && t.N === c.N))
127+
return Vararg{VaT, t.N}
128+
end
129+
return Vararg{VaT}
121130
elseif isa(t, DataType)
122131
if isa(c, DataType)
123132
tP = t.parameters
124133
cP = c.parameters
125134
if t.name === c.name && !isempty(cP)
126-
if isvarargtype(t)
127-
VaT = _limit_type_size(tP[1], cP[1], sources, depth + 1, 0)
128-
N = tP[2]
129-
if isa(N, TypeVar) || N === cP[2]
130-
return Vararg{VaT, N}
131-
end
132-
return Vararg{VaT}
133-
elseif t.name === Tuple.name
135+
if t.name === Tuple.name
134136
# for covariant datatypes (Tuple),
135137
# apply type-size limit element-wise
136138
ltP = length(tP)
@@ -155,21 +157,17 @@ function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVec
155157
end
156158
return Tuple{Q...}
157159
end
158-
elseif isvarargtype(c)
159-
# Tuple{Vararg{T}} --> Tuple{T} is OK
160-
return _limit_type_size(t, cP[1], sources, depth, 0)
161160
end
161+
elseif isa(c, Core.TypeofVararg)
162+
# Tuple{Vararg{T}} --> Tuple{T} is OK
163+
return _limit_type_size(t, c.T, sources, depth, 0)
162164
end
163165
if isType(t) # allow taking typeof as Type{...}, but ensure it doesn't start nesting
164166
tt = unwrap_unionall(t.parameters[1])
165167
if isa(tt, DataType) && !isType(tt)
166168
is_derived_type_from_any(tt, sources, depth) && return t
167169
end
168170
end
169-
if isvarargtype(t)
170-
# never replace Vararg with non-Vararg
171-
return Vararg
172-
end
173171
if allowed_tuplelen < 1 && t.name === Tuple.name
174172
return Any
175173
end

base/compiler/typeutils.jl

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,17 @@ function valid_tparam(@nospecialize(x))
6161
return isa(x, Symbol) || isbits(x)
6262
end
6363

64+
function compatible_vatuple(a::DataType, b::DataType)
65+
vaa = a.parameters[end]
66+
vab = a.parameters[end]
67+
if !(isa(vaa, Core.TypeofVararg) && isa(vab, Core.TypeofVararg))
68+
return isa(vaa, Core.TypeofVararg) == isa(vab, Core.TypeofVararg)
69+
end
70+
(isdefined(vaa, :N) == isdefined(vab, :N)) || return false
71+
!isdefined(vaa, :N) && return true
72+
return vaa.N === vab.N
73+
end
74+
6475
# return an upper-bound on type `a` with type `b` removed
6576
# such that `return <: a` && `Union{return, b} == Union{a, b}`
6677
function typesubtract(@nospecialize(a), @nospecialize(b), MAX_UNION_SPLITTING::Int)
@@ -80,19 +91,23 @@ function typesubtract(@nospecialize(a), @nospecialize(b), MAX_UNION_SPLITTING::I
8091
ta = switchtupleunion(a)
8192
return typesubtract(Union{ta...}, b, 0)
8293
elseif b isa DataType
94+
if !compatible_vatuple(a, b)
95+
return a
96+
end
8397
# if exactly one element is not bottom after calling typesubtract
8498
# then the result is all of the elements as normal except that one
8599
notbottom = fill(false, length(a.parameters))
86100
for i = 1:length(notbottom)
87-
ap = a.parameters[i]
88-
bp = b.parameters[i]
101+
ap = unwrapva(a.parameters[i])
102+
bp = unwrapva(b.parameters[i])
89103
notbottom[i] = !(ap <: bp && isnotbrokensubtype(ap, bp))
90104
end
91105
let i = findfirst(notbottom)
92106
if i !== nothing && findnext(notbottom, i + 1) === nothing
93107
ta = collect(a.parameters)
94108
ap = a.parameters[i]
95109
bp = b.parameters[i]
110+
(isa(ap, Core.TypeofVararg) || isa(bp, Core.TypeofVararg)) && return a
96111
ta[i] = typesubtract(ap, bp, min(2, MAX_UNION_SPLITTING))
97112
return Tuple{ta...}
98113
end
@@ -196,6 +211,7 @@ function unioncomplexity(t::DataType)
196211
return c
197212
end
198213
unioncomplexity(u::UnionAll) = max(unioncomplexity(u.body), unioncomplexity(u.var.ub))
214+
unioncomplexity(t::Core.TypeofVararg) = isdefined(t, :T) ? unioncomplexity(t.T) : 0
199215
unioncomplexity(@nospecialize(x)) = 0
200216

201217
function improvable_via_constant_propagation(@nospecialize(t))

0 commit comments

Comments
 (0)