-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make Vararg not a DataType #38136
Make Vararg not a DataType #38136
Conversation
👍 Completely agree, Because it took me moment to wrap my head around it, some remarks that might help others grok what's happening: |
src/dump.c
Outdated
write_uint8(s->s, TAG_VARARG); | ||
jl_serialize_value(s, ((jl_vararg_marker_t*)v)->T); | ||
jl_serialize_value(s, ((jl_vararg_marker_t*)v)->N); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you encounter any particular reason this is needed? I would think the default case would work for these.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I thought I did, but I think the default is fine. I'll try removing it and see if things work out.
src/subtype.c
Outdated
// N.B.: This case is only used for raw varargs that are not part | ||
// of a tuple (those that are have special handling in subtype_tuple). | ||
// Vararg isn't really a proper type, but it does sometimes show up | ||
// as e.g. Type{Vararg}, so we'd like to handle that correctly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type{Vararg}
is not allowed. Ideally we would give an error here too, but if that's too breaking we can instead just change this comment to say it's provided for compatibility with code that uses <:
to compare Vararg objects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is old, it just moved from below. I can try giving an error and see what needs to be updated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, as Martin noticed above it looks like passing Vararg to <:
is already an error in this PR :) We can try to go with that, and change this to an assertion.
Hmm, at first I thought it would be more compatible to keep |
Ok, I can try that. |
f88f3fd
to
1add963
Compare
I've updated this along these lines, disallowing |
Would this change close #34690?
|
;) |
3480882
to
6876f2c
Compare
@nanosoldier |
base/boot.jl
Outdated
|
||
UnionAll(v::TypeVar, @nospecialize(t)) = ccall(:jl_type_unionall, Any, (Any, Any), v, t) | ||
|
||
const Vararg = ccall(:jl_toplevel_eval_in, Any, (Any, Any), Core, _expr(:new, VarargMarker)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, maybe call it TypeofVararg
to go with TypeofBottom
? This could also use jl_new_struct instead of eval.
Your package evaluation job has completed - possible new issues were detected. A full report can be found here. cc @maleadt |
Alright, fully disallowing Vararg in UnionAll turned out to be a little aggressive. Apparently people like to write |
Alright, @JeffBezanson this works now, but there's some decisions to make for subtyping. Essentially, we need some sentinel value to indicate that a typevar must remain unbound. Previously, we'd just set it to the inner typevar, but we a) don't have that anymore and b) I don't think it necessarily makes much sense. In the commit I just pushed I'm using the vararg itself as the typevar, but of course the assertion that I added that we're not putting a vararg into subtyping fires there, so basically we need to decide what to use as the sentinel value here. |
(I've pushed something I think is reasonable, but this stuff is pretty messy, since it isn't really designed to reason about int ranges) |
@nanosoldier |
Your package evaluation job has completed - possible new issues were detected. A full report can be found here. cc @maleadt |
Alright, looks like we're down to just the StaticArrays open-coding unwrapva issue, which I have a PR for at JuliaArrays/StaticArrays.jl#843. |
MbedTLS segfault is unrelated: JuliaLang/MbedTLS.jl#227 |
@@ -192,6 +192,8 @@ function sptypes_from_meth_instance(linfo::MethodInstance) | |||
ty = UnionAll(tv, Type{tv}) | |||
end | |||
end | |||
elseif isa(v, Core.VarargMarker) | |||
ty = Int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixes #37316?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't look like this was enough:
julia> f(::NTuple{N}) where {N} = N
f (generic function with 1 method)
julia> code_warntype(f, Tuple{Tuple})
Variables
#self#::Core.Const(f)
#unused#::Tuple{Vararg{T, N}} where T where N
Body::Any
1 ─ return $(Expr(:static_parameter, 1))
Or is this check flawed in some way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking very good so far
@@ -1045,10 +1045,11 @@ function sp_type_rewrap(@nospecialize(T), linfo::MethodInstance, isreturn::Bool) | |||
spsig = linfo.def.sig | |||
if isa(spsig, UnionAll) | |||
if !isempty(linfo.sparam_vals) | |||
env = pointer_from_objref(linfo.sparam_vals) + sizeof(Ptr{Cvoid}) | |||
T = ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), T, spsig, env) | |||
sparam_vals = Any[isa(v, Core.VarargMarker) ? TypeVar(:N, Union{}, Any) : |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would an sparam ever have been a Vararg object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's new in this PR to mark an unbound intvalued parameter (We used to use the TypeVar of the unbound Vararg as the sentinel, but we don't have that anymore). Any other non-Type, non-Int sentinel value would do also. I suppose we could change the C interface from spvals
to sptypes
as we've done elsewhere. Since Const
is now a builtin, that should actually work ok.
base/compiler/tfuncs.jl
Outdated
tt = tuple_tfunc(new_fields) | ||
if isa(tt, PartialStruct) | ||
if !(widenconst(tt) <: ti) | ||
return PartialStruct(ti, tt.fields) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ti is not the typeof this object. That is always know to be exactly tt.typ (currently guaranteed by construction)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the concern here is that we used to typeassert_type_instance
the Vararg
, which while I don't think is entirely correct, did give some precision that we're now losing here. I didn't see a great way to get that back without essentially duplicating intersection over the inference lattice, so I though at least this way we'd recover some precision.
base/compiler/tfuncs.jl
Outdated
return PartialStruct(ti, tt.fields) | ||
end | ||
elseif !(tt <: ti) | ||
return ti |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated?
base/compiler/typelattice.jl
Outdated
@@ -220,6 +220,7 @@ widenconst(c::PartialTypeVar) = TypeVar | |||
widenconst(t::PartialStruct) = t.typ | |||
widenconst(t::Type) = t | |||
widenconst(t::TypeVar) = t | |||
widenconst(t::Core.VarargMarker) = t |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would the typeof a variable be VarargMarker, which isn’t a type?
if isa(N, Int) | ||
return length(p)::Int + N - 1, true | ||
if isdefined(last, :N) && isa(last.N, Int) | ||
return length(p)::Int + last.N - 1, true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might need a type assert (someone seems to have put effort into them previously, so I assume it mattered for invalidation)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type assert on last.N?
base/compiler/typelattice.jl
Outdated
@@ -220,6 +220,7 @@ widenconst(c::PartialTypeVar) = TypeVar | |||
widenconst(t::PartialStruct) = t.typ | |||
widenconst(t::Type) = t | |||
widenconst(t::TypeVar) = t | |||
widenconst(t::Core.VarargMarker) = t |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't widenconst not be called on one of these?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally yes, but currently inference is pretty liberal about calling both this and rewrap_unionall on non-Types and I just wanted to preserve the status quo for now. I'd argue a similar argument could be made for TypeVar
just above.
@@ -627,7 +627,7 @@ function maybe_vararg_tuple_1() | |||
end | |||
@test Type{Tuple{Vararg{Int}}} <: Base.return_types(maybe_vararg_tuple_1, ())[1] | |||
function maybe_vararg_tuple_2() | |||
x = Type[Vararg{Int}][1] | |||
x = [Vararg{Int}][1] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
x = [Vararg{Int}][1] | |
x = Any[Vararg{Int}][1] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is already tested by the test just above. I figured the most natural translation would be to just have this be typed as typeof(Vararg)
..
This code has been dead since #38136. Clean it up some.
In the packages I've looked at, where this seems to be causing the most trouble is places like map(T -> T <: Real, sig.parameters) Is the absence of support for having |
Yes, because the code you posted is probably buggy. You want |
Does this change qualify to be included in NEWS.md section "deprecated" section? |
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.
This code has been dead since JuliaLang#38136. Clean it up some.
Currently
Vararg
is a DataType, but is special cased in abunch 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 #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.VarargMarker
). The user facingbehavior should be mostly unchanged, since
boot.jl
now has:i.e. we have a handly UnionAll wrapper that looks just like it
used to. The biggest difference is probably that VarargMarker
does not have
.parameters
, so code that tries to reach intothat explicitly will need to be adjusted. We could provide
a compatibility
getproperty
method to adapt that, but I'dprefer to see how many packages need updating first, before
going that route.
This is essentially complete, but a few cleanup items remain.
Closes #30995 #34690 #26625 #37316