-
-
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
Add a few new reflection methods (after type system merge) #20006
Conversation
Names subject to bikeshedding of course. |
LGTM. These and various other type-related functions scattered around Base should probably be collected into a |
base/exports.jl
Outdated
@@ -1002,6 +1002,7 @@ export | |||
fieldoffset, | |||
fieldname, | |||
fieldnames, | |||
isabstract, |
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.
needs to go in stdlib doc index
base/reflection.jl
Outdated
Base.parameter_upper_bound(t::UnionAll, idx) | ||
|
||
Determine the upper bound of a type parameter in the underlying type. E.g.: | ||
``` |
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.
would make a good doctest (though I don't think unexported docstrings' doctests get run by default)
Looks like you've used 3-space indents throughout the diff. For consistency it should be 4 spaces. (Indeed, AFAIK, to get the correct formatting in the resulting markdown from docstrings, the signature actually has to be indented 4 spaces.) |
Unintentional. Sometimes editors are too smart for their own good. |
e5e3164
to
8c58732
Compare
@JeffBezanson expressed a preference for not exporting them from base until we create the TypeUtils module. As a result, I've unexported |
c9103ad
to
bb7c07b
Compare
Sorry, you'll have to rebase this. |
bb7c07b
to
cff3cd6
Compare
8c58732
to
eecbf66
Compare
Rebased + a new commit that allows ignoring ambiguities (in |
7d2692f
to
697c2ed
Compare
base/reflection.jl
Outdated
``` | ||
""" | ||
unparameterized_type(t::DataType) = t.name.wrapper | ||
unparameterized_type(t::UnionAll) = unparameterized_type(unwrap_unionall(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.
maybe we could try to give this a better name, although I'm not sure what. I think this is essentially supertype
? So maybe something like basetype
? In general, I think we've tried to avoid providing this, since it makes it tempting to assume you could re-parameterize it (even though basetype(T){tparam}
is not generally a well-defined operation)
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.
(even though basetype(T){tparam} is not generally a well-defined operation)
Could you elaborate on this point?
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 transform isn't meaningful for an arbitrary type T
. It's actually not really even valid for a non-arbitrary type T
(for example, f(D::Type{Dict}, K, V) = D{K, V}
cannot be inferred, leading to this needed correction: 210d4ec#diff-d4736682004c1996e5d9b1c5c33c8c8e).
In the only cases where it is valid (e.g. you passed a type literal to basetype
), you could just write that same type literal without the call to basetype
. And the resulting code would also be less error-prone if someone replaced the original type with a type-alias (in addition to being shorter, by virtue of not containing no-op function calls).
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.
What's your definition of valid? That function definition you wrote down works for at least some uses.
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.
I mean that it's not valid to infer the output of that example function, and it's brittle to changes in the definition of Dict
. Or consider Vector{Int}
-- it has multiple base types, and one of the proposals for #4774 would have involved adding another. Thus you can't generally assume that you can still parameterize a type object after extracting the base type and get back something meaningful.
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.
Ok, I think I see, the point, but I'm not sure I understand why we can't infer D{K,V}.
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.
D
is known to be type-equal to Dict
, but it is not known to actually be Dict
e95c0f8
to
7ae0bd2
Compare
eecbf66
to
7d3c9de
Compare
Rebased and renamed |
Not to derail my bikeshedding of |
What would the semantics of that be? |
The motivating example for |
hm, that seems tricky. That code is implementing the sorts of dubious assumptions I was mentioning are usually best avoided (usually by implementing
|
elseif isa(argtypes[2], PartialTypeVar) | ||
ptv = argtypes[2] | ||
tv = ptv.tv | ||
canconst = false |
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.
Does this depend on lb_certain
and ub_certain
?
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. Two invocations of this functions do not create egal types (because it allocates a new one every time). I though we could do something with that at first, but I had to take that out because of that.
b4228f7
to
21a751f
Compare
I've addressed some of the review here. @JeffBezanson @vtjnash, I'd appreciate if you could finish this up, since I'll be traveling the rest of this week and this is required for ColorTypes. |
21a751f
to
1c6e437
Compare
base/inference.jl
Outdated
t = argtypes[2] | ||
if isa(t, Const) || isType(t) | ||
return typename_static(t) | ||
end |
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.
indent is a bit off
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 it is. Sorry, I'm used to sublime doing this all automatically for me, but I've been trying to use Atom more. Will fix
1c6e437
to
49af99f
Compare
Review comments addressed. |
base/inference.jl
Outdated
tv::TypeVar | ||
lb_certain::Bool | ||
ub_certain::Bool | ||
PartialTypeVar(tv::TypeVar, lb_certain::ANY, ub_certain::ANY) = new(tv, lb_certain, ub_certain) |
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 use ANY
here if the arguments have to be Bool?
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 reason. Will change.
base/inference.jl
Outdated
function _typename(a::Union) | ||
ta = _typename(a.a) | ||
tb = _typename(a.b) | ||
ta == tb ? tb : (ta === Any || tb == Any) ? Any : Union{} |
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.
Should this use ta === tb
? (also tb === Any
for consistency)
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 don't think it should since Const(T.name) != Const(T.name)
. tb === Any
is fine though.
|
||
fComplicatedUnionAll{T}(::Type{T}) = Type{Tuple{S,rand() >= 0.5 ? Int : Float64}} where S <: T | ||
let pub = Base.parameter_upper_bound, x = fComplicatedUnionAll(Real) | ||
@test pub(pub(x, 1), 1) == Real |
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.
Do these belong in the reflection tests instead?
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.
I put them here, because at some point I had inference mess up and constant fold this to the wrong answer.
a0187ee
to
68dcd5c
Compare
68dcd5c
to
3a79b5b
Compare
This is informed by attempting to port some packages to the new type system. I have abstracted out the functionality required by those packages into documented/tested functions. The hope is to avoid introducing too many implementation details to packages. These functions can also be implemented in Compat for older julia versions, such that packages can immediate replace their use of internal APIs with these functions.
Because of the way methods are specified, there is often spurious ambiguities due to a type parameter being able to take the value Union{}, (e.g. Type{T} becomes Type{Union{}}). Since detect_ambiguities reports these, it can drown out more serious ambiguities among non-Union{} types. Add a keyword argument to detect_ambiguities, that ignores all ambiguities that only occur if one of the type parameters has to be Union{}.
Make inference be able to infer the type constructor. This is a little tricky, since we don't really have a good way to represent this. It's not quite `Const(TypeVar(:T,lb,ub))`, because a) the bounds may only be accurate up to type equality and b) TypeVar is not `isbits`, so it's not actually egal to the value we'll have at runtime. Additionally Type{T} already has meaning as a partially constructed type (e.g. an unwrapped UnionAll), so using T::Type{T} runs the risk of confusing this with an unwrapped type. Instead, introduce a new Const-like type, `PartialTypeVar`, which carries the type var and also keeps track of whether the bounds were egal or only typequal (we don't take advantage of that yet, but we could in the future). Additionally, improve the inference of `typename` and allow constant folding of field accesses on TypeName and SimpleVectors (to be able to constant fold T.parameters[1]).
for DataType/TypeName
3a79b5b
to
b84483f
Compare
const TypeName_wrapper_fieldindex = fieldindex(TypeName, :wrapper) | ||
|
||
function const_datatype_getfield_tfunc(sv, fld) | ||
if (fld == DataType_name_fieldindex || |
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.
wrong indent, again
This is informed by attempting to port some packages to the new type system.
I have abstracted out the functionality required by those packages into
documented/tested functions. The hope is to avoid introducing too many
implementation details to packages. These functions can also be implemented
in Compat for older julia versions, such that packages can immediate replace
their use of internal APIs with these functions.