Skip to content

Commit 1e2f8a5

Browse files
KenoKristofferC
authored andcommitted
Align interpreter and codegen error behavior of setglobal! and friends (#59766)
Currently this is an ErrorException in the runtime/interpreter, but a TypeError in codegen. This is not permitted - which error is thrown is semantically observable and codegen is not permitted to change it. Worse, inference is also inconsistent about whether this is TypeError or ErrorException, so this could actually lead to type confusion and crashes. Fix all that by having the runtime also emit a TypeError here. However, in order to not lose the binding name in the error message, adjust the TypeError context field to permit a binding. (cherry picked from commit 17e0df5)
1 parent 3eb52df commit 1e2f8a5

File tree

12 files changed

+48
-30
lines changed

12 files changed

+48
-30
lines changed

Compiler/src/abstractinterpretation.jl

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2488,7 +2488,7 @@ function abstract_eval_setglobal!(interp::AbstractInterpreter, sv::AbsIntState,
24882488
(rt, exct) = global_assignment_rt_exct(interp, sv, saw_latestworld, gr, v)
24892489
return CallMeta(rt, exct, Effects(setglobal!_effects, nothrow=exct===Bottom), GlobalAccessInfo(convert(Core.Binding, gr)))
24902490
end
2491-
return CallMeta(Union{}, TypeError, EFFECTS_THROWS, NoCallInfo())
2491+
return CallMeta(Union{}, Union{TypeError, ErrorException}, EFFECTS_THROWS, NoCallInfo())
24922492
end
24932493
= partialorder(typeinf_lattice(interp))
24942494
if !(hasintersect(widenconst(M), Module) && hasintersect(widenconst(s), Symbol))
@@ -3732,7 +3732,7 @@ end
37323732

37333733
function global_assignment_rt_exct(interp::AbstractInterpreter, sv::AbsIntState, saw_latestworld::Bool, g::GlobalRef, @nospecialize(newty))
37343734
if saw_latestworld
3735-
return Pair{Any,Any}(newty, ErrorException)
3735+
return Pair{Any,Any}(newty, Union{TypeError, ErrorException})
37363736
end
37373737
newty′ = RefValue{Any}(newty)
37383738
(valid_worlds, ret) = scan_partitions(interp, g, sv.world) do interp::AbstractInterpreter, ::Core.Binding, partition::Core.BindingPartition
@@ -3747,15 +3747,16 @@ function global_assignment_binding_rt_exct(interp::AbstractInterpreter, partitio
37473747
if is_some_guard(kind)
37483748
return Pair{Any,Any}(newty, ErrorException)
37493749
elseif is_some_const_binding(kind) || is_some_imported(kind)
3750-
return Pair{Any,Any}(Bottom, ErrorException)
3750+
# N.B.: Backdating should not improve inference in an earlier world
3751+
return Pair{Any,Any}(kind == PARTITION_KIND_BACKDATED_CONST ? newty : Bottom, ErrorException)
37513752
end
37523753
ty = kind == PARTITION_KIND_DECLARED ? Any : partition_restriction(partition)
37533754
wnewty = widenconst(newty)
37543755
if !hasintersect(wnewty, ty)
3755-
return Pair{Any,Any}(Bottom, ErrorException)
3756+
return Pair{Any,Any}(Bottom, TypeError)
37563757
elseif !(wnewty <: ty)
37573758
retty = tmeet(typeinf_lattice(interp), newty, ty)
3758-
return Pair{Any,Any}(retty, ErrorException)
3759+
return Pair{Any,Any}(retty, TypeError)
37593760
end
37603761
return Pair{Any,Any}(newty, Bottom)
37613762
end

Compiler/test/inference.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6235,7 +6235,7 @@ g57292(xs::String...) = getfield(("abc",), 1, :not_atomic, xs...)
62356235
global invalid_setglobal!_exct_modeling::Int
62366236
@test Base.infer_exception_type((Float64,)) do x
62376237
setglobal!(@__MODULE__, :invalid_setglobal!_exct_modeling, x)
6238-
end == ErrorException
6238+
end == TypeError
62396239

62406240
# Issue #58257 - Hang in inference during BindingPartition resolution
62416241
module A58257

base/boot.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ struct TypeError <: Exception
410410
# `context` optionally adds extra detail, e.g. the name of the type parameter
411411
# that got a bad value.
412412
func::Symbol
413-
context::Union{AbstractString,Symbol}
413+
context::Union{AbstractString,GlobalRef,Symbol}
414414
expected::Type
415415
got
416416
TypeError(func, context, @nospecialize(expected::Type), @nospecialize(got)) =

base/errorshow.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ function showerror(io::IO, ex::TypeError)
9191
end
9292
if ex.context == ""
9393
ctx = "in $(ex.func)"
94+
elseif isa(ex.context, Core.GlobalRef)
95+
gr = ex.context
96+
ctx = "in $(ex.func) of global binding `$(gr.mod).$(gr.name)`"
9497
elseif ex.func === :var"keyword argument"
9598
ctx = "in keyword argument $(ex.context)"
9699
else

src/codegen.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3251,8 +3251,7 @@ static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *s
32513251
if (ty != nullptr) {
32523252
const std::string fname = issetglobal ? "setglobal!" : isreplaceglobal ? "replaceglobal!" : isswapglobal ? "swapglobal!" : ismodifyglobal ? "modifyglobal!" : "setglobalonce!";
32533253
if (!ismodifyglobal) {
3254-
// TODO: use typeassert in jl_check_binding_assign_value too
3255-
emit_typecheck(ctx, rval, ty, "typeassert");
3254+
emit_typecheck(ctx, rval, ty, fname.c_str());
32563255
rval = update_julia_type(ctx, rval, ty);
32573256
if (rval.typ == jl_bottom_type)
32583257
return jl_cgval_t();

src/datatype.c

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1981,11 +1981,11 @@ jl_value_t *swap_nth_field(jl_datatype_t *st, jl_value_t *v, size_t i, jl_value_
19811981
}
19821982
}
19831983

1984-
inline jl_value_t *modify_value(jl_value_t *ty, _Atomic(jl_value_t*) *p, jl_value_t *parent, jl_value_t *op, jl_value_t *rhs, int isatomic, jl_module_t *mod, jl_sym_t *name)
1984+
inline jl_value_t *modify_value(jl_value_t *ty, _Atomic(jl_value_t*) *p, jl_value_t *parent, jl_value_t *op, jl_value_t *rhs, int isatomic, jl_binding_t *b, jl_module_t *mod, jl_sym_t *name)
19851985
{
19861986
jl_value_t *r = isatomic ? jl_atomic_load(p) : jl_atomic_load_relaxed(p);
19871987
if (__unlikely(r == NULL)) {
1988-
if (mod && name)
1988+
if (b)
19891989
jl_undefined_var_error(name, (jl_value_t*)mod);
19901990
jl_throw(jl_undefref_exception);
19911991
}
@@ -1996,11 +1996,10 @@ inline jl_value_t *modify_value(jl_value_t *ty, _Atomic(jl_value_t*) *p, jl_valu
19961996
args[1] = rhs;
19971997
jl_value_t *y = jl_apply_generic(op, args, 2);
19981998
args[1] = y;
1999-
if (!jl_isa(y, ty)) {
2000-
if (mod && name)
2001-
jl_errorf("cannot assign an incompatible value to the global %s.%s.", jl_symbol_name(mod->name), jl_symbol_name(name));
1999+
if (b)
2000+
jl_check_binding_assign_value(b, mod, name, y, "modifyglobal!");
2001+
else if (!jl_isa(y, ty))
20022002
jl_type_error(jl_is_genericmemory(parent) ? "memoryrefmodify!" : "modifyfield!", ty, y);
2003-
}
20042003
if (isatomic ? jl_atomic_cmpswap(p, &r, y) : jl_atomic_cmpswap_release(p, &r, y)) {
20052004
jl_gc_wb(parent, y);
20062005
break;
@@ -2114,7 +2113,7 @@ jl_value_t *modify_nth_field(jl_datatype_t *st, jl_value_t *v, size_t i, jl_valu
21142113
jl_value_t *ty = jl_field_type_concrete(st, i);
21152114
char *p = (char*)v + offs;
21162115
if (jl_field_isptr(st, i)) {
2117-
return modify_value(ty, (_Atomic(jl_value_t*)*)p, v, op, rhs, isatomic, NULL, NULL);
2116+
return modify_value(ty, (_Atomic(jl_value_t*)*)p, v, op, rhs, isatomic, NULL, NULL, NULL);
21182117
}
21192118
else {
21202119
uint8_t *psel = jl_is_uniontype(ty) ? (uint8_t*)&p[jl_field_size(st, i) - 1] : NULL;

src/genericmemory.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ JL_DLLEXPORT jl_value_t *jl_memoryrefmodify(jl_genericmemoryref_t m, jl_value_t
543543
char *data = (char*)m.ptr_or_offset;
544544
if (layout->flags.arrayelem_isboxed) {
545545
assert(data - (char*)m.mem->ptr < sizeof(jl_value_t*) * m.mem->length);
546-
return modify_value(eltype, (_Atomic(jl_value_t*)*)data, owner, op, rhs, isatomic, NULL, NULL);
546+
return modify_value(eltype, (_Atomic(jl_value_t*)*)data, owner, op, rhs, isatomic, NULL, NULL, NULL);
547547
}
548548
size_t fsz = layout->size;
549549
uint8_t *psel = NULL;

src/julia.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2121,7 +2121,7 @@ JL_DLLEXPORT jl_value_t *jl_get_module_binding_or_nothing(jl_module_t *m, jl_sym
21212121

21222122
// get binding for reading
21232123
JL_DLLEXPORT jl_binding_t *jl_get_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var);
2124-
JL_DLLEXPORT jl_value_t *jl_module_globalref(jl_module_t *m, jl_sym_t *var);
2124+
JL_DLLEXPORT jl_value_t *jl_module_globalref(jl_module_t *m, jl_sym_t *var) JL_GLOBALLY_ROOTED;
21252125
JL_DLLEXPORT jl_value_t *jl_get_binding_type(jl_module_t *m, jl_sym_t *var);
21262126
// get binding for assignment
21272127
JL_DLLEXPORT void jl_check_binding_currently_writable(jl_binding_t *b, jl_module_t *m, jl_sym_t *s);
@@ -2197,6 +2197,10 @@ JL_DLLEXPORT void JL_NORETURN jl_type_error_rt(const char *fname,
21972197
const char *context,
21982198
jl_value_t *ty JL_MAYBE_UNROOTED,
21992199
jl_value_t *got JL_MAYBE_UNROOTED);
2200+
JL_DLLEXPORT void JL_NORETURN jl_type_error_global(const char *fname,
2201+
jl_module_t *mod, jl_sym_t *sym,
2202+
jl_value_t *ty JL_MAYBE_UNROOTED,
2203+
jl_value_t *got JL_MAYBE_UNROOTED);
22002204
JL_DLLEXPORT void JL_NORETURN jl_undefined_var_error(jl_sym_t *var, jl_value_t *scope JL_MAYBE_UNROOTED);
22012205
JL_DLLEXPORT void JL_NORETURN jl_has_no_field_error(jl_datatype_t *t, jl_sym_t *var);
22022206
JL_DLLEXPORT void JL_NORETURN jl_argument_error(char *str);

src/julia_internal.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -851,7 +851,7 @@ int set_nth_fieldonce(jl_datatype_t *st, jl_value_t *v, size_t i, jl_value_t *rh
851851
jl_value_t *swap_bits(jl_value_t *ty, char *v, uint8_t *psel, jl_value_t *parent, jl_value_t *rhs, enum atomic_kind isatomic);
852852
jl_value_t *replace_value(jl_value_t *ty, _Atomic(jl_value_t*) *p, jl_value_t *parent, jl_value_t *expected, jl_value_t *rhs, int isatomic, jl_module_t *mod, jl_sym_t *name);
853853
jl_value_t *replace_bits(jl_value_t *ty, char *p, uint8_t *psel, jl_value_t *parent, jl_value_t *expected, jl_value_t *rhs, enum atomic_kind isatomic);
854-
jl_value_t *modify_value(jl_value_t *ty, _Atomic(jl_value_t*) *p, jl_value_t *parent, jl_value_t *op, jl_value_t *rhs, int isatomic, jl_module_t *mod, jl_sym_t *name);
854+
jl_value_t *modify_value(jl_value_t *ty, _Atomic(jl_value_t*) *p, jl_value_t *parent, jl_value_t *op, jl_value_t *rhs, int isatomic, jl_binding_t *b, jl_module_t *mod, jl_sym_t *name);
855855
jl_value_t *modify_bits(jl_value_t *ty, char *p, uint8_t *psel, jl_value_t *parent, jl_value_t *op, jl_value_t *rhs, enum atomic_kind isatomic);
856856
int setonce_bits(jl_datatype_t *rty, char *p, jl_value_t *owner, jl_value_t *rhs, enum atomic_kind isatomic);
857857
jl_expr_t *jl_exprn(jl_sym_t *head, size_t n);
@@ -865,6 +865,7 @@ JL_DLLEXPORT int jl_datatype_isinlinealloc(jl_datatype_t *ty, int pointerfree);
865865
int jl_type_equality_is_identity(jl_value_t *t1, jl_value_t *t2) JL_NOTSAFEPOINT;
866866

867867
JL_DLLEXPORT void jl_eval_const_decl(jl_module_t *m, jl_value_t *arg, jl_value_t *val);
868+
jl_value_t *jl_check_binding_assign_value(jl_binding_t *b JL_PROPAGATES_ROOT, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs JL_MAYBE_UNROOTED, const char *msg);
868869
void jl_binding_set_type(jl_binding_t *b, jl_module_t *mod, jl_sym_t *sym, jl_value_t *ty);
869870
void jl_eval_global_expr(jl_module_t *m, jl_expr_t *ex, int set_type);
870871
JL_DLLEXPORT void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type, int strong);

src/module.c

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1877,34 +1877,32 @@ void jl_binding_deprecation_warning(jl_binding_t *b)
18771877

18781878
// For a generally writable binding (checked using jl_check_binding_currently_writable in this world age), check whether
18791879
// we can actually write the value `rhs` to it.
1880-
jl_value_t *jl_check_binding_assign_value(jl_binding_t *b JL_PROPAGATES_ROOT, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs JL_MAYBE_UNROOTED)
1880+
jl_value_t *jl_check_binding_assign_value(jl_binding_t *b JL_PROPAGATES_ROOT, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs JL_MAYBE_UNROOTED, const char *msg)
18811881
{
18821882
JL_GC_PUSH1(&rhs); // callee-rooted
18831883
jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age);
18841884
enum jl_partition_kind kind = jl_binding_kind(bpart);
18851885
assert(kind == PARTITION_KIND_DECLARED || kind == PARTITION_KIND_GLOBAL);
18861886
jl_value_t *old_ty = kind == PARTITION_KIND_DECLARED ? (jl_value_t*)jl_any_type : bpart->restriction;
18871887
JL_GC_PROMISE_ROOTED(old_ty);
1888-
if (old_ty != (jl_value_t*)jl_any_type && jl_typeof(rhs) != old_ty) {
1889-
if (!jl_isa(rhs, old_ty))
1890-
jl_errorf("cannot assign an incompatible value to the global %s.%s.",
1891-
jl_symbol_name(mod->name), jl_symbol_name(var));
1888+
if (old_ty != (jl_value_t*)jl_any_type && jl_typeof(rhs) != old_ty && !jl_isa(rhs, old_ty)) {
1889+
jl_type_error_global(msg, mod, var, old_ty, rhs);
18921890
}
18931891
JL_GC_POP();
18941892
return old_ty;
18951893
}
18961894

18971895
JL_DLLEXPORT void jl_checked_assignment(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs)
18981896
{
1899-
if (jl_check_binding_assign_value(b, mod, var, rhs) != NULL) {
1897+
if (jl_check_binding_assign_value(b, mod, var, rhs, "setglobal!") != NULL) {
19001898
jl_atomic_store_release(&b->value, rhs);
19011899
jl_gc_wb(b, rhs);
19021900
}
19031901
}
19041902

19051903
JL_DLLEXPORT jl_value_t *jl_checked_swap(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs)
19061904
{
1907-
jl_check_binding_assign_value(b, mod, var, rhs);
1905+
jl_check_binding_assign_value(b, mod, var, rhs, "swapglobal!");
19081906
jl_value_t *old = jl_atomic_exchange(&b->value, rhs);
19091907
jl_gc_wb(b, rhs);
19101908
if (__unlikely(old == NULL))
@@ -1914,7 +1912,7 @@ JL_DLLEXPORT jl_value_t *jl_checked_swap(jl_binding_t *b, jl_module_t *mod, jl_s
19141912

19151913
JL_DLLEXPORT jl_value_t *jl_checked_replace(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *expected, jl_value_t *rhs)
19161914
{
1917-
jl_value_t *ty = jl_check_binding_assign_value(b, mod, var, rhs);
1915+
jl_value_t *ty = jl_check_binding_assign_value(b, mod, var, rhs, "replaceglobal!");
19181916
return replace_value(ty, &b->value, (jl_value_t*)b, expected, rhs, 1, mod, var);
19191917
}
19201918

@@ -1928,12 +1926,12 @@ JL_DLLEXPORT jl_value_t *jl_checked_modify(jl_binding_t *b, jl_module_t *mod, jl
19281926
jl_symbol_name(mod->name), jl_symbol_name(var));
19291927
jl_value_t *ty = bpart->restriction;
19301928
JL_GC_PROMISE_ROOTED(ty);
1931-
return modify_value(ty, &b->value, (jl_value_t*)b, op, rhs, 1, mod, var);
1929+
return modify_value(ty, &b->value, (jl_value_t*)b, op, rhs, 1, b, mod, var);
19321930
}
19331931

19341932
JL_DLLEXPORT jl_value_t *jl_checked_assignonce(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs )
19351933
{
1936-
jl_check_binding_assign_value(b, mod, var, rhs);
1934+
jl_check_binding_assign_value(b, mod, var, rhs, "setglobalonce!");
19371935
jl_value_t *old = NULL;
19381936
if (jl_atomic_cmpswap(&b->value, &old, rhs))
19391937
jl_gc_wb(b, rhs);

0 commit comments

Comments
 (0)