From 3217db65c055440f4a8c666bc7478167f700021e Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 11 Jan 2017 21:59:00 -0500 Subject: [PATCH 01/12] allow running tests in any random subdirectory, not just linalg --- test/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Makefile b/test/Makefile index 50cf2c51219f0..2e2ca44796304 100644 --- a/test/Makefile +++ b/test/Makefile @@ -4,7 +4,8 @@ BUILDDIR := . include $(JULIAHOME)/Make.inc # TODO: this Makefile ignores BUILDDIR, except for computing JULIA_EXECUTABLE -TESTS = all linalg sparse unicode strings dates $(filter-out TestHelpers runtests testdefs,$(patsubst $(SRCDIR)/%.jl,%,$(wildcard $(SRCDIR)/*.jl $(SRCDIR)/linalg/*.jl))) +TESTS = all linalg sparse unicode strings dates \ + $(filter-out TestHelpers runtests testdefs,$(patsubst $(SRCDIR)/%.jl,%,$(wildcard $(SRCDIR)/*.jl $(SRCDIR)/*/*.jl))) default: all From a73af2147f2023e59b5261dd4cd9996f6e2b53e6 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 1 Feb 2017 15:52:26 -0500 Subject: [PATCH 02/12] fix offsetof(Task, ctx) ...by removing it. This type can't be represented in Julia correctly, but it also isn't needed. --- src/task.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/task.c b/src/task.c index 7b2ce67637292..be99b9afdda9a 100644 --- a/src/task.c +++ b/src/task.c @@ -663,7 +663,7 @@ void jl_init_tasks(void) jl_new_datatype(jl_symbol("Task"), jl_any_type, jl_emptysvec, - jl_svec(13, + jl_svec(9, jl_symbol("parent"), jl_symbol("storage"), jl_symbol("state"), @@ -672,19 +672,17 @@ void jl_init_tasks(void) jl_symbol("result"), jl_symbol("exception"), jl_symbol("backtrace"), - jl_symbol("code"), - jl_symbol("ctx"), - jl_symbol("bufsz"), - jl_symbol("stkbuf"), - jl_symbol("ssize")), - jl_svec(13, + jl_symbol("code")), + jl_svec(9, jl_any_type, - jl_any_type, jl_sym_type, - jl_any_type, jl_any_type, - jl_any_type, jl_any_type, - jl_any_type, jl_any_type, - jl_tupletype_fill(sizeof(jl_jmp_buf), (jl_value_t*)jl_uint8_type), - jl_long_type, jl_voidpointer_type, jl_long_type), + jl_any_type, + jl_sym_type, + jl_any_type, + jl_any_type, + jl_any_type, + jl_any_type, + jl_any_type, + jl_any_type), 0, 1, 8); jl_svecset(jl_task_type->types, 0, (jl_value_t*)jl_task_type); From 6cc099c9396094c7664db458c797e146d1ec514b Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 1 Feb 2017 16:13:13 -0500 Subject: [PATCH 03/12] simplify / improve jl_typemap_intersection visitor now that isa should be correct --- src/typemap.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/typemap.c b/src/typemap.c index 88f608490936f..e7eb1c32be346 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -434,14 +434,14 @@ static int jl_typemap_intersection_array_visitor(struct jl_ordereddict_t *a, jl_ if (tparam) t = jl_tparam0(t); } + // `t` is a leaftype, so intersection test becomes subtype if (ty == (jl_value_t*)jl_any_type || // easy case: Any always matches - (tparam ? // need to compute `ty <: Type{t}` - (jl_is_uniontype(ty) || // punt on Union{...} right now - jl_typeof(t) == ty || // deal with kinds (e.g. ty == DataType && t == Type{t}) - jl_isa(t, ty)) // deal with ty == Type{T} - : jl_subtype(t, ty))) // `t` is a leaftype, so intersection test becomes subtype - if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) + (tparam + ? (jl_typeof(t) == ty || jl_isa(t, ty)) // (Type{t} <: ty), where is_leaf_type(t) => isa(t, ty) + : (t == ty || jl_subtype(t, ty)))) { + if (!jl_typemap_intersection_visitor(ml, offs + 1, closure)) return 0; + } } return 1; } From 893a65bddea0bd89c3df2eb0cf8727b1f967738d Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 1 Feb 2017 17:18:06 -0500 Subject: [PATCH 04/12] simplify process of printing UnionAll type-variables Since UnionAll bounds are encountered recursively, it's easiest to represent this the same way in the IOContext. Also avoid the performance / allocation overhead of using `IOContext(io)` as a no-op convert (not a no-op since keyword support was added) --- base/methodshow.jl | 8 +++-- base/multimedia.jl | 2 +- base/range.jl | 2 +- base/replutil.jl | 2 +- base/show.jl | 69 +++++++++++++++--------------------- base/sparse/sparsematrix.jl | 4 +-- base/sparse/sparsevector.jl | 4 +-- doc/src/stdlib/io-network.md | 1 - test/dict.jl | 6 ++-- test/nullable.jl | 6 ++-- test/offsetarray.jl | 6 ++-- test/ranges.jl | 2 +- test/show.jl | 6 ++-- 13 files changed, 54 insertions(+), 64 deletions(-) diff --git a/base/methodshow.jl b/base/methodshow.jl index 269a1dfeb89cc..f6a6d32cc1cdb 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -44,7 +44,7 @@ end function arg_decl_parts(m::Method) tv = m.tvars - if !isa(tv,SimpleVector) + if !isa(tv, SimpleVector) tv = Any[tv] else tv = Any[tv...] @@ -59,7 +59,11 @@ function arg_decl_parts(m::Method) if src !== nothing && src.slotnames !== nothing argnames = src.slotnames[1:m.nargs] sig = unwrap_unionall(m.sig) - decls = Any[argtype_decl(:tvar_env => tv, argnames[i], sig, i, m.nargs, m.isva) + show_env = ImmutableDict{Symbol, Any}() + for t in tv + show_env = ImmutableDict(show_env, :unionall_env => t) + end + decls = Any[argtype_decl(show_env, argnames[i], sig, i, m.nargs, m.isva) for i = 1:m.nargs] else decls = Any[("", "") for i = 1:length(unwrap_unionall(m.sig).parameters)] diff --git a/base/multimedia.jl b/base/multimedia.jl index c364dedebef10..7403ae7f4c349 100644 --- a/base/multimedia.jl +++ b/base/multimedia.jl @@ -39,7 +39,7 @@ mimewritable{mime}(::MIME{mime}, x) = show(io::IO, m::AbstractString, x) = show(io, MIME(m), x) mimewritable(m::AbstractString, x) = mimewritable(MIME(m), x) -verbose_show(io, m, x) = show(IOContext(io,limit=false), m, x) +verbose_show(io, m, x) = show(IOContext(io, :limit => false), m, x) """ reprmime(mime, x) diff --git a/base/range.jl b/base/range.jl index 628cfecb6cdfd..00f681eb9cda9 100644 --- a/base/range.jl +++ b/base/range.jl @@ -254,7 +254,7 @@ function print_range(io::IO, r::Range, limit = get(io, :limit, false) sz = displaysize(io) if !haskey(io, :compact) - io = IOContext(io, compact=true) + io = IOContext(io, :compact => true) end screenheight, screenwidth = sz[1] - 4, sz[2] screenwidth -= length(pre) + length(post) diff --git a/base/replutil.jl b/base/replutil.jl index c191442ceacfd..43cec450004c1 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -39,7 +39,7 @@ function show{K,V}(io::IO, ::MIME"text/plain", t::Associative{K,V}) recur_io = IOContext(io, :SHOWN_SET => t) limit::Bool = get(io, :limit, false) if !haskey(io, :compact) - recur_io = IOContext(recur_io, compact=true) + recur_io = IOContext(recur_io, :compact => true) end print(io, summary(t)) diff --git a/base/show.jl b/base/show.jl index e7af6d76d4d52..a3404aba33083 100644 --- a/base/show.jl +++ b/base/show.jl @@ -24,21 +24,24 @@ end The same as `IOContext(io::IO, KV::Pair)`, but accepting properties as keyword arguments. """ -IOContext(io::IO; kws...) = IOContext(IOContext(io, ImmutableDict{Symbol,Any}()); kws...) +IOContext(io::IO; kws...) = IOContext(convert(IOContext, io); kws...) function IOContext(io::IOContext; kws...) for (k, v) in kws io = IOContext(io, k, v) end - io + return io end +convert(::Type{IOContext}, io::IOContext) = io +convert(::Type{IOContext}, io::IO) = IOContext(io, ImmutableDict{Symbol, Any}()) + IOContext(io::IOContext, dict::ImmutableDict) = typeof(io)(io.io, dict) IOContext(io::IO, dict::ImmutableDict) = IOContext{typeof(io)}(io, dict) +IOContext(io::IOContext, key, value) = IOContext(io.io, ImmutableDict{Symbol, Any}(io.dict, key, value)) IOContext(io::IO, key, value) = IOContext(io, ImmutableDict{Symbol, Any}(key, value)) -IOContext(io::IOContext, key, value) = IOContext(io, ImmutableDict{Symbol, Any}(io.dict, key, value)) -IOContext(io::IO, context::IO) = IOContext(io) +IOContext(io::IO, context::IO) = convert(IOContext, io) """ IOContext(io::IO, context::IOContext) @@ -183,32 +186,14 @@ function show(io::IO, x::UnionAll) if print_without_params(x) return show(io, unwrap_unionall(x).name) end - tvar_env = get(io, :tvar_env, false) - if tvar_env !== false && isa(tvar_env, AbstractVector) - tvar_env = Any[tvar_env..., x.var] - else - tvar_env = Any[x.var] - end - show(IOContext(io, tvar_env = tvar_env), x.body) + show(IOContext(io, :unionall_env => x.var), x.body) print(io, " where ") show(io, x.var) end -function show_type_parameter(io::IO, p::ANY, has_tvar_env::Bool) - if has_tvar_env - show(io, p) - else - show(IOContext(io, :tvar_env, true), p) - end -end - show(io::IO, x::DataType) = show_datatype(io, x) function show_datatype(io::IO, x::DataType) - # tvar_env is a `::Vector{Any}` when we are printing a method signature - # and `true` if we are printing type parameters outside a method signature. - has_tvar_env = get(io, :tvar_env, false) !== false - if (!isempty(x.parameters) || x.name === Tuple.name) && x !== Tuple n = length(x.parameters) @@ -223,7 +208,7 @@ function show_datatype(io::IO, x::DataType) # since this information is still useful. print(io, '{') for (i, p) in enumerate(x.parameters) - show_type_parameter(io, p, has_tvar_env) + show(io, p) i < n && print(io, ',') end print(io, '}') @@ -1063,16 +1048,12 @@ function ismodulecall(ex::Expr) end function show(io::IO, tv::TypeVar) - # If `tvar_env` exist and we are in it, the type constraint are - # already printed and we don't need to print it again. + # If we are in the `unionall_env`, the type-variable is bound + # and the type constraints are already printed. + # We don't need to print it again. # Otherwise, the lower bound should be printed if it is not `Bottom` # and the upper bound should be printed if it is not `Any`. - tvar_env = isa(io, IOContext) && get(io, :tvar_env, false) - if isa(tvar_env, Vector{Any}) - in_env = (tv in tvar_env::Vector{Any}) - else - in_env = false - end + in_env = (:unionall_env => tv) in io function show_bound(io::IO, b::ANY) parens = isa(b,UnionAll) && !print_without_params(b) parens && print(io, "(") @@ -1191,16 +1172,22 @@ function dump(io::IO, x::DataType, n::Int, indent) if x !== Any print(io, " <: ", supertype(x)) end - if !(x <: Tuple) - tvar_io = IOContext(io, :tvar_env => Any[x.parameters...]) - fields = fieldnames(x) - if n > 0 - for idx in 1:length(fields) - println(io) - print(io, indent, " ", fields[idx], "::") - print(tvar_io, fieldtype(x, idx)) + if n > 0 && !(x <: Tuple) + tvar_io::IOContext = io + for tparam in x.parameters + # approximately recapture the list of tvar parameterization + # that may be used by the internal fields + if isa(tparam, TypeVar) + tvar_io = IOContext(tvar_io, :unionall_env => tparam) end end + fields = fieldnames(x) + fieldtypes = x.types + for idx in 1:length(fields) + println(io) + print(io, indent, " ", fields[idx], "::") + print(tvar_io, fieldtypes[idx]) + end end nothing end @@ -1634,7 +1621,7 @@ function showarray(io::IO, X::AbstractArray, repr::Bool = true; header = true) return show_vector(io, X, "[", "]") end if !haskey(io, :compact) - io = IOContext(io, compact=true) + io = IOContext(io, :compact => true) end if !repr && get(io, :limit, false) && eltype(X) === Method # override usual show method for Vector{Method}: don't abbreviate long lists diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 58e8371e86b96..675f89936625a 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -124,7 +124,8 @@ function Base.show(io::IO, ::MIME"text/plain", S::SparseMatrixCSC) end end -function Base.show(io::IO, S::SparseMatrixCSC) +Base.show(io::IO, S::SparseMatrixCSC) = Base.show(convert(IOContext, io), S::SparseMatrixCSC) +function Base.show(io::IOContext, S::SparseMatrixCSC) if nnz(S) == 0 return show(io, MIME("text/plain"), S) end @@ -139,7 +140,6 @@ function Base.show(io::IO, S::SparseMatrixCSC) pad = ndigits(max(S.m,S.n)) k = 0 sep = "\n " - io = IOContext(io) if !haskey(io, :compact) io = IOContext(io, :compact => true) end diff --git a/base/sparse/sparsevector.jl b/base/sparse/sparsevector.jl index 8a4ea93c4e82a..ab0814576de36 100644 --- a/base/sparse/sparsevector.jl +++ b/base/sparse/sparsevector.jl @@ -682,7 +682,8 @@ function show(io::IO, ::MIME"text/plain", x::AbstractSparseVector) show(io, x) end -function show(io::IO, x::AbstractSparseVector) +show(io::IO, x::AbstractSparseVector) = show(convert(IOContext, io), x) +function show(io::IOContext, x::AbstractSparseVector) # TODO: make this a one-line form n = length(x) nzind = nonzeroinds(x) @@ -693,7 +694,6 @@ function show(io::IO, x::AbstractSparseVector) half_screen_rows = limit ? div(displaysize(io)[1] - 8, 2) : typemax(Int) pad = ndigits(n) sep = "\n\t" - io = IOContext(io) if !haskey(io, :compact) io = IOContext(io, :compact => true) end diff --git a/doc/src/stdlib/io-network.md b/doc/src/stdlib/io-network.md index 0d710513d427a..cb697abfbac33 100644 --- a/doc/src/stdlib/io-network.md +++ b/doc/src/stdlib/io-network.md @@ -51,7 +51,6 @@ Base.readavailable Base.IOContext Base.IOContext(::IO, ::Pair) Base.IOContext(::IO, ::IOContext) -Base.IOContext(::IO) ``` ## Text I/O diff --git a/test/dict.jl b/test/dict.jl index 3fbb3dd73bc3d..5b995e7ca7e0f 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -268,7 +268,7 @@ for d in (Dict("\n" => "\n", "1" => "\n", "\n" => "2"), for cols in (12, 40, 80), rows in (2, 10, 24) # Ensure output is limited as requested s = IOBuffer() - io = Base.IOContext(s, limit=true, displaysize=(rows, cols)) + io = Base.IOContext(Base.IOContext(s, :limit => true), :displaysize => (rows, cols)) Base.show(io, MIME("text/plain"), d) out = split(String(take!(s)),'\n') for line in out[2:end] @@ -278,7 +278,7 @@ for d in (Dict("\n" => "\n", "1" => "\n", "\n" => "2"), for f in (keys, values) s = IOBuffer() - io = Base.IOContext(s, limit=true, displaysize=(rows, cols)) + io = Base.IOContext(Base.IOContext(s, :limit => true), :displaysize => (rows, cols)) Base.show(io, MIME("text/plain"), f(d)) out = split(String(take!(s)),'\n') for line in out[2:end] @@ -311,7 +311,7 @@ end type Alpha end Base.show(io::IO, ::Alpha) = print(io,"α") let sbuff = IOBuffer(), - io = Base.IOContext(sbuff, limit=true, displaysize=(10, 20)) + io = Base.IOContext(Base.IOContext(sbuff, :limit => true), :displaysize => (10, 20)) Base.show(io, MIME("text/plain"), Dict(Alpha()=>1)) @test !contains(String(sbuff), "…") diff --git a/test/nullable.jl b/test/nullable.jl index cf103f2e203e6..fed344fd1e9f4 100644 --- a/test/nullable.jl +++ b/test/nullable.jl @@ -103,13 +103,13 @@ for (i, T) in enumerate(types) @test String(take!(io1)) == @sprintf("Nullable{%s}(%s)", T, String(take!(io2))) a1 = [x2] - show(IOContext(io1, compact=false), a1) - show(IOContext(io2, compact=false), x2) + show(IOContext(io1, :compact => false), a1) + show(IOContext(io2, :compact => false), x2) @test String(take!(io1)) == @sprintf("Nullable{%s}[%s]", string(T), String(take!(io2))) show(io1, a1) - show(IOContext(io2, compact=true), x2) + show(IOContext(io2, :compact => true), x2) @test String(take!(io1)) == @sprintf("Nullable{%s}[%s]", string(T), String(take!(io2))) end diff --git a/test/offsetarray.jl b/test/offsetarray.jl index 45240f75d1cdb..377a5875cec46 100644 --- a/test/offsetarray.jl +++ b/test/offsetarray.jl @@ -139,7 +139,7 @@ smry = summary(v) @test contains(smry, "OffsetArray{Float64,1") @test contains(smry, "with indices -1:1") function cmp_showf(printfunc, io, A) - ioc = IOContext(io, limit=true, compact=true) + ioc = IOContext(IOContext(io, :limit => true), :compact => true) printfunc(ioc, A) str1 = String(take!(io)) printfunc(ioc, parent(A)) @@ -162,9 +162,9 @@ targets2 = ["(1.0, 1.0)", "([1.0], [1.0])"] for n = 0:4 a = OffsetArray(ones(Float64,ntuple(d->1,n)), ntuple(identity,n)) - show(IOContext(io, limit=true), MIME("text/plain"), a) + show(IOContext(io, :limit => true), MIME("text/plain"), a) @test String(take!(io)) == targets1[n+1] - show(IOContext(io, limit=true), MIME("text/plain"), (a,a)) + show(IOContext(io, :limit => true), MIME("text/plain"), (a,a)) @test String(take!(io)) == targets2[n+1] end P = OffsetArray(rand(8,8), (1,1)) diff --git a/test/ranges.jl b/test/ranges.jl index ca94a1e00b7af..0692dda712b4d 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -595,7 +595,7 @@ end # stringmime/show should display the range or linspace nicely # to test print_range in range.jl -replstrmime(x) = sprint((io,x) -> show(IOContext(io, limit=true), MIME("text/plain"), x), x) +replstrmime(x) = sprint((io,x) -> show(IOContext(io, :limit => true), MIME("text/plain"), x), x) @test replstrmime(1:4) == "1:4" @test stringmime("text/plain", 1:4) == "1:4" @test stringmime("text/plain", linspace(1,5,7)) == "1.0:0.6666666666666666:5.0" diff --git a/test/show.jl b/test/show.jl index ca2c20997c3b7..72d94d2b66646 100644 --- a/test/show.jl +++ b/test/show.jl @@ -4,7 +4,7 @@ const curmod = current_module() const curmod_name = fullname(curmod) const curmod_prefix = "$(["$m." for m in curmod_name]...)" -replstr(x) = sprint((io,x) -> show(IOContext(io, limit=true), MIME("text/plain"), x), x) +replstr(x) = sprint((io,x) -> show(IOContext(io, :limit => true), MIME("text/plain"), x), x) @test replstr(Array{Any}(2)) == "2-element Array{Any,1}:\n #undef\n #undef" @test replstr(Array{Any}(2,2)) == "2×2 Array{Any,2}:\n #undef #undef\n #undef #undef" @@ -533,14 +533,14 @@ end # PR #16651 @test !contains(repr(ones(10,10)), "\u2026") -@test contains(sprint((io,x)->show(IOContext(io,:limit=>true), x), ones(30,30)), "\u2026") +@test contains(sprint((io, x) -> show(IOContext(io, :limit => true), x), ones(30, 30)), "\u2026") # showcompact() also sets :multiline=>false (#16817) let io = IOBuffer() x = [1, 2] showcompact(io, x) @test String(take!(io)) == "[1, 2]" - showcompact(IOContext(io, :compact=>true), x) + showcompact(IOContext(io, :compact => true), x) @test String(take!(io)) == "[1, 2]" end From 6cd36bc8e02cf8bf85b4c0118917baeb67f5564a Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 1 Feb 2017 17:32:35 -0500 Subject: [PATCH 05/12] correct the ambiguity detection test in ml_matches the previous version was effectively equivalent, but did more work than necessary and could fail in the wrong direction in the unlikely case that `ambi` was not a strict subtype of `mambig->sig`. --- src/gf.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/gf.c b/src/gf.c index d4cd133de1a6a..afeaf04f357f2 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2598,11 +2598,9 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio } } else { - // the current method doesn't match if there is an intersection with an - // ambiguous method that covers our intersection with this one. - jl_value_t *ambi = jl_type_intersection_env((jl_value_t*)ml->sig, - (jl_value_t*)mambig->sig, &env); - if (jl_subtype(closure->match.ti, ambi)) { + // the current method definitely never matches if the intersection with this method + // is also fully covered by an ambiguous method's signature + if (jl_subtype(closure->match.ti, mambig->sig)) { return_this_match = 0; break; } From ec984b670d3c0df66e7f5ebf12e9e7079e6a4739 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 1 Feb 2017 17:39:10 -0500 Subject: [PATCH 06/12] remove unnecessary work from check_ambiguous_visitor --- src/gf.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/gf.c b/src/gf.c index afeaf04f357f2..fcfc5b3ad266c 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1095,22 +1095,21 @@ static int check_ambiguous_visitor(jl_typemap_entry_t *oldentry, struct typemap_ jl_method_t *m = closure->newentry->func.method; jl_tupletype_t *sig = oldentry->sig; jl_value_t *isect = closure->match.ti; - if (jl_types_equal(isect, (jl_value_t*)(closure->after ? sig : type))) { - // we're ok if the new definition is actually the one we just - // inferred to be required (see issue #3609). ideally this would - // never happen, since if New ⊓ Old == New then we should have - // considered New more specific, but jl_type_morespecific is not - // perfect, so this is a useful fallback. - return 1; - } // we know type ∩ sig != Union{} and - // we know !jl_type_morespecific(type, sig) [before] - // or !jl_type_morespecific(sig, type) [after] + // we are assuming that + // !jl_type_morespecific(type, sig) [before] + // or !jl_type_morespecific(sig, type) [after] + // based on their sort order in the typemap // now we are checking that the reverse is true if (!jl_type_morespecific((jl_value_t*)(closure->after ? type : sig), (jl_value_t*)(closure->after ? sig : type))) { - jl_typemap_entry_t *l = jl_typemap_assoc_by_type(map, (jl_tupletype_t*)isect, NULL, 0, 0, 0, + // see if the intersection is covered by another existing method + // that will resolve the ambiguity (by being more specific than either) + // (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, closure->newentry->min_world); if (l != NULL) // ok, intersection is covered return 1; From 209be036b9eb813cdd3fc1659dae8c47aefd7164 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 1 Feb 2017 17:45:17 -0500 Subject: [PATCH 07/12] add parenthetical to manual regarding absence of Float typealias Seems to be a common FAQ. Not sure if those users would have seen it here, but it's right after explaining the existance of the `Int` type-alias. --- doc/src/manual/types.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/src/manual/types.md b/doc/src/manual/types.md index 86abb5ebe1d3c..31e12a49a0830 100644 --- a/doc/src/manual/types.md +++ b/doc/src/manual/types.md @@ -1064,6 +1064,10 @@ end Of course, this depends on what `Int` is aliased to -- but that is predefined to be the correct type -- either `Int32` or `Int64`. +(Note that unlike `Int`, `Float` does not exist as a type-alias for a specific sized `AbstractFloat`. +Unlike with integer registers, the floating point register sizes are specified by the IEEE-754 standard. +Whereas the size of `Int` reflects the size of a native pointer on that machine.) + For parametric types, `typealias` can be convenient for providing names for cases where some of the parameter choices are fixed: From 19051e40f051c6507907ad8ec2bbe8014bee1d12 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 1 Feb 2017 18:00:33 -0500 Subject: [PATCH 08/12] add supertype operator >: for symmetry with issubtype --- base/exports.jl | 1 + base/inference.jl | 24 ++++++++++++++++++++++++ base/operators.jl | 9 ++++++++- doc/src/stdlib/base.md | 1 + doc/src/stdlib/punctuation.md | 3 +++ test/subtype.jl | 4 ++++ 6 files changed, 41 insertions(+), 1 deletion(-) diff --git a/base/exports.jl b/base/exports.jl index 78b9ee477cc2b..3c1ee984fe40e 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -219,6 +219,7 @@ export ≤, ==, >, + >:, >=, ≥, >>, diff --git a/base/inference.jl b/base/inference.jl index 3e76d2d196a58..65e5d25e7a08b 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -1704,6 +1704,12 @@ function abstract_call(f::ANY, fargs::Union{Tuple{},Vector{Any}}, argtypes::Vect return Const(rty.val === false) end return rty + elseif length(fargs) == 3 && istopfunction(tm, f, :(>:)) + # swap T1 and T2 arguments and call issubtype + fargs = Any[issubtype, fargs[3], fargs[2]] + argtypes = Any[typeof(issubtype), argtypes[3], argtypes[2]] + rty = abstract_call(issubtype, fargs, argtypes, vtypes, sv) + return rty end if length(argtypes)>2 && argtypes[3] ⊑ Int @@ -3605,6 +3611,24 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference not_is.typ = Bool not_is.args[2].typ = Bool return (not_is, ()) + elseif length(atypes) == 3 && istopfunction(topmod, f, :(>:)) + # special-case inliner for issupertype + # that works, even though inference generally avoids inferring the `>:` Method + if isa(e.typ, Const) + return inline_as_constant(e.typ.val, argexprs, sv, nothing) + end + arg_T1 = argexprs[2] + arg_T2 = argexprs[3] + issubtype_stmts = () + if !effect_free(arg_T2, sv.src, sv.mod, false) + # spill first argument to preserve order-of-execution + issubtype_vnew = newvar!(sv, widenconst(exprtype(arg_T1, sv.src, sv.mod))) + issubtype_stmts = Any[ Expr(:(=), issubtype_vnew, arg_T1) ] + arg_T1 = issubtype_vnew + end + issubtype_expr = Expr(:call, GlobalRef(Core, :issubtype), arg_T2, arg_T1) + issubtype_expr.typ = Bool + return (issubtype_expr, issubtype_stmts) end if length(atype_unlimited.parameters) - 1 > sv.params.MAX_TUPLETYPE_LEN diff --git a/base/operators.jl b/base/operators.jl index ebf140ca0e599..b4a6c8c7741fd 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -9,7 +9,7 @@ typealias Indices{N} NTuple{N,AbstractUnitRange} """ <:(T1, T2) -Subtype operator, equivalent to `issubtype(T1,T2)`. +Subtype operator, equivalent to `issubtype(T1, T2)`. ```jldoctest julia> Float64 <: AbstractFloat @@ -24,6 +24,13 @@ false """ const (<:) = issubtype +""" + >:(T1, T2) + +Supertype operator, equivalent to `issubtype(T2, T1)`. +""" +const (>:)(a::ANY, b::ANY) = issubtype(b, a) + """ supertype(T::DataType) diff --git a/doc/src/stdlib/base.md b/doc/src/stdlib/base.md index ec8ff88a4ad51..018a1a4c25315 100644 --- a/doc/src/stdlib/base.md +++ b/doc/src/stdlib/base.md @@ -89,6 +89,7 @@ Base.identity Base.supertype Core.issubtype Base.:(<:) +Base.:(>:) Base.subtypes Base.typemin Base.typemax diff --git a/doc/src/stdlib/punctuation.md b/doc/src/stdlib/punctuation.md index 0b44fbb77391a..e380a378854ef 100644 --- a/doc/src/stdlib/punctuation.md +++ b/doc/src/stdlib/punctuation.md @@ -42,3 +42,6 @@ Extended documentation for mathematical symbols & functions is [here](@ref math- | `::` | type annotation, depending on context | | `:( )` | quoted expression | | `:a` | symbol a | +| `<:` | [`subtype operator`](@ref <:) | +| `>:` | [`supertype operator`](@ref >:) (reverse of subtype operator) | +| `===` | [`egal comparison operator`](@ref ===) | diff --git a/test/subtype.jl b/test/subtype.jl index a5c19129b44e2..ef852e13567e1 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -907,3 +907,7 @@ ftwoparams(::TwoParams{<:Real,<:Real}) = 3 @test TwoParams{Real,Complex}(3,0im) isa TwoParams{>:Int,<:Number} @test !(TwoParams(3.0,0im) isa TwoParams{>:Int,<:Number}) @test !(TwoParams(3,'x') isa TwoParams{>:Int,<:Number}) + +# supertype operator +@test !(Int >: Integer) +@test Integer >: Int From ea556708096212855a3b46a44c2dfee47fe72997 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 2 Feb 2017 13:16:41 -0500 Subject: [PATCH 09/12] fix typetree dump to consider that a UnionAll may be the primary type declaration --- base/show.jl | 23 +++++++++++++++++++++-- test/show.jl | 2 +- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/base/show.jl b/base/show.jl index a3404aba33083..05709eca478f2 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1216,9 +1216,28 @@ function dumpsubtypes(io::IO, x::DataType, m::Module, n::Int, indent) # recurse into primary module bindings dumpsubtypes(io, x, t, n, indent) elseif isa(t, UnionAll) && directsubtype(t::UnionAll, x) + dt = unwrap_unionall(t) println(io) - print(io, indent, " ", m, ".", s) - print(io, " = ", t) + if isa(dt, DataType) && dt.name.wrapper === t + # primary type binding + print(io, indent, " ") + dumptype(io, dt, n - 1, string(indent, " ")) + else + # aliases to types + print(io, indent, " ", m, ".", s, "{") + tp = t + while true + print(io, tp.var.name) + tp = tp.body + if isa(tp, UnionAll) + print(io, ", ") + else + print(io, "} = ") + break + end + end + print(io, t) + end elseif isa(t, Union) && directsubtype(t::Union, x) println(io) print(io, indent, " ", m, ".", s, " = ", t) diff --git a/test/show.jl b/test/show.jl index 72d94d2b66646..178042445d583 100644 --- a/test/show.jl +++ b/test/show.jl @@ -569,7 +569,7 @@ let repr = sprint(dump, Any) @test length(repr) > 100000 @test ismatch(r"^Any\n [^ \t\n]", repr) @test endswith(repr, '\n') - @test_broken contains(repr, " Base.Vector{T} = Array{T,1}\n") + @test contains(repr, " Base.Vector{T} = Array{T,1} where T\n") @test !contains(repr, "Core.Vector{T}") end let repr = sprint(dump, Integer) From 5fc582b452fbe312a9ebe7312a1bcbcd919017bb Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 2 Feb 2017 14:19:01 -0500 Subject: [PATCH 10/12] fix typetree example to demonstrate new type representations and subtyping relationships --- examples/typetree.jl | 57 +++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/examples/typetree.jl b/examples/typetree.jl index a86bc7c3e4aaa..6a3c520043bb9 100644 --- a/examples/typetree.jl +++ b/examples/typetree.jl @@ -14,7 +14,7 @@ Base.isless(a::Binding, b::Binding) = isless(a.sym, b.sym) # The node type holds the type of the current node and a dict of subtypes immutable TTNode - typ::Type + typ # ::Type subtypes::Dict{Binding, TTNode} TTNode(t::ANY) = new(t, Dict{Binding, TTNode}()) @@ -38,21 +38,29 @@ function store_type(sname::Binding, t::Union) # store unions under Union type subtypes = store_type(Binding(suptype.name), suptype) add_ttnode(subtypes, sname, tnode) + store_union(sname, tnode, t) # unions are also in a sense related to the types of their components - for suptype = t.types - if isa(suptype, DataType) # ignore TypeConstructors - subtypes = store_type(Binding(suptype.name), suptype) - add_ttnode(subtypes, sname, tnode) - end - end return tnode.subtypes end +function store_union(sname::Binding, tnode::TTNode, t::ANY) + t = Base.unwrap_unionall(t) + if isa(t, Union) + store_union(sname, tnode, t.a) + store_union(sname, tnode, t.b) + elseif isa(t, DataType) + binding = Binding(t.name) + subtypes = store_type(binding, t) + add_ttnode(subtypes, sname, tnode) + end + nothing +end -function store_type(sname::Binding, t::TypeConstructor) - suptype = t.body - subtypes = store_type(isa(suptype, DataType) ? Binding(suptype.name) : Binding(Main, string(suptype::Union)), suptype) +function store_type(sname::Binding, t::UnionAll) + suptype = Base.unwrap_unionall(t) + binding = isa(suptype, DataType) ? Binding(suptype.name) : Binding(Main, string(suptype::Union)) + subtypes = store_type(binding, suptype) tnode = add_ttnode(subtypes, sname, t) return tnode.subtypes end @@ -69,7 +77,7 @@ function store_all_from(m::Module) for s in names(m, true) if isdefined(m, s) && !Base.isdeprecated(m, s) t = getfield(m, s) - if isa(t, Type) + if isa(t, Type) && t !== Union{} store_type(Binding(m, s), t) elseif isa(t, Module) && module_name(t) === s && module_parent(t) === m && t !== m store_all_from(t) @@ -93,17 +101,34 @@ type_props(typ::DataType) = string("<<", function print_tree(subtypes::Dict{Binding, TTNode}, pfx::String="") for b in sort!(collect(keys(subtypes))) v = subtypes[b] + ishidden = unsafe_load(Base.unsafe_convert(Ptr{UInt8}, b.sym)) == UInt8('#') + if ishidden && supertype(v.typ) === Function + continue + end if b.mod === Main n = string(b.sym) elseif !isa(v.typ, DataType) || v.typ.name.module != b.mod || v.typ.name.name != b.sym - n = string(b.mod, '.', b.sym, (isa(v.typ, TypeConstructor) ? ("{", join(v.typ.parameters, ","), "}") : ())...) + n_io = IOBuffer() + print(n_io, b.mod, '.', b.sym) + ua = v.typ + if isa(ua, UnionAll) + print(n_io, "{") + while true + print(n_io, ua.var) + ua = ua.body + if isa(ua, UnionAll) + print(n_io, ", ") + else + break + end + end + print(n_io, "}") + end + n = String(take!(n_io)) else n = string(v.typ) end - ishidden = unsafe_load(Base.unsafe_convert(Ptr{UInt8}, b.sym)) == UInt8('#') - if ishidden && supertype(v.typ) === Function - continue - elseif n == string(v.typ) + if n == string(v.typ) println(pfx, "+- ", n, " ", type_props(v.typ)) else println(pfx, "+- ", n, " = ", v.typ, " ", type_props(v.typ)) From 0c819fe925009381664b893f2a0176a06efc3226 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 6 Feb 2017 16:38:08 -0500 Subject: [PATCH 11/12] remove several unnecessary special-case inliners the general inliner is better and also respects effect_free (related to #9765, although none of these specific cases were mentioned there) --- base/inference.jl | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/base/inference.jl b/base/inference.jl index 65e5d25e7a08b..42bdf250606a7 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -3516,36 +3516,19 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference # typeassert(x::S, T) => x, when S<:T if isType(atypes[3]) && isleaftype(atypes[3]) && atypes[2] ⊑ atypes[3].parameters[1] - return (argexprs[2],()) + return (argexprs[2], ()) end end topmod = _topmod(sv) # special-case inliners for known pure functions that compute types if sv.params.inlining if isa(e.typ, Const) # || isconstType(e.typ) - # XXX: this is needlessly buggy and should just call `inline_as_constant` if (f === apply_type || f === fieldtype || f === typeof || istopfunction(topmod, f, :typejoin) || - istopfunction(topmod, f, :promote_type)) - # XXX: compute effect_free for the actual arguments - if length(argexprs) < 2 || effect_free(argexprs[2], sv.src, sv.mod, true) - return (e.typ.val, ()) - else - return (e.typ.val, Any[argexprs[2]]) - end - end - end - if istopfunction(topmod, f, :isbits) && length(atypes)==2 && isType(atypes[2]) && - effect_free(argexprs[2], sv.src, sv.mod, true) && isleaftype(atypes[2].parameters[1]) - # TODO: this is needlessly complicated and should just call `inline_as_constant` - return (isbits(atypes[2].parameters[1]),()) - end - if f === Core.kwfunc && length(argexprs) == 2 && isa(e.typ, Const) - # TODO: replace with a call to `inline_as_constant` - if effect_free(argexprs[2], sv.src, sv.mod, true) - return (e.typ.val, ()) - else - return (e.typ.val, Any[argexprs[2]]) + istopfunction(topmod, f, :isbits) || + istopfunction(topmod, f, :promote_type) || + (f === Core.kwfunc && length(argexprs) == 2)) + return inline_as_constant(e.typ.val, argexprs, sv, nothing) end end end From dee86b3835abfac0921e8f15f0fdf0d152e26833 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 9 Feb 2017 15:39:34 -0500 Subject: [PATCH 12/12] use new "where" syntax in dumpsubtypes --- base/show.jl | 9 ++++++--- test/show.jl | 7 +++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/base/show.jl b/base/show.jl index 05709eca478f2..05d85c5486c0f 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1225,9 +1225,11 @@ function dumpsubtypes(io::IO, x::DataType, m::Module, n::Int, indent) else # aliases to types print(io, indent, " ", m, ".", s, "{") + tvar_io::IOContext = io tp = t while true - print(io, tp.var.name) + show(tvar_io, tp.var) + tvar_io = IOContext(tvar_io, :unionall_env, tp.var) tp = tp.body if isa(tp, UnionAll) print(io, ", ") @@ -1236,7 +1238,7 @@ function dumpsubtypes(io::IO, x::DataType, m::Module, n::Int, indent) break end end - print(io, t) + show(tvar_io, tp) end elseif isa(t, Union) && directsubtype(t::Union, x) println(io) @@ -1245,7 +1247,8 @@ function dumpsubtypes(io::IO, x::DataType, m::Module, n::Int, indent) println(io) if t.name.module !== m || t.name.name != s # aliases to types - print(io, indent, " ", m, ".", s, " = ", t) + print(io, indent, " ", m, ".", s, " = ") + show(io, t) else # primary type binding print(io, indent, " ") diff --git a/test/show.jl b/test/show.jl index 178042445d583..b0b1aa1647314 100644 --- a/test/show.jl +++ b/test/show.jl @@ -564,12 +564,15 @@ let repr = sprint(dump, Int64) @test repr == "Int64 <: Signed\n" end # Make sure a `TypeVar` in a `Union` doesn't break subtype dump. -typealias BreakDump17529{T} Union{T,Void} +typealias BreakDump17529{T} Union{T, Void} +# make sure dependent parameters are represented correctly +typealias VectorVI{I, VI<:AbstractVector{I}} Vector{VI} let repr = sprint(dump, Any) @test length(repr) > 100000 @test ismatch(r"^Any\n [^ \t\n]", repr) @test endswith(repr, '\n') - @test contains(repr, " Base.Vector{T} = Array{T,1} where T\n") + @test contains(repr, " Base.Vector{T} = Array{T,1}\n") + @test contains(repr, ".VectorVI{I, VI<:AbstractArray{I,1}} = Array{VI,1}\n") @test !contains(repr, "Core.Vector{T}") end let repr = sprint(dump, Integer)