From 27b5d9af268b837a0c9c09ea991fa04da7de5b7c Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Tue, 15 Sep 2015 15:57:25 -0400 Subject: [PATCH] add the ability to deprecate bindings. fixes #11200 This is a rather minimal implementation that only really works for types and functions, since it relies on printing those objects to show the replacement. Fortunately all existing deprecated bindings are for types. This also avoids tab-completing deprecated names, by excluding them from the result of `names`. The tradeoff is that it's now impossible to enumerate deprecated names by reflection. Hopefully that will be ok. We could add `deprecate(:f)` calls for functions that are entirely deprecated. Then they would be excluded from tab completion as well. (cherry picked from commit 1b6efc7ab297b7b8aea4d7cb0b52fb589392042b) --- base/deprecated.jl | 55 +++++++++++++++++++++++---------------------- base/docs/helpdb.jl | 2 +- base/require.jl | 2 +- src/codegen.cpp | 24 ++++++++++++++++++-- src/dump.c | 3 ++- src/julia.h | 1 + src/module.c | 39 +++++++++++++++++++++++++++++++- 7 files changed, 93 insertions(+), 33 deletions(-) diff --git a/base/deprecated.jl b/base/deprecated.jl index d1dc73c590da7..62fe4d7522b1f 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -101,6 +101,16 @@ function firstcaller(bt::Array{Ptr{Void},1}, funcsym::Symbol) return C_NULL end +deprecate(s::Symbol) = deprecate(current_module(), s) +deprecate(m::Module, s::Symbol) = ccall(:jl_deprecate_binding, Void, (Any, Any), m, s) + +macro deprecate_binding(old, new) + Expr(:toplevel, + Expr(:export, esc(old)), + Expr(:(=), esc(old), esc(new)), + Expr(:call, :deprecate, Expr(:quote, old))) +end + # 0.4 deprecations @deprecate split(x,y,l::Integer,k::Bool) split(x,y;limit=l,keep=k) @@ -111,19 +121,17 @@ end @deprecate rsplit(x,y,l::Integer) rsplit(x,y;limit=l) @deprecate rsplit(x,y,k::Bool) rsplit(x,y;keep=k) -export UdpSocket const TcpSocket = TCPSocket -const UdpSocket = UDPSocket +deprecate(:TcpSocket) const IpAddr = IPAddr +deprecate(:IpAddr) +@deprecate_binding UdpSocket UDPSocket @deprecate isblank(c::Char) c == ' ' || c == '\t' @deprecate isblank(s::AbstractString) all(c -> c == ' ' || c == '\t', s) -export Nothing -const Nothing = Void - -export None -const None = Union{} +@deprecate_binding Nothing Void +@deprecate_binding None Union{} export apply @noinline function apply(f, args...) @@ -150,16 +158,14 @@ end @deprecate inf{T<:AbstractFloat}(::Type{T}) convert(T,Inf) @deprecate nan{T<:AbstractFloat}(::Type{T}) convert(T,NaN) -export String -const String = AbstractString +@deprecate_binding String AbstractString -export Uint, Uint8, Uint16, Uint32, Uint64, Uint128 -const Uint = UInt -const Uint8 = UInt8 -const Uint16 = UInt16 -const Uint32 = UInt32 -const Uint64 = UInt64 -const Uint128 = UInt128 +@deprecate_binding Uint UInt +@deprecate_binding Uint8 UInt8 +@deprecate_binding Uint16 UInt16 +@deprecate_binding Uint32 UInt32 +@deprecate_binding Uint64 UInt64 +@deprecate_binding Uint128 UInt128 @deprecate zero{T}(::Type{Ptr{T}}) Ptr{T}(0) @deprecate zero{T}(x::Ptr{T}) Ptr{T}(0) @@ -178,8 +184,7 @@ const Uint128 = UInt128 @deprecate iround(x) round(Integer,x) @deprecate iround{T}(::Type{T},x) round(T,x) -export Base64Pipe -const Base64Pipe = Base64EncodePipe +@deprecate_binding Base64Pipe Base64EncodePipe @deprecate base64 base64encode @deprecate prevind(a::Any, i::Integer) i-1 @@ -208,8 +213,7 @@ const Base64Pipe = Base64EncodePipe @deprecate error(ex::Exception) throw(ex) @deprecate error{E<:Exception}(::Type{E}) throw(E()) -export MemoryError -const MemoryError = OutOfMemoryError +@deprecate_binding MemoryError OutOfMemoryError @deprecate map!(f::Callable, dest::StridedArray, A::StridedArray, B::Number) broadcast!(f, dest, A, B) @deprecate map!(f::Callable, dest::StridedArray, A::Number, B::StridedArray) broadcast!(f, dest, A, B) @@ -542,17 +546,15 @@ function start_timer(t, d, r) error("start_timer is deprecated. Use Timer(callback, delay, repeat) instead.") end -const UnionType = Union -export UnionType +@deprecate_binding UnionType Union -const MathConst = Irrational +@deprecate_binding MathConst Irrational macro math_const(sym, val, def) depwarn("@math_const is deprecated and renamed to @irrational.", symbol("@math_const")) :(@irrational $(esc(sym)) $(esc(val)) $(esc(def))) end - -export MathConst, @math_const +export @math_const # 11280, mmap @@ -789,8 +791,7 @@ end @deprecate iseltype(x,T) eltype(x) <: T -const FloatingPoint = AbstractFloat -export FloatingPoint +@deprecate_binding FloatingPoint AbstractFloat # 11447 diff --git a/base/docs/helpdb.jl b/base/docs/helpdb.jl index 51c4edcbe3d6f..7f0bcabe660bf 100644 --- a/base/docs/helpdb.jl +++ b/base/docs/helpdb.jl @@ -6144,7 +6144,7 @@ doc""" The distance between `x` and the next larger representable floating-point value of the same `DataType` as `x`. """ -eps(::FloatingPoint) +eps(::AbstractFloat) doc""" rem1(x,m) diff --git a/base/require.jl b/base/require.jl index 50f3510961432..49b4d0d827857 100644 --- a/base/require.jl +++ b/base/require.jl @@ -67,7 +67,7 @@ end # remote/parallel load -function source_path(default::Union(AbstractString,Nothing)="") +function source_path(default::Union(AbstractString,Void)="") t = current_task() while true s = t.storage diff --git a/src/codegen.cpp b/src/codegen.cpp index 96001ab55d9de..2a40c5b1835a6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1264,6 +1264,21 @@ extern "C" void jl_write_malloc_log(void) // --- constant determination --- +static void show_source_loc(JL_STREAM *out, jl_codectx_t *ctx) +{ + if (ctx == NULL) return; + jl_printf(out, "in %s at %s:%d", ctx->linfo->name->name, ctx->linfo->file->name, ctx->lineno); +} + +extern "C" void jl_binding_deprecation_warning(jl_binding_t *b); + +static void cg_bdw(jl_binding_t *b, jl_codectx_t *ctx) +{ + jl_binding_deprecation_warning(b); + show_source_loc(JL_STDERR, ctx); + jl_printf(JL_STDERR, "\n"); +} + // try to statically evaluate, NULL if not possible extern "C" jl_value_t *jl_static_eval(jl_value_t *ex, void *ctx_, jl_module_t *mod, @@ -1316,8 +1331,10 @@ jl_value_t *jl_static_eval(jl_value_t *ex, void *ctx_, jl_module_t *mod, s = (jl_sym_t*)jl_globalref_name(ex); if (s && jl_is_symbol(s)) { jl_binding_t *b = jl_get_binding(jl_globalref_mod(ex), s); - if (b && b->constp) + if (b && b->constp) { + if (b->deprecated) cg_bdw(b, ctx); return b->value; + } } return NULL; } @@ -1332,8 +1349,10 @@ jl_value_t *jl_static_eval(jl_value_t *ex, void *ctx_, jl_module_t *mod, s = (jl_sym_t*)jl_static_eval(jl_exprarg(e,2),ctx,mod,sp,ast,sparams,allow_alloc); if (m && jl_is_module(m) && s && jl_is_symbol(s)) { jl_binding_t *b = jl_get_binding(m, s); - if (b && b->constp) + if (b && b->constp) { + if (b->deprecated) cg_bdw(b, ctx); return b->value; + } } } else if (fptr == &jl_f_tuple || fptr == &jl_f_instantiate_type) { @@ -2803,6 +2822,7 @@ static Value *global_binding_pointer(jl_module_t *m, jl_sym_t *s, p->addIncoming(bval, not_found); return julia_binding_gv(builder.CreateBitCast(p,jl_ppvalue_llvmt)); } + if (b->deprecated) cg_bdw(b, ctx); } if (pbnd) *pbnd = b; return julia_binding_gv(b); diff --git a/src/dump.c b/src/dump.c index ce2426576c8b0..b4d385a557536 100644 --- a/src/dump.c +++ b/src/dump.c @@ -563,7 +563,7 @@ static void jl_serialize_module(ios_t *s, jl_module_t *m) jl_serialize_value(s, b->value); jl_serialize_value(s, b->globalref); jl_serialize_value(s, b->owner); - write_int8(s, (b->constp<<2) | (b->exportp<<1) | (b->imported)); + write_int8(s, (b->deprecated<<3) | (b->constp<<2) | (b->exportp<<1) | (b->imported)); jl_serialize_gv(s, (jl_value_t*)b); } } @@ -1426,6 +1426,7 @@ static jl_value_t *jl_deserialize_value_(ios_t *s, jl_value_t *vtag, jl_value_t b->owner = (jl_module_t*)jl_deserialize_value(s, (jl_value_t**)&b->owner); if (b->owner != NULL) jl_gc_wb(m, b->owner); int8_t flags = read_int8(s); + b->deprecated = (flags>>3) & 1; b->constp = (flags>>2) & 1; b->exportp = (flags>>1) & 1; b->imported = (flags) & 1; diff --git a/src/julia.h b/src/julia.h index 22e19de7a6b84..beef7b5a9913c 100644 --- a/src/julia.h +++ b/src/julia.h @@ -301,6 +301,7 @@ typedef struct { unsigned constp:1; unsigned exportp:1; unsigned imported:1; + unsigned deprecated:1; } jl_binding_t; typedef struct _jl_module_t { diff --git a/src/module.c b/src/module.c index 4162113cbc126..ec21d7f7f7ac5 100644 --- a/src/module.c +++ b/src/module.c @@ -76,6 +76,7 @@ static jl_binding_t *new_binding(jl_sym_t *name) b->constp = 0; b->exportp = 0; b->imported = 0; + b->deprecated = 0; return b; } @@ -232,11 +233,15 @@ DLLEXPORT jl_binding_t *jl_get_binding(jl_module_t *m, jl_sym_t *var) return jl_get_binding_(m, var, NULL); } +void jl_binding_deprecation_warning(jl_binding_t *b); + DLLEXPORT jl_binding_t *jl_get_binding_or_error(jl_module_t *m, jl_sym_t *var) { jl_binding_t *b = jl_get_binding_(m, var, NULL); if (b == NULL) jl_undefined_var_error(var); + if (b->deprecated) + jl_binding_deprecation_warning(b); return b; } @@ -324,6 +329,7 @@ static void module_import_(jl_module_t *to, jl_module_t *from, jl_sym_t *s, jl_binding_t *nb = new_binding(s); nb->owner = b->owner; nb->imported = (explici!=0); + nb->deprecated = b->deprecated; *bp = nb; jl_gc_wb_buf(to, nb); } @@ -431,6 +437,7 @@ jl_value_t *jl_get_global(jl_module_t *m, jl_sym_t *var) { jl_binding_t *b = jl_get_binding(m, var); if (b == NULL) return NULL; + if (b->deprecated) jl_binding_deprecation_warning(b); return b->value; } @@ -460,6 +467,35 @@ DLLEXPORT int jl_is_const(jl_module_t *m, jl_sym_t *var) return b && b->constp; } +DLLEXPORT void jl_deprecate_binding(jl_module_t *m, jl_sym_t *var) +{ + jl_binding_t *b = jl_get_binding(m, var); + if (b) b->deprecated = 1; +} + +DLLEXPORT int jl_is_binding_deprecated(jl_module_t *m, jl_sym_t *var) +{ + jl_binding_t *b = jl_get_binding(m, var); + return b && b->deprecated; +} + +void jl_binding_deprecation_warning(jl_binding_t *b) +{ + if (b->deprecated) { + if (b->owner) + jl_printf(JL_STDERR, "WARNING: %s.%s is deprecated", b->owner->name->name, b->name->name); + else + jl_printf(JL_STDERR, "WARNING: %s is deprecated", b->name->name); + jl_value_t *v = b->value; + if (v && (jl_is_type(v) || (jl_is_function(v) && jl_is_gf(v)))) { + jl_printf(JL_STDERR, ", use "); + jl_static_show(JL_STDERR, v); + jl_printf(JL_STDERR, " instead"); + } + jl_printf(JL_STDERR, ".\n"); + } +} + DLLEXPORT void jl_checked_assignment(jl_binding_t *b, jl_value_t *rhs) { if (b->constp && b->value != NULL) { @@ -517,7 +553,8 @@ DLLEXPORT jl_value_t *jl_module_names(jl_module_t *m, int all, int imported) for(i=1; i < m->bindings.size; i+=2) { if (table[i] != HT_NOTFOUND) { jl_binding_t *b = (jl_binding_t*)table[i]; - if (b->exportp || ((imported || b->owner == m) && (all || m == jl_main_module))) { + if ((b->exportp || ((imported || b->owner == m) && (all || m == jl_main_module))) && + !b->deprecated) { jl_array_grow_end(a, 1); //XXX: change to jl_arrayset if array storage allocation for Array{Symbols,1} changes: jl_cellset(a, jl_array_dim0(a)-1, (jl_value_t*)b->name);