Skip to content

Commit

Permalink
handle Type{Union{}} like typeof(Union{}) more (#55508)
Browse files Browse the repository at this point in the history
We could try to make them both pointers (by setting mayinlinealloc=false
on Core.TypeofBottom), but let's try to make them both equivalent
representations of the typeof Union{} as a singleton value.

Fixes #55208
  • Loading branch information
vtjnash authored Aug 20, 2024
1 parent bec4702 commit a218e82
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 6 deletions.
9 changes: 7 additions & 2 deletions src/cgutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,7 @@ static Type *_julia_type_to_llvm(jl_codegen_params_t *ctx, LLVMContext &ctxt, jl
{
// this function converts a Julia Type into the equivalent LLVM type
if (isboxed) *isboxed = false;
if (jt == (jl_value_t*)jl_bottom_type)
if (jt == (jl_value_t*)jl_bottom_type || jt == (jl_value_t*)jl_typeofbottom_type || jt == (jl_value_t*)jl_typeofbottom_type->super)
return getVoidTy(ctxt);
if (jl_is_concrete_immutable(jt)) {
if (jl_datatype_nbits(jt) == 0)
Expand Down Expand Up @@ -760,7 +760,7 @@ static Type *_julia_struct_to_llvm(jl_codegen_params_t *ctx, LLVMContext &ctxt,
// use this where C-compatible (unboxed) structs are desired
// use julia_type_to_llvm directly when you want to preserve Julia's type semantics
if (isboxed) *isboxed = false;
if (jt == (jl_value_t*)jl_bottom_type)
if (jt == (jl_value_t*)jl_bottom_type || jt == (jl_value_t*)jl_typeofbottom_type || jt == (jl_value_t*)jl_typeofbottom_type->super)
return getVoidTy(ctxt);
if (jl_is_primitivetype(jt))
return bitstype_to_llvm(jt, ctxt, llvmcall);
Expand Down Expand Up @@ -948,6 +948,9 @@ static bool for_each_uniontype_small(
allunbox &= for_each_uniontype_small(f, ((jl_uniontype_t*)ty)->b, counter);
return allunbox;
}
else if (ty == (jl_value_t*)jl_typeofbottom_type->super) {
f(++counter, jl_typeofbottom_type); // treat Tuple{union{}} as identical to typeof(Union{})
}
else if (jl_is_pointerfree(ty)) {
f(++counter, (jl_datatype_t*)ty);
return true;
Expand Down Expand Up @@ -1691,6 +1694,8 @@ static std::pair<Value*, bool> emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x,
if (intersected_type == (jl_value_t*)jl_bottom_type)
known_isa = false;
}
if (intersected_type == (jl_value_t*)jl_typeofbottom_type->super)
intersected_type = (jl_value_t*)jl_typeofbottom_type; // swap abstract Type{Union{}} for concrete typeof(Union{})
if (known_isa) {
if (!*known_isa && !msg.isTriviallyEmpty()) {
emit_type_error(ctx, x, literal_pointer_val(ctx, type), msg);
Expand Down
10 changes: 6 additions & 4 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2240,6 +2240,8 @@ static inline jl_cgval_t ghostValue(jl_codectx_t &ctx, jl_value_t *typ)
// replace T::Type{T} with T, by assuming that T must be a leaftype of some sort
jl_cgval_t constant(NULL, true, typ, NULL, best_tbaa(ctx.tbaa(), typ));
constant.constant = jl_tparam0(typ);
if (typ == (jl_value_t*)jl_typeofbottom_type->super)
constant.isghost = true;
return constant;
}
return jl_cgval_t(typ);
Expand All @@ -2252,7 +2254,7 @@ static inline jl_cgval_t ghostValue(jl_codectx_t &ctx, jl_datatype_t *typ)
static inline jl_cgval_t mark_julia_const(jl_codectx_t &ctx, jl_value_t *jv)
{
jl_value_t *typ;
if (jl_is_type(jv)) {
if (jl_is_type(jv) && jv != jl_bottom_type) {
typ = (jl_value_t*)jl_wrap_Type(jv); // TODO: gc-root this?
}
else {
Expand Down Expand Up @@ -3619,8 +3621,8 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva
if (arg1.constant && arg2.constant)
return ConstantInt::get(getInt1Ty(ctx.builder.getContext()), jl_egal(arg1.constant, arg2.constant));

jl_value_t *rt1 = arg1.typ;
jl_value_t *rt2 = arg2.typ;
jl_value_t *rt1 = (arg1.constant ? jl_typeof(arg1.constant) : arg1.typ);
jl_value_t *rt2 = (arg2.constant ? jl_typeof(arg2.constant) : arg2.typ);
if (jl_is_concrete_type(rt1) && jl_is_concrete_type(rt2) && !jl_is_kind(rt1) && !jl_is_kind(rt2) && rt1 != rt2) {
// disjoint concrete leaf types are never equal (quick test)
return ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 0);
Expand Down Expand Up @@ -9534,7 +9536,7 @@ static jl_llvm_functions_t
RTindex = UndefValue::get(getInt8Ty(ctx.builder.getContext()));
}
else if (jl_is_concrete_type(val.typ) || val.constant) {
size_t tindex = get_box_tindex((jl_datatype_t*)val.typ, phiType);
size_t tindex = get_box_tindex((jl_datatype_t*)(val.constant ? jl_typeof(val.constant) : val.typ), phiType);
if (tindex == 0) {
if (VN)
V = boxed(ctx, val);
Expand Down
4 changes: 4 additions & 0 deletions src/datatype.c
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,8 @@ int jl_struct_try_layout(jl_datatype_t *dt)

int jl_datatype_isinlinealloc(jl_datatype_t *ty, int pointerfree)
{
if (jl_typeofbottom_type && ty == jl_typeofbottom_type->super)
ty = jl_typeofbottom_type;
if (ty->name->mayinlinealloc && jl_struct_try_layout(ty)) {
if (ty->layout->npointers > 0) {
if (pointerfree)
Expand Down Expand Up @@ -1656,6 +1658,8 @@ JL_DLLEXPORT jl_value_t *jl_new_struct_uninit(jl_datatype_t *type)
{
jl_task_t *ct = jl_current_task;
if (!jl_is_datatype(type) || !type->isconcretetype || type->layout == NULL || jl_is_layout_opaque(type->layout)) {
if (type == jl_typeofbottom_type->super)
return jl_bottom_type; // ::Type{Union{}} is an abstract type, but is also a singleton when used as a field type
jl_type_error("new", (jl_value_t*)jl_datatype_type, (jl_value_t*)type);
}
if (type->instance != NULL)
Expand Down
1 change: 1 addition & 0 deletions src/jltypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -3024,6 +3024,7 @@ void jl_init_types(void) JL_GC_DISABLED
jl_anytuple_type->layout = NULL;

jl_typeofbottom_type->super = jl_wrap_Type(jl_bottom_type);
jl_typeofbottom_type->super->layout = jl_typeofbottom_type->layout; // the only abstract type with a layout
jl_emptytuple_type = (jl_datatype_t*)jl_apply_tuple_type(jl_emptysvec, 0);
jl_emptytuple = jl_gc_permobj(0, jl_emptytuple_type);
jl_emptytuple_type->instance = jl_emptytuple;
Expand Down
15 changes: 15 additions & 0 deletions test/compiler/codegen.jl
Original file line number Diff line number Diff line change
Expand Up @@ -969,3 +969,18 @@ end

# Core.getptls() special handling
@test !occursin("call ptr @jlplt", get_llvm(Core.getptls, Tuple{})) #It should lower to a direct load of the ptls and not a ccall

# issue 55208
@noinline function f55208(x, i)
z = (i == 0 ? x[1] : x[i])
return z isa Core.TypeofBottom
end
@test f55208((Union{}, 5, 6, 7), 0)

@noinline function g55208(x, i)
z = (i == 0 ? x[1] : x[i])
typeof(z)
end
@test g55208((Union{}, true, true), 0) === typeof(Union{})

@test string((Core.Union{}, true, true, true)) == "(Union{}, true, true, true)"
2 changes: 2 additions & 0 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8265,3 +8265,5 @@ end
@test Tuple{Vararg{Int}} === Union{Tuple{Int}, Tuple{}, Tuple{Int, Int, Vararg{Int}}}
@test (Tuple{Vararg{T}} where T) === (Union{Tuple{T, T, Vararg{T}}, Tuple{}, Tuple{T}} where T)
@test_broken (Tuple{Vararg{T}} where T) === Union{Tuple{T, T, Vararg{T}} where T, Tuple{}, Tuple{T} where T}

@test sizeof(Pair{Union{typeof(Union{}),Nothing}, Union{Type{Union{}},Nothing}}(Union{}, Union{})) == 2

0 comments on commit a218e82

Please sign in to comment.