From da29ff248ede430c3e6084acc9632c9320e38b36 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 7 Jan 2017 15:37:10 -0500 Subject: [PATCH] remove additional rule that parameters must match for dispath matching a method signature is a simple subtyping test, with no additional rule to ensure that all parameters have a value and adds a Test for possibly unbound parameters, which allows detecting methods definitions that this change affects --- NEWS.md | 9 ++ base/essentials.jl | 60 ++++++-- base/inference.jl | 54 +++++--- base/promotion.jl | 20 +-- base/reflection.jl | 6 +- base/test.jl | 103 +++++++++++++- base/tuple.jl | 17 ++- src/ast.c | 3 +- src/codegen.cpp | 29 +++- src/gf.c | 317 +++++++++++++++++++------------------------ src/interpreter.c | 27 ++-- src/julia_internal.h | 11 +- src/method.c | 61 ++++----- src/precompile.c | 24 +--- src/subtype.c | 35 ++++- src/typemap.c | 64 ++------- test/ambiguous.jl | 30 ++++ test/core.jl | 40 +++--- test/inference.jl | 12 +- test/reflection.jl | 3 +- test/subtype.jl | 2 +- test/tuple.jl | 62 ++++++++- 22 files changed, 605 insertions(+), 384 deletions(-) diff --git a/NEWS.md b/NEWS.md index fa7a2f4630de6..07ccb1881e301 100644 --- a/NEWS.md +++ b/NEWS.md @@ -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]) + Breaking changes ---------------- diff --git a/base/essentials.jl b/base/essentials.jl index 23cb3ebd6228d..95f1dc75c86bb 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -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 @@ -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,))) @@ -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) diff --git a/base/inference.jl b/base/inference.jl index 8f89dcbd43879..0c4a90e27aa2f 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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] @@ -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 @@ -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...] @@ -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 @@ -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 diff --git a/base/promotion.jl b/base/promotion.jl index 10c8b2a34170e..ca79a451bfa07 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -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) @@ -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, diff --git a/base/reflection.jl b/base/reflection.jl index 2d79fe19617d4..032c20c4b4009 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -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 diff --git a/base/test.jl b/base/test.jl index 32cd0e1be0ce4..9faad132c9902 100644 --- a/base/test.jl +++ b/base/test.jl @@ -19,7 +19,7 @@ export @test, @test_throws, @test_broken, @test_skip, @test_warn, @test_nowarn export @testset # Legacy approximate testing functions, yet to be included export @inferred -export detect_ambiguities +export detect_ambiguities, detect_unbound_args export GenericString, GenericSet, GenericDict, GenericArray #----------------------------------------------------------------------- @@ -1257,6 +1257,107 @@ function detect_ambiguities(mods...; return collect(ambs) end +""" + detect_unbound_args(mod1, mod2...; imported=false, recursive=false) + +Returns a vector of `Method`s which may have unbound type parameters. +Use `imported=true` if you wish to also test functions that were +imported into these modules from elsewhere. +Use `recursive=true` to test in all submodules. +""" +function detect_unbound_args(mods...; + imported::Bool = false, + recursive::Bool = false) + ambs = Set{Method}() + for mod in mods + for n in names(mod, true, imported) + Base.isdeprecated(mod, n) && continue + if !isdefined(mod, n) + println("Skipping ", mod, '.', n) # typically stale exports + continue + end + f = Base.unwrap_unionall(getfield(mod, n)) + if recursive && isa(f, Module) && module_parent(f) === mod && module_name(f) === n + subambs = detect_unbound_args(f, imported=imported, recursive=recursive) + union!(ambs, subambs) + elseif isa(f, DataType) && isdefined(f.name, :mt) + mt = Base.MethodList(f.name.mt) + for m in mt + if has_unbound_vars(m.sig) + tuple_sig = Base.unwrap_unionall(m.sig)::DataType + if Base.isvatuple(tuple_sig) + params = tuple_sig.parameters[1:(end - 1)] + tuple_sig = Base.rewrap_unionall(Tuple{params...}, m.sig) + mf = ccall(:jl_gf_invoke_lookup, Any, (Any, UInt), tuple_sig, typemax(UInt)) + if mf != nothing && mf.func !== m && mf.func.sig <: tuple_sig + continue + end + end + push!(ambs, m) + end + end + end + end + end + return collect(ambs) +end + +# find if var will be constrained to have a definite value +# in any concrete leaftype subtype of typ +function constrains_param(var::TypeVar, @nospecialize(typ), covariant::Bool) + typ === var && return true + while typ isa UnionAll + covariant && constrains_param(var, typ.var.ub, covariant) && return true + # typ.var.lb doesn't constrain var + typ = typ.body + end + if typ isa Union + # for unions, verify that both options would constrain var + ba = constrains_param(var, typ.a, covariant) + bb = constrains_param(var, typ.b, covariant) + (ba && bb) && return true + elseif typ isa DataType + # return true if any param constrains var + fc = length(typ.parameters) + if fc > 0 + if typ.name === Tuple.name + # vararg tuple needs special handling + for i in 1:(fc - 1) + p = typ.parameters[i] + constrains_param(var, p, covariant) && return true + end + lastp = typ.parameters[fc] + vararg = Base.unwrap_unionall(lastp) + if vararg isa DataType && vararg.name === Base._va_typename + N = vararg.parameters[2] + constrains_param(var, N, covariant) && return true + # T = vararg.parameters[1] doesn't constrain var + else + constrains_param(var, lastp, covariant) && return true + end + else + for i in 1:fc + p = typ.parameters[i] + constrains_param(var, p, false) && return true + end + end + end + end + return false +end + +function has_unbound_vars(@nospecialize sig) + while sig isa UnionAll + var = sig.var + sig = sig.body + if !constrains_param(var, sig, true) + return true + end + end + return false +end + + """ The `GenericString` can be used to test generic string APIs that program to the `AbstractString` interface, in order to ensure that functions can work diff --git a/base/tuple.jl b/base/tuple.jl index cce81f0151830..bab87697249e6 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -63,8 +63,18 @@ first(t::Tuple) = t[1] # eltype eltype(::Type{Tuple{}}) = Bottom -eltype(::Type{<:Tuple{Vararg{E}}}) where {E} = E -function eltype(t::Type{<:Tuple}) +eltype(::Type{Tuple{Vararg{E}}}) where {E} = E +function eltype(t::Type{<:Tuple{Vararg{E}}}) where {E} + if @isdefined(E) + return E + else + # TODO: need to guard against E being miscomputed by subtyping (ref #23017) + # and compute the result manually in this case + return _compute_eltype(t) + end +end +eltype(t::Type{<:Tuple}) = _compute_eltype(t) +function _compute_eltype(t::Type{<:Tuple}) @_pure_meta t isa Union && return typejoin(eltype(t.a), eltype(t.b)) t´ = unwrap_unionall(t) @@ -199,7 +209,8 @@ fill_to_length(t::Tuple{}, val, ::Val{2}) = (val, val) # constructing from an iterator # only define these in Base, to avoid overwriting the constructors -if isdefined(Main, :Base) +# NOTE: this means this constructor must be avoided in Inference! +if module_name(@__MODULE__) === :Base (::Type{T})(x::Tuple) where {T<:Tuple} = convert(T, x) # still use `convert` for tuples diff --git a/src/ast.c b/src/ast.c index 514e1dfdfb25c..984163f698626 100644 --- a/src/ast.c +++ b/src/ast.c @@ -897,7 +897,8 @@ jl_value_t *jl_parse_eval_all(const char *fname, jl_get_ptls_states()->world_age = jl_world_counter; form = scm_to_julia(fl_ctx, expansion, 0); jl_sym_t *head = NULL; - if (jl_is_expr(form)) head = ((jl_expr_t*)form)->head; + if (jl_is_expr(form)) + head = ((jl_expr_t*)form)->head; JL_SIGATOMIC_END(); jl_get_ptls_states()->world_age = jl_world_counter; if (head == jl_incomplete_sym) diff --git a/src/codegen.cpp b/src/codegen.cpp index 2374f480b4a19..4d19a5adac3aa 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3083,7 +3083,12 @@ static jl_cgval_t emit_sparam(jl_codectx_t &ctx, size_t i) T_prjlvalue, ctx.spvals_ptr, i + sizeof(jl_svec_t) / sizeof(jl_value_t*)); - return mark_julia_type(ctx, tbaa_decorate(tbaa_const, ctx.builder.CreateLoad(bp)), true, jl_any_type, false); + Value *sp = tbaa_decorate(tbaa_const, ctx.builder.CreateLoad(bp)); + Value *isnull = ctx.builder.CreateICmpNE(emit_typeof(ctx, sp), + maybe_decay_untracked(literal_pointer_val(ctx, (jl_value_t*)jl_tvar_type))); + jl_sym_t *name = (jl_sym_t*)jl_svecref(ctx.linfo->def.method->sparam_syms, i); + undef_var_error_ifnot(ctx, isnull, name); + return mark_julia_type(ctx, sp, true, jl_any_type, false); } static jl_cgval_t emit_global(jl_codectx_t &ctx, jl_sym_t *sym) @@ -3134,8 +3139,20 @@ static jl_cgval_t emit_isdefined(jl_codectx_t &ctx, jl_value_t *sym) else if (jl_is_expr(sym)) { assert(((jl_expr_t*)sym)->head == static_parameter_sym && "malformed isdefined expression"); size_t i = jl_unbox_long(jl_exprarg(sym, 0)) - 1; - (void)i; - return mark_julia_const(jl_true); + if (jl_svec_len(ctx.linfo->sparam_vals) > 0) { + jl_value_t *e = jl_svecref(ctx.linfo->sparam_vals, i); + if (!jl_is_typevar(e)) { + return mark_julia_const(jl_true); + } + } + assert(ctx.spvals_ptr != NULL); + Value *bp = ctx.builder.CreateConstInBoundsGEP1_32( + T_prjlvalue, + ctx.spvals_ptr, + i + sizeof(jl_svec_t) / sizeof(jl_value_t*)); + Value *sp = tbaa_decorate(tbaa_const, ctx.builder.CreateLoad(bp)); + isnull = ctx.builder.CreateICmpNE(emit_typeof(ctx, sp), + maybe_decay_untracked(literal_pointer_val(ctx, (jl_value_t*)jl_tvar_type))); } else { jl_module_t *modu; @@ -4431,10 +4448,10 @@ static Function *jl_cfunction_object(jl_function_t *ff, jl_value_t *declrt, jl_t // check the cache jl_typemap_entry_t *sf = NULL; if (jl_cfunction_list.unknown != jl_nothing) { - sf = jl_typemap_assoc_by_type(jl_cfunction_list, (jl_tupletype_t*)cfunc_sig, NULL, 1, /*subtype*/0, /*offs*/0, /*world*/1); - if (sf != NULL) { + sf = jl_typemap_assoc_by_type(jl_cfunction_list, (jl_tupletype_t*)cfunc_sig, NULL, /*subtype*/0, /*offs*/0, /*world*/1); + if (sf) { jl_value_t *v = sf->func.value; - if (v != NULL) { + if (v) { if (jl_is_svec(v)) v = jl_svecref(v, 0); Function *f = (Function*)jl_unbox_voidpointer(v); diff --git a/src/gf.c b/src/gf.c index 3cdbb33b4c18b..42c4d60fe7c52 100644 --- a/src/gf.c +++ b/src/gf.c @@ -146,7 +146,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_specializations_get_linfo(jl_method_t *m, assert(world >= m->min_world && "typemap lookup is corrupted"); JL_LOCK(&m->writelock); jl_typemap_entry_t *sf = - jl_typemap_assoc_by_type(m->specializations, (jl_tupletype_t*)type, NULL, 1, /*subtype*/0, /*offs*/0, world); + jl_typemap_assoc_by_type(m->specializations, (jl_tupletype_t*)type, NULL, /*subtype*/0, /*offs*/0, world); if (sf && jl_is_method_instance(sf->func.value)) { jl_method_instance_t *linfo = (jl_method_instance_t*)sf->func.value; assert(linfo->min_world <= sf->min_world && linfo->max_world >= sf->max_world); @@ -180,7 +180,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_specializations_get_linfo(jl_method_t *m, JL_DLLEXPORT jl_value_t *jl_specializations_lookup(jl_method_t *m, jl_tupletype_t *type, size_t world) { jl_typemap_entry_t *sf = jl_typemap_assoc_by_type( - m->specializations, type, NULL, 1, /*subtype*/0, /*offs*/0, world); + m->specializations, type, NULL, /*subtype*/0, /*offs*/0, world); if (!sf) return jl_nothing; return sf->func.value; @@ -189,7 +189,7 @@ JL_DLLEXPORT jl_value_t *jl_specializations_lookup(jl_method_t *m, jl_tupletype_ JL_DLLEXPORT jl_value_t *jl_methtable_lookup(jl_methtable_t *mt, jl_tupletype_t *type, size_t world) { jl_typemap_entry_t *sf = jl_typemap_assoc_by_type( - mt->defs, type, NULL, 1, /*subtype*/0, /*offs*/0, world); + mt->defs, type, NULL, /*subtype*/0, /*offs*/0, world); if (!sf) return jl_nothing; return sf->func.value; @@ -523,11 +523,13 @@ jl_value_t *jl_nth_slot_type(jl_value_t *sig, size_t i) // after intersection, the argument tuple type needs to be corrected to reflect the signature match // that occurred, if the arguments contained a Type but the signature matched on the kind -static jl_tupletype_t *join_tsig(jl_tupletype_t *tt, jl_tupletype_t *sig) +// if sharp_match is returned as false, this tt may have matched only because of bug in subtyping +static jl_tupletype_t *join_tsig(jl_tupletype_t *tt, jl_tupletype_t *sig, int *sharp_match) { jl_svec_t *newparams = NULL; JL_GC_PUSH1(&newparams); size_t i, np; + *sharp_match = 1; for (i = 0, np = jl_nparams(tt); i < np; i++) { jl_value_t *elt = jl_tparam(tt, i); jl_value_t *newelt = NULL; @@ -554,6 +556,11 @@ static jl_tupletype_t *join_tsig(jl_tupletype_t *tt, jl_tupletype_t *sig) } } } + if (jl_is_kind(elt)) { + // check whether this match may be exact at runtime + if (!jl_subtype(elt, decl_i)) + *sharp_match = 0; + } // prepare to build a new type with the replacement above if (newelt) { if (!newparams) newparams = jl_svec_copy(tt->parameters); @@ -821,17 +828,17 @@ JL_DLLEXPORT int jl_is_cacheable_sig( return 1; } -static jl_method_instance_t *cache_method(jl_methtable_t *mt, union jl_typemap_t *cache, jl_value_t *parent, - jl_tupletype_t *type, // the specialized type signature for type lambda - jl_tupletype_t *tt, // the original tupletype of the signature - jl_typemap_entry_t *m, - size_t world, - jl_svec_t *sparams, - int allow_exec) +static jl_method_instance_t *cache_method( + jl_methtable_t *mt, union jl_typemap_t *cache, jl_value_t *parent, + jl_tupletype_t *type, // the specialized type signature for type lambda + jl_tupletype_t *tt, // the original tupletype of the signature + jl_method_t *definition, + size_t world, + jl_svec_t *sparams, + int allow_exec) { // caller must hold the mt->writelock - jl_method_t *definition = m->func.method; - jl_value_t *decl = (jl_value_t*)m->sig; + jl_value_t *decl = (jl_value_t*)definition->sig; jl_value_t *temp = NULL; jl_value_t *temp2 = NULL; jl_value_t *temp3 = NULL; @@ -891,7 +898,7 @@ static jl_method_instance_t *cache_method(jl_methtable_t *mt, union jl_typemap_t if (nsp > 0) { jl_svec_t *env = jl_alloc_svec_uninit(2 * nsp); temp2 = (jl_value_t*)env; - jl_unionall_t *ua = (jl_unionall_t*)m->sig; + jl_unionall_t *ua = (jl_unionall_t*)definition->sig; for (j = 0; j < nsp; j++) { assert(jl_is_unionall(ua)); jl_svecset(env, j * 2, ua->var); @@ -1029,40 +1036,36 @@ static jl_method_instance_t *cache_method(jl_methtable_t *mt, union jl_typemap_t return newmeth; } -static jl_method_instance_t *jl_mt_assoc_by_type(jl_methtable_t *mt, jl_datatype_t *tt, int cache, int inexact, int allow_exec, size_t world) +static jl_method_instance_t *jl_mt_assoc_by_type(jl_methtable_t *mt, jl_datatype_t *tt, int mt_cache, int allow_exec, size_t world) { // caller must hold the mt->writelock jl_typemap_entry_t *entry = NULL; jl_svec_t *env = jl_emptysvec; jl_method_t *func = NULL; jl_tupletype_t *sig = NULL; + jl_method_instance_t *nf = NULL; JL_GC_PUSH4(&env, &entry, &func, &sig); - entry = jl_typemap_assoc_by_type(mt->defs, tt, &env, inexact, 1, 0, world); - if (entry == NULL || entry == INEXACT_ENTRY) { - JL_GC_POP(); - return NULL; - } - - jl_method_t *m = entry->func.method; - if (jl_has_call_ambiguities(tt, m)) { - JL_GC_POP(); - return NULL; - } + entry = jl_typemap_assoc_by_type(mt->defs, tt, &env, /*subtype*/1, /*offs*/0, world); + if (entry != NULL) { + jl_method_t *m = entry->func.method; + if (!jl_has_call_ambiguities(tt, m)) { #ifdef TRACE_COMPILE - if (!jl_has_free_typevars((jl_value_t*)tt)) { - jl_printf(JL_STDERR, "precompile("); - jl_static_show(JL_STDERR, (jl_value_t*)tt); - jl_printf(JL_STDERR, ")\n"); - } + if (!jl_has_free_typevars((jl_value_t*)tt)) { + jl_printf(JL_STDERR, "precompile("); + jl_static_show(JL_STDERR, (jl_value_t*)tt); + jl_printf(JL_STDERR, ")\n"); + } #endif - sig = join_tsig(tt, entry->sig); - jl_method_instance_t *nf; - if (!cache) { - nf = jl_specializations_get_linfo(m, (jl_value_t*)sig, env, world); - } - else { - nf = cache_method(mt, &mt->cache, (jl_value_t*)mt, sig, tt, entry, world, env, allow_exec); + int sharp_match; + sig = join_tsig(tt, (jl_tupletype_t*)m->sig, &sharp_match); + if (!mt_cache) { + nf = jl_specializations_get_linfo(m, (jl_value_t*)sig, env, world); + } + else { + nf = cache_method(mt, &mt->cache, (jl_value_t*)mt, sig, tt, m, world, env, allow_exec); + } + } } JL_GC_POP(); return nf; @@ -1138,7 +1141,7 @@ static int check_ambiguous_visitor(jl_typemap_entry_t *oldentry, struct typemap_ // (if type-morespecific made a mistake, this also might end up finding // that isect == type or isect == sig and return the original match) jl_typemap_entry_t *l = jl_typemap_assoc_by_type( - map, (jl_tupletype_t*)isect, NULL, 0, 0, 0, + map, (jl_tupletype_t*)isect, NULL, /*subtype*/0, /*offs*/0, closure->newentry->min_world); if (l != NULL) // ok, intersection is covered return 1; @@ -1531,9 +1534,9 @@ jl_tupletype_t *arg_type_tuple(jl_value_t **args, size_t nargs) } jl_method_instance_t *jl_method_lookup_by_type(jl_methtable_t *mt, jl_tupletype_t *types, - int cache, int inexact, int allow_exec, size_t world) + int cache, int allow_exec, size_t world) { - jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(mt->cache, types, NULL, 0, 1, jl_cachearg_offset(mt), world); + jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(mt->cache, types, NULL, /*subtype*/1, jl_cachearg_offset(mt), world); if (entry) { jl_method_instance_t *linfo = (jl_method_instance_t*)entry->func.value; assert(linfo->min_world <= entry->min_world && linfo->max_world >= entry->max_world && @@ -1541,7 +1544,7 @@ jl_method_instance_t *jl_method_lookup_by_type(jl_methtable_t *mt, jl_tupletype_ return linfo; } JL_LOCK(&mt->writelock); - entry = jl_typemap_assoc_by_type(mt->cache, types, NULL, 0, 1, jl_cachearg_offset(mt), world); + entry = jl_typemap_assoc_by_type(mt->cache, types, NULL, /*subtype*/1, jl_cachearg_offset(mt), world); if (entry) { jl_method_instance_t *linfo = (jl_method_instance_t*)entry->func.value; assert(linfo->min_world <= entry->min_world && linfo->max_world >= entry->max_world && @@ -1551,7 +1554,7 @@ jl_method_instance_t *jl_method_lookup_by_type(jl_methtable_t *mt, jl_tupletype_ } if (jl_is_leaf_type((jl_value_t*)types)) cache = 1; - jl_method_instance_t *sf = jl_mt_assoc_by_type(mt, types, cache, inexact, allow_exec, world); + jl_method_instance_t *sf = jl_mt_assoc_by_type(mt, types, cache, allow_exec, world); if (cache) { JL_UNLOCK(&mt->writelock); } @@ -1565,7 +1568,7 @@ jl_method_instance_t *jl_method_lookup_by_type(jl_methtable_t *mt, jl_tupletype_ JL_DLLEXPORT int jl_method_exists(jl_methtable_t *mt, jl_tupletype_t *types, size_t world) { - return jl_method_lookup_by_type(mt, types, 0, 0, 1, world) != NULL; + return jl_method_lookup_by_type(mt, types, /*cache*/0, /*allow_exec*/1, world) != NULL; } jl_method_instance_t *jl_method_lookup(jl_methtable_t *mt, jl_value_t **args, size_t nargs, int cache, size_t world) @@ -1582,7 +1585,7 @@ jl_method_instance_t *jl_method_lookup(jl_methtable_t *mt, jl_value_t **args, si jl_tupletype_t *tt = arg_type_tuple(args, nargs); jl_method_instance_t *sf = NULL; JL_GC_PUSH2(&tt, &sf); - sf = jl_mt_assoc_by_type(mt, tt, cache, 0, 1, world); + sf = jl_mt_assoc_by_type(mt, tt, cache, 1, world); if (cache) { JL_UNLOCK(&mt->writelock); } @@ -1675,43 +1678,51 @@ jl_llvm_functions_t jl_compile_for_dispatch(jl_method_instance_t **pli, size_t w jl_method_instance_t *jl_get_specialization1(jl_tupletype_t *types, size_t world) { JL_TIMING(METHOD_LOOKUP_COMPILE); - if (!jl_is_leaf_type((jl_value_t*)types) || jl_has_free_typevars((jl_value_t*)types)) + if (/* TODO: !jl_is_cacheable_sig((jl_value_t*)types) &&*/ !jl_is_leaf_type((jl_value_t*)types)) + return NULL; + if (jl_has_free_typevars((jl_value_t*)types)) + return NULL; // don't poison the cache due to a malformed query + if (!jl_has_concrete_subtype((jl_value_t*)types)) return NULL; - jl_value_t *args = jl_unwrap_unionall((jl_value_t*)types); - assert(jl_is_datatype(args)); - - // make sure exactly 1 method matches (issue #7302). - int i; - for (i = 0; i < jl_nparams(args); i++) { - jl_value_t *ti = jl_tparam(args, i); - // if one argument type is DataType, multiple Type{} definitions - // might match. also be conservative with tuples rather than trying - // to analyze them in detail. - size_t min_valid = 0; - size_t max_valid = ~(size_t)0; - if (ti == (jl_value_t*)jl_datatype_type || jl_is_tuple_type(ti)) { - jl_value_t *matches = jl_matching_methods(types, 1, 0, world, &min_valid, &max_valid); - if (matches == jl_false) - return NULL; - break; - } - } - - jl_datatype_t *dt = jl_first_argument_datatype((jl_value_t*)types); - assert(dt != NULL); - jl_methtable_t *mt = dt->name->mt; - // most of the time sf is rooted in mt, but if the method is staged it may - // not be the case - // TODO: the above should be false, but better safe than sorry? - jl_method_instance_t *sf = jl_method_lookup_by_type(mt, types, 1, 1, 1, world); - assert(sf == NULL || (sf->min_world <= world && sf->max_world >= world)); - JL_GC_PUSH1(&sf); - if (sf != NULL && jl_has_call_ambiguities(types, sf->def.method)) { - sf = NULL; - } + // find if exactly 1 method matches (issue #7302) + size_t min_valid = 0; + size_t max_valid = ~(size_t)0; + jl_value_t *matches = jl_matching_methods(types, 1, 1, world, &min_valid, &max_valid); + if (matches == jl_false || jl_array_len(matches) != 1) + return NULL; + jl_tupletype_t *sig = NULL; + jl_svec_t *newparams = NULL; + JL_GC_PUSH3(&matches, &sig, &newparams); + jl_svec_t *match = (jl_svec_t*)jl_array_ptr_ref(matches, 0); + jl_method_t *m = (jl_method_t*)jl_svecref(match, 2); + jl_svec_t *env = (jl_svec_t*)jl_svecref(match, 1); + jl_tupletype_t *ti = (jl_tupletype_t*)jl_unwrap_unionall(jl_svecref(match, 0)); + jl_method_instance_t *nf = NULL; + if (ti == types && !jl_has_call_ambiguities(types, m)) { + jl_datatype_t *dt = jl_first_argument_datatype(jl_unwrap_unionall((jl_value_t*)types)); + assert(jl_is_datatype(dt)); + jl_methtable_t *mt = dt->name->mt; + int sharp_match; + sig = join_tsig(ti, (jl_tupletype_t*)m->sig, &sharp_match); + if (sharp_match) { + JL_LOCK(&mt->writelock); + nf = cache_method(mt, &mt->cache, (jl_value_t*)mt, sig, ti, m, world, env, /*allow_exec*/1); + JL_UNLOCK(&mt->writelock); + } + // // get the specialization without caching it + // int need_guard_entries = 0; + // int makesimplesig = 0; + // jl_cacheable_sig(sig, ti, (jl_tupletype_t*)m->sig, m, + // (jl_svec_t**)&newparams, &need_guard_entries, &makesimplesig); + // TODO: apply Varargs transform + // if (newparams) + // sig = jl_apply_tuple_type(newparams); + // nf = jl_specializations_get_linfo(m, (jl_value_t*)sig, env, world); + } + assert(nf == NULL || (nf->min_world <= world && nf->max_world >= world)); JL_GC_POP(); - return sf; + return nf; } JL_DLLEXPORT int jl_compile_hint(jl_tupletype_t *types) @@ -1894,7 +1905,7 @@ STATIC_INLINE jl_method_instance_t *jl_lookup_generic_(jl_value_t **args, uint32 JL_TIMING(METHOD_LOOKUP_SLOW); jl_tupletype_t *tt = arg_type_tuple(args, nargs); JL_GC_PUSH1(&tt); - mfunc = jl_mt_assoc_by_type(mt, tt, 1, 0, 1, world); + mfunc = jl_mt_assoc_by_type(mt, tt, /*cache*/1, /*allow_exec*/1, world); JL_GC_POP(); } JL_UNLOCK(&mt->writelock); @@ -1933,8 +1944,11 @@ JL_DLLEXPORT jl_value_t *jl_apply_generic(jl_value_t **args, uint32_t nargs) JL_DLLEXPORT jl_value_t *jl_gf_invoke_lookup(jl_datatype_t *types, size_t world) { jl_methtable_t *mt = ((jl_datatype_t*)jl_tparam0(types))->name->mt; - jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(mt->defs, types, /*don't record env*/NULL, - /*exact match*/0, /*subtype*/1, /*offs*/0, world); + jl_svec_t *env = jl_emptysvec; + JL_GC_PUSH1(&env); + jl_typemap_entry_t *entry = jl_typemap_assoc_by_type( + mt->defs, types, /*env*/&env, /*subtype*/1, /*offs*/0, world); + JL_GC_POP(); if (!entry) return jl_nothing; return (jl_value_t*)entry; @@ -1987,18 +2001,17 @@ jl_value_t *jl_gf_invoke(jl_tupletype_t *types0, jl_value_t **args, size_t nargs } else { tt = arg_type_tuple(args, nargs); - if (jl_is_unionall(entry->sig)) { - jl_value_t *ti = jl_type_intersection_env((jl_value_t*)tt, (jl_value_t*)entry->sig, &tpenv); - assert(ti != (jl_value_t*)jl_bottom_type); - (void)ti; + if (jl_is_unionall(method->sig)) { + int sub = jl_subtype_matching((jl_value_t*)tt, (jl_value_t*)method->sig, &tpenv); + assert(sub); (void)sub; } - sig = join_tsig(tt, entry->sig); - jl_method_t *func = entry->func.method; - if (func->invokes.unknown == NULL) - func->invokes.unknown = jl_nothing; + if (method->invokes.unknown == NULL) + method->invokes.unknown = jl_nothing; - mfunc = cache_method(mt, &func->invokes, entry->func.value, sig, tt, entry, world, tpenv, 1); + int sharp_match; + sig = join_tsig(tt, (jl_tupletype_t*)method->sig, &sharp_match); + mfunc = cache_method(mt, &method->invokes, entry->func.value, sig, tt, method, world, tpenv, 1); } JL_UNLOCK(&method->writelock); } @@ -2049,7 +2062,7 @@ JL_DLLEXPORT jl_value_t *jl_get_invoke_lambda(jl_methtable_t *mt, jl_method_t *method = entry->func.method; jl_typemap_entry_t *tm = NULL; if (method->invokes.unknown != NULL) { - tm = jl_typemap_assoc_by_type(method->invokes, tt, NULL, 0, 1, + tm = jl_typemap_assoc_by_type(method->invokes, tt, NULL, /*subtype*/1, jl_cachearg_offset(mt), world); if (tm) { return (jl_value_t*)tm->func.linfo; @@ -2058,7 +2071,7 @@ JL_DLLEXPORT jl_value_t *jl_get_invoke_lambda(jl_methtable_t *mt, JL_LOCK(&method->writelock); if (method->invokes.unknown != NULL) { - tm = jl_typemap_assoc_by_type(method->invokes, tt, NULL, 0, 1, + tm = jl_typemap_assoc_by_type(method->invokes, tt, NULL, /*subtype*/1, jl_cachearg_offset(mt), world); if (tm) { jl_method_instance_t *mfunc = tm->func.linfo; @@ -2075,14 +2088,14 @@ JL_DLLEXPORT jl_value_t *jl_get_invoke_lambda(jl_methtable_t *mt, assert(ti != (jl_value_t*)jl_bottom_type); (void)ti; } - sig = join_tsig(tt, entry->sig); - jl_method_t *func = entry->func.method; - if (func->invokes.unknown == NULL) - func->invokes.unknown = jl_nothing; + if (method->invokes.unknown == NULL) + method->invokes.unknown = jl_nothing; - jl_method_instance_t *mfunc = cache_method(mt, &func->invokes, entry->func.value, - sig, tt, entry, world, tpenv, 1); + int sharp_match; + sig = join_tsig(tt, (jl_tupletype_t*)method->sig, &sharp_match); + jl_method_instance_t *mfunc = cache_method(mt, &method->invokes, entry->func.value, + sig, tt, method, world, tpenv, 1); JL_GC_POP(); JL_UNLOCK(&method->writelock); return (jl_value_t*)mfunc; @@ -2142,62 +2155,6 @@ jl_function_t *jl_new_generic_function(jl_sym_t *name, jl_module_t *module) return jl_new_generic_function_with_supertype(name, module, jl_function_type, 0); } -JL_DLLEXPORT jl_svec_t *jl_match_method(jl_value_t *type, jl_value_t *sig) -{ - jl_svec_t *env = jl_emptysvec; - jl_value_t *ti=NULL; - JL_GC_PUSH2(&env, &ti); - ti = jl_type_intersection_env(type, (jl_value_t*)sig, &env); - jl_svec_t *result = jl_svec2(ti, env); - JL_GC_POP(); - return result; -} - -// Determine whether a typevar exists inside at most one DataType. -// These are the typevars that will always be matched by any matching -// arguments. -static int tvar_exists_at_top_level(jl_tvar_t *tv, jl_value_t *sig) -{ - sig = jl_unwrap_unionall(sig); - int i, l=jl_nparams(sig); - for(i=0; i < l; i++) { - jl_value_t *a = jl_tparam(sig, i); - a = jl_unwrap_unionall(a); - if (jl_is_vararg_type(a)) - return 0; - if (jl_is_type_type(a)) - a = jl_unwrap_unionall(jl_tparam0(a)); - if (a == (jl_value_t*)tv) - return 1; - if (jl_is_datatype(a)) { - jl_svec_t *p = ((jl_datatype_t*)a)->parameters; - int j; - for(j=0; j < jl_svec_len(p); j++) { - if (jl_svecref(p,j) == (jl_value_t*)tv) - return 1; - } - } - } - return 0; -} - -static int matched_all_tvars(jl_value_t *sig, jl_value_t **env, int envsz) -{ - size_t i; - jl_value_t *temp = sig; - for (i = 0; i < envsz; i++) { - assert(jl_is_unionall(temp)); - if (jl_is_typevar(env[i]) && - // if tvar is at the top level it will definitely be matched. - // see issue #5575 - !tvar_exists_at_top_level(((jl_unionall_t*)temp)->var, sig)) { - return 0; - } - temp = ((jl_unionall_t*)temp)->body; - } - return 1; -} - struct ml_matches_env { struct typemap_intersection_env match; // results: @@ -2261,26 +2218,12 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio } } if (!skip) { - /* - Check whether all static parameters matched. If not, then we - might have argument type `Tuple{}` and signature type `Tuple{Vararg{T}}`, - which doesn't match due to there being no value for `T`. - This is a remaining case of issue #4731 after introducing UnionAll. - */ - int done = 0, return_this_match = 1; - jl_svec_t *env = closure->match.env; - if (closure0->issubty) { - // if the queried type is a subtype, but not all tvars matched, then - // this method is excluded by the static-parameters-must-have-values rule - if (!matched_all_tvars((jl_value_t*)ml->sig, jl_svec_data(env), jl_svec_len(env))) - return_this_match = !jl_is_leaf_type(closure->match.type); - else - done = 1; // stop; signature fully covers queried type - } + int done = closure0->issubty; // stop; signature fully covers queried type // if we reach a definition that fully covers the arguments but there are // ambiguities, then this method might not actually match, so we shouldn't // add it to the results. - if (return_this_match && meth->ambig != jl_nothing && (!closure->include_ambiguous || done)) { + int return_this_match = 1; + if (meth->ambig != jl_nothing && (!closure->include_ambiguous || done)) { jl_svec_t *env = NULL; jl_value_t *mti = NULL; JL_GC_PUSH2(&env, &mti); @@ -2293,7 +2236,7 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio if (closure->include_ambiguous) { assert(done); int k; - for(k=0; k < len; k++) { + for (k = 0; k < len; k++) { if ((jl_value_t*)mambig == jl_svecref(jl_array_ptr_ref(closure->t, k), 2)) break; } @@ -2332,7 +2275,8 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio jl_array_ptr_1d_push((jl_array_t*)closure->t, (jl_value_t*)closure->matc); } } - if (done) return 0; + if (done) + return 0; } return 1; } @@ -2377,6 +2321,31 @@ static jl_value_t *ml_matches(union jl_typemap_t defs, int offs, return env.t; } +// see if it might be possible to construct an instance of `typ` +// if ninitialized == nfields, but a fieldtype is Union{}, +// that type will not be constructable, for example, tested recursively +int jl_has_concrete_subtype(jl_value_t *typ) +{ + if (typ == jl_bottom_type) + return 0; + typ = jl_unwrap_unionall(typ); + if (jl_is_vararg_type(typ)) + typ = jl_unwrap_vararg(typ); + if (!jl_is_datatype(typ)) + return 1; + jl_svec_t *fields = ((jl_datatype_t*)typ)->types; + size_t i, l = jl_svec_len(fields); + if (l != ((jl_datatype_t*)typ)->ninitialized) + if (((jl_datatype_t*)typ)->name != jl_tuple_typename) + return 1; + for (i = 0; i < l; i++) { + jl_value_t *ft = jl_svecref(fields, i); + if (!jl_has_concrete_subtype(ft)) + return 0; + } + return 1; +} + // TODO: separate the codegen and typeinf locks // currently using a coarser lock seems like // the best way to avoid acquisition priority diff --git a/src/interpreter.c b/src/interpreter.c index a0c67b2fe7e14..b995aa4cbe8ec 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -15,10 +15,11 @@ extern "C" { #endif typedef struct { - jl_code_info_t *src; - jl_module_t *module; - jl_value_t **locals; - jl_svec_t *sparam_vals; + jl_code_info_t *src; // contains the names and number of slots + jl_module_t *module; // context for globals + jl_value_t **locals; // slots for holding local slots and ssavalues + jl_svec_t *sparam_vals; // method static parameters, if eval-ing a method body + int preevaluation; // use special rules for pre-evaluating expressions } interpreter_state; static jl_value_t *eval(jl_value_t *e, interpreter_state *s); @@ -38,11 +39,11 @@ jl_value_t *jl_interpret_toplevel_expr_in(jl_module_t *m, jl_value_t *e, jl_value_t *v=NULL; jl_module_t *last_m = ptls->current_module; jl_module_t *task_last_m = ptls->current_task->current_module; - interpreter_state s; + interpreter_state s = {}; s.src = src; s.module = m; - s.locals = NULL; s.sparam_vals = sparam_vals; + s.preevaluation = (sparam_vals != NULL); JL_TRY { ptls->current_task->current_module = ptls->current_module = m; @@ -279,7 +280,10 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s) ssize_t n = jl_unbox_long(args[0]); assert(n > 0); if (s->sparam_vals && n <= jl_svec_len(s->sparam_vals)) { - return jl_svecref(s->sparam_vals, n - 1); + jl_value_t *sp = jl_svecref(s->sparam_vals, n - 1); + if (jl_is_typevar(sp) && !s->preevaluation) + jl_undefined_var_error(((jl_tvar_t*)sp)->name); + return sp; } // static parameter val unknown needs to be an error for ccall jl_error("could not determine static parameter value"); @@ -502,11 +506,8 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s) jl_value_t *jl_toplevel_eval_body(jl_module_t *m, jl_array_t *stmts) { size_t last_age = jl_get_ptls_states()->world_age; - interpreter_state s; - s.src = NULL; + interpreter_state s = {}; s.module = m; - s.locals = NULL; - s.sparam_vals = NULL; jl_value_t *ret = eval_body(stmts, &s, 0, 1); jl_get_ptls_states()->world_age = last_age; return ret; @@ -656,7 +657,7 @@ jl_value_t *jl_interpret_call(jl_method_instance_t *lam, jl_value_t **args, uint JL_GC_PUSHARGS(locals, jl_source_nslots(src) + jl_source_nssavalues(src) + 2); locals[0] = (jl_value_t*)src; locals[1] = (jl_value_t*)stmts; - interpreter_state s; + interpreter_state s = {}; s.src = src; s.module = lam->def.method->module; s.locals = locals + 2; @@ -679,7 +680,7 @@ jl_value_t *jl_interpret_toplevel_thunk(jl_module_t *m, jl_code_info_t *src) assert(jl_typeis(stmts, jl_array_any_type)); jl_value_t **locals; JL_GC_PUSHARGS(locals, jl_source_nslots(src) + jl_source_nssavalues(src)); - interpreter_state s; + interpreter_state s = {}; s.src = src; s.locals = locals; s.module = m; diff --git a/src/julia_internal.h b/src/julia_internal.h index 5d9ada3f02933..0970ae02fa611 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -459,12 +459,14 @@ int jl_tuple_isa(jl_value_t **child, size_t cl, jl_datatype_t *pdt); int jl_has_intersect_type_not_kind(jl_value_t *t); int jl_subtype_invariant(jl_value_t *a, jl_value_t *b, int ta); +int jl_has_concrete_subtype(jl_value_t *typ); jl_datatype_t *jl_inst_concrete_tupletype_v(jl_value_t **p, size_t np); jl_datatype_t *jl_inst_concrete_tupletype(jl_svec_t *p); JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method, jl_tupletype_t *simpletype); void jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_t fptr); jl_value_t *jl_type_intersection_env_s(jl_value_t *a, jl_value_t *b, jl_svec_t **penv, int *issubty); jl_value_t *jl_type_intersection_env(jl_value_t *a, jl_value_t *b, jl_svec_t **penv); +int jl_subtype_matching(jl_value_t *a, jl_value_t *b, jl_svec_t **penv); // specificity comparison assuming !(a <: b) and !(b <: a) JL_DLLEXPORT int jl_type_morespecific_no_subtype(jl_value_t *a, jl_value_t *b); jl_value_t *jl_instantiate_type_with(jl_value_t *t, jl_value_t **env, size_t n); @@ -481,7 +483,6 @@ void jl_assign_bits(void *dest, jl_value_t *bits); jl_expr_t *jl_exprn(jl_sym_t *head, size_t n); jl_function_t *jl_new_generic_function(jl_sym_t *name, jl_module_t *module); jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_t *module, jl_datatype_t *st, int iskw); -jl_function_t *jl_module_call_func(jl_module_t *m); int jl_is_submodule(jl_module_t *child, jl_module_t *parent); jl_value_t *jl_toplevel_eval_flex(jl_module_t *m, jl_value_t *e, int fast, int expanded); @@ -498,8 +499,6 @@ jl_value_t *jl_interpret_toplevel_expr_in(jl_module_t *m, jl_value_t *e, int jl_is_toplevel_only_expr(jl_value_t *e); jl_value_t *jl_call_scm_on_ast(const char *funcname, jl_value_t *expr, jl_module_t *inmodule); -jl_method_instance_t *jl_method_lookup_by_type(jl_methtable_t *mt, jl_tupletype_t *types, - int cache, int inexact, int allow_exec, size_t world); jl_method_instance_t *jl_method_lookup(jl_methtable_t *mt, jl_value_t **args, size_t nargs, int cache, size_t world); jl_value_t *jl_gf_invoke(jl_tupletype_t *types, jl_value_t **args, size_t nargs); jl_method_instance_t *jl_lookup_generic(jl_value_t **args, uint32_t nargs, uint32_t callsite, size_t world); @@ -909,9 +908,9 @@ jl_typemap_entry_t *jl_typemap_insert(union jl_typemap_t *cache, jl_value_t *par size_t min_world, size_t max_world, jl_value_t **overwritten); -jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_tupletype_t *types, jl_svec_t **penv, - int8_t inexact, int8_t subtype, int8_t offs, size_t world); -static jl_typemap_entry_t *const INEXACT_ENTRY = (jl_typemap_entry_t*)(uintptr_t)-1; +jl_typemap_entry_t *jl_typemap_assoc_by_type( + union jl_typemap_t ml_or_cache, jl_tupletype_t *types, jl_svec_t **penv, + int8_t subtype, int8_t offs, size_t world); jl_typemap_entry_t *jl_typemap_level_assoc_exact(jl_typemap_level_t *cache, jl_value_t **args, size_t n, int8_t offs, size_t world); jl_typemap_entry_t *jl_typemap_entry_assoc_exact(jl_typemap_entry_t *mn, jl_value_t **args, size_t n, size_t world); STATIC_INLINE jl_typemap_entry_t *jl_typemap_assoc_exact(union jl_typemap_t ml_or_cache, jl_value_t **args, size_t n, int8_t offs, size_t world) diff --git a/src/method.c b/src/method.c index 62e5423684418..6f43a7b95d499 100644 --- a/src/method.c +++ b/src/method.c @@ -663,6 +663,7 @@ JL_DLLEXPORT jl_value_t *jl_argument_datatype(jl_value_t *argt) } extern tracer_cb jl_newmeth_tracer; + JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, jl_code_info_t *f, jl_module_t *module, @@ -681,9 +682,11 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, jl_methtable_t *mt; jl_sym_t *name; jl_method_t *m = NULL; + jl_value_t *argtype = NULL; + JL_GC_PUSH3(&f, &m, &argtype); size_t i, na = jl_svec_len(atypes); int32_t nospec = 0; - for (i=1; i < na; i++) { + for (i = 1; i < na; i++) { jl_value_t *ti = jl_svecref(atypes, i); if (ti == jl_ANY_flag || (jl_is_vararg_type(ti) && jl_tparam0(jl_unwrap_unionall(ti)) == jl_ANY_flag)) { @@ -694,17 +697,15 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, jl_svecset(atypes, i, jl_substitute_var(ti, (jl_tvar_t*)jl_ANY_flag, (jl_value_t*)jl_any_type)); } } - jl_value_t *argtype = (jl_value_t*)jl_apply_tuple_type(atypes); - JL_GC_PUSH3(&f, &m, &argtype); - if (!jl_is_code_info(f)) { - // this occurs when there is a closure being added to an out-of-scope function - // the user should only do this at the toplevel - // the result is that the closure variables get interpolated directly into the AST - f = jl_new_code_info_from_ast((jl_expr_t*)f); + argtype = (jl_value_t*)jl_apply_tuple_type(atypes); + for (i = jl_svec_len(tvars); i > 0; i--) { + jl_value_t *tv = jl_svecref(tvars, i - 1); + if (!jl_is_typevar(tv)) + jl_type_error_rt("method definition", "type parameter", (jl_value_t*)jl_tvar_type, tv); + argtype = jl_new_struct(jl_unionall_type, tv, argtype); } - assert(jl_is_code_info(f)); jl_datatype_t *ftype = jl_first_argument_datatype(argtype); if (ftype == NULL || !(jl_is_type_type((jl_value_t*)ftype) || @@ -712,20 +713,17 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, (!ftype->abstract || jl_is_leaf_type((jl_value_t*)ftype)) && ftype->name->mt != NULL))) jl_error("cannot add methods to an abstract type"); - mt = ftype->name->mt; - name = mt->name; - if (jl_subtype((jl_value_t*)ftype, (jl_value_t*)jl_builtin_type)) jl_error("cannot add methods to a builtin function"); - int j; - for (j = (int)jl_svec_len(tvars) - 1; j >= 0 ; j--) { - jl_value_t *tv = jl_svecref(tvars,j); - if (!jl_is_typevar(tv)) - jl_type_error_rt(jl_symbol_name(name), "method definition", (jl_value_t*)jl_tvar_type, tv); - argtype = jl_new_struct(jl_unionall_type, tv, argtype); + mt = ftype->name->mt; + name = mt->name; + if (!jl_is_code_info(f)) { + // this occurs when there is a closure being added to an out-of-scope function + // the user should only do this at the toplevel + // the result is that the closure variables get interpolated directly into the AST + f = jl_new_code_info_from_ast((jl_expr_t*)f); } - m = jl_new_method(f, name, module, (jl_tupletype_t*)argtype, nargs, isva, tvars, isstaged == jl_true); m->nospecialize |= nospec; @@ -737,8 +735,6 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, m->line); } - jl_check_static_parameter_conflicts(m, f, tvars); - for (i = 0; i < na; i++) { jl_value_t *elt = jl_svecref(atypes, i); if (!jl_is_type(elt) && !jl_is_typevar(elt)) { @@ -767,18 +763,21 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, } int ishidden = !!strchr(jl_symbol_name(name), '#'); - jl_value_t *atemp = argtype; - while (jl_is_unionall(atemp)) { - jl_unionall_t *ua = (jl_unionall_t*)atemp; - jl_tvar_t *tv = ua->var; - if (!ishidden && !jl_has_typevar(ua->body, tv)) { - jl_printf(JL_STDERR, "WARNING: static parameter %s does not occur in signature for %s", - jl_symbol_name(tv->name), jl_symbol_name(name)); - print_func_loc(JL_STDERR, m); - jl_printf(JL_STDERR, ".\nThe method will not be callable.\n"); + if (!ishidden) { + jl_value_t *atemp = argtype; + while (jl_is_unionall(atemp)) { + jl_unionall_t *ua = (jl_unionall_t*)atemp; + jl_tvar_t *tv = ua->var; + if (!jl_has_typevar(ua->body, tv)) { + jl_printf(JL_STDERR, "WARNING: static parameter %s does not occur in signature for %s", + jl_symbol_name(tv->name), jl_symbol_name(name)); + print_func_loc(JL_STDERR, m); + jl_printf(JL_STDERR, ".\n"); + } + atemp = ua->body; } - atemp = ua->body; } + jl_check_static_parameter_conflicts(m, f, tvars); jl_method_table_insert(mt, m, NULL); if (jl_newmeth_tracer) diff --git a/src/precompile.c b/src/precompile.c index 9843b3bedbdbe..9830bb41744b5 100644 --- a/src/precompile.c +++ b/src/precompile.c @@ -89,28 +89,6 @@ void jl_write_compiler_output(void) JL_GC_POP(); } -static int any_bottom_field(jl_value_t *typ) -{ - if (typ == jl_bottom_type) - return 1; - typ = jl_unwrap_unionall(typ); - if (jl_is_vararg_type(typ)) - typ = jl_unwrap_vararg(typ); - if (!jl_is_datatype(typ)) - return 0; - jl_svec_t *fields = ((jl_datatype_t*)typ)->types; - size_t i, l = jl_svec_len(fields); - if (l != ((jl_datatype_t*)typ)->ninitialized) - if (((jl_datatype_t*)typ)->name != jl_tuple_typename) - return 0; - for (i = 0; i < l; i++) { - jl_value_t *ft = jl_svecref(fields, i); - if (any_bottom_field(ft)) - return 1; - } - return 0; -} - // f{<:Union{...}}(...) is a common pattern // and expanding the Union may give a leaf function static void _compile_all_tvar_union(jl_value_t *methsig) @@ -146,7 +124,7 @@ static void _compile_all_tvar_union(jl_value_t *methsig) JL_CATCH { goto getnext; // sigh, we found an invalid type signature. should we warn the user? } - if (any_bottom_field(sig)) + if (!jl_has_concrete_subtype(sig)) goto getnext; // signature wouldn't be callable / is invalid -- skip it if (jl_is_leaf_type(sig)) { if (jl_compile_hint((jl_tupletype_t*)sig)) diff --git a/src/subtype.c b/src/subtype.c index a379b374c64c3..78a39b6ca481d 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2178,16 +2178,39 @@ JL_DLLEXPORT jl_value_t *jl_type_intersection(jl_value_t *a, jl_value_t *b) return jl_type_intersection_env(a, b, NULL); } -JL_DLLEXPORT jl_svec_t *jl_env_from_type_intersection(jl_value_t *a, jl_value_t *b) +JL_DLLEXPORT jl_svec_t *jl_type_intersection_with_env(jl_value_t *a, jl_value_t *b) { jl_svec_t *env = jl_emptysvec; - JL_GC_PUSH1(&env); - jl_value_t *ti = jl_type_intersection_env(a, b, &env); + jl_value_t *ti = NULL; + JL_GC_PUSH2(&env, &ti); + ti = jl_type_intersection_env(a, b, &env); jl_svec_t *pair = jl_svec2(ti, env); JL_GC_POP(); return pair; } +int jl_subtype_matching(jl_value_t *a, jl_value_t *b, jl_svec_t **penv) +{ + int szb = penv ? jl_subtype_env_size(b) : 0; + if (szb == 0) + return jl_subtype_env(a, b, NULL, szb); + + jl_value_t **env; + JL_GC_PUSHARGS(env, szb); + int sub = jl_subtype_env(a, b, env, szb); + if (sub) { + // copy env to svec for return + int i = 0; + jl_svec_t *e = jl_alloc_svec(szb); + *penv = e; + for (i = 0; i < szb; i++) + jl_svecset(e, i, env[i]); + } + JL_GC_POP(); + return sub; +} + + // specificity comparison static int eq_msp(jl_value_t *a, jl_value_t *b, jl_typeenv_t *env) @@ -2573,8 +2596,10 @@ JL_DLLEXPORT int jl_type_morespecific(jl_value_t *a, jl_value_t *b) { if (obviously_disjoint(a, b, 1)) return 0; - if (jl_subtype(b, a)) return 0; - if (jl_subtype(a, b)) return 1; + if (jl_subtype(b, a)) + return 0; + if (jl_subtype(a, b)) + return 1; return type_morespecific_(a, b, 0, NULL); } diff --git a/src/typemap.c b/src/typemap.c index 9d612538390d4..f86c1c59aede8 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -550,6 +550,7 @@ int jl_typemap_intersection_visitor(union jl_typemap_t map, int offs, } } + /* Method caches are divided into three parts: one for signatures where the first argument is a singleton kind (Type{Foo}), one indexed by the @@ -561,7 +562,7 @@ int jl_typemap_intersection_visitor(union jl_typemap_t map, int offs, (the function) is always the same for most functions. */ static jl_typemap_entry_t *jl_typemap_assoc_by_type_(jl_typemap_entry_t *ml, jl_tupletype_t *types, - int8_t inexact, jl_svec_t **penv, size_t world) + jl_svec_t **penv, size_t world) { size_t n = jl_field_count(types); int typesisva = n == 0 ? 0 : jl_is_vararg_type(jl_tparam(types, n-1)); @@ -589,51 +590,16 @@ static jl_typemap_entry_t *jl_typemap_assoc_by_type_(jl_typemap_entry_t *ml, jl_ else if (ml->issimplesig && !typesisva) ismatch = sig_match_by_type_simple(jl_svec_data(types->parameters), n, ml->sig, lensig, ml->va); - else if (!jl_is_unionall(ml->sig) || penv == NULL) - ismatch = jl_subtype((jl_value_t*)types, (jl_value_t*)ml->sig); else { - // TODO: this is missing the actual subtype test, - // which works currently because types is typically a leaf tt, - // or inexact is set (which then does a sort of subtype test via jl_types_equal) - // but this isn't entirely general - jl_value_t *ti = jl_type_intersection_env((jl_value_t*)types, (jl_value_t*)ml->sig, penv); - resetenv = 1; - ismatch = (ti != (jl_value_t*)jl_bottom_type); - if (ismatch) { - // parametric methods only match if all typevars are matched by - // non-typevars. - size_t i, l; - for (i = 0, l = jl_svec_len(*penv); i < l; i++) { - if (jl_is_typevar(jl_svecref(*penv, i))) { - if (inexact) { - // "inexact" means the given type is compile-time, - // where a failure to determine the value of a - // static parameter is inconclusive. - // this is issue #3182, see test/core.jl - return INEXACT_ENTRY; - } - ismatch = 0; - break; - } - } - if (inexact) { - // the compiler might attempt jl_get_specialization on e.g. - // convert(::Type{Type{Int}}, ::DataType), which is concrete but might not - // equal the run time type. in this case ti would be {Type{Type{Int}}, Type{Int}} - // but tt would be {Type{Type{Int}}, DataType}. - JL_GC_PUSH1(&ti); - ismatch = jl_types_equal(ti, (jl_value_t*)types); - JL_GC_POP(); - if (!ismatch) - return INEXACT_ENTRY; - } - } + ismatch = jl_subtype_matching((jl_value_t*)types, (jl_value_t*)ml->sig, penv); + if (ismatch && penv) + resetenv = 1; } if (ismatch) { size_t i, l; for (i = 0, l = jl_svec_len(ml->guardsigs); i < l; i++) { - // see corresponding code in jl_typemap_assoc_exact + // see corresponding code in jl_typemap_entry_assoc_exact if (jl_subtype((jl_value_t*)types, jl_svecref(ml->guardsigs, i))) { ismatch = 0; break; @@ -683,9 +649,9 @@ static jl_typemap_entry_t *jl_typemap_lookup_by_type_(jl_typemap_entry_t *ml, jl // this is the general entry point for looking up a type in the cache -// (as a subtype, or with typeseq) +// as a subtype, or with type_equal jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_tupletype_t *types, jl_svec_t **penv, - int8_t inexact, int8_t subtype, int8_t offs, size_t world) + int8_t subtype, int8_t offs, size_t world) { if (jl_typeof(ml_or_cache.unknown) == (jl_value_t*)jl_typemap_level_type) { jl_typemap_level_t *cache = ml_or_cache.node; @@ -712,7 +678,7 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_ // If there is a type at offs, look in the optimized caches if (!subtype) { if (ty && jl_is_any(ty)) - return jl_typemap_assoc_by_type(cache->any, types, penv, inexact, subtype, offs+1, world); + return jl_typemap_assoc_by_type(cache->any, types, penv, subtype, offs + 1, world); if (isva) // in lookup mode, want to match Vararg exactly, not as a subtype ty = NULL; } @@ -723,7 +689,7 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_ union jl_typemap_t ml = mtcache_hash_lookup(&cache->targ, a0, 1, offs); if (ml.unknown != jl_nothing) { jl_typemap_entry_t *li = - jl_typemap_assoc_by_type(ml, types, penv, inexact, subtype, offs+1, world); + jl_typemap_assoc_by_type(ml, types, penv, subtype, offs + 1, world); if (li) return li; } } @@ -733,7 +699,7 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_ union jl_typemap_t ml = mtcache_hash_lookup(&cache->arg1, ty, 0, offs); if (ml.unknown != jl_nothing) { jl_typemap_entry_t *li = - jl_typemap_assoc_by_type(ml, types, penv, inexact, subtype, offs+1, world); + jl_typemap_assoc_by_type(ml, types, penv, subtype, offs + 1, world); if (li) return li; } } @@ -741,9 +707,9 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_ } // Always check the list (since offs doesn't always start at 0) if (subtype) { - jl_typemap_entry_t *li = jl_typemap_assoc_by_type_(cache->linear, types, inexact, penv, world); + jl_typemap_entry_t *li = jl_typemap_assoc_by_type_(cache->linear, types, penv, world); if (li) return li; - return jl_typemap_assoc_by_type(cache->any, types, penv, inexact, subtype, offs+1, world); + return jl_typemap_assoc_by_type(cache->any, types, penv, subtype, offs + 1, world); } else { return jl_typemap_lookup_by_type_(cache->linear, types, world); @@ -751,7 +717,7 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_ } else { return subtype ? - jl_typemap_assoc_by_type_(ml_or_cache.leaf, types, inexact, penv, world) : + jl_typemap_assoc_by_type_(ml_or_cache.leaf, types, penv, world) : jl_typemap_lookup_by_type_(ml_or_cache.leaf, types, world); } } @@ -1011,7 +977,7 @@ jl_typemap_entry_t *jl_typemap_insert(union jl_typemap_t *cache, jl_value_t *par jl_value_t *ttype = jl_unwrap_unionall((jl_value_t*)type); if ((jl_value_t*)simpletype == jl_nothing) { - jl_typemap_entry_t *ml = jl_typemap_assoc_by_type(*cache, type, NULL, 1, 0, offs, min_world); + jl_typemap_entry_t *ml = jl_typemap_assoc_by_type(*cache, type, NULL, 0, offs, min_world); if (ml && ml->simplesig == (void*)jl_nothing) { if (newvalue == ml->func.value) // no change. TODO: involve world in computation! return ml; diff --git a/test/ambiguous.jl b/test/ambiguous.jl index 3dd0c85951d59..9c1b8d71b148c 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -245,4 +245,34 @@ end @test length(detect_ambiguities(Ambig9, ambiguous_bottom=true)) == 1 @test length(detect_ambiguities(Ambig9)) == 0 +# Test that Core and Base are free of UndefVarErrors +# not using isempty so this prints more information when it fails +@testset "detect_unbound_args in Base and Core" begin + # TODO: review this list and remove everything between test_broken and test + let need_to_handle_undef_sparam = + Set{Method}(detect_unbound_args(Core; recursive=true)) + pop!(need_to_handle_undef_sparam, which(Core.Inference.eltype, Tuple{Type{Tuple{Vararg{E}}} where E})) + pop!(need_to_handle_undef_sparam, which(Core.Inference.eltype, Tuple{Type{Tuple{Any}}})) + @test_broken need_to_handle_undef_sparam == Set() + pop!(need_to_handle_undef_sparam, which(Core.Inference.cat, Tuple{Any, AbstractArray})) + @test need_to_handle_undef_sparam == Set() + end + let need_to_handle_undef_sparam = + Set{Method}(detect_unbound_args(Base; recursive=true)) + pop!(need_to_handle_undef_sparam, which(Base._totuple, (Type{Tuple{Vararg{E}}} where E, Any, Any))) + pop!(need_to_handle_undef_sparam, which(Base.eltype, Tuple{Type{Tuple{Vararg{E}}} where E})) + pop!(need_to_handle_undef_sparam, which(Base.eltype, Tuple{Type{Tuple{Any}}})) + @test_broken need_to_handle_undef_sparam == Set() + pop!(need_to_handle_undef_sparam, which(Base.cat, Tuple{Any, AbstractArray})) + pop!(need_to_handle_undef_sparam, which(Base.byteenv, (Union{AbstractArray{Pair{T}, 1}, Tuple{Vararg{Pair{T}}}} where T<:AbstractString,))) + pop!(need_to_handle_undef_sparam, which(Base.LinAlg.promote_leaf_eltypes, (Union{AbstractArray{T}, Tuple{Vararg{T}}} where T<:Number,))) + pop!(need_to_handle_undef_sparam, which(Base.LinAlg.promote_leaf_eltypes, + (Union{AbstractArray{T}, Tuple{Vararg{T}}} where T<:(AbstractArray{<:Number}),))) + pop!(need_to_handle_undef_sparam, which(Base.SparseArrays._absspvec_vcat, (AbstractSparseArray{Tv, Ti, 1} where {Tv, Ti},))) + pop!(need_to_handle_undef_sparam, which(Base.SparseArrays._absspvec_hcat, (AbstractSparseArray{Tv, Ti, 1} where {Tv, Ti},))) + pop!(need_to_handle_undef_sparam, which(Base.cat, (Any, Base.SparseArrays._TypedDenseConcatGroup{T} where T))) + @test need_to_handle_undef_sparam == Set() + end +end + nothing # don't return a module from the remote include diff --git a/test/core.jl b/test/core.jl index 463deadccebce..e760be057451d 100644 --- a/test/core.jl +++ b/test/core.jl @@ -124,21 +124,6 @@ end @test promote_type(Bool,Bottom) === Bool -# ntuples -nttest1(x::NTuple{n,Int}) where {n} = n -@test nttest1(()) == 0 -@test nttest1((1,2)) == 2 -@test NTuple <: Tuple -@test (NTuple{T,Int32} where T) <: Tuple{Vararg{Int32}} -@test !((NTuple{T,Int32} where T) <: Tuple{Int32,Vararg{Int32}}) -@test Tuple{Vararg{Int32}} <: (NTuple{T,Int32} where T) -@test Tuple{Int32,Vararg{Int32}} <: (NTuple{T,Int32} where T) - -# #17198 -@test_throws MethodError convert(Tuple{Int}, (1.0, 2.0, 3.0)) -# #21238 -@test_throws MethodError convert(Tuple{Int,Int,Int}, (1, 2)) - # type declarations abstract type Sup_{A,B} end @@ -1466,8 +1451,8 @@ end import Base: promote_rule promote_rule(A::Type{SIQ{T,T2}},B::Type{SIQ{S,S2}}) where {T,T2,S,S2} = SIQ{promote_type(T,S)} @test_throws ErrorException promote_type(SIQ{Int},SIQ{Float64}) -f4731(x::T...) where {T} = 0 -f4731(x...) = "" +f4731(x::T...) where {T} = "" +f4731(x...) = 0 g4731() = f4731() @test f4731() == "" @test g4731() == "" @@ -3464,9 +3449,13 @@ end @test_throws MethodError @eval @m8846(a,b,c) # a simple case of parametric dispatch with unions -let foo(x::Union{T,Void},y::Union{T,Void}) where {T} = 1 +let foo(x::Union{T, Void}, y::Union{T, Void}) where {T} = 1 @test foo(1, nothing) === 1 - @test_throws MethodError foo(nothing, nothing) # can't determine T + @test foo(nothing, nothing) === 1 +end +let foo(x::Union{T, Void}, y::Union{T, Void}) where {T} = T + @test foo(1, nothing) === Int + @test_throws UndefVarError(:T) foo(nothing, nothing) end module TestMacroGlobalFunction @@ -4556,8 +4545,15 @@ gc_enable(true) # issue #18710 bad_tvars() where {T} = 1 -@test_throws ErrorException @which(bad_tvars()) -@test_throws MethodError bad_tvars() +@test isa(@which(bad_tvars()), Method) +@test bad_tvars() === 1 +bad_tvars2() where {T} = T +@test_throws UndefVarError(:T) bad_tvars2() +missing_tvar(::T...) where {T} = T +@test_throws UndefVarError(:T) missing_tvar() +@test missing_tvar(1) === Int +@test missing_tvar(1, 2, 3) === Int +@test_throws MethodError missing_tvar(1, 2, "3") # issue #19059 - test for lowering of `let` with assignment not adding Box in simple cases contains_Box(e::GlobalRef) = (e.name === :Box) @@ -5098,7 +5094,7 @@ f_isdefined_cl_6() = (local x; () -> @isdefined x) f_isdefined_tv(::T) where {T} = @isdefined T @test f_isdefined_tv(1) f_isdefined_va(::T...) where {T} = @isdefined T -@test_throws MethodError f_isdefined_va() +@test !f_isdefined_va() @test f_isdefined_va(1, 2, 3) mutable struct MyStruct22929 diff --git a/test/inference.jl b/test/inference.jl index 19b446af34335..0d6458dba76b6 100644 --- a/test/inference.jl +++ b/test/inference.jl @@ -293,11 +293,17 @@ let g() = Int <: Real ? 1 : "" end const NInt{N} = Tuple{Vararg{Int, N}} +const NInt1{N} = Tuple{Int, Vararg{Int, N}} @test Base.eltype(NInt) === Int -@test Base.return_types(eltype, (NInt,)) == Any[Union{Type{Int}, Type{Union{}}}] # issue 21763 +@test Base.eltype(NInt1) === Int +@test Base.eltype(NInt{0}) === Union{} +@test Base.eltype(NInt{1}) === Int +@test Base.eltype(NInt1{0}) === Int +@test Base.eltype(NInt1{1}) === Int fNInt(x::NInt) = (x...) gNInt() = fNInt(x) @test Base.return_types(gNInt, ()) == Any[NInt] +@test Base.return_types(eltype, (NInt,)) == Any[Union{Type{Int}, Type{Union{}}}] # issue 21763 # issue #17572 function f17572(::Type{Val{A}}) where A @@ -368,7 +374,7 @@ end f18222(::Union{T, Int}) where {T<:AbstractFloat} = false f18222(x) = true g18222(x) = f18222(x) -@test f18222(1) == g18222(1) == true +@test f18222(1) == g18222(1) == false @test f18222(1.0) == g18222(1.0) == false # issue #18399 @@ -976,7 +982,7 @@ function get_linfo(@nospecialize(f), @nospecialize(t)) ft = isa(f, Type) ? Type{f} : typeof(f) tt = Tuple{ft, t.parameters...} precompile(tt) - (ti, env) = ccall(:jl_match_method, Ref{SimpleVector}, (Any, Any), tt, meth.sig) + (ti, env) = ccall(:jl_type_intersection_with_env, Ref{SimpleVector}, (Any, Any), tt, meth.sig) meth = Base.func_for_method_checked(meth, tt) return ccall(:jl_specializations_get_linfo, Ref{Core.MethodInstance}, (Any, Any, Any, UInt), meth, tt, env, world) diff --git a/test/reflection.jl b/test/reflection.jl index 8d4f0ef9f49d8..10e2a17f9aafe 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -348,7 +348,8 @@ for (f, t) in Any[(definitely_not_in_sysimg, Tuple{}), (Base.:+, Tuple{Int, Int})] meth = which(f, t) tt = Tuple{typeof(f), t.parameters...} - env = (ccall(:jl_match_method, Any, (Any, Any), tt, meth.sig))[2] + (ti, env) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), tt, meth.sig)::SimpleVector + @test ti === tt # intersection should be a subtype world = typemax(UInt) linfo = ccall(:jl_specializations_get_linfo, Ref{Core.MethodInstance}, (Any, Any, Any, UInt), meth, tt, env, world) params = Base.CodegenParams() diff --git a/test/subtype.jl b/test/subtype.jl index c2e72f1c68912..458843661792e 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -14,7 +14,7 @@ notequal_type(@nospecialize(x),@nospecialize(y)) = !isequal_type(x, y) _type_intersect(@nospecialize(x), @nospecialize(y)) = ccall(:jl_intersect_types, Any, (Any, Any), x, y) -intersection_env(@nospecialize(x), @nospecialize(y)) = ccall(:jl_env_from_type_intersection, Any, (Any,Any), x, y) +intersection_env(@nospecialize(x), @nospecialize(y)) = ccall(:jl_type_intersection_with_env, Any, (Any,Any), x, y) # level 1: no varags, union, UnionAll function test_1() diff --git a/test/tuple.jl b/test/tuple.jl index f6e5f7c789eb9..435454321a03a 100644 --- a/test/tuple.jl +++ b/test/tuple.jl @@ -9,8 +9,59 @@ struct BitPerm_19352 BitPerm_19352(xs::Vararg{Any,8}) = BitPerm(map(UInt8, xs)) end +# ntuples +nttest1(x::NTuple{n, Int}) where {n} = n +@test nttest1(()) == 0 +@test nttest1((1, 2)) == 2 +@test NTuple <: Tuple +@test (NTuple{T, Int32} where T) <: Tuple{Vararg{Int32}} +@test !((NTuple{T, Int32} where T) <: Tuple{Int32, Vararg{Int32}}) +@test Tuple{Vararg{Int32}} <: (NTuple{T, Int32} where T) +@test Tuple{Int32, Vararg{Int32}} <: (NTuple{T, Int32} where T) + +# #17198 +@test_throws BoundsError convert(Tuple{Int}, (1.0, 2.0, 3.0)) +# #21238 +@test_throws MethodError convert(Tuple{Int, Int, Int}, (1, 2)) + @testset "conversion and construction" begin - @test convert(Tuple, (1,2)) == (1,2) + @test convert(Tuple, ()) === () + @test convert(Tuple, (1, 2)) === (1, 2) + @test convert(Tuple, (1.0, 2)) === (1.0, 2) + + @test convert(NTuple, ()) === () + @test convert(Tuple{}, ()) === () + @test convert(Tuple{Vararg{Int}}, ()) === () + @test convert(Tuple{Vararg{T}} where T<:Integer, ()) === () + + @test convert(NTuple{3, Int}, (1, 2, 3)) === (1, 2, 3) + @test convert(NTuple, (1, 2, 3)) === (1, 2, 3) + @test convert(Tuple{Vararg{Int}}, (1, 2, 3)) === (1, 2, 3) + @test convert(Tuple{Int, Vararg{Int}}, (1, 2, 3)) === (1, 2, 3) + @test convert(Tuple{Vararg{T}} where T<:Integer, (1, 2, 3)) === (1, 2, 3) + @test convert(Tuple{T, Vararg{T}} where T<:Integer, (1, 2, 3)) === (1, 2, 3) + @test convert(Tuple{Int, Int, Float64}, (1, 2, 3)) === (1, 2, 3.0) + + @test convert(Tuple{Float64, Int, UInt8}, (1.0, 2, 0x3)) === (1.0, 2, 0x3) + @test convert(NTuple, (1.0, 2, 0x3)) === (1.0, 2, 0x3) + @test convert(Tuple{Vararg{Int}}, (1.0, 2, 0x3)) === (1, 2, 3) + @test convert(Tuple{Int, Vararg{Int}}, (1.0, 2, 0x3)) === (1, 2, 3) + @test convert(Tuple{Vararg{T}} where T<:Integer, (1.0, 2, 0x3)) === (1, 2, 0x3) + @test convert(Tuple{T, Vararg{T}} where T<:Integer, (1.0, 2, 0x3)) === (1, 2, 0x3) + @test convert(NTuple{3, Int}, (1.0, 2, 0x3)) === (1, 2, 3) + @test convert(Tuple{Int, Int, Float64}, (1.0, 2, 0x3)) === (1, 2, 3.0) + + # TODO: seems like these all should throw BoundsError? + @test_throws MethodError convert(Tuple{Int}, ()) + @test_throws MethodError convert(Tuple{Int, Vararg{Int}}, ()) + @test_throws BoundsError convert(Tuple{}, (1, 2, 3)) + @test_throws BoundsError convert(Tuple{}, (1.0, 2, 3)) + @test_throws MethodError convert(NTuple{3, Int}, ()) + @test_throws MethodError convert(NTuple{3, Int}, (1, 2)) + @test_throws BoundsError convert(NTuple{3, Int}, (1, 2, 3, 4)) + @test_throws MethodError convert(Tuple{Int, Int, Float64}, ()) + @test_throws MethodError convert(Tuple{Int, Int, Float64}, (1, 2)) + @test_throws BoundsError convert(Tuple{Int, Int, Float64}, (1, 2, 3, 4)) # PR #15516 @test Tuple{Char,Char}("za") === ('z','a') @@ -295,3 +346,12 @@ end # https://github.com/JuliaLang/julia/issues/21026#issuecomment-317113307 const VecTuple21026{T} = Tuple{VecElement{T}} @test convert(VecTuple21026, (1,)) === (VecElement(1),) + +@test convert(Tuple{Complex{T}, Complex{T}} where T<:Real, (1, 2)) === + (Complex(1), Complex(2)) +@test convert(Tuple{Complex{T}, Complex{T}} where T<:Real, (1, 2.0)) === + (Complex(1), Complex(2.0)) +@test convert(Tuple{Complex, Complex}, (1, 2)) === + (Complex(1), Complex(2)) +@test convert(Tuple{Complex, Complex}, (1, 2.0)) === + (Complex(1), Complex(2.0))