Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new deprecate_moved macro, better support for replacing deprecated bindings #22763

Merged
merged 5 commits into from Jul 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
65 changes: 20 additions & 45 deletions base/deprecated.jl
Expand Up @@ -103,7 +103,7 @@ function firstcaller(bt::Array{Ptr{Void},1}, funcsyms)
return lkup
end

deprecate(m::Module, s::Symbol) = ccall(:jl_deprecate_binding, Void, (Any, Any), m, s)
deprecate(m::Module, s::Symbol, flag=1) = ccall(:jl_deprecate_binding, Void, (Any, Any, Cint), m, s, flag)

macro deprecate_binding(old, new, export_old=true)
return Expr(:toplevel,
Expand All @@ -112,6 +112,18 @@ macro deprecate_binding(old, new, export_old=true)
Expr(:call, :deprecate, __module__, Expr(:quote, old)))
end

macro deprecate_moved(old, new, export_old=true)
eold = esc(old)
return Expr(:toplevel,
:(function $eold(args...; kwargs...)
error($eold, " has been moved to the package ", $new, ".jl.\n",
"Run `Pkg.add(\"", $new, "\")` to install it, restart Julia,\n",
Copy link
Member

Choose a reason for hiding this comment

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

Would it make sense to check whether new has been loaded and just forward the call in that case to avoid the need to restart Julia?

Copy link
Member Author

Choose a reason for hiding this comment

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

If you already did using new, you would never see this error message.

Copy link
Member

Choose a reason for hiding this comment

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

Exactly. Wouldn't that solve #22763 (comment)?

Copy link
Member Author

@stevengj stevengj Jul 12, 2017

Choose a reason for hiding this comment

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

If new has already been "loaded" (i.e. you already ran using new and it exports the eold symbol), then it will resolve eold to the new module and you won't see any error message.

If you haven't yet run using new in the current scope, then even if we could do eval(:(import $new)) and pass through the arguments to new.eold(...) with a warning message, I'm skeptical that we should. I doubt that Base should ever automatically import an external package as part of a deprecation.

"and then run `using ", $new, "` to load it.")
end),
export_old ? Expr(:export, eold) : nothing,
Expr(:call, :deprecate, __module__, Expr(:quote, old), 2))
end

# BEGIN 0.6-alpha deprecations (delete when 0.6 is released)

@deprecate isambiguous(m1::Method, m2::Method, b::Bool) isambiguous(m1, m2, ambiguous_bottom=b) false
Expand Down Expand Up @@ -684,21 +696,12 @@ end
@deprecate xor(A::AbstractArray, B::AbstractArray) xor.(A, B)

# QuadGK moved to a package (#19741)
function quadgk(args...; kwargs...)
error(string(quadgk, args, " has been moved to the package QuadGK.jl.\n",
"Run Pkg.add(\"QuadGK\") to install QuadGK on Julia v0.6 and later, and then run `using QuadGK`."))
end
export quadgk
@deprecate_moved quadgk "QuadGK"

# Collections functions moved to a package (#19800)
module Collections
export PriorityQueue, enqueue!, dequeue!, heapify!, heapify, heappop!, heappush!, isheap, peek
for f in (:PriorityQueue, :enqueue!, :dequeue!, :heapify!, :heapify, :heappop!, :heappush!, :isheap, :peek)
@eval function ($f)(args...; kwargs...)
error(string($f, args, " has been moved to the package DataStructures.jl.\n",
"Run Pkg.add(\"DataStructures\") to install DataStructures on Julia v0.6 and later, ",
"and then run `using DataStructures`."))
end
@eval Base.@deprecate_moved $f "DataStructures"
end
end
export Collections
Expand Down Expand Up @@ -1288,14 +1291,7 @@ for f in (:airyai, :airyaiprime, :airybi, :airybiprime, :airyaix, :airyaiprimex,
:eta, :zeta, :digamma, :invdigamma, :polygamma, :trigamma,
:hankelh1, :hankelh1x, :hankelh2, :hankelh2x,
:airy, :airyx, :airyprime)
@eval begin
function $f(args...; kwargs...)
error(string($f, args, " has been moved to the package SpecialFunctions.jl.\n",
"Run Pkg.add(\"SpecialFunctions\") to install SpecialFunctions on Julia v0.6 and later,\n",
"and then run `using SpecialFunctions`."))
end
export $f
end
@eval @deprecate_moved $f "SpecialFunctions"
end

@deprecate_binding LinearIndexing IndexStyle false
Expand Down Expand Up @@ -1443,43 +1439,22 @@ module DFT
:plan_dct, :plan_dct!, :plan_fft, :plan_fft!, :plan_idct, :plan_idct!,
:plan_ifft, :plan_ifft!, :plan_irfft, :plan_rfft, :rfft]
pkg = endswith(String(f), "shift") ? "AbstractFFTs" : "FFTW"
@eval begin
function $f(args...; kwargs...)
error($f, " has been moved to the package $($pkg).jl.\n",
"Run `Pkg.add(\"$($pkg)\")` to install $($pkg) then run `using $($pkg)` ",
"to load it.")
end
export $f
end
@eval Base.@deprecate_moved $f $pkg
end
module FFTW
for f in [:r2r, :r2r!, :plan_r2r, :plan_r2r!]
@eval begin
function $f(args...; kwargs...)
error($f, " has been moved to the package FFTW.jl.\n",
"Run `Pkg.add(\"FFTW\")` to install FFTW then run `using FFTW` ",
"to load it.")
end
export $f
end
@eval Base.@deprecate_moved $f "FFTW"
end
end
export FFTW
end
using .DFT
for f in names(DFT)
for f in filter(s -> isexported(DFT, s), names(DFT, true))
@eval export $f
end
module DSP
for f in [:conv, :conv2, :deconv, :filt, :filt!, :xcorr]
@eval begin
function $f(args...; kwargs...)
error($f, " has been moved to the package DSP.jl.\n",
"Run `Pkg.add(\"DSP\")` to install DSP then run `using DSP` ",
"to load it.")
end
export $f
end
@eval Base.@deprecate_moved $f "DSP"
end
end
using .DSP
Expand Down
2 changes: 1 addition & 1 deletion src/julia.h
Expand Up @@ -401,7 +401,7 @@ typedef struct {
uint8_t constp:1;
uint8_t exportp:1;
uint8_t imported:1;
uint8_t deprecated:1;
uint8_t deprecated:2; // 0=not deprecated, 1=renamed, 2=moved to another package
} jl_binding_t;

typedef struct _jl_module_t {
Expand Down
12 changes: 9 additions & 3 deletions src/module.c
Expand Up @@ -191,6 +191,7 @@ static jl_binding_t *jl_get_binding_(jl_module_t *m, jl_sym_t *var, modstack_t *
// couldn't resolve; try next using (see issue #6105)
continue;
if (owner != NULL && tempb->owner != b->owner &&
!tempb->deprecated && !b->deprecated &&
!(tempb->constp && tempb->value && b->constp && b->value == tempb->value)) {
jl_printf(JL_STDERR,
"WARNING: both %s and %s export \"%s\"; uses of it in module %s must be qualified\n",
Expand Down Expand Up @@ -461,10 +462,12 @@ JL_DLLEXPORT int jl_is_const(jl_module_t *m, jl_sym_t *var)
return b && b->constp;
}

JL_DLLEXPORT void jl_deprecate_binding(jl_module_t *m, jl_sym_t *var)
// set the deprecated flag for a binding:
// 0=not deprecated, 1=renamed, 2=moved to another package
JL_DLLEXPORT void jl_deprecate_binding(jl_module_t *m, jl_sym_t *var, int flag)
{
jl_binding_t *b = jl_get_binding(m, var);
if (b) b->deprecated = 1;
if (b) b->deprecated = flag;
}

JL_DLLEXPORT int jl_is_binding_deprecated(jl_module_t *m, jl_sym_t *var)
Expand All @@ -478,7 +481,10 @@ extern int jl_lineno;

void jl_binding_deprecation_warning(jl_binding_t *b)
{
if (b->deprecated && jl_options.depwarn) {
// Only print a warning for deprecated == 1 (renamed).
// For deprecated == 2 (moved to a package) the binding is to a function
// that throws an error, so we don't want to print a warning too.
if (b->deprecated == 1 && jl_options.depwarn) {
if (jl_options.depwarn != JL_OPTIONS_DEPWARN_ERROR)
jl_printf(JL_STDERR, "WARNING: ");
if (b->owner)
Expand Down
12 changes: 12 additions & 0 deletions test/misc.jl
Expand Up @@ -769,10 +769,22 @@ module DeprecationTests # to test @deprecate
# test deprecation of a constructor
struct A{T} end
@deprecate A{T}(x::S) where {T, S} f()

# test that @deprecate_moved can be overridden by an import
Base.@deprecate_moved foo1234 "Foo"
Base.@deprecate_moved bar "Bar" false
end # module
module Foo1234
export foo1234
foo1234(x) = x+1
end

@testset "@deprecate" begin
using .DeprecationTests
using .Foo1234
@test foo1234(3) == 4
@test_throws ErrorException DeprecationTests.bar(3)

# enable when issue #22043 is fixed
# @test @test_warn "f1 is deprecated, use f instead." f1()
# @test @test_nowarn f1()
Expand Down