-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Add a few new reflection methods (after type system merge) #20006
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
Changes from all commits
7786ff5
e101000
0292c42
b84483f
9db934c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -82,6 +82,15 @@ type Conditional | |
end | ||
end | ||
|
||
immutable PartialTypeVar | ||
tv::TypeVar | ||
# N.B.: Currently unused, but would allow turning something back | ||
# into Const, if the bounds are pulled out of this TypeVar | ||
lb_certain::Bool | ||
ub_certain::Bool | ||
PartialTypeVar(tv::TypeVar, lb_certain::Bool, ub_certain::Bool) = new(tv, lb_certain, ub_certain) | ||
end | ||
|
||
function rewrap(t::ANY, u::ANY) | ||
isa(t, Const) && return t | ||
isa(t, Conditional) && return t | ||
|
@@ -688,10 +697,25 @@ function limit_type_depth(t::ANY, d::Int, cov::Bool=true, var::Union{Void,TypeVa | |
return (cov && !stillcov) ? UnionAll(var, R) : R | ||
end | ||
|
||
const DataType_name_fieldindex = fieldindex(DataType, :name) | ||
const DataType_parameters_fieldindex = fieldindex(DataType, :parameters) | ||
const DataType_types_fieldindex = fieldindex(DataType, :types) | ||
const DataType_super_fieldindex = fieldindex(DataType, :super) | ||
|
||
const TypeName_name_fieldindex = fieldindex(TypeName, :name) | ||
const TypeName_module_fieldindex = fieldindex(TypeName, :module) | ||
const TypeName_wrapper_fieldindex = fieldindex(TypeName, :wrapper) | ||
|
||
function const_datatype_getfield_tfunc(sv, fld) | ||
if (fld == DataType_name_fieldindex || | ||
fld == DataType_parameters_fieldindex || | ||
fld == DataType_types_fieldindex || | ||
fld == DataType_super_fieldindex) | ||
return abstract_eval_constant(getfield(sv, fld)) | ||
end | ||
return nothing | ||
end | ||
|
||
# returns (type, isexact) | ||
function getfield_tfunc(s00::ANY, name) | ||
if isa(s00, TypeVar) | ||
|
@@ -719,11 +743,22 @@ function getfield_tfunc(s00::ANY, name) | |
elseif nv === :body || nv === 2 | ||
return Const(sv.body) | ||
end | ||
elseif isa(sv, DataType) | ||
t = const_datatype_getfield_tfunc(sv, isa(nv, Symbol) ? | ||
fieldindex(DataType, nv, false) : nv) | ||
t !== nothing && return t | ||
elseif isa(sv, TypeName) | ||
fld = isa(nv, Symbol) ? fieldindex(TypeName, nv, false) : nv | ||
if (fld == TypeName_name_fieldindex || | ||
fld == TypeName_module_fieldindex || | ||
fld == TypeName_wrapper_fieldindex) | ||
return abstract_eval_constant(getfield(sv, fld)) | ||
end | ||
end | ||
if isa(sv, Module) && isa(nv, Symbol) | ||
return abstract_eval_global(sv, nv) | ||
end | ||
if (isa(sv, DataType) || isimmutable(sv)) && isdefined(sv, nv) | ||
if (isa(sv, SimpleVector) || isimmutable(sv)) && isdefined(sv, nv) | ||
return abstract_eval_constant(getfield(sv, nv)) | ||
end | ||
end | ||
|
@@ -783,11 +818,9 @@ function getfield_tfunc(s00::ANY, name) | |
else | ||
sp = nothing | ||
end | ||
if (sp !== nothing && | ||
(fld == DataType_parameters_fieldindex || | ||
fld == DataType_types_fieldindex || | ||
fld == DataType_super_fieldindex)) | ||
return Const(getfield(sp, fld)) | ||
if sp !== nothing | ||
t = const_datatype_getfield_tfunc(sp, fld) | ||
t !== nothing && return t | ||
end | ||
R = s.types[fld] | ||
if isempty(s.parameters) | ||
|
@@ -915,15 +948,20 @@ function apply_type_tfunc(headtypetype::ANY, args::ANY...) | |
return Any | ||
end | ||
uncertain = false | ||
canconst = true | ||
tparams = Any[] | ||
outervars = Any[] | ||
for i = 1:largs | ||
ai = args[i] | ||
if isType(ai) | ||
aip1 = ai.parameters[1] | ||
canconst &= isleaftype(aip1) | ||
push!(tparams, aip1) | ||
elseif isa(ai, Const) && (isa(ai.val, Type) || valid_tparam(ai.val)) | ||
elseif isa(ai, Const) && (isa(ai.val, Type) || isa(ai.val, TypeVar) || valid_tparam(ai.val)) | ||
push!(tparams, ai.val) | ||
elseif isa(ai, PartialTypeVar) | ||
canconst = false | ||
push!(tparams, ai.tv) | ||
else | ||
# TODO: return `Bottom` for trying to apply a non-UnionAll | ||
uncertain = true | ||
|
@@ -966,11 +1004,11 @@ function apply_type_tfunc(headtypetype::ANY, args::ANY...) | |
# doesn't match, which could happen if a type estimate is too coarse | ||
return Type{_} where _<:headtype | ||
end | ||
!uncertain && return Const(appl) | ||
!uncertain && canconst && return Const(appl) | ||
if isvarargtype(headtype) | ||
return Type | ||
end | ||
if type_too_complex(appl,0) | ||
if uncertain && type_too_complex(appl,0) | ||
return Type{_} where _<:headtype | ||
end | ||
if istuple | ||
|
@@ -1486,6 +1524,25 @@ function Pair_name() | |
return _Pair_name | ||
end | ||
|
||
_typename(a) = Union{} | ||
_typename(a::Vararg) = Any | ||
_typename(a::TypeVar) = Any | ||
_typename(a::DataType) = Const(a.name) | ||
function _typename(a::Union) | ||
ta = _typename(a.a) | ||
tb = _typename(a.b) | ||
ta === tb ? tb : (ta === Any || tb === Any) ? Any : Union{} | ||
end | ||
_typename(union::UnionAll) = _typename(union.body) | ||
function typename_static(t) | ||
# N.B.: typename maps type equivalence classes to a single value | ||
if isa(t, Const) || isType(t) | ||
return _typename(isa(t, Const) ? t.val : t.parameters[1]) | ||
else | ||
return Any | ||
end | ||
end | ||
|
||
function abstract_call(f::ANY, fargs::Union{Tuple{},Vector{Any}}, argtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState) | ||
if f === _apply | ||
length(fargs) > 1 || return Any | ||
|
@@ -1567,19 +1624,65 @@ function abstract_call(f::ANY, fargs::Union{Tuple{},Vector{Any}}, argtypes::Vect | |
end | ||
end | ||
return Any | ||
elseif f === UnionAll | ||
if length(fargs) == 3 && isa(argtypes[2], Const) | ||
tv = argtypes[2].val | ||
if isa(tv, TypeVar) | ||
elseif f === TypeVar | ||
lb = Union{} | ||
ub = Any | ||
ub_certain = lb_certain = true | ||
if length(fargs) >= 2 && isa(argtypes[2], Const) | ||
nv = argtypes[2].val | ||
ubidx = 3 | ||
if length(fargs) >= 4 | ||
ubidx = 4 | ||
if isa(argtypes[3], Const) | ||
body = argtypes[3].val | ||
lb = argtypes[3].val | ||
elseif isType(argtypes[3]) | ||
body = argtypes[3].parameters[1] | ||
lb = argtypes[3].parameters[1] | ||
lb_certain = false | ||
else | ||
return Any | ||
return TypeVar | ||
end | ||
end | ||
if length(fargs) >= ubidx | ||
if isa(argtypes[ubidx], Const) | ||
ub = argtypes[ubidx].val | ||
elseif isType(argtypes[ubidx]) | ||
ub = argtypes[ubidx].parameters[1] | ||
ub_certain = false | ||
else | ||
return TypeVar | ||
end | ||
return abstract_eval_constant(UnionAll(tv, body)) | ||
end | ||
tv = TypeVar(nv, lb, ub) | ||
return PartialTypeVar(tv, lb_certain, ub_certain) | ||
end | ||
return TypeVar | ||
elseif f === UnionAll | ||
if length(fargs) == 3 | ||
canconst = true | ||
if isa(argtypes[3], Const) | ||
body = argtypes[3].val | ||
elseif isType(argtypes[3]) | ||
body = argtypes[3].parameters[1] | ||
canconst = false | ||
else | ||
return Any | ||
end | ||
if isa(argtypes[2], Const) | ||
tv = argtypes[2].val | ||
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 commentThe reason will be displayed to describe this comment to others. Learn more. Does this depend on There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
else | ||
return Any | ||
end | ||
!isa(tv, TypeVar) && return Any | ||
if !isa(body, Type) && !isa(body, TypeVar) | ||
return Any | ||
end | ||
theunion = UnionAll(tv, body) | ||
ret = canconst ? abstract_eval_constant(theunion) : Type{theunion} | ||
return ret | ||
end | ||
return Any | ||
elseif f === return_type | ||
|
@@ -1605,7 +1708,16 @@ function abstract_call(f::ANY, fargs::Union{Tuple{},Vector{Any}}, argtypes::Vect | |
|
||
if length(argtypes)>2 && argtypes[3] ⊑ Int | ||
at2 = widenconst(argtypes[2]) | ||
if (at2 <: Tuple || | ||
if at2 <: SimpleVector && istopfunction(tm, f, :getindex) | ||
if isa(argtypes[2], Const) && isa(argtypes[3], Const) | ||
svecval = argtypes[2].val | ||
idx = argtypes[3].val | ||
if isa(idx, Int) && 1 <= idx <= length(svecval) & | ||
isassigned(svecval, idx) | ||
return Const(getindex(svecval, idx)) | ||
end | ||
end | ||
elseif (at2 <: Tuple || | ||
(isa(at2, DataType) && (at2::DataType).name === Pair_name())) | ||
# allow tuple indexing functions to take advantage of constant | ||
# index arguments. | ||
|
@@ -1627,6 +1739,12 @@ function abstract_call(f::ANY, fargs::Union{Tuple{},Vector{Any}}, argtypes::Vect | |
|
||
if istopfunction(tm, f, :promote_type) || istopfunction(tm, f, :typejoin) | ||
return Type | ||
elseif length(argtypes) == 2 && istopfunction(tm, f, :typename) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should consider trying to handle this by marking the function There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not just pure though, it also maps the type equality equivalence class to one value. |
||
t = argtypes[2] | ||
if isa(t, Const) || isType(t) | ||
return typename_static(t) | ||
end | ||
return Any | ||
end | ||
|
||
if sv.params.inlining | ||
|
@@ -1915,6 +2033,7 @@ function widenconst(c::Const) | |
return typeof(c.val) | ||
end | ||
end | ||
widenconst(c::PartialTypeVar) = TypeVar | ||
widenconst(t::ANY) = t | ||
|
||
issubstate(a::VarState, b::VarState) = (a.typ ⊑ b.typ && a.undef <= b.undef) | ||
|
@@ -3562,7 +3681,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference | |
if method.name == :getindex || method.name == :next || method.name == :indexed_next | ||
if length(atypes) > 2 && atypes[3] ⊑ Int | ||
at2 = widenconst(atypes[2]) | ||
if (at2 <: Tuple || | ||
if (at2 <: Tuple || at2 <: SimpleVector || | ||
(isa(at2, DataType) && (at2::DataType).name === Pair_name())) | ||
force_infer = true | ||
end | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -589,6 +589,17 @@ g11015(::Type{Bool}, ::Bool) = 2.0 | |
@test Int <: Base.return_types(f11015, (AT11015,))[1] | ||
@test f11015(AT11015(true)) === 1 | ||
|
||
# Inference for some type-level computation | ||
fUnionAll{T}(::Type{T}) = Type{S} where S <: T | ||
@inferred fUnionAll(Real) == Type{T} where T <: Real | ||
@inferred fUnionAll(Rational{T} where T <: AbstractFloat) == Type{T} where T<:(Rational{S} where S <: AbstractFloat) | ||
|
||
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 commentThe 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 commentThe 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. |
||
@test pub(pub(x, 1), 2) == Int || pub(pub(x, 1), 2) == Float64 | ||
end | ||
|
||
# issue #20267 | ||
type T20267{T} | ||
inds::Vector{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.
wrong indent, again