Skip to content

Commit

Permalink
make Tuple{Union{}} unconstructable
Browse files Browse the repository at this point in the history
Type intersection assumed it was equal to Union{}, so this makes it
unconstructable so that holds true. This is similar to what the
NamedTuple constructor does.

Secondarily, this fixes an inference bug where it would create
Vararg{Union{}} and then incorrectly handle that fieldtype.

- Fixes #32392
- Addresses part of the concerns discussed in
  #24614 (comment)
- Addresses part of the issues presented in
  #26175
- May allow improving jl_type_equality_is_identity
  (https://github.com/JuliaLang/julia/pull/49017/files#diff-882927c6e612596e22406ae0d06adcee88a9ec05e8b61ad81b48942e2cb266e9R986)
- May allow improving intersection (finish_unionall can be more
  aggressive at computing varval for any typevars that appears in
  covariant position and has lb=Union{} and ub=leaf type)
  • Loading branch information
vtjnash committed Mar 22, 2023
1 parent 6d678fe commit bc9e9b9
Show file tree
Hide file tree
Showing 27 changed files with 205 additions and 120 deletions.
25 changes: 19 additions & 6 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,8 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp
add_remark!(interp, sv, "Refusing to infer into `depwarn`")
return MethodCallResult(Any, false, false, nothing, Effects())
end
sigtuple = unwrap_unionall(sig)
sigtuple isa DataType || return MethodCallResult(Any, false, false, nothing, Effects())

# Limit argument type tuple growth of functions:
# look through the parents list to see if there's a call to the same method
Expand Down Expand Up @@ -581,7 +583,6 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp
washardlimit = hardlimit

if topmost !== nothing
sigtuple = unwrap_unionall(sig)::DataType
msig = unwrap_unionall(method.sig)::DataType
spec_len = length(msig.parameters) + 1
ls = length(sigtuple.parameters)
Expand Down Expand Up @@ -1375,7 +1376,11 @@ function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft)
va = isvarargtype(last)
elts = Any[ fieldtype(tti0, i) for i = 1:len ]
if va
elts[len] = Vararg{elts[len]}
if elts[len] === Union{}
pop!(elts)
else
elts[len] = Vararg{elts[len]}
end
end
return AbstractIterationResult(elts, nothing)
end
Expand All @@ -1384,6 +1389,9 @@ function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft)
elseif tti0 === Any
return AbstractIterationResult(Any[Vararg{Any}], nothing, Effects())
elseif tti0 <: Array
if eltype(tti0) === Union{}
return AbstractIterationResult(Any[], nothing)
end
return AbstractIterationResult(Any[Vararg{eltype(tti0)}], nothing)
else
return abstract_iteration(interp, itft, typ, sv)
Expand Down Expand Up @@ -2097,7 +2105,7 @@ end

function sp_type_rewrap(@nospecialize(T), linfo::MethodInstance, isreturn::Bool)
isref = false
if T === Bottom
if unwrapva(T) === Bottom
return Bottom
elseif isa(T, Type)
if isa(T, DataType) && (T::DataType).name === _REF_NAME
Expand Down Expand Up @@ -2134,8 +2142,13 @@ end
function abstract_eval_cfunction(interp::AbstractInterpreter, e::Expr, vtypes::VarTable, sv::InferenceState)
f = abstract_eval_value(interp, e.args[2], vtypes, sv)
# rt = sp_type_rewrap(e.args[3], sv.linfo, true)
at = Any[ sp_type_rewrap(argt, sv.linfo, false) for argt in e.args[4]::SimpleVector ]
pushfirst!(at, f)
atv = e.args[4]::SimpleVector
at = Vector{Any}(undef, length(atv) + 1)
at[1] = f
for i = 1:length(atv)
at[i + 1] = sp_type_rewrap(at[i], sv.linfo, false)
at[i + 1] === Bottom && return
end
# this may be the wrong world for the call,
# but some of the result is likely to be valid anyways
# and that may help generate better codegen
Expand Down Expand Up @@ -2338,7 +2351,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
let t = t, at = at; all(i::Int->getfield(at.val::Tuple, i) isa fieldtype(t, i), 1:n); end
nothrow = isexact
t = Const(ccall(:jl_new_structt, Any, (Any, Any), t, at.val))
elseif isa(at, PartialStruct) && at ᵢ Tuple && n == length(at.fields::Vector{Any}) &&
elseif isa(at, PartialStruct) && at ᵢ Tuple && n > 0 && n == length(at.fields::Vector{Any}) && !isvarargtype(at.fields[end]) &&
let t = t, at = at; all(i::Int->(at.fields::Vector{Any})[i] fieldtype(t, i), 1:n); end
nothrow = isexact
t = PartialStruct(t, at.fields::Vector{Any})
Expand Down
4 changes: 2 additions & 2 deletions base/compiler/inferenceresult.jl
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ function most_general_argtypes(method::Union{Method, Nothing}, @nospecialize(spe
# to the appropriate `Tuple` type or `PartialStruct` instance.
if !toplevel && isva
if specTypes::Type == Tuple
linfo_argtypes = Any[Any for i = 1:nargs]
if nargs > 1
linfo_argtypes = Any[Any for i = 1:nargs]
linfo_argtypes[end] = Vararg{Any}
linfo_argtypes[end] = Tuple
end
vargtype = Tuple
else
Expand Down
2 changes: 1 addition & 1 deletion base/compiler/ssair/inlining.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1170,7 +1170,7 @@ function handle_invoke_call!(todo::Vector{Pair{Int,Any}},
if isa(result, ConstPropResult)
mi = result.result.linfo
validate_sparams(mi.sparam_vals) || return nothing
if argtypes_to_type(argtypes) <: mi.def.sig
if Union{} !== argtypes_to_type(argtypes) <: mi.def.sig
item = resolve_todo(mi, result.result, argtypes, info, flag, state; invokesig)
handle_single_case!(todo, ir, idx, stmt, item, OptimizationParams(state.interp), true)
return nothing
Expand Down
10 changes: 10 additions & 0 deletions base/compiler/tfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1872,7 +1872,15 @@ add_tfunc(apply_type, 1, INT_INF, apply_type_tfunc, 10)
# convert the dispatch tuple type argtype to the real (concrete) type of
# the tuple of those values
function tuple_tfunc(𝕃::AbstractLattice, argtypes::Vector{Any})
isempty(argtypes) && return Const(())
argtypes = anymap(widenslotwrapper, argtypes)
if isvarargtype(argtypes[end]) && unwrapva(argtypes[end]) === Union{}
# Drop the Vararg in Tuple{...,Vararg{Union{}}} since it must be length 0.
# If there is a Vararg num also, it must be a TypeVar, and it must be
# zero, but that generally shouldn't show up here, since it implies a
# UnionAll context is missing around this.
pop!(argtypes)
end
all_are_const = true
for i in 1:length(argtypes)
if !isa(argtypes[i], Const)
Expand Down Expand Up @@ -1915,6 +1923,8 @@ function tuple_tfunc(𝕃::AbstractLattice, argtypes::Vector{Any})
params[i] = x
elseif !isvarargtype(x) && hasintersect(x, Type)
params[i] = Union{x, Type}
elseif x === Union{}
return Bottom # argtypes is malformed, but try not to crash
else
params[i] = x
end
Expand Down
1 change: 1 addition & 0 deletions base/compiler/typeutils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ function typesubtract(@nospecialize(a), @nospecialize(b), max_union_splitting::I
bp = b.parameters[i]
(isvarargtype(ap) || isvarargtype(bp)) && return a
ta[i] = typesubtract(ap, bp, min(2, max_union_splitting))
ta[i] === Union{} && return Union{}
return Tuple{ta...}
end
end
Expand Down
14 changes: 10 additions & 4 deletions base/iterators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ if Base !== Core.Compiler
export partition
end

function TupleOrBottom(tt...)
any(p -> p === Union{}, tt) && return Union{}
return Tuple{tt...}
end


"""
Iterators.map(f, iterators...)
Expand Down Expand Up @@ -209,7 +215,7 @@ size(e::Enumerate) = size(e.itr)
end
last(e::Enumerate) = (length(e.itr), e.itr[end])

eltype(::Type{Enumerate{I}}) where {I} = Tuple{Int, eltype(I)}
eltype(::Type{Enumerate{I}}) where {I} = TupleOrBottom(Int, eltype(i))

IteratorSize(::Type{Enumerate{I}}) where {I} = IteratorSize(I)
IteratorEltype(::Type{Enumerate{I}}) where {I} = IteratorEltype(I)
Expand Down Expand Up @@ -394,7 +400,7 @@ _promote_tuple_shape((m,)::Tuple{Integer}, (n,)::Tuple{Integer}) = (min(m, n),)
_promote_tuple_shape(a, b) = promote_shape(a, b)
_promote_tuple_shape(a, b...) = _promote_tuple_shape(a, _promote_tuple_shape(b...))
_promote_tuple_shape(a) = a
eltype(::Type{Zip{Is}}) where {Is<:Tuple} = Tuple{map(eltype, fieldtypes(Is))...}
eltype(::Type{Zip{Is}}) where {Is<:Tuple} = TupleOrBottom(map(eltype, fieldtypes(Is))...)
#eltype(::Type{Zip{Tuple{}}}) = Tuple{}
#eltype(::Type{Zip{Tuple{A}}}) where {A} = Tuple{eltype(A)}
#eltype(::Type{Zip{Tuple{A, B}}}) where {A, B} = Tuple{eltype(A), eltype(B)}
Expand Down Expand Up @@ -1072,8 +1078,7 @@ end

eltype(::Type{ProductIterator{I}}) where {I} = _prod_eltype(I)
_prod_eltype(::Type{Tuple{}}) = Tuple{}
_prod_eltype(::Type{I}) where {I<:Tuple} =
Tuple{ntuple(n -> eltype(fieldtype(I, n)), _counttuple(I)::Int)...}
_prod_eltype(::Type{I}) where {I<:Tuple} = TupleOrBottom(ntuple(n -> eltype(fieldtype(I, n)), _counttuple(I)::Int)...)

iterate(::ProductIterator{Tuple{}}) = (), true
iterate(::ProductIterator{Tuple{}}, state) = nothing
Expand Down Expand Up @@ -1442,6 +1447,7 @@ end
function _approx_iter_type(itrT::Type, vstate::Type)
vstate <: Union{Nothing, Tuple{Any, Any}} || return Any
vstate <: Union{} && return Union{}
itrT <: Union{} && return Union{}
nextvstate = Base._return_type(doiterate, Tuple{itrT, vstate})
return (nextvstate <: vstate ? vstate : Any)
end
Expand Down
2 changes: 1 addition & 1 deletion src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -1318,7 +1318,7 @@ JL_CALLABLE(jl_f_apply_type)
jl_type_error_rt("Tuple", "parameter", (jl_value_t*)jl_type_type, pi);
}
}
return (jl_value_t*)jl_apply_tuple_type_v(&args[1], nargs-1);
return jl_apply_tuple_type_v(&args[1], nargs-1);
}
else if (args[0] == (jl_value_t*)jl_uniontype_type) {
// Union{} has extra restrictions, so it needs to be checked after
Expand Down
16 changes: 8 additions & 8 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5146,7 +5146,7 @@ static std::pair<Function*, Function*> get_oc_function(jl_codectx_t &ctx, jl_met
for (size_t i = 0; i < jl_svec_len(argt_typ->parameters); ++i) {
jl_svecset(sig_args, 1+i, jl_svecref(argt_typ->parameters, i));
}
sigtype = (jl_value_t*)jl_apply_tuple_type_v(jl_svec_data(sig_args), nsig);
sigtype = jl_apply_tuple_type_v(jl_svec_data(sig_args), nsig);

jl_method_instance_t *mi = jl_specializations_get_linfo(closure_method, sigtype, jl_emptysvec);
jl_code_instance_t *ci = (jl_code_instance_t*)jl_rettype_inferred(mi, ctx.world, ctx.world);
Expand Down Expand Up @@ -5469,7 +5469,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_

if (can_optimize) {
jl_value_t *closure_t = NULL;
jl_tupletype_t *env_t = NULL;
jl_value_t *env_t = NULL;
JL_GC_PUSH2(&closure_t, &env_t);

jl_value_t **env_component_ts = (jl_value_t**)alloca(sizeof(jl_value_t*) * (nargs-4));
Expand All @@ -5479,10 +5479,10 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_

env_t = jl_apply_tuple_type_v(env_component_ts, nargs-4);
// we need to know the full env type to look up the right specialization
if (jl_is_concrete_type((jl_value_t*)env_t)) {
if (jl_is_concrete_type(env_t)) {
jl_tupletype_t *argt_typ = (jl_tupletype_t*)argt.constant;
Function *F, *specF;
std::tie(F, specF) = get_oc_function(ctx, (jl_method_t*)source.constant, env_t, argt_typ, ub.constant);
std::tie(F, specF) = get_oc_function(ctx, (jl_method_t*)source.constant, (jl_datatype_t*)env_t, argt_typ, ub.constant);
if (F) {
jl_cgval_t jlcall_ptr = mark_julia_type(ctx, F, false, jl_voidpointer_type);
jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_gcframe);
Expand All @@ -5495,7 +5495,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_
fptr = mark_julia_type(ctx, (llvm::Value*)Constant::getNullValue(ctx.types().T_size), false, jl_voidpointer_type);

// TODO: Inline the env at the end of the opaque closure and generate a descriptor for GC
jl_cgval_t env = emit_new_struct(ctx, (jl_value_t*)env_t, nargs-4, &argv.data()[4]);
jl_cgval_t env = emit_new_struct(ctx, env_t, nargs-4, &argv.data()[4]);

jl_cgval_t closure_fields[5] = {
env,
Expand Down Expand Up @@ -6441,7 +6441,7 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con
sigt = NULL;
}
else {
sigt = (jl_value_t*)jl_apply_tuple_type((jl_svec_t*)sigt);
sigt = jl_apply_tuple_type((jl_svec_t*)sigt);
}
if (sigt && !(unionall_env && jl_has_typevar_from_unionall(rt, unionall_env))) {
unionall_env = NULL;
Expand Down Expand Up @@ -6891,9 +6891,9 @@ static jl_datatype_t *compute_va_type(jl_method_instance_t *lam, size_t nreq)
}
jl_svecset(tupargs, i-nreq, argType);
}
jl_datatype_t *typ = jl_apply_tuple_type(tupargs);
jl_value_t *typ = jl_apply_tuple_type(tupargs);
JL_GC_POP();
return typ;
return (jl_datatype_t*)typ;
}


Expand Down
6 changes: 3 additions & 3 deletions src/gf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1224,7 +1224,7 @@ static jl_method_instance_t *cache_method(
intptr_t nspec = (kwmt == NULL || kwmt == jl_type_type_mt || kwmt == jl_nonfunction_mt || kwmt == jl_kwcall_mt ? definition->nargs + 1 : jl_atomic_load_relaxed(&kwmt->max_args) + 2 + 2 * (mt == jl_kwcall_mt));
jl_compilation_sig(tt, sparams, definition, nspec, &newparams);
if (newparams) {
temp2 = (jl_value_t*)jl_apply_tuple_type(newparams);
temp2 = jl_apply_tuple_type(newparams);
// Now there may be a problem: the widened signature is more general
// than just the given arguments, so it might conflict with another
// definition that does not have cache instances yet. To fix this, we
Expand Down Expand Up @@ -1347,7 +1347,7 @@ static jl_method_instance_t *cache_method(
}
}
if (newparams) {
simplett = jl_apply_tuple_type(newparams);
simplett = (jl_datatype_t*)jl_apply_tuple_type(newparams);
temp2 = (jl_value_t*)simplett;
}

Expand Down Expand Up @@ -2510,7 +2510,7 @@ JL_DLLEXPORT jl_value_t *jl_normalize_to_compilable_sig(jl_methtable_t *mt, jl_t
jl_compilation_sig(ti, env, m, nspec, &newparams);
int is_compileable = ((jl_datatype_t*)ti)->isdispatchtuple;
if (newparams) {
tt = jl_apply_tuple_type(newparams);
tt = (jl_datatype_t*)jl_apply_tuple_type(newparams);
if (!is_compileable) {
// compute new env, if used below
jl_value_t *ti = jl_type_intersection_env((jl_value_t*)tt, (jl_value_t*)m->sig, &newparams);
Expand Down
2 changes: 1 addition & 1 deletion src/intrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1409,7 +1409,7 @@ static Value *emit_untyped_intrinsic(jl_codectx_t &ctx, intrinsic f, Value **arg
jl_value_t *params[2];
params[0] = xtyp;
params[1] = (jl_value_t*)jl_bool_type;
jl_datatype_t *tuptyp = jl_apply_tuple_type_v(params, 2);
jl_datatype_t *tuptyp = (jl_datatype_t*)jl_apply_tuple_type_v(params, 2);
*newtyp = tuptyp;

Value *tupval;
Expand Down
1 change: 0 additions & 1 deletion src/jl_exported_funcs.inc
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,6 @@
XX(jl_try_substrtod) \
XX(jl_try_substrtof) \
XX(jl_tty_set_mode) \
XX(jl_tupletype_fill) \
XX(jl_typeassert) \
XX(jl_typeinf_lock_begin) \
XX(jl_typeinf_lock_end) \
Expand Down
Loading

0 comments on commit bc9e9b9

Please sign in to comment.