diff --git a/Compiler/src/Compiler.jl b/Compiler/src/Compiler.jl index 2c68729ee1dc2..b7c04f3b647ae 100644 --- a/Compiler/src/Compiler.jl +++ b/Compiler/src/Compiler.jl @@ -50,6 +50,7 @@ using Core: ABIOverride, Builtin, CodeInstance, IntrinsicFunction, MethodInstanc using Base using Base: @_foldable_meta, @_gc_preserve_begin, @_gc_preserve_end, @nospecializeinfer, BINDING_KIND_GLOBAL, BINDING_KIND_UNDEF_CONST, BINDING_KIND_BACKDATED_CONST, BINDING_KIND_DECLARED, + BINDING_FLAG_DEPWARN, Base, BitVector, Bottom, Callable, DataTypeFieldDesc, EffectsOverride, Filter, Generator, IteratorSize, JLOptions, NUM_EFFECTS_OVERRIDES, OneTo, Ordering, RefValue, SizeUnknown, _NAMEDTUPLE_NAME, diff --git a/Compiler/src/abstractinterpretation.jl b/Compiler/src/abstractinterpretation.jl index ebfdaba45d34b..4296ec9057277 100644 --- a/Compiler/src/abstractinterpretation.jl +++ b/Compiler/src/abstractinterpretation.jl @@ -2391,7 +2391,7 @@ function abstract_throw_methoderror(interp::AbstractInterpreter, argtypes::Vecto return Future(CallMeta(Union{}, exct, EFFECTS_THROWS, NoCallInfo())) end -const generic_getglobal_effects = Effects(EFFECTS_THROWS, consistent=ALWAYS_FALSE, inaccessiblememonly=ALWAYS_FALSE) +const generic_getglobal_effects = Effects(EFFECTS_THROWS, effect_free=ALWAYS_FALSE, consistent=ALWAYS_FALSE, inaccessiblememonly=ALWAYS_FALSE) #= effect_free for depwarn =# const generic_getglobal_exct = Union{ArgumentError, TypeError, ConcurrencyViolationError, UndefVarError} function abstract_eval_getglobal(interp::AbstractInterpreter, sv::AbsIntState, saw_latestworld::Bool, @nospecialize(M), @nospecialize(s)) ⊑ = partialorder(typeinf_lattice(interp)) @@ -3519,13 +3519,15 @@ end function abstract_eval_partition_load(interp::AbstractInterpreter, partition::Core.BindingPartition) kind = binding_kind(partition) + isdepwarn = (partition.kind & BINDING_FLAG_DEPWARN) != 0 + local_getglobal_effects = Effects(generic_getglobal_effects, effect_free=isdepwarn ? ALWAYS_FALSE : ALWAYS_TRUE) if is_some_guard(kind) || kind == BINDING_KIND_UNDEF_CONST if InferenceParams(interp).assume_bindings_static return RTEffects(Union{}, UndefVarError, EFFECTS_THROWS) else # We do not currently assume an invalidation for guard -> defined transitions # return RTEffects(Union{}, UndefVarError, EFFECTS_THROWS) - return RTEffects(Any, UndefVarError, generic_getglobal_effects) + return RTEffects(Any, UndefVarError, local_getglobal_effects) end end @@ -3533,10 +3535,12 @@ function abstract_eval_partition_load(interp::AbstractInterpreter, partition::Co if kind == BINDING_KIND_BACKDATED_CONST # Infer this as guard. We do not want a later const definition to retroactively improve # inference results in an earlier world. - return RTEffects(Any, UndefVarError, generic_getglobal_effects) + return RTEffects(Any, UndefVarError, local_getglobal_effects) end rt = Const(partition_restriction(partition)) - return RTEffects(rt, Union{}, Effects(EFFECTS_TOTAL, inaccessiblememonly=is_mutation_free_argtype(rt) ? ALWAYS_TRUE : ALWAYS_FALSE)) + return RTEffects(rt, Union{}, Effects(EFFECTS_TOTAL, + inaccessiblememonly=is_mutation_free_argtype(rt) ? ALWAYS_TRUE : ALWAYS_FALSE, + effect_free=isdepwarn ? ALWAYS_FALSE : ALWAYS_TRUE)) end if kind == BINDING_KIND_DECLARED @@ -3544,7 +3548,7 @@ function abstract_eval_partition_load(interp::AbstractInterpreter, partition::Co else rt = partition_restriction(partition) end - return RTEffects(rt, UndefVarError, generic_getglobal_effects) + return RTEffects(rt, UndefVarError, local_getglobal_effects) end function abstract_eval_globalref(interp::AbstractInterpreter, g::GlobalRef, saw_latestworld::Bool, sv::AbsIntState) diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index 2c7bfa70055ae..cce71bdecffb4 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -210,6 +210,11 @@ const BINDING_KIND_UNDEF_CONST = 0x9 const BINDING_KIND_BACKDATED_CONST = 0xa const BINDING_FLAG_EXPORTED = 0x10 +const BINDING_FLAG_DEPRECATED = 0x20 +const BINDING_FLAG_DEPWARN = 0x40 + +const BINDING_KIND_MASK = 0x0f +const BINDING_FLAG_MASK = 0xf0 is_defined_const_binding(kind::UInt8) = (kind == BINDING_KIND_CONST || kind == BINDING_KIND_CONST_IMPORT || kind == BINDING_KIND_BACKDATED_CONST) is_some_const_binding(kind::UInt8) = (is_defined_const_binding(kind) || kind == BINDING_KIND_UNDEF_CONST) diff --git a/base/show.jl b/base/show.jl index e106b1479c990..4f3a27c14b8b2 100644 --- a/base/show.jl +++ b/base/show.jl @@ -3375,8 +3375,21 @@ function print_partition(io::IO, partition::Core.BindingPartition) else print(io, max_world) end - if (partition.kind & BINDING_FLAG_EXPORTED) != 0 - print(io, " [exported]") + if (partition.kind & BINDING_FLAG_MASK) != 0 + first = false + print(io, " [") + if (partition.kind & BINDING_FLAG_EXPORTED) != 0 + print(io, "exported") + end + if (partition.kind & BINDING_FLAG_DEPRECATED) != 0 + first ? (first = false) : print(io, ",") + print(io, "deprecated") + end + if (partition.kind & BINDING_FLAG_DEPWARN) != 0 + first ? (first = false) : print(io, ",") + print(io, "depwarn") + end + print(io, "]") end print(io, " - ") kind = binding_kind(partition) diff --git a/src/codegen.cpp b/src/codegen.cpp index fe2bd4fc3d097..5a7fc758f89af 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -907,13 +907,12 @@ static const auto jldeclareglobal_func = new JuliaFunction<>{ {T_pjlvalue, T_pjlvalue, T_prjlvalue, getInt32Ty(C)}, false); }, nullptr, }; -static const auto jlgetbindingorerror_func = new JuliaFunction<>{ - XSTR(jl_get_binding_or_error), +static const auto jldepcheck_func = new JuliaFunction<>{ + XSTR(jl_binding_deprecation_check), [](LLVMContext &C) { auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C); - return FunctionType::get(T_pjlvalue, - {T_pjlvalue, T_pjlvalue}, false); - }, + return FunctionType::get(getVoidTy(C), + {T_pjlvalue}, false); }, nullptr, }; static const auto jlcheckbpwritable_func = new JuliaFunction<>{ @@ -3067,20 +3066,6 @@ static void mallocVisitLine(jl_codectx_t &ctx, StringRef filename, int line, Val // --- constant determination --- -static void show_source_loc(jl_codectx_t &ctx, JL_STREAM *out) -{ - jl_printf(out, "in %s at %s", ctx.name, ctx.file.str().c_str()); -} - -static void cg_bdw(jl_codectx_t &ctx, jl_sym_t *var, jl_binding_t *b) -{ - jl_binding_deprecation_warning(ctx.module, var, b); - if (b->deprecated == 1 && jl_options.depwarn) { - show_source_loc(ctx, JL_STDERR); - jl_printf(JL_STDERR, "\n"); - } -} - static jl_value_t *static_apply_type(jl_codectx_t &ctx, ArrayRef args, size_t nargs) { assert(nargs > 1); @@ -3105,6 +3090,12 @@ static jl_value_t *static_apply_type(jl_codectx_t &ctx, ArrayRef arg return result; } +static void emit_depwarn_check(jl_codectx_t &ctx, jl_binding_t *b) +{ + Value *bp = julia_binding_gv(ctx, b); + ctx.builder.CreateCall(prepare_call(jldepcheck_func), { bp }); +} + // try to statically evaluate, NULL if not possible. note that this may allocate, and as // such the resulting value should not be embedded directly in the generated code. static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex) @@ -3113,9 +3104,13 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex) jl_sym_t *sym = (jl_sym_t*)ex; jl_binding_t *bnd = jl_get_module_binding(ctx.module, sym, 0); jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world); - jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world); - if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart))) + int possibly_deprecated = 0; + jl_walk_binding_inplace_all(&bnd, &bpart, &possibly_deprecated, ctx.min_world, ctx.max_world); + if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart))) { + if (possibly_deprecated) + emit_depwarn_check(ctx, bnd); return bpart->restriction; + } return NULL; } if (jl_is_slotnumber(ex) || jl_is_argument(ex)) @@ -3138,13 +3133,14 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex) s = jl_globalref_name(ex); jl_binding_t *bnd = jl_get_module_binding(jl_globalref_mod(ex), s, 0); jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world); - jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world); + int possibly_deprecated = 0; + jl_walk_binding_inplace_all(&bnd, &bpart, &possibly_deprecated, ctx.min_world, ctx.max_world); jl_value_t *v = NULL; if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart))) v = bpart->restriction; if (v) { - if (bnd->deprecated) - cg_bdw(ctx, s, bnd); + if (possibly_deprecated) + emit_depwarn_check(ctx, bnd); return v; } return NULL; @@ -3165,13 +3161,14 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex) if (s && jl_is_symbol(s)) { jl_binding_t *bnd = jl_get_module_binding(m, s, 0); jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world); - jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world); + int possibly_deprecated = 0; + jl_walk_binding_inplace_all(&bnd, &bpart, &possibly_deprecated, ctx.min_world, ctx.max_world); jl_value_t *v = NULL; if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart))) v = bpart->restriction; if (v) { - if (bnd->deprecated) - cg_bdw(ctx, s, bnd); + if (possibly_deprecated) + emit_depwarn_check(ctx, bnd); return v; } } @@ -3417,48 +3414,47 @@ static jl_cgval_t emit_globalref(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t * if (!bpart) { return emit_globalref_runtime(ctx, bnd, mod, name); } - // bpart was updated in place - this will change with full partition - if (jl_bkind_is_some_guard(jl_binding_kind(bpart))) { - // Redo the lookup at runtime - return emit_globalref_runtime(ctx, bnd, mod, name); - } else { - while (true) { - if (!bpart) - break; - if (!jl_bkind_is_some_import(jl_binding_kind(bpart))) - break; - if (bnd->deprecated) { - cg_bdw(ctx, name, bnd); - } - bnd = (jl_binding_t*)bpart->restriction; - bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world); - if (!bpart) - break; - } - if (bpart) { - enum jl_partition_kind kind = jl_binding_kind(bpart); - if (jl_bkind_is_some_constant(kind) && kind != BINDING_KIND_BACKDATED_CONST) { - jl_value_t *constval = bpart->restriction; - if (!constval) { - undef_var_error_ifnot(ctx, ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0), name, (jl_value_t*)mod); - return jl_cgval_t(); - } - return mark_julia_const(ctx, constval); + int possibly_deprecated = 0; + int saw_explicit = 0; + while (bpart) { + if (!saw_explicit && (bpart->kind & BINDING_FLAG_DEPWARN)) + possibly_deprecated = 1; + enum jl_partition_kind kind = jl_binding_kind(bpart); + if (!jl_bkind_is_some_import(kind)) + break; + if (kind != BINDING_KIND_IMPLICIT) + saw_explicit = 1; + bnd = (jl_binding_t*)bpart->restriction; + bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world); + } + Value *bp = NULL; + if (bpart) { + enum jl_partition_kind kind = jl_binding_kind(bpart); + if (jl_bkind_is_some_constant(kind) && kind != BINDING_KIND_BACKDATED_CONST) { + if (possibly_deprecated) { + bp = julia_binding_gv(ctx, bnd); + ctx.builder.CreateCall(prepare_call(jldepcheck_func), { bp }); + } + jl_value_t *constval = bpart->restriction; + if (!constval) { + undef_var_error_ifnot(ctx, ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0), name, (jl_value_t*)mod); + return jl_cgval_t(); } + return mark_julia_const(ctx, constval); } } if (!bpart || jl_binding_kind(bpart) != BINDING_KIND_GLOBAL) { return emit_globalref_runtime(ctx, bnd, mod, name); } - Value *bp = julia_binding_gv(ctx, bnd); - if (bnd->deprecated) { - cg_bdw(ctx, name, bnd); + bp = julia_binding_gv(ctx, bnd); + if (possibly_deprecated) { + ctx.builder.CreateCall(prepare_call(jldepcheck_func), { bp }); } jl_value_t *ty = bpart->restriction; - bp = julia_binding_pvalue(ctx, bp); + Value *bpval = julia_binding_pvalue(ctx, bp); if (ty == nullptr) ty = (jl_value_t*)jl_any_type; - return update_julia_type(ctx, emit_checked_var(ctx, bp, name, (jl_value_t*)mod, false, ctx.tbaa().tbaa_binding), ty); + return update_julia_type(ctx, emit_checked_var(ctx, bpval, name, (jl_value_t*)mod, false, ctx.tbaa().tbaa_binding), ty); } static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *sym, jl_cgval_t rval, const jl_cgval_t &cmp, @@ -3471,6 +3467,7 @@ static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *s Value *bp = julia_binding_gv(ctx, bnd); if (bpart) { if (jl_binding_kind(bpart) == BINDING_KIND_GLOBAL) { + int possibly_deprecated = bpart->kind & BINDING_FLAG_DEPWARN; jl_value_t *ty = bpart->restriction; if (ty != nullptr) { const std::string fname = issetglobal ? "setglobal!" : isreplaceglobal ? "replaceglobal!" : isswapglobal ? "swapglobal!" : ismodifyglobal ? "modifyglobal!" : "setglobalonce!"; @@ -3483,6 +3480,9 @@ static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *s } bool isboxed = true; bool maybe_null = jl_atomic_load_relaxed(&bnd->value) == NULL; + if (possibly_deprecated) { + ctx.builder.CreateCall(prepare_call(jldepcheck_func), { bp }); + } return typed_store(ctx, julia_binding_pvalue(ctx, bp), rval, cmp, ty, @@ -10308,7 +10308,6 @@ static void init_jit_functions(void) add_named_global(memcmp_func, &memcmp); add_named_global(jltypeerror_func, &jl_type_error); add_named_global(jlcheckassign_func, &jl_checked_assignment); - add_named_global(jlgetbindingorerror_func, &jl_get_binding_or_error); add_named_global(jlcheckbpwritable_func, &jl_check_binding_currently_writable); add_named_global(jlboundp_func, &jl_boundp); for (auto it : builtin_func_map()) diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 664e1270c7381..046f690253183 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -193,7 +193,6 @@ XX(jl_get_backtrace) \ XX(jl_get_binding) \ XX(jl_get_binding_for_method_def) \ - XX(jl_get_binding_or_error) \ XX(jl_get_binding_wr) \ XX(jl_check_binding_currently_writable) \ XX(jl_get_cpu_name) \ diff --git a/src/julia.h b/src/julia.h index 6acd82fdd3401..cb62f249f2418 100644 --- a/src/julia.h +++ b/src/julia.h @@ -707,8 +707,21 @@ enum jl_partition_kind { BINDING_KIND_IMPLICIT_RECOMPUTE = 0xb }; -// These are flags that get anded into the above +static const uint8_t BINDING_KIND_MASK = 0x0f; +static const uint8_t BINDING_FLAG_MASK = 0xf0; + +//// These are flags that get anded into the above +// +// _EXPORTED: This binding partition is exported. In the world ranges covered by this partitions, +// other modules that `using` this module, may implicit import this binding. static const uint8_t BINDING_FLAG_EXPORTED = 0x10; +// _DEPRECATED: This binding partition is deprecated. It is considered weak for the purposes of +// implicit import resolution. +static const uint8_t BINDING_FLAG_DEPRECATED = 0x20; +// _DEPWARN: This binding partition will print a deprecation warning on access. Note that _DEPWARN +// implies _DEPRECATED. However, the reverse is not true. Such bindings are usually used for functions, +// where calling the function itself will provide a (better) deprecation warning/error. +static const uint8_t BINDING_FLAG_DEPWARN = 0x40; typedef struct __attribute__((aligned(8))) _jl_binding_partition_t { JL_DATA_TYPE @@ -2054,7 +2067,6 @@ JL_DLLEXPORT jl_value_t *jl_get_module_binding_or_nothing(jl_module_t *m, jl_sym // get binding for reading JL_DLLEXPORT jl_binding_t *jl_get_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var); -JL_DLLEXPORT jl_binding_t *jl_get_binding_or_error(jl_module_t *m, jl_sym_t *var); JL_DLLEXPORT jl_value_t *jl_module_globalref(jl_module_t *m, jl_sym_t *var); JL_DLLEXPORT jl_value_t *jl_get_binding_type(jl_module_t *m, jl_sym_t *var); // get binding for assignment diff --git a/src/julia_internal.h b/src/julia_internal.h index 782e0bd0b1a96..f065c6becb3a0 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -913,7 +913,7 @@ JL_DLLEXPORT jl_value_t *jl_nth_slot_type(jl_value_t *sig JL_PROPAGATES_ROOT, si void jl_compute_field_offsets(jl_datatype_t *st); void jl_module_run_initializer(jl_module_t *m); JL_DLLEXPORT jl_binding_t *jl_get_module_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, int alloc); -JL_DLLEXPORT void jl_binding_deprecation_warning(jl_module_t *m, jl_sym_t *sym, jl_binding_t *b); +JL_DLLEXPORT void jl_binding_deprecation_warning(jl_binding_t *b); JL_DLLEXPORT jl_binding_partition_t *jl_replace_binding_locked(jl_binding_t *b JL_PROPAGATES_ROOT, jl_binding_partition_t *old_bpart, jl_value_t *restriction_val, enum jl_partition_kind kind, size_t new_world) JL_GLOBALLY_ROOTED; JL_DLLEXPORT jl_binding_partition_t *jl_replace_binding_locked2(jl_binding_t *b JL_PROPAGATES_ROOT, @@ -964,7 +964,7 @@ EXTERN_INLINE_DECLARE uint8_t jl_bpart_get_kind(jl_binding_partition_t *bpart) J } STATIC_INLINE void jl_walk_binding_inplace(jl_binding_t **bnd, jl_binding_partition_t **bpart JL_PROPAGATES_ROOT, size_t world) JL_NOTSAFEPOINT; -STATIC_INLINE void jl_walk_binding_inplace_all(jl_binding_t **bnd, jl_binding_partition_t **bpart JL_PROPAGATES_ROOT, size_t min_world, size_t max_world) JL_NOTSAFEPOINT; +STATIC_INLINE void jl_walk_binding_inplace_all(jl_binding_t **bnd, jl_binding_partition_t **bpart JL_PROPAGATES_ROOT, int *depwarn, size_t min_world, size_t max_world) JL_NOTSAFEPOINT; #ifndef __clang_analyzer__ STATIC_INLINE void jl_walk_binding_inplace(jl_binding_t **bnd, jl_binding_partition_t **bpart, size_t world) JL_NOTSAFEPOINT @@ -977,13 +977,40 @@ STATIC_INLINE void jl_walk_binding_inplace(jl_binding_t **bnd, jl_binding_partit } } -STATIC_INLINE void jl_walk_binding_inplace_all(jl_binding_t **bnd, jl_binding_partition_t **bpart, size_t min_world, size_t max_world) JL_NOTSAFEPOINT +STATIC_INLINE void jl_walk_binding_inplace_depwarn(jl_binding_t **bnd, jl_binding_partition_t **bpart, size_t world, int *depwarn) JL_NOTSAFEPOINT { + int passed_explicit = 0; while (1) { - if (!(*bpart)) + enum jl_partition_kind kind = jl_binding_kind(*bpart); + if (!jl_bkind_is_some_import(kind)) { + if (!passed_explicit && depwarn) + *depwarn |= (*bpart)->kind & BINDING_FLAG_DEPWARN; return; - if (!jl_bkind_is_some_import(jl_binding_kind(*bpart))) + } + if (!passed_explicit && depwarn) + *depwarn |= (*bpart)->kind & BINDING_FLAG_DEPWARN; + if (kind != BINDING_KIND_IMPLICIT) + passed_explicit = 1; + *bnd = (jl_binding_t*)(*bpart)->restriction; + *bpart = jl_get_binding_partition(*bnd, world); + } +} + + +STATIC_INLINE void jl_walk_binding_inplace_all(jl_binding_t **bnd, jl_binding_partition_t **bpart, int *depwarn, size_t min_world, size_t max_world) JL_NOTSAFEPOINT +{ + int passed_explicit = 0; + while (*bpart) { + enum jl_partition_kind kind = jl_binding_kind(*bpart); + if (!jl_bkind_is_some_import(kind)) { + if (!passed_explicit && depwarn) + *depwarn |= (*bpart)->kind & BINDING_FLAG_DEPWARN; return; + } + if (!passed_explicit && depwarn) + *depwarn |= (*bpart)->kind & BINDING_FLAG_DEPWARN; + if (kind != BINDING_KIND_IMPLICIT) + passed_explicit = 1; *bnd = (jl_binding_t*)(*bpart)->restriction; *bpart = jl_get_binding_partition_all(*bnd, min_world, max_world); } diff --git a/src/module.c b/src/module.c index 50d4d90c7f4bb..98bfadeaf07b0 100644 --- a/src/module.c +++ b/src/module.c @@ -110,7 +110,7 @@ void jl_check_new_binding_implicit( continue; if (impb) { - if (tempb->deprecated) + if (tempbpart->kind & BINDING_FLAG_DEPRECATED) continue; if (jl_binding_kind(tempbpart) == BINDING_KIND_GUARD && jl_binding_kind(impbpart) != BINDING_KIND_GUARD) @@ -128,7 +128,7 @@ void jl_check_new_binding_implicit( guard_kind = BINDING_KIND_FAILED; break; } - else if (tempb->deprecated) { + else if (tempbpart->kind & BINDING_FLAG_DEPRECATED) { if (deprecated_impb) { if (!eq_bindings(tempbpart, deprecated_impb, world)) { guard_kind = BINDING_KIND_FAILED; @@ -315,7 +315,7 @@ JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3( jl_symbol_name(mod->name), jl_symbol_name(var)); } if (bpart->min_world == new_world) { - bpart->kind = constant_kind | (bpart->kind & 0xf0); + bpart->kind = constant_kind | (bpart->kind & BINDING_FLAG_MASK); bpart->restriction = val; if (val) jl_gc_wb(bpart, val); @@ -497,7 +497,6 @@ static jl_binding_t *new_binding(jl_module_t *mod, jl_sym_t *name) b->globalref = NULL; b->backedges = NULL; b->publicp = 0; - b->deprecated = 0; b->did_print_backdate_admonition = 0; b->did_print_implicit_import_admonition = 0; JL_GC_PUSH1(&b); @@ -544,6 +543,9 @@ static jl_module_t *jl_binding_dbgmodule(jl_binding_t *b, jl_module_t *m, jl_sym JL_DLLEXPORT void jl_check_binding_currently_writable(jl_binding_t *b, jl_module_t *m, jl_sym_t *s) { jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + if (jl_options.depwarn && (bpart->kind & BINDING_FLAG_DEPWARN)) { + jl_binding_deprecation_warning(b); + } enum jl_partition_kind kind = jl_binding_kind(bpart); if (kind != BINDING_KIND_GLOBAL && kind != BINDING_KIND_DECLARED && !jl_bkind_is_some_constant(kind)) { if (jl_bkind_is_some_guard(kind)) { @@ -615,6 +617,29 @@ JL_DLLEXPORT jl_value_t *jl_get_binding_value(jl_binding_t *b) return jl_atomic_load_relaxed(&b->value); } +JL_DLLEXPORT jl_value_t *jl_get_binding_value_depwarn(jl_binding_t *b) +{ + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + if (jl_options.depwarn) { + int needs_depwarn = 0; + jl_walk_binding_inplace_depwarn(&b, &bpart, jl_current_task->world_age, &needs_depwarn); + if (needs_depwarn) + jl_binding_deprecation_warning(b); + } else { + jl_walk_binding_inplace(&b, &bpart, jl_current_task->world_age); + } + enum jl_partition_kind kind = jl_binding_kind(bpart); + if (jl_bkind_is_some_guard(kind)) + return NULL; + if (jl_bkind_is_some_constant(kind)) { + check_backdated_binding(b, kind); + return bpart->restriction; + } + assert(!jl_bkind_is_some_import(kind)); + return jl_atomic_load_relaxed(&b->value); +} + + JL_DLLEXPORT jl_value_t *jl_get_binding_value_seqcst(jl_binding_t *b) { jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); @@ -772,7 +797,7 @@ static jl_module_t *jl_binding_dbgmodule(jl_binding_t *b, jl_module_t *m, jl_sym return m; } -static void jl_binding_dep_message(jl_module_t *m, jl_sym_t *name, jl_binding_t *b); +static void jl_binding_dep_message(jl_binding_t *b); // get type of binding m.var, without resolving the binding JL_DLLEXPORT jl_value_t *jl_get_binding_type(jl_module_t *m, jl_sym_t *var) @@ -800,17 +825,6 @@ JL_DLLEXPORT jl_binding_t *jl_get_binding(jl_module_t *m, jl_sym_t *var) return jl_get_module_binding(m, var, 1); } -JL_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); - if (b == NULL) - jl_undefined_var_error(var, (jl_value_t*)m); - // XXX: this only considers if the original is deprecated, not the binding in m - if (b->deprecated) - jl_binding_deprecation_warning(m, var, b); - return b; -} - JL_DLLEXPORT jl_value_t *jl_module_globalref(jl_module_t *m, jl_sym_t *var) { jl_binding_t *b = jl_get_module_binding(m, var, 1); @@ -832,8 +846,10 @@ extern int jl_lineno; static char const dep_message_prefix[] = "_dep_message_"; -static void jl_binding_dep_message(jl_module_t *m, jl_sym_t *name, jl_binding_t *b) +static void jl_binding_dep_message(jl_binding_t *b) { + jl_module_t *m = b->globalref->mod; + jl_sym_t *name = b->globalref->name; size_t prefix_len = strlen(dep_message_prefix); size_t name_len = strlen(jl_symbol_name(name)); char *dep_binding_name = (char*)alloca(prefix_len+name_len+1); @@ -893,7 +909,7 @@ static void module_import_(jl_module_t *to, jl_module_t *from, jl_sym_t *asname, check_safe_import_from(from); jl_binding_t *b = jl_get_binding(from, s); jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - if (b->deprecated) { + if (bpart->kind & BINDING_FLAG_DEPRECATED) { if (jl_get_binding_value(b) == jl_nothing) { // silently skip importing deprecated values assigned to nothing (to allow later mutation) return; @@ -909,7 +925,7 @@ static void module_import_(jl_module_t *to, jl_module_t *from, jl_sym_t *asname, jl_symbol_name(to->name), asname == s ? "" : " as ", asname == s ? "" : jl_symbol_name(asname)); - jl_binding_dep_message(from, s, b); + jl_binding_dep_message(b); } } @@ -1204,19 +1220,13 @@ JL_DLLEXPORT jl_value_t *jl_get_globalref_value(jl_globalref_t *gr) jl_binding_t *b = gr->binding; if (!b) b = jl_get_module_binding(gr->mod, gr->name, 1); - // ignores b->deprecated - return b == NULL ? NULL : jl_get_binding_value(b); + return jl_get_binding_value_depwarn(b); } JL_DLLEXPORT 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; - // XXX: this only considers if the original is deprecated, not the binding in m - if (b->deprecated) - jl_binding_deprecation_warning(m, var, b); - return jl_get_binding_value(b); + jl_binding_t *b = jl_get_module_binding(m, var, 1); + return jl_get_binding_value_depwarn(b); } JL_DLLEXPORT void jl_set_global(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT) @@ -1232,7 +1242,7 @@ JL_DLLEXPORT void jl_set_const(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var jl_binding_partition_t *bpart = jl_get_binding_partition(bp, jl_current_task->world_age); bpart->min_world = 0; jl_atomic_store_release(&bpart->max_world, ~(size_t)0); - bpart->kind = BINDING_KIND_CONST | (bpart->kind & 0xf0); + bpart->kind = BINDING_KIND_CONST | (bpart->kind & BINDING_FLAG_MASK); bpart->restriction = val; jl_gc_wb(bpart, val); } @@ -1291,7 +1301,7 @@ JL_DLLEXPORT jl_binding_partition_t *jl_replace_binding_locked(jl_binding_t *b, jl_binding_partition_t *old_bpart, jl_value_t *restriction_val, enum jl_partition_kind kind, size_t new_world) { // Copy flags from old bpart - return jl_replace_binding_locked2(b, old_bpart, restriction_val, (size_t)kind | (size_t)(old_bpart->kind & 0xf0), + return jl_replace_binding_locked2(b, old_bpart, restriction_val, (size_t)kind | (size_t)(old_bpart->kind & BINDING_FLAG_MASK), new_world); } @@ -1313,10 +1323,10 @@ JL_DLLEXPORT jl_binding_partition_t *jl_replace_binding_locked2(jl_binding_t *b, jl_binding_partition_t *new_bpart = new_binding_partition(); JL_GC_PUSH1(&new_bpart); new_bpart->min_world = new_world; - if ((kind & 0x0f) == BINDING_KIND_IMPLICIT_RECOMPUTE) { + if ((kind & BINDING_KIND_MASK) == BINDING_KIND_IMPLICIT_RECOMPUTE) { assert(!restriction_val); jl_check_new_binding_implicit(new_bpart /* callee rooted */, b, NULL, new_world); - new_bpart->kind |= kind & 0xf0; + new_bpart->kind |= kind & BINDING_FLAG_MASK; } else { new_bpart->kind = kind; @@ -1402,44 +1412,95 @@ JL_DLLEXPORT int jl_is_const(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 +static const size_t DEPWARN_FLAGS = BINDING_FLAG_DEPRECATED | BINDING_FLAG_DEPWARN; JL_DLLEXPORT void jl_deprecate_binding(jl_module_t *m, jl_sym_t *var, int flag) { - // XXX: this deprecates the original value, which might be imported from elsewhere jl_binding_t *b = jl_get_binding(m, var); - if (b) b->deprecated = flag; + size_t new_flags = flag == 1 ? BINDING_FLAG_DEPRECATED | BINDING_FLAG_DEPWARN : + flag == 2 ? BINDING_FLAG_DEPRECATED : + 0; + JL_LOCK(&world_counter_lock); + size_t new_world = jl_atomic_load_acquire(&jl_world_counter)+1; + jl_binding_partition_t *old_bpart = jl_get_binding_partition(b, jl_current_task->world_age); + if ((old_bpart->kind & DEPWARN_FLAGS) == new_flags) { + JL_UNLOCK(&world_counter_lock); + return; + } + jl_replace_binding_locked2(b, old_bpart, old_bpart->restriction, + (old_bpart->kind & ~DEPWARN_FLAGS) | new_flags, new_world); + jl_atomic_store_release(&jl_world_counter, new_world); + JL_UNLOCK(&world_counter_lock); +} + +static int should_depwarn(jl_binding_t *b, uint8_t flag) +{ + // We consider bindings deprecated, if: + // + // 1. The binding itself is deprecated, or + // 2. We implicitly import any deprecated binding. + // + // However, we do not consider the binding deprecated if the import was an explicit + // (`using` or `import`). The logic here is that the thing that needs to be adjusted + // is not the use itself, but rather the `using` or `import` (which already prints + // an appropriate warning). + for (;;) { + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + if (bpart->kind & BINDING_FLAG_DEPRECATED) + return 1; + if ((bpart->kind & BINDING_KIND_MASK) != BINDING_KIND_IMPLICIT) + break; + b = (jl_binding_t*)bpart->restriction; + } + return 0; +} + +JL_DLLEXPORT void jl_binding_deprecation_check(jl_binding_t *b) +{ + if (jl_options.depwarn && should_depwarn(b, BINDING_FLAG_DEPWARN)) + jl_binding_deprecation_warning(b); } JL_DLLEXPORT int jl_is_binding_deprecated(jl_module_t *m, jl_sym_t *var) { - // XXX: this only considers if the original is deprecated, not this precise binding - jl_binding_t *b = jl_get_binding(m, var); - return b && b->deprecated; + jl_binding_t *b = jl_get_module_binding(m, var, 0); + if (!b) + return 0; + return should_depwarn(b, BINDING_FLAG_DEPRECATED); } -void jl_binding_deprecation_warning(jl_module_t *m, jl_sym_t *s, jl_binding_t *b) +void jl_binding_deprecation_warning(jl_binding_t *b) { - // 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: "); - jl_printf(JL_STDERR, "%s.%s is deprecated", - jl_symbol_name(m->name), jl_symbol_name(s)); - jl_binding_dep_message(m, s, b); + if (jl_options.depwarn != JL_OPTIONS_DEPWARN_ERROR) + jl_printf(JL_STDERR, "WARNING: "); + jl_printf(JL_STDERR, "Use of "); - if (jl_options.depwarn != JL_OPTIONS_DEPWARN_ERROR) { - if (jl_lineno != 0) { - jl_printf(JL_STDERR, " likely near %s:%d\n", jl_filename, jl_lineno); - } + jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); + int first = 0; + while (!(bpart->kind & BINDING_FLAG_DEPWARN)) { + if (first) { + jl_printf(JL_STDERR, "binding implicitly imported via "); + first = 0; } + jl_printf(JL_STDERR, "%s.%s -> ", jl_symbol_name(b->globalref->mod->name), jl_symbol_name(b->globalref->name)); + assert(jl_binding_kind(bpart) == BINDING_KIND_IMPLICIT); + b = (jl_binding_t*)bpart->restriction; + bpart = jl_get_binding_partition(b, jl_current_task->world_age); + } + jl_printf(JL_STDERR, "%s.%s is deprecated", + jl_symbol_name(b->globalref->mod->name), jl_symbol_name(b->globalref->name)); + jl_binding_dep_message(b); - if (jl_options.depwarn == JL_OPTIONS_DEPWARN_ERROR) { - jl_errorf("use of deprecated variable: %s.%s", - jl_symbol_name(m->name), - jl_symbol_name(s)); + if (jl_options.depwarn != JL_OPTIONS_DEPWARN_ERROR) { + if (jl_lineno != 0) { + jl_printf(JL_STDERR, " likely near %s:%d\n", jl_filename, jl_lineno); } } + + if (jl_options.depwarn == JL_OPTIONS_DEPWARN_ERROR) { + jl_errorf("use of deprecated variable: %s.%s", + jl_symbol_name(b->globalref->mod->name), + jl_symbol_name(b->globalref->name)); + } } // For a generally writable binding (checked using jl_check_binding_currently_writable in this world age), check whether @@ -1555,7 +1616,7 @@ void append_module_names(jl_array_t* a, jl_module_t *m, int all, int imported, i (imported && (kind == BINDING_KIND_CONST_IMPORT || kind == BINDING_KIND_IMPORTED)) || (usings && kind == BINDING_KIND_EXPLICIT) || ((kind == BINDING_KIND_GLOBAL || kind == BINDING_KIND_CONST || kind == BINDING_KIND_DECLARED) && (all || main_public))) && - (all || (!b->deprecated && !hidden))) + (all || (!(bpart->kind & BINDING_FLAG_DEPRECATED) && !hidden))) _append_symbol_to_bindings_array(a, asname); } } @@ -1568,7 +1629,7 @@ void append_exported_names(jl_array_t* a, jl_module_t *m, int all) if ((void*)b == jl_nothing) break; jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age); - if ((bpart->kind & BINDING_FLAG_EXPORTED) && (all || !b->deprecated)) + if ((bpart->kind & BINDING_FLAG_EXPORTED) && (all || !(bpart->kind & BINDING_FLAG_DEPRECATED))) _append_symbol_to_bindings_array(a, b->globalref->name); } } diff --git a/src/staticdata.c b/src/staticdata.c index bc69b6a683821..16593f6cfdec5 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -3547,10 +3547,10 @@ static int jl_validate_binding_partition(jl_binding_t *b, jl_binding_partition_t if (jl_atomic_load_relaxed(&bpart->max_world) != ~(size_t)0) return 1; size_t raw_kind = bpart->kind; - enum jl_partition_kind kind = (enum jl_partition_kind)(raw_kind & 0x0f); + enum jl_partition_kind kind = (enum jl_partition_kind)(raw_kind & BINDING_KIND_MASK); if (!unchanged_implicit && jl_bkind_is_some_implicit(kind)) { jl_check_new_binding_implicit(bpart, b, NULL, jl_atomic_load_relaxed(&jl_world_counter)); - bpart->kind |= (raw_kind & 0xf0); + bpart->kind |= (raw_kind & BINDING_FLAG_MASK); if (bpart->min_world > jl_require_world) goto invalidated; } diff --git a/src/toplevel.c b/src/toplevel.c index dd9adb9bb480f..abba4336e39ed 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -319,7 +319,7 @@ void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type, in } check_safe_newbinding(gm, gs); if (bpart->min_world == new_world) { - bpart->kind = new_kind | (bpart->kind & 0xf0); + bpart->kind = new_kind | (bpart->kind & BINDING_FLAG_MASK); bpart->restriction = global_type; if (global_type) jl_gc_wb(bpart, global_type);