Skip to content

Commit d01041a

Browse files
committed
Align interpreter and codegen error behavior of setglobal! and friends
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.
1 parent fa66b63 commit d01041a

File tree

12 files changed

+46
-29
lines changed

12 files changed

+46
-29
lines changed

Compiler/src/abstractinterpretation.jl

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

37523752
function global_assignment_rt_exct(interp::AbstractInterpreter, sv::AbsIntState, saw_latestworld::Bool, g::GlobalRef, @nospecialize(newty))
37533753
if saw_latestworld
3754-
return Pair{Any,Any}(newty, ErrorException)
3754+
return Pair{Any,Any}(newty, Union{TypeError, ErrorException})
37553755
end
37563756
newty′ = RefValue{Any}(newty)
37573757
(valid_worlds, ret) = scan_partitions(interp, g, sv.world) do interp::AbstractInterpreter, ::Core.Binding, partition::Core.BindingPartition
@@ -3766,15 +3766,16 @@ function global_assignment_binding_rt_exct(interp::AbstractInterpreter, partitio
37663766
if is_some_guard(kind)
37673767
return Pair{Any,Any}(newty, ErrorException)
37683768
elseif is_some_const_binding(kind) || is_some_imported(kind)
3769-
return Pair{Any,Any}(Bottom, ErrorException)
3769+
# N.B.: Backdating should not improve inference in an earlier world
3770+
return Pair{Any,Any}(kind == PARTITION_KIND_BACKDATED_CONST ? newty : Bottom, ErrorException)
37703771
end
37713772
ty = kind == PARTITION_KIND_DECLARED ? Any : partition_restriction(partition)
37723773
wnewty = widenconst(newty)
37733774
if !hasintersect(wnewty, ty)
3774-
return Pair{Any,Any}(Bottom, ErrorException)
3775+
return Pair{Any,Any}(Bottom, TypeError)
37753776
elseif !(wnewty <: ty)
37763777
retty = tmeet(typeinf_lattice(interp), newty, ty)
3777-
return Pair{Any,Any}(retty, ErrorException)
3778+
return Pair{Any,Any}(retty, TypeError)
37783779
end
37793780
return Pair{Any,Any}(newty, Bottom)
37803781
end

Compiler/test/inference.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6458,7 +6458,7 @@ end
64586458
global invalid_setglobal!_exct_modeling::Int
64596459
@test Base.infer_exception_type((Float64,)) do x
64606460
setglobal!(@__MODULE__, :invalid_setglobal!_exct_modeling, x)
6461-
end == ErrorException
6461+
end == TypeError
64626462

64636463
# Issue #58257 - Hang in inference during BindingPartition resolution
64646464
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,Binding,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.Binding)
95+
gr = ex.context.globalref
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
@@ -3231,8 +3231,7 @@ static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *s
32313231
if (ty != nullptr) {
32323232
const std::string fname = issetglobal ? "setglobal!" : isreplaceglobal ? "replaceglobal!" : isswapglobal ? "swapglobal!" : ismodifyglobal ? "modifyglobal!" : "setglobalonce!";
32333233
if (!ismodifyglobal) {
3234-
// TODO: use typeassert in jl_check_binding_assign_value too
3235-
emit_typecheck(ctx, rval, ty, "typeassert");
3234+
emit_typecheck(ctx, rval, ty, fname.c_str());
32363235
rval = update_julia_type(ctx, rval, ty);
32373236
if (rval.typ == jl_bottom_type)
32383237
return jl_cgval_t();

src/datatype.c

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1992,11 +1992,11 @@ jl_value_t *swap_nth_field(jl_datatype_t *st, jl_value_t *v, size_t i, jl_value_
19921992
}
19931993
}
19941994

1995-
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)
1995+
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)
19961996
{
19971997
jl_value_t *r = isatomic ? jl_atomic_load(p) : jl_atomic_load_relaxed(p);
19981998
if (__unlikely(r == NULL)) {
1999-
if (mod && name)
1999+
if (b)
20002000
jl_undefined_var_error(name, (jl_value_t*)mod);
20012001
jl_throw(jl_undefref_exception);
20022002
}
@@ -2007,11 +2007,10 @@ inline jl_value_t *modify_value(jl_value_t *ty, _Atomic(jl_value_t*) *p, jl_valu
20072007
args[1] = rhs;
20082008
jl_value_t *y = jl_apply_generic(op, args, 2);
20092009
args[1] = y;
2010-
if (!jl_isa(y, ty)) {
2011-
if (mod && name)
2012-
jl_errorf("cannot assign an incompatible value to the global %s.%s.", jl_symbol_name(mod->name), jl_symbol_name(name));
2010+
if (b)
2011+
jl_check_binding_assign_value(b, mod, name, y, "modifyglobal!");
2012+
else if (!jl_isa(y, ty))
20132013
jl_type_error(jl_is_genericmemory(parent) ? "memoryrefmodify!" : "modifyfield!", ty, y);
2014-
}
20152014
if (isatomic ? jl_atomic_cmpswap(p, &r, y) : jl_atomic_cmpswap_release(p, &r, y)) {
20162015
jl_gc_wb(parent, y);
20172016
break;
@@ -2125,7 +2124,7 @@ jl_value_t *modify_nth_field(jl_datatype_t *st, jl_value_t *v, size_t i, jl_valu
21252124
jl_value_t *ty = jl_field_type_concrete(st, i);
21262125
char *p = (char*)v + offs;
21272126
if (jl_field_isptr(st, i)) {
2128-
return modify_value(ty, (_Atomic(jl_value_t*)*)p, v, op, rhs, isatomic, NULL, NULL);
2127+
return modify_value(ty, (_Atomic(jl_value_t*)*)p, v, op, rhs, isatomic, NULL, NULL, NULL);
21292128
}
21302129
else {
21312130
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
@@ -506,7 +506,7 @@ JL_DLLEXPORT jl_value_t *jl_memoryrefmodify(jl_genericmemoryref_t m, jl_value_t
506506
char *data = (char*)m.ptr_or_offset;
507507
if (layout->flags.arrayelem_isboxed) {
508508
assert(data - (char*)m.mem->ptr < sizeof(jl_value_t*) * m.mem->length);
509-
return modify_value(eltype, (_Atomic(jl_value_t*)*)data, owner, op, rhs, isatomic, NULL, NULL);
509+
return modify_value(eltype, (_Atomic(jl_value_t*)*)data, owner, op, rhs, isatomic, NULL, NULL, NULL);
510510
}
511511
size_t fsz = layout->size;
512512
uint8_t *psel = NULL;

src/julia.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2086,6 +2086,10 @@ JL_DLLEXPORT void JL_NORETURN jl_type_error_rt(const char *fname,
20862086
const char *context,
20872087
jl_value_t *ty JL_MAYBE_UNROOTED,
20882088
jl_value_t *got JL_MAYBE_UNROOTED);
2089+
JL_DLLEXPORT void JL_NORETURN jl_type_error_global(const char *fname,
2090+
jl_binding_t *b JL_MAYBE_UNROOTED,
2091+
jl_value_t *ty JL_MAYBE_UNROOTED,
2092+
jl_value_t *got JL_MAYBE_UNROOTED);
20892093
JL_DLLEXPORT void JL_NORETURN jl_undefined_var_error(jl_sym_t *var, jl_value_t *scope JL_MAYBE_UNROOTED);
20902094
JL_DLLEXPORT void JL_NORETURN jl_has_no_field_error(jl_datatype_t *t, jl_sym_t *var);
20912095
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
@@ -882,7 +882,7 @@ int set_nth_fieldonce(jl_datatype_t *st, jl_value_t *v, size_t i, jl_value_t *rh
882882
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);
883883
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);
884884
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);
885-
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);
885+
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);
886886
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);
887887
int setonce_bits(jl_datatype_t *rty, char *p, jl_value_t *owner, jl_value_t *rhs, enum atomic_kind isatomic);
888888
jl_expr_t *jl_exprn(jl_sym_t *head, size_t n);
@@ -895,6 +895,7 @@ jl_array_t *jl_get_loaded_modules(void);
895895
JL_DLLEXPORT int jl_datatype_isinlinealloc(jl_datatype_t *ty, int pointerfree);
896896
int jl_type_equality_is_identity(jl_value_t *t1, jl_value_t *t2) JL_NOTSAFEPOINT;
897897

898+
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);
898899
void jl_binding_set_type(jl_binding_t *b, jl_module_t *mod, jl_sym_t *sym, jl_value_t *ty);
899900
JL_DLLEXPORT void jl_declare_global(jl_module_t *m, jl_value_t *arg, jl_value_t *set_type, int strong);
900901
JL_DLLEXPORT jl_binding_partition_t *jl_declare_constant_val3(jl_binding_t *b JL_ROOTING_ARGUMENT, jl_module_t *mod, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED, enum jl_partition_kind, size_t new_world) JL_GLOBALLY_ROOTED;

src/module.c

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

18871887
// For a generally writable binding (checked using jl_check_binding_currently_writable in this world age), check whether
18881888
// we can actually write the value `rhs` to it.
1889-
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)
1889+
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)
18901890
{
18911891
JL_GC_PUSH1(&rhs); // callee-rooted
18921892
jl_binding_partition_t *bpart = jl_get_binding_partition(b, jl_current_task->world_age);
18931893
enum jl_partition_kind kind = jl_binding_kind(bpart);
18941894
assert(kind == PARTITION_KIND_DECLARED || kind == PARTITION_KIND_GLOBAL);
18951895
jl_value_t *old_ty = kind == PARTITION_KIND_DECLARED ? (jl_value_t*)jl_any_type : bpart->restriction;
18961896
JL_GC_PROMISE_ROOTED(old_ty);
1897-
if (old_ty != (jl_value_t*)jl_any_type && jl_typeof(rhs) != old_ty) {
1898-
if (!jl_isa(rhs, old_ty))
1899-
jl_errorf("cannot assign an incompatible value to the global %s.%s.",
1900-
jl_symbol_name(mod->name), jl_symbol_name(var));
1897+
if (old_ty != (jl_value_t*)jl_any_type && jl_typeof(rhs) != old_ty && !jl_isa(rhs, old_ty)) {
1898+
jl_type_error_global(msg, b, old_ty, rhs);
19011899
}
19021900
JL_GC_POP();
19031901
return old_ty;
19041902
}
19051903

19061904
JL_DLLEXPORT void jl_checked_assignment(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs)
19071905
{
1908-
if (jl_check_binding_assign_value(b, mod, var, rhs) != NULL) {
1906+
if (jl_check_binding_assign_value(b, mod, var, rhs, "setglobal!") != NULL) {
19091907
jl_atomic_store_release(&b->value, rhs);
19101908
jl_gc_wb(b, rhs);
19111909
}
19121910
}
19131911

19141912
JL_DLLEXPORT jl_value_t *jl_checked_swap(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs)
19151913
{
1916-
jl_check_binding_assign_value(b, mod, var, rhs);
1914+
jl_check_binding_assign_value(b, mod, var, rhs, "swapglobal!");
19171915
jl_value_t *old = jl_atomic_exchange(&b->value, rhs);
19181916
jl_gc_wb(b, rhs);
19191917
if (__unlikely(old == NULL))
@@ -1923,7 +1921,7 @@ JL_DLLEXPORT jl_value_t *jl_checked_swap(jl_binding_t *b, jl_module_t *mod, jl_s
19231921

19241922
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)
19251923
{
1926-
jl_value_t *ty = jl_check_binding_assign_value(b, mod, var, rhs);
1924+
jl_value_t *ty = jl_check_binding_assign_value(b, mod, var, rhs, "replaceglobal!");
19271925
return replace_value(ty, &b->value, (jl_value_t*)b, expected, rhs, 1, mod, var);
19281926
}
19291927

@@ -1937,12 +1935,12 @@ JL_DLLEXPORT jl_value_t *jl_checked_modify(jl_binding_t *b, jl_module_t *mod, jl
19371935
jl_symbol_name(mod->name), jl_symbol_name(var));
19381936
jl_value_t *ty = bpart->restriction;
19391937
JL_GC_PROMISE_ROOTED(ty);
1940-
return modify_value(ty, &b->value, (jl_value_t*)b, op, rhs, 1, mod, var);
1938+
return modify_value(ty, &b->value, (jl_value_t*)b, op, rhs, 1, b, mod, var);
19411939
}
19421940

19431941
JL_DLLEXPORT jl_value_t *jl_checked_assignonce(jl_binding_t *b, jl_module_t *mod, jl_sym_t *var, jl_value_t *rhs )
19441942
{
1945-
jl_check_binding_assign_value(b, mod, var, rhs);
1943+
jl_check_binding_assign_value(b, mod, var, rhs, "setglobalonce!");
19461944
jl_value_t *old = NULL;
19471945
if (jl_atomic_cmpswap(&b->value, &old, rhs))
19481946
jl_gc_wb(b, rhs);

0 commit comments

Comments
 (0)