Skip to content
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

remove additional rule that parameters must match for dispatch #23117

Merged
merged 1 commit into from
Aug 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ Language changes
For example, `f() = (global sin = "gluttony"; nothing)` will now resolve which module
contains `sin` eagerly, rather than delaying that decision until `f` is run. ([#22984]).

* Dispatch rules have been simplified:
matching methods is now determined exclusively by subtyping;
the rule that method type parameters must be also be captured has been removed.
Instead, attempting to access the uncontrained parameters will throw an `UndefVarError`.
Linting in package tests is recommended to confirm that the set of methods
which might throw `UndefVarError` when accessing the static parameters
(`need_to_handle_undef_sparam = Set{Any}(m.sig for m in Test.detect_unbound_args(Base, recursive=true))`)
is equal (`==`) to some known set (`expected = Set()`). ([#TBD])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should update the TBD here



Breaking changes
----------------
Expand Down
60 changes: 49 additions & 11 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,6 @@ end
convert(::Type{Any}, @nospecialize(x)) = x
convert(::Type{T}, x::T) where {T} = x

convert(::Type{Tuple{}}, ::Tuple{}) = ()
convert(::Type{Tuple}, x::Tuple) = x
convert(::Type{Tuple{Vararg{T}}}, x::Tuple) where {T} = cnvt_all(T, x...)
cnvt_all(T) = ()
cnvt_all(T, x, rest...) = tuple(convert(T,x), cnvt_all(T, rest...)...)

"""
@eval [mod,] ex

Expand All @@ -86,13 +80,24 @@ end
argtail(x, rest...) = rest
tail(x::Tuple) = argtail(x...)

# TODO: a better / more infer-able definition would pehaps be
# tuple_type_head(T::Type) = fieldtype(T::Type{<:Tuple}, 1)
tuple_type_head(T::UnionAll) = (@_pure_meta; UnionAll(T.var, tuple_type_head(T.body)))
function tuple_type_head(T::Union)
@_pure_meta
return Union{tuple_type_head(T.a), tuple_type_head(T.b)}
end
function tuple_type_head(T::DataType)
@_pure_meta
T.name === Tuple.name || throw(MethodError(tuple_type_head, (T,)))
return unwrapva(T.parameters[1])
end

tuple_type_tail(T::UnionAll) = (@_pure_meta; UnionAll(T.var, tuple_type_tail(T.body)))
function tuple_type_tail(T::Union)
@_pure_meta
return Union{tuple_type_tail(T.a), tuple_type_tail(T.b)}
end
function tuple_type_tail(T::DataType)
@_pure_meta
T.name === Tuple.name || throw(MethodError(tuple_type_tail, (T,)))
Expand Down Expand Up @@ -159,11 +164,44 @@ function typename(a::Union)
end
typename(union::UnionAll) = typename(union.body)

convert(::Type{T}, x::Tuple{Any,Vararg{Any}}) where {T<:Tuple{Any,Vararg{Any}}} =
tuple(convert(tuple_type_head(T),x[1]), convert(tuple_type_tail(T), tail(x))...)
convert(::Type{T}, x::T) where {T<:Tuple{Any,Vararg{Any}}} = x

oftype(x,c) = convert(typeof(x),c)
convert(::Type{T}, x::T) where {T<:Tuple{Any, Vararg{Any}}} = x
convert(::Type{T}, x::Tuple{Any, Vararg{Any}}) where {T<:Tuple} =
(convert(tuple_type_head(T), x[1]), convert(tuple_type_tail(T), tail(x))...)

# TODO: the following definitions are equivalent (behaviorally) to the above method
# I think they may be faster / more efficient for inference,
# if we could enable them, but are they?
# TODO: These currently can't be used (#21026, #23017) since with
# z(::Type{<:Tuple{Vararg{T}}}) where {T} = T
# calling
# z(Tuple{Val{T}} where T)
# fails, even though `Type{Tuple{Val}} == Type{Tuple{Val{S}} where S}`
# and so T should be `Val` (aka `Val{S} where S`)
#convert(_::Type{Tuple{S}}, x::Tuple{S}) where {S} = x
#convert(_::Type{Tuple{S}}, x::Tuple{Any}) where {S} = (convert(S, x[1]),)
#convert(_::Type{T}, x::T) where {S, N, T<:Tuple{S, Vararg{S, N}}} = x
#convert(_::Type{Tuple{S, Vararg{S, N}}},
# x::Tuple{Any, Vararg{Any, N}}) where
# {S, N} = cnvt_all(S, x...)
#convert(_::Type{Tuple{Vararg{S, N}}},
# x::Tuple{Vararg{Any, N}}) where
# {S, N} = cnvt_all(S, x...)
# TODO: These currently can't be used since
# Type{NTuple} <: (Type{Tuple{Vararg{S}}} where S) is true
# even though the value S doesn't exist
#convert(_::Type{Tuple{Vararg{S}}},
# x::Tuple{Any, Vararg{Any}}) where
# {S} = cnvt_all(S, x...)
#convert(_::Type{Tuple{Vararg{S}}},
# x::Tuple{Vararg{Any}}) where
# {S} = cnvt_all(S, x...)
#cnvt_all(T) = ()
#cnvt_all(T, x, rest...) = (convert(T, x), cnvt_all(T, rest...)...)
# TODO: These may be necessary if the above are enabled
#convert(::Type{Tuple{}}, ::Tuple{}) = ()
#convert(::Type{Tuple{Vararg{S}}} where S, ::Tuple{}) = ()

oftype(x, c) = convert(typeof(x), c)

unsigned(x::Int) = reinterpret(UInt, x)
signed(x::UInt) = reinterpret(Int, x)
Expand Down
54 changes: 33 additions & 21 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1448,8 +1448,7 @@ function invoke_tfunc(@nospecialize(f), @nospecialize(types), @nospecialize(argt
return Any
end
meth = entry.func
(ti, env) = ccall(:jl_match_method, Ref{SimpleVector}, (Any, Any),
argtype, meth.sig)
(ti, env) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), argtype, meth.sig)::SimpleVector
rt, edge = typeinf_edge(meth::Method, ti, env, sv)
edge !== nothing && add_backedge!(edge::MethodInstance, sv)
return rt
Expand Down Expand Up @@ -1826,7 +1825,7 @@ function abstract_call_method(method::Method, @nospecialize(f), @nospecialize(si

# if sig changed, may need to recompute the sparams environment
if isa(method.sig, UnionAll) && isempty(sparams)
recomputed = ccall(:jl_env_from_type_intersection, Ref{SimpleVector}, (Any, Any), sig, method.sig)
recomputed = ccall(:jl_type_intersection_with_env, Any, (Any, Any), sig, method.sig)::SimpleVector
sig = recomputed[1]
if !isa(unwrap_unionall(sig), DataType) # probably Union{}
return Any
Expand Down Expand Up @@ -2448,7 +2447,9 @@ function abstract_eval(@nospecialize(e), vtypes::VarTable, sv::InferenceState)
n = sym.args[1]
if 1 <= n <= length(sv.sp)
val = sv.sp[n]
t = Const(true)
if !isa(val, TypeVar)
t = Const(true)
end
end
end
else
Expand Down Expand Up @@ -3857,9 +3858,13 @@ function effect_free(@nospecialize(e), src::CodeInfo, mod::Module, allow_volatil
elseif isa(e, Expr)
e = e::Expr
head = e.head
if head === :static_parameter || is_meta_expr_head(head)
if is_meta_expr_head(head)
return true
end
if head === :static_parameter
# if we aren't certain about the type, it might be an UndefVarError at runtime
return isa(e.typ, DataType) && isleaftype(e.typ)
end
if e.typ === Bottom
return false
end
Expand Down Expand Up @@ -4268,8 +4273,8 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector
else
invoke_data = invoke_data::InvokeData
method = invoke_data.entry.func
(metharg, methsp) = ccall(:jl_match_method, Ref{SimpleVector}, (Any, Any),
atype_unlimited, method.sig)
(metharg, methsp) = ccall(:jl_type_intersection_with_env, Any, (Any, Any),
atype_unlimited, method.sig)::SimpleVector
methsp = methsp::SimpleVector
end

Expand Down Expand Up @@ -5008,20 +5013,21 @@ function inlining_pass(e::Expr, sv::InferenceState, stmts, ins)
aarg = e.args[i]
argt = exprtype(aarg, sv.src, sv.mod)
t = widenconst(argt)
if isa(aarg,Expr) && (is_known_call(aarg, tuple, sv.src, sv.mod) || is_known_call(aarg, svec, sv.src, sv.mod))
# apply(f,tuple(x,y,...)) => f(x,y,...)
newargs[i-2] = aarg.args[2:end]
elseif isa(argt,Const) && (isa(argt.val, Tuple) || isa(argt.val, SimpleVector)) &&
if isa(aarg, Expr) && (is_known_call(aarg, tuple, sv.src, sv.mod) || is_known_call(aarg, svec, sv.src, sv.mod))
# apply(f, tuple(x, y, ...)) => f(x, y, ...)
newargs[i - 2] = aarg.args[2:end]
elseif isa(argt, Const) && (isa(argt.val, Tuple) || isa(argt.val, SimpleVector)) &&
effect_free(aarg, sv.src, sv.mod, true)
newargs[i-2] = Any[ QuoteNode(x) for x in argt.val ]
val = argt.val
newargs[i - 2] = Any[ QuoteNode(val[i]) for i in 1:(length(val)::Int) ] # avoid making a tuple Generator here!
elseif isa(aarg, Tuple) || (isa(aarg, QuoteNode) && (isa(aarg.value, Tuple) || isa(aarg.value, SimpleVector)))
if isa(aarg, QuoteNode)
aarg = aarg.value
end
newargs[i-2] = Any[ QuoteNode(x) for x in aarg ]
newargs[i - 2] = Any[ QuoteNode(aarg[i]) for i in 1:(length(aarg)::Int) ] # avoid making a tuple Generator here!
elseif isa(t, DataType) && t.name === Tuple.name && !isvatuple(t) &&
length(t.parameters) <= sv.params.MAX_TUPLE_SPLAT
for k = (effect_free_upto+1):(i-3)
for k = (effect_free_upto + 1):(i - 3)
as = newargs[k]
for kk = 1:length(as)
ak = as[kk]
Expand All @@ -5032,7 +5038,7 @@ function inlining_pass(e::Expr, sv::InferenceState, stmts, ins)
end
end
end
effect_free_upto = i-3
effect_free_upto = i - 3
if effect_free(aarg, sv.src, sv.mod, true)
# apply(f,t::(x,y)) => f(t[1],t[2])
tmpv = aarg
Expand All @@ -5046,13 +5052,13 @@ function inlining_pass(e::Expr, sv::InferenceState, stmts, ins)
else
tp = t.parameters
end
newargs[i-2] = Any[ mk_getfield(tmpv,j,tp[j]) for j=1:length(tp) ]
newargs[i - 2] = Any[ mk_getfield(tmpv, j, tp[j]) for j in 1:(length(tp)::Int) ]
else
# not all args expandable
return e
end
end
splice!(stmts, ins:ins-1, newstmts)
splice!(stmts, ins:(ins - 1), newstmts)
ins += length(newstmts)
e.args = [Any[e.args[2]]; newargs...]

Expand Down Expand Up @@ -5180,11 +5186,17 @@ normvar(@nospecialize(s)) = s

# given a single-assigned var and its initializer `init`, return what we can
# replace `var` with, or `var` itself if we shouldn't replace it
function get_replacement(table, var::Union{SlotNumber, SSAValue}, @nospecialize(init), nargs, slottypes, ssavaluetypes)
function get_replacement(table::ObjectIdDict, var::Union{SlotNumber, SSAValue}, @nospecialize(init),
nargs::Int, slottypes::Vector{Any}, ssavaluetypes::Vector{Any})
#if isa(init, QuoteNode) # this can cause slight code size increases
# return init
if (isa(init, Expr) && init.head === :static_parameter) || isa(init, corenumtype) ||
init === () || init === nothing
if isa(init, Expr) && init.head === :static_parameter
# if we aren't certain about the type, it might be an UndefVarError at runtime (!effect_free)
# so we need to preserve the original point of assignment
if isa(init.typ, DataType) && isleaftype(init.typ)
return init
end
elseif isa(init, corenumtype) || init === () || init === nothing
return init
elseif isa(init, Slot) && is_argument(nargs, init::Slot)
# the transformation is not ideal if the assignment
Expand Down Expand Up @@ -5231,7 +5243,7 @@ function remove_redundant_temp_vars!(src::CodeInfo, nargs::Int, sa::ObjectIdDict
repls = ObjectIdDict()
for (v, init) in sa
repl = get_replacement(sa, v, init, nargs, slottypes, ssavaluetypes)
compare = isa(repl,TypedSlot) ? normslot(repl) : repl
compare = isa(repl, TypedSlot) ? normslot(repl) : repl
if compare !== v
repls[v] = repl
end
Expand Down
20 changes: 11 additions & 9 deletions base/promotion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,6 @@ end

## promotion mechanism ##

promote_type() = Bottom
promote_type(T) = T
promote_type(T, S, U, V...) = (@_inline_meta; promote_type(T, promote_type(S, U, V...)))

promote_type(::Type{Bottom}, ::Type{Bottom}) = Bottom
promote_type(::Type{T}, ::Type{T}) where {T} = T
promote_type(::Type{T}, ::Type{Bottom}) where {T} = T
promote_type(::Type{Bottom}, ::Type{T}) where {T} = T

"""
promote_type(type1, type2)

Expand All @@ -151,6 +142,17 @@ julia> promote_type(Float32, BigInt)
BigFloat
```
"""
function promote_type end

promote_type() = Bottom
promote_type(T) = T
promote_type(T, S, U, V...) = (@_inline_meta; promote_type(T, promote_type(S, U, V...)))

promote_type(::Type{Bottom}, ::Type{Bottom}) = Bottom
promote_type(::Type{T}, ::Type{T}) where {T} = T
promote_type(::Type{T}, ::Type{Bottom}) where {T} = T
promote_type(::Type{Bottom}, ::Type{T}) where {T} = T

function promote_type(::Type{T}, ::Type{S}) where {T,S}
@_inline_meta
# Try promote_rule in both orders. Typically only one is defined,
Expand Down
6 changes: 3 additions & 3 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -737,9 +737,9 @@ function _dump_function(@nospecialize(f), @nospecialize(t), native::Bool, wrappe
t = to_tuple_type(t)
ft = isa(f, Type) ? Type{f} : typeof(f)
tt = Tuple{ft, t.parameters...}
(ti, env) = ccall(:jl_match_method, Any, (Any, Any), tt, meth.sig)::SimpleVector
meth = func_for_method_checked(meth, tt)
linfo = ccall(:jl_specializations_get_linfo, Ref{Core.MethodInstance}, (Any, Any, Any, UInt), meth, tt, env, world)
(ti, env) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), tt, meth.sig)::SimpleVector
meth = func_for_method_checked(meth, ti)
linfo = ccall(:jl_specializations_get_linfo, Ref{Core.MethodInstance}, (Any, Any, Any, UInt), meth, ti, env, world)
# get the code for it
return _dump_function_linfo(linfo, world, native, wrapper, strip_ir_metadata, dump_module, syntax, optimize, params)
end
Expand Down
Loading