Skip to content

Commit 1a3cbb1

Browse files
authored
bpart: Also partition ->deprecated (#57449)
This repeats the exercise in #57405. This is required for correctness, because the ->deprecated flag also affects `using` resolution (since it makes the tagged binding weaker for `using` purposes). That said, in general our `->deprecated` semantics have been somewhat underspecified with lots of `XXX` comments in the surrounding code. This tries to define the semantics to give a depwarn on *access* (read or write) when: 1. Either the binding itself is deprecated; or 2. The implicit imports pass through a deprecated binding. However, we do not give depwarns on access to bindings that were explicitly imported (although we do give those warnings on the import) - the reasoning being that it's the import that needs to be adjusted not the access. Additionally, this PR moves into the direction of making the depwarn a semantic part of the global access, by adjusting codegen and inference appropriately.
1 parent ad61241 commit 1a3cbb1

File tree

13 files changed

+263
-138
lines changed

13 files changed

+263
-138
lines changed

Compiler/src/Compiler.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ using Core: ABIOverride, Builtin, CodeInstance, IntrinsicFunction, MethodInstanc
5050
using Base
5151
using Base: @_foldable_meta, @_gc_preserve_begin, @_gc_preserve_end, @nospecializeinfer,
5252
BINDING_KIND_GLOBAL, BINDING_KIND_UNDEF_CONST, BINDING_KIND_BACKDATED_CONST, BINDING_KIND_DECLARED,
53+
BINDING_FLAG_DEPWARN,
5354
Base, BitVector, Bottom, Callable, DataTypeFieldDesc,
5455
EffectsOverride, Filter, Generator, IteratorSize, JLOptions, NUM_EFFECTS_OVERRIDES,
5556
OneTo, Ordering, RefValue, SizeUnknown, _NAMEDTUPLE_NAME,

Compiler/src/abstractinterpretation.jl

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2375,7 +2375,7 @@ function abstract_throw_methoderror(interp::AbstractInterpreter, argtypes::Vecto
23752375
return Future(CallMeta(Union{}, exct, EFFECTS_THROWS, NoCallInfo()))
23762376
end
23772377

2378-
const generic_getglobal_effects = Effects(EFFECTS_THROWS, consistent=ALWAYS_FALSE, inaccessiblememonly=ALWAYS_FALSE)
2378+
const generic_getglobal_effects = Effects(EFFECTS_THROWS, effect_free=ALWAYS_FALSE, consistent=ALWAYS_FALSE, inaccessiblememonly=ALWAYS_FALSE) #= effect_free for depwarn =#
23792379
const generic_getglobal_exct = Union{ArgumentError, TypeError, ConcurrencyViolationError, UndefVarError}
23802380
function abstract_eval_getglobal(interp::AbstractInterpreter, sv::AbsIntState, saw_latestworld::Bool, @nospecialize(M), @nospecialize(s))
23812381
= partialorder(typeinf_lattice(interp))
@@ -3503,32 +3503,36 @@ end
35033503

35043504
function abstract_eval_partition_load(interp::AbstractInterpreter, partition::Core.BindingPartition)
35053505
kind = binding_kind(partition)
3506+
isdepwarn = (partition.kind & BINDING_FLAG_DEPWARN) != 0
3507+
local_getglobal_effects = Effects(generic_getglobal_effects, effect_free=isdepwarn ? ALWAYS_FALSE : ALWAYS_TRUE)
35063508
if is_some_guard(kind) || kind == BINDING_KIND_UNDEF_CONST
35073509
if InferenceParams(interp).assume_bindings_static
35083510
return RTEffects(Union{}, UndefVarError, EFFECTS_THROWS)
35093511
else
35103512
# We do not currently assume an invalidation for guard -> defined transitions
35113513
# return RTEffects(Union{}, UndefVarError, EFFECTS_THROWS)
3512-
return RTEffects(Any, UndefVarError, generic_getglobal_effects)
3514+
return RTEffects(Any, UndefVarError, local_getglobal_effects)
35133515
end
35143516
end
35153517

35163518
if is_defined_const_binding(kind)
35173519
if kind == BINDING_KIND_BACKDATED_CONST
35183520
# Infer this as guard. We do not want a later const definition to retroactively improve
35193521
# inference results in an earlier world.
3520-
return RTEffects(Any, UndefVarError, generic_getglobal_effects)
3522+
return RTEffects(Any, UndefVarError, local_getglobal_effects)
35213523
end
35223524
rt = Const(partition_restriction(partition))
3523-
return RTEffects(rt, Union{}, Effects(EFFECTS_TOTAL, inaccessiblememonly=is_mutation_free_argtype(rt) ? ALWAYS_TRUE : ALWAYS_FALSE))
3525+
return RTEffects(rt, Union{}, Effects(EFFECTS_TOTAL,
3526+
inaccessiblememonly=is_mutation_free_argtype(rt) ? ALWAYS_TRUE : ALWAYS_FALSE,
3527+
effect_free=isdepwarn ? ALWAYS_FALSE : ALWAYS_TRUE))
35243528
end
35253529

35263530
if kind == BINDING_KIND_DECLARED
35273531
rt = Any
35283532
else
35293533
rt = partition_restriction(partition)
35303534
end
3531-
return RTEffects(rt, UndefVarError, generic_getglobal_effects)
3535+
return RTEffects(rt, UndefVarError, local_getglobal_effects)
35323536
end
35333537

35343538
function abstract_eval_globalref(interp::AbstractInterpreter, g::GlobalRef, saw_latestworld::Bool, sv::AbsIntState)

Compiler/test/irpasses.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1179,7 +1179,10 @@ let ci = code_typed(foo_cfg_empty, Tuple{Bool}, optimize=true)[1][1]
11791179
end
11801180

11811181
@test Compiler.is_effect_free(Base.infer_effects(getfield, (Complex{Int}, Symbol)))
1182-
@test Compiler.is_effect_free(Base.infer_effects(getglobal, (Module, Symbol)))
1182+
1183+
# We consider a potential deprecatio warning an effect, so for completely unkown getglobal,
1184+
# we taint the effect_free bit.
1185+
@test !Compiler.is_effect_free(Base.infer_effects(getglobal, (Module, Symbol)))
11831186

11841187
# Test that UseRefIterator gets SROA'd inside of new_to_regular (#44557)
11851188
# expression and new_to_regular offset are arbitrary here, we just want to see the UseRefIterator erased

base/runtime_internals.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,11 @@ const BINDING_KIND_UNDEF_CONST = 0x9
210210
const BINDING_KIND_BACKDATED_CONST = 0xa
211211

212212
const BINDING_FLAG_EXPORTED = 0x10
213+
const BINDING_FLAG_DEPRECATED = 0x20
214+
const BINDING_FLAG_DEPWARN = 0x40
215+
216+
const BINDING_KIND_MASK = 0x0f
217+
const BINDING_FLAG_MASK = 0xf0
213218

214219
is_defined_const_binding(kind::UInt8) = (kind == BINDING_KIND_CONST || kind == BINDING_KIND_CONST_IMPORT || kind == BINDING_KIND_BACKDATED_CONST)
215220
is_some_const_binding(kind::UInt8) = (is_defined_const_binding(kind) || kind == BINDING_KIND_UNDEF_CONST)

base/show.jl

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3377,8 +3377,21 @@ function print_partition(io::IO, partition::Core.BindingPartition)
33773377
else
33783378
print(io, max_world)
33793379
end
3380-
if (partition.kind & BINDING_FLAG_EXPORTED) != 0
3381-
print(io, " [exported]")
3380+
if (partition.kind & BINDING_FLAG_MASK) != 0
3381+
first = false
3382+
print(io, " [")
3383+
if (partition.kind & BINDING_FLAG_EXPORTED) != 0
3384+
print(io, "exported")
3385+
end
3386+
if (partition.kind & BINDING_FLAG_DEPRECATED) != 0
3387+
first ? (first = false) : print(io, ",")
3388+
print(io, "deprecated")
3389+
end
3390+
if (partition.kind & BINDING_FLAG_DEPWARN) != 0
3391+
first ? (first = false) : print(io, ",")
3392+
print(io, "depwarn")
3393+
end
3394+
print(io, "]")
33823395
end
33833396
print(io, " - ")
33843397
kind = binding_kind(partition)

src/codegen.cpp

Lines changed: 59 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -908,13 +908,12 @@ static const auto jldeclareglobal_func = new JuliaFunction<>{
908908
{T_pjlvalue, T_pjlvalue, T_prjlvalue, getInt32Ty(C)}, false); },
909909
nullptr,
910910
};
911-
static const auto jlgetbindingorerror_func = new JuliaFunction<>{
912-
XSTR(jl_get_binding_or_error),
911+
static const auto jldepcheck_func = new JuliaFunction<>{
912+
XSTR(jl_binding_deprecation_check),
913913
[](LLVMContext &C) {
914914
auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C);
915-
return FunctionType::get(T_pjlvalue,
916-
{T_pjlvalue, T_pjlvalue}, false);
917-
},
915+
return FunctionType::get(getVoidTy(C),
916+
{T_pjlvalue}, false); },
918917
nullptr,
919918
};
920919
static const auto jlcheckbpwritable_func = new JuliaFunction<>{
@@ -2894,20 +2893,6 @@ static void mallocVisitLine(jl_codectx_t &ctx, StringRef filename, int line, Val
28942893

28952894
// --- constant determination ---
28962895

2897-
static void show_source_loc(jl_codectx_t &ctx, JL_STREAM *out)
2898-
{
2899-
jl_printf(out, "in %s at %s", ctx.name, ctx.file.str().c_str());
2900-
}
2901-
2902-
static void cg_bdw(jl_codectx_t &ctx, jl_sym_t *var, jl_binding_t *b)
2903-
{
2904-
jl_binding_deprecation_warning(ctx.module, var, b);
2905-
if (b->deprecated == 1 && jl_options.depwarn) {
2906-
show_source_loc(ctx, JL_STDERR);
2907-
jl_printf(JL_STDERR, "\n");
2908-
}
2909-
}
2910-
29112896
static jl_value_t *static_apply_type(jl_codectx_t &ctx, ArrayRef<jl_cgval_t> args, size_t nargs)
29122897
{
29132898
assert(nargs > 1);
@@ -2932,6 +2917,12 @@ static jl_value_t *static_apply_type(jl_codectx_t &ctx, ArrayRef<jl_cgval_t> arg
29322917
return result;
29332918
}
29342919

2920+
static void emit_depwarn_check(jl_codectx_t &ctx, jl_binding_t *b)
2921+
{
2922+
Value *bp = julia_binding_gv(ctx, b);
2923+
ctx.builder.CreateCall(prepare_call(jldepcheck_func), { bp });
2924+
}
2925+
29352926
// try to statically evaluate, NULL if not possible. note that this may allocate, and as
29362927
// such the resulting value should not be embedded directly in the generated code.
29372928
static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex)
@@ -2940,9 +2931,13 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex)
29402931
jl_sym_t *sym = (jl_sym_t*)ex;
29412932
jl_binding_t *bnd = jl_get_module_binding(ctx.module, sym, 0);
29422933
jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
2943-
jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world);
2944-
if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart)))
2934+
int possibly_deprecated = 0;
2935+
jl_walk_binding_inplace_all(&bnd, &bpart, &possibly_deprecated, ctx.min_world, ctx.max_world);
2936+
if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart))) {
2937+
if (possibly_deprecated)
2938+
emit_depwarn_check(ctx, bnd);
29452939
return bpart->restriction;
2940+
}
29462941
return NULL;
29472942
}
29482943
if (jl_is_slotnumber(ex) || jl_is_argument(ex))
@@ -2965,13 +2960,14 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex)
29652960
s = jl_globalref_name(ex);
29662961
jl_binding_t *bnd = jl_get_module_binding(jl_globalref_mod(ex), s, 0);
29672962
jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
2968-
jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world);
2963+
int possibly_deprecated = 0;
2964+
jl_walk_binding_inplace_all(&bnd, &bpart, &possibly_deprecated, ctx.min_world, ctx.max_world);
29692965
jl_value_t *v = NULL;
29702966
if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart)))
29712967
v = bpart->restriction;
29722968
if (v) {
2973-
if (bnd->deprecated)
2974-
cg_bdw(ctx, s, bnd);
2969+
if (possibly_deprecated)
2970+
emit_depwarn_check(ctx, bnd);
29752971
return v;
29762972
}
29772973
return NULL;
@@ -2992,13 +2988,14 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex)
29922988
if (s && jl_is_symbol(s)) {
29932989
jl_binding_t *bnd = jl_get_module_binding(m, s, 0);
29942990
jl_binding_partition_t *bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
2995-
jl_walk_binding_inplace_all(&bnd, &bpart, ctx.min_world, ctx.max_world);
2991+
int possibly_deprecated = 0;
2992+
jl_walk_binding_inplace_all(&bnd, &bpart, &possibly_deprecated, ctx.min_world, ctx.max_world);
29962993
jl_value_t *v = NULL;
29972994
if (bpart && jl_bkind_is_some_constant(jl_binding_kind(bpart)))
29982995
v = bpart->restriction;
29992996
if (v) {
3000-
if (bnd->deprecated)
3001-
cg_bdw(ctx, s, bnd);
2997+
if (possibly_deprecated)
2998+
emit_depwarn_check(ctx, bnd);
30022999
return v;
30033000
}
30043001
}
@@ -3244,48 +3241,47 @@ static jl_cgval_t emit_globalref(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *
32443241
if (!bpart) {
32453242
return emit_globalref_runtime(ctx, bnd, mod, name);
32463243
}
3247-
// bpart was updated in place - this will change with full partition
3248-
if (jl_bkind_is_some_guard(jl_binding_kind(bpart))) {
3249-
// Redo the lookup at runtime
3250-
return emit_globalref_runtime(ctx, bnd, mod, name);
3251-
} else {
3252-
while (true) {
3253-
if (!bpart)
3254-
break;
3255-
if (!jl_bkind_is_some_import(jl_binding_kind(bpart)))
3256-
break;
3257-
if (bnd->deprecated) {
3258-
cg_bdw(ctx, name, bnd);
3259-
}
3260-
bnd = (jl_binding_t*)bpart->restriction;
3261-
bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
3262-
if (!bpart)
3263-
break;
3264-
}
3265-
if (bpart) {
3266-
enum jl_partition_kind kind = jl_binding_kind(bpart);
3267-
if (jl_bkind_is_some_constant(kind) && kind != BINDING_KIND_BACKDATED_CONST) {
3268-
jl_value_t *constval = bpart->restriction;
3269-
if (!constval) {
3270-
undef_var_error_ifnot(ctx, ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0), name, (jl_value_t*)mod);
3271-
return jl_cgval_t();
3272-
}
3273-
return mark_julia_const(ctx, constval);
3244+
int possibly_deprecated = 0;
3245+
int saw_explicit = 0;
3246+
while (bpart) {
3247+
if (!saw_explicit && (bpart->kind & BINDING_FLAG_DEPWARN))
3248+
possibly_deprecated = 1;
3249+
enum jl_partition_kind kind = jl_binding_kind(bpart);
3250+
if (!jl_bkind_is_some_import(kind))
3251+
break;
3252+
if (kind != BINDING_KIND_IMPLICIT)
3253+
saw_explicit = 1;
3254+
bnd = (jl_binding_t*)bpart->restriction;
3255+
bpart = jl_get_binding_partition_all(bnd, ctx.min_world, ctx.max_world);
3256+
}
3257+
Value *bp = NULL;
3258+
if (bpart) {
3259+
enum jl_partition_kind kind = jl_binding_kind(bpart);
3260+
if (jl_bkind_is_some_constant(kind) && kind != BINDING_KIND_BACKDATED_CONST) {
3261+
if (possibly_deprecated) {
3262+
bp = julia_binding_gv(ctx, bnd);
3263+
ctx.builder.CreateCall(prepare_call(jldepcheck_func), { bp });
3264+
}
3265+
jl_value_t *constval = bpart->restriction;
3266+
if (!constval) {
3267+
undef_var_error_ifnot(ctx, ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0), name, (jl_value_t*)mod);
3268+
return jl_cgval_t();
32743269
}
3270+
return mark_julia_const(ctx, constval);
32753271
}
32763272
}
32773273
if (!bpart || jl_binding_kind(bpart) != BINDING_KIND_GLOBAL) {
32783274
return emit_globalref_runtime(ctx, bnd, mod, name);
32793275
}
3280-
Value *bp = julia_binding_gv(ctx, bnd);
3281-
if (bnd->deprecated) {
3282-
cg_bdw(ctx, name, bnd);
3276+
bp = julia_binding_gv(ctx, bnd);
3277+
if (possibly_deprecated) {
3278+
ctx.builder.CreateCall(prepare_call(jldepcheck_func), { bp });
32833279
}
32843280
jl_value_t *ty = bpart->restriction;
3285-
bp = julia_binding_pvalue(ctx, bp);
3281+
Value *bpval = julia_binding_pvalue(ctx, bp);
32863282
if (ty == nullptr)
32873283
ty = (jl_value_t*)jl_any_type;
3288-
return update_julia_type(ctx, emit_checked_var(ctx, bp, name, (jl_value_t*)mod, false, ctx.tbaa().tbaa_binding), ty);
3284+
return update_julia_type(ctx, emit_checked_var(ctx, bpval, name, (jl_value_t*)mod, false, ctx.tbaa().tbaa_binding), ty);
32893285
}
32903286

32913287
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,
@@ -3298,6 +3294,7 @@ static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *s
32983294
Value *bp = julia_binding_gv(ctx, bnd);
32993295
if (bpart) {
33003296
if (jl_binding_kind(bpart) == BINDING_KIND_GLOBAL) {
3297+
int possibly_deprecated = bpart->kind & BINDING_FLAG_DEPWARN;
33013298
jl_value_t *ty = bpart->restriction;
33023299
if (ty != nullptr) {
33033300
const std::string fname = issetglobal ? "setglobal!" : isreplaceglobal ? "replaceglobal!" : isswapglobal ? "swapglobal!" : ismodifyglobal ? "modifyglobal!" : "setglobalonce!";
@@ -3310,6 +3307,9 @@ static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *s
33103307
}
33113308
bool isboxed = true;
33123309
bool maybe_null = jl_atomic_load_relaxed(&bnd->value) == NULL;
3310+
if (possibly_deprecated) {
3311+
ctx.builder.CreateCall(prepare_call(jldepcheck_func), { bp });
3312+
}
33133313
return typed_store(ctx,
33143314
julia_binding_pvalue(ctx, bp),
33153315
rval, cmp, ty,
@@ -9913,7 +9913,6 @@ static void init_jit_functions(void)
99139913
add_named_global(memcmp_func, &memcmp);
99149914
add_named_global(jltypeerror_func, &jl_type_error);
99159915
add_named_global(jlcheckassign_func, &jl_checked_assignment);
9916-
add_named_global(jlgetbindingorerror_func, &jl_get_binding_or_error);
99179916
add_named_global(jlcheckbpwritable_func, &jl_check_binding_currently_writable);
99189917
add_named_global(jlboundp_func, &jl_boundp);
99199918
for (auto it : builtin_func_map())

src/jl_exported_funcs.inc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,6 @@
194194
XX(jl_get_backtrace) \
195195
XX(jl_get_binding) \
196196
XX(jl_get_binding_for_method_def) \
197-
XX(jl_get_binding_or_error) \
198197
XX(jl_get_binding_wr) \
199198
XX(jl_check_binding_currently_writable) \
200199
XX(jl_get_cpu_name) \

src/julia.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -708,8 +708,21 @@ enum jl_partition_kind {
708708
BINDING_KIND_IMPLICIT_RECOMPUTE = 0xb
709709
};
710710

711-
// These are flags that get anded into the above
711+
static const uint8_t BINDING_KIND_MASK = 0x0f;
712+
static const uint8_t BINDING_FLAG_MASK = 0xf0;
713+
714+
//// These are flags that get anded into the above
715+
//
716+
// _EXPORTED: This binding partition is exported. In the world ranges covered by this partitions,
717+
// other modules that `using` this module, may implicit import this binding.
712718
static const uint8_t BINDING_FLAG_EXPORTED = 0x10;
719+
// _DEPRECATED: This binding partition is deprecated. It is considered weak for the purposes of
720+
// implicit import resolution.
721+
static const uint8_t BINDING_FLAG_DEPRECATED = 0x20;
722+
// _DEPWARN: This binding partition will print a deprecation warning on access. Note that _DEPWARN
723+
// implies _DEPRECATED. However, the reverse is not true. Such bindings are usually used for functions,
724+
// where calling the function itself will provide a (better) deprecation warning/error.
725+
static const uint8_t BINDING_FLAG_DEPWARN = 0x40;
713726

714727
typedef struct __attribute__((aligned(8))) _jl_binding_partition_t {
715728
JL_DATA_TYPE
@@ -2041,7 +2054,6 @@ JL_DLLEXPORT jl_value_t *jl_get_module_binding_or_nothing(jl_module_t *m, jl_sym
20412054

20422055
// get binding for reading
20432056
JL_DLLEXPORT jl_binding_t *jl_get_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var);
2044-
JL_DLLEXPORT jl_binding_t *jl_get_binding_or_error(jl_module_t *m, jl_sym_t *var);
20452057
JL_DLLEXPORT jl_value_t *jl_module_globalref(jl_module_t *m, jl_sym_t *var);
20462058
JL_DLLEXPORT jl_value_t *jl_get_binding_type(jl_module_t *m, jl_sym_t *var);
20472059
// get binding for assignment

0 commit comments

Comments
 (0)