Skip to content

Commit 9be2edb

Browse files
committed
inference: inter-procedural conditional constraint back-propagation
This PR propagates `Conditional`s inter-procedurally when a `Conditional` at return site imposes a constraint on the call arguments. When inference exits local frame and the return type is annotated as `Conditional`, it will be converted into `InterConditional` object, which is implemented in `Core` and can be directly put into the global cache. Finally after going back to caller frame, `InterConditional` will be re-converted into `Conditional` in the context of the caller frame. ## improvements So now some simple "is-wrapper" functions will propagate its constraint as expected, e.g.: ```julia isaint(a) = isa(a, Int) @test Base.return_types((Any,)) do a isaint(a) && return a # a::Int return 0 end == Any[Int] isaint2(::Any) = false isaint2(::Int) = true @test Base.return_types((Any,)) do a isaint2(a) && return a # a::Int return 0 end == Any[Int] function isa_int_or_float64(a) isa(a, Int) && return true isa(a, Float64) && return true return false end @test Base.return_types((Any,)) do a isa_int_or_float64(a) && return a # a::Union{Float64,Int} 0 end == Any[Union{Float64,Int}] ``` (and now we don't need something like JuliaLang#38636) ## benchmarks A compile time comparison: > on the current master (82d79ce) ``` Sysimage built. Summary: Total ─────── 55.295376 seconds Base: ─────── 23.359226 seconds 42.2444% Stdlibs: ──── 31.934773 seconds 57.7531% JULIA usr/lib/julia/sys-o.a Generating REPL precompile statements... 29/29 Executing precompile statements... 1283/1283 Precompilation complete. Summary: Total ─────── 91.129162 seconds Generation ── 68.800937 seconds 75.4983% Execution ─── 22.328225 seconds 24.5017% LINK usr/lib/julia/sys.dylib ``` > on this PR (37e279b) ``` Sysimage built. Summary: Total ─────── 51.694730 seconds Base: ─────── 21.943914 seconds 42.449% Stdlibs: ──── 29.748987 seconds 57.5474% JULIA usr/lib/julia/sys-o.a Generating REPL precompile statements... 29/29 Executing precompile statements... 1357/1357 Precompilation complete. Summary: Total ─────── 88.956226 seconds Generation ── 67.077710 seconds 75.4053% Execution ─── 21.878515 seconds 24.5947% LINK usr/lib/julia/sys.dylib ``` Here is a sample code that benefits from this PR: ```julia function summer(ary) r = 0 for a in ary if ispositive(a) r += a end end r end ispositive(a) = isa(a, Int) && a > 0 ary = Any[] for _ in 1:100_000 if rand(Bool) push!(ary, rand(-100:100)) elseif rand(Bool) push!(ary, rand('a':'z')) else push!(ary, nothing) end end using BenchmarkTools @Btime summer($(ary)) ``` > on the current master (82d79ce) ``` ❯ julia summer.jl 1.214 ms (24923 allocations: 389.42 KiB) ``` > on this PR (37e279b) ``` ❯ julia summer.jl 421.223 μs (0 allocations: 0 bytes) ``` ## caveats Within the `Conditional`/`InterConditional` framework, only a single constraint can be back-propagated inter-procedurally. This PR implements a naive heuristic to "pick up" a constraint to be propagated when a return type is a boolean. The heuristic may fail to select an "interesting" constraint in some cases. For example, we may expect `::Expr` constraint to be imposed on the first argument of `Meta.isexpr`, but the current heuristic ends up picking up a constraint on the second argument (i.e. `ex.head === head`). ```julia isexpr(@nospecialize(ex), head::Symbol) = isa(ex, Expr) && ex.head === head @test_broken Base.return_types((Any,)) do x Meta.isexpr(x, :call) && return x # x::Expr, ideally return nothing end == Any[Union{Nothing,Expr}] ``` I think We can get rid of this limitation by extending `Conditional` and `InterConditional` so that they can convey multiple constraints, but I'd like to leave this as a future work. --- - closes JuliaLang#38636 - closes JuliaLang#37342
1 parent 83f3be9 commit 9be2edb

File tree

11 files changed

+236
-39
lines changed

11 files changed

+236
-39
lines changed

base/boot.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,7 @@ eval(Core, :(CodeInstance(mi::MethodInstance, @nospecialize(rettype), @nospecial
425425
eval(Core, :(Const(@nospecialize(v)) = $(Expr(:new, :Const, :v))))
426426
eval(Core, :(PartialStruct(@nospecialize(typ), fields::Array{Any, 1}) = $(Expr(:new, :PartialStruct, :typ, :fields))))
427427
eval(Core, :(PartialOpaque(@nospecialize(typ), @nospecialize(env), isva::Bool, parent::MethodInstance, source::Method) = $(Expr(:new, :PartialOpaque, :typ, :env, :isva, :parent, :source))))
428+
eval(Core, :(InterConditional(slot::Int, @nospecialize(vtype), @nospecialize(elsetype)) = $(Expr(:new, :InterConditional, :slot, :vtype, :elsetype))))
428429
eval(Core, :(MethodMatch(@nospecialize(spec_types), sparams::SimpleVector, method::Method, fully_covers::Bool) =
429430
$(Expr(:new, :MethodMatch, :spec_types, :sparams, :method, :fully_covers))))
430431

base/compiler/abstractinterpretation.jl

Lines changed: 84 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ function is_improvable(@nospecialize(rtype))
2424
# already at Bottom
2525
return rtype !== Union{}
2626
end
27-
# Could be improved to `Const` or a more precise PartialStruct
28-
return isa(rtype, PartialStruct)
27+
# Could be improved to `Const` or a more precise wrapper
28+
return isa(rtype, PartialStruct) || isa(rtype, InterConditional)
2929
end
3030

3131
function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), argtypes::Vector{Any}, @nospecialize(atype), sv::InferenceState,
@@ -186,6 +186,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
186186
rettype = Any
187187
end
188188
add_call_backedges!(interp, rettype, edges, fullmatch, mts, atype, sv)
189+
@assert !(rettype isa Conditional) "invalid lattice element returned from inter-procedural context"
189190
#print("=> ", rettype, "\n")
190191
if rettype isa LimitedAccuracy
191192
union!(sv.pclimitations, rettype.causes)
@@ -1098,9 +1099,29 @@ function abstract_call(interp::AbstractInterpreter, fargs::Union{Nothing,Vector{
10981099
add_remark!(interp, sv, "Could not identify method table for call")
10991100
return CallMeta(Any, false)
11001101
end
1101-
return abstract_call_gf_by_type(interp, nothing, argtypes, argtypes_to_type(argtypes), sv, max_methods)
1102+
callinfo = abstract_call_gf_by_type(interp, nothing, argtypes, argtypes_to_type(argtypes), sv, max_methods)
1103+
return callinfo_from_interprocedural(callinfo, fargs)
11021104
end
1103-
return abstract_call_known(interp, f, fargs, argtypes, sv, max_methods)
1105+
callinfo = abstract_call_known(interp, f, fargs, argtypes, sv, max_methods)
1106+
return callinfo_from_interprocedural(callinfo, fargs)
1107+
end
1108+
1109+
function callinfo_from_interprocedural(callinfo::CallMeta, ea::Union{Nothing,Vector{Any}})
1110+
rt = callinfo.rt
1111+
if isa(rt, InterConditional)
1112+
if ea !== nothing
1113+
# convert inter-procedural conditional constraint from callee into the constraint
1114+
# on slots of the current frame; `InterConditional` only comes from a "valid"
1115+
# `abstract_call` as such its slot should always be within the bound of this
1116+
# call arguments `ea`
1117+
e = ea[rt.slot]
1118+
if isa(e, Slot)
1119+
return CallMeta(Conditional(e, rt.vtype, rt.elsetype), callinfo.info)
1120+
end
1121+
end
1122+
return CallMeta(widenconditional(rt), callinfo.info)
1123+
end
1124+
return callinfo
11041125
end
11051126

11061127
function sp_type_rewrap(@nospecialize(T), linfo::MethodInstance, isreturn::Bool)
@@ -1375,17 +1396,43 @@ function abstract_eval_ssavalue(s::SSAValue, src::CodeInfo)
13751396
return typ
13761397
end
13771398

1378-
function widenreturn(@nospecialize rt)
1399+
function widenreturn(@nospecialize(rt), @nospecialize(bestguess), isva::Bool, nargs::Int, changes::VarTable)
1400+
if isva
1401+
# give up inter-procedural constraint back-propagation from vararg methods
1402+
# because types of same slot may differ between callee and caller
1403+
rt = widenconditional(rt)
1404+
else
1405+
if isa(rt, Conditional) && !(1 slot_id(rt.var) nargs)
1406+
# discard this `Conditional` imposed on non-call arguments,
1407+
# since it's not interesting in inter-procedural context;
1408+
# we may give constraints on other call argument
1409+
rt = widenconditional(rt)
1410+
end
1411+
if !isa(rt, Conditional) && rt Bool
1412+
if isa(bestguess, InterConditional)
1413+
# if the bestguess so far is already `Conditional`, try to convert
1414+
# this `rt` into `Conditional` on the slot to avoid overapproximation
1415+
# due to conflict of different slots
1416+
rt = boolean_rt_to_conditional(rt, changes, bestguess.slot)
1417+
elseif nargs 1
1418+
# pick up the first "interesting" slot, convert `rt` to its `Conditional`
1419+
# TODO: this is very naive heuristic, ideally we want `Conditional`
1420+
# and `InterConditional` to convey constraints on multiple slots
1421+
rt = boolean_rt_to_conditional(rt, changes, nargs > 1 ? 2 : 1)
1422+
end
1423+
end
1424+
end
1425+
13791426
# only propagate information we know we can store
13801427
# and is valid and good inter-procedurally
1381-
rt = widenconditional(rt)
1428+
isa(rt, Conditional) && return InterConditional(slot_id(rt.var), rt.vtype, rt.elsetype)
13821429
isa(rt, Const) && return rt
13831430
isa(rt, Type) && return rt
13841431
if isa(rt, PartialStruct)
13851432
fields = copy(rt.fields)
13861433
haveconst = false
13871434
for i in 1:length(fields)
1388-
a = widenreturn(fields[i])
1435+
a = widenreturn(fields[i], bestguess, isva, nargs, changes)
13891436
if !haveconst && has_const_info(a)
13901437
# TODO: consider adding && const_prop_profitable(a) here?
13911438
haveconst = true
@@ -1407,6 +1454,9 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
14071454
W = frame.ip
14081455
s = frame.stmt_types
14091456
n = frame.nstmts
1457+
nargs = frame.nargs
1458+
def = frame.linfo.def
1459+
isva = isa(def, Method) && def.isva
14101460
while frame.pc´´ <= n
14111461
# make progress on the active ip set
14121462
local pc::Int = frame.pc´´ # current program-counter
@@ -1461,12 +1511,8 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
14611511
frame.handler_at[l] = frame.cur_hand
14621512
changes_else = changes
14631513
if isa(condt, Conditional)
1464-
if condt.elsetype !== Any && condt.elsetype !== changes[slot_id(condt.var)]
1465-
changes_else = StateUpdate(condt.var, VarState(condt.elsetype, false), changes_else)
1466-
end
1467-
if condt.vtype !== Any && condt.vtype !== changes[slot_id(condt.var)]
1468-
changes = StateUpdate(condt.var, VarState(condt.vtype, false), changes)
1469-
end
1514+
changes_else = conditional_changes(changes_else, condt.elsetype, condt.var)
1515+
changes = conditional_changes(changes, condt.vtype, condt.var)
14701516
end
14711517
newstate_else = stupdate!(s[l], changes_else)
14721518
if newstate_else !== nothing
@@ -1480,7 +1526,8 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
14801526
end
14811527
elseif isa(stmt, ReturnNode)
14821528
pc´ = n + 1
1483-
rt = widenreturn(abstract_eval_value(interp, stmt.val, changes, frame))
1529+
bestguess = frame.bestguess
1530+
rt = widenreturn(abstract_eval_value(interp, stmt.val, changes, frame), bestguess, isva, nargs, changes)
14841531
# copy limitations to return value
14851532
if !isempty(frame.pclimitations)
14861533
union!(frame.limitations, frame.pclimitations)
@@ -1489,9 +1536,9 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
14891536
if !isempty(frame.limitations)
14901537
rt = LimitedAccuracy(rt, copy(frame.limitations))
14911538
end
1492-
if tchanged(rt, frame.bestguess)
1539+
if tchanged(rt, bestguess)
14931540
# new (wider) return type for frame
1494-
frame.bestguess = tmerge(frame.bestguess, rt)
1541+
frame.bestguess = tmerge(bestguess, rt)
14951542
for (caller, caller_pc) in frame.cycle_backedges
14961543
# notify backedges of updated type information
14971544
typeassert(caller.stmt_types[caller_pc], VarTable) # we must have visited this statement before
@@ -1600,6 +1647,27 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
16001647
nothing
16011648
end
16021649

1650+
function conditional_changes(changes::VarTable, @nospecialize(typ), var::Slot)
1651+
if typ (changes[slot_id(var)]::VarState).typ
1652+
return StateUpdate(var, VarState(typ, false), changes)
1653+
end
1654+
return changes
1655+
end
1656+
1657+
function boolean_rt_to_conditional(@nospecialize(rt), state::VarTable, slot_id::Int)
1658+
typ = widenconditional((state[slot_id]::VarState).typ) # avoid nested conditional
1659+
if isa(rt, Const)
1660+
if rt.val === true
1661+
return Conditional(SlotNumber(slot_id), typ, Bottom)
1662+
elseif rt.val === false
1663+
return Conditional(SlotNumber(slot_id), Bottom, typ)
1664+
end
1665+
elseif rt === Bool
1666+
return Conditional(SlotNumber(slot_id), typ, typ)
1667+
end
1668+
return rt
1669+
end
1670+
16031671
# make as much progress on `frame` as possible (by handling cycles)
16041672
function typeinf_nocycle(interp::AbstractInterpreter, frame::InferenceState)
16051673
typeinf_local(interp, frame)

base/compiler/typeinfer.jl

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,9 @@ function CodeInstance(result::InferenceResult, @nospecialize(inferred_result::An
298298
elseif isa(result_type, PartialStruct)
299299
rettype_const = result_type.fields
300300
const_flags = 0x2
301+
elseif isa(result_type, InterConditional)
302+
rettype_const = result_type
303+
const_flags = 0x2
301304
else
302305
rettype_const = nothing
303306
const_flags = 0x00
@@ -773,16 +776,20 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize
773776
code = get(code_cache(interp), mi, nothing)
774777
if code isa CodeInstance # return existing rettype if the code is already inferred
775778
update_valid_age!(caller, WorldRange(min_world(code), max_world(code)))
779+
rettype = code.rettype
776780
if isdefined(code, :rettype_const)
777-
if isa(code.rettype_const, Vector{Any}) && !(Vector{Any} <: code.rettype)
778-
return PartialStruct(code.rettype, code.rettype_const), mi
779-
elseif code.rettype <: Core.OpaqueClosure && isa(code.rettype_const, PartialOpaque)
780-
return code.rettype_const, mi
781+
rettype_const = code.rettype_const
782+
if isa(rettype_const, Vector{Any}) && !(Vector{Any} <: rettype)
783+
return PartialStruct(rettype, rettype_const), mi
784+
elseif rettype <: Core.OpaqueClosure && isa(rettype_const, PartialOpaque)
785+
return rettype_const, mi
786+
elseif isa(rettype_const, InterConditional)
787+
return rettype_const, mi
781788
else
782-
return Const(code.rettype_const), mi
789+
return Const(rettype_const), mi
783790
end
784791
else
785-
return code.rettype, mi
792+
return rettype, mi
786793
end
787794
end
788795
if ccall(:jl_get_module_infer, Cint, (Any,), method.module) == 0

base/compiler/typelattice.jl

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# structs/constants #
55
#####################
66

7-
# N.B.: Const/PartialStruct are defined in Core, to allow them to be used
7+
# N.B.: Const/PartialStruct/InterConditional are defined in Core, to allow them to be used
88
# inside the global code cache.
99
#
1010
# # The type of a value might be constant
@@ -18,7 +18,6 @@
1818
# end
1919
import Core: Const, PartialStruct
2020

21-
2221
# The type of this value might be Bool.
2322
# However, to enable a limited amount of back-propagagation,
2423
# we also keep some information about how this Bool value was created.
@@ -45,6 +44,18 @@ struct Conditional
4544
end
4645
end
4746

47+
# # Similar to `Conditional`, but conveys inter-procedural constraints imposed on call arguments.
48+
# # This is separate from `Conditional` to catch logic errors: the lattice element name is InterConditional
49+
# # while processing a call, then Conditional everywhere else. Thus InterConditional does not appear in
50+
# # CompilerTypes—these type's usages are disjoint—though we define the lattice for InterConditional.
51+
# struct InterConditional
52+
# slot::Int
53+
# vtype
54+
# elsetype
55+
# end
56+
import Core: InterConditional
57+
const AnyConditional = Union{Conditional,InterConditional}
58+
4859
struct PartialTypeVar
4960
tv::TypeVar
5061
# N.B.: Currently unused, but would allow turning something back
@@ -101,11 +112,10 @@ const CompilerTypes = Union{MaybeUndef, Const, Conditional, NotFound, PartialStr
101112
# lattice logic #
102113
#################
103114

104-
function issubconditional(a::Conditional, b::Conditional)
105-
avar = a.var
106-
bvar = b.var
107-
if (isa(avar, Slot) && isa(bvar, Slot) && slot_id(avar) === slot_id(bvar)) ||
108-
(isa(avar, SSAValue) && isa(bvar, SSAValue) && avar === bvar)
115+
# `Conditional` and `InterConditional` are valid in opposite contexts
116+
# (i.e. local inference and inter-procedural call), as such they will never be compared
117+
function issubconditional(a::C, b::C) where {C<:AnyConditional}
118+
if is_same_conditionals(a, b)
109119
if a.vtype b.vtype
110120
if a.elsetype b.elsetype
111121
return true
@@ -114,9 +124,11 @@ function issubconditional(a::Conditional, b::Conditional)
114124
end
115125
return false
116126
end
127+
is_same_conditionals(a::Conditional, b::Conditional) = slot_id(a.var) === slot_id(b.var)
128+
is_same_conditionals(a::InterConditional, b::InterConditional) = a.slot === b.slot
117129

118-
maybe_extract_const_bool(c::Const) = isa(c.val, Bool) ? c.val : nothing
119-
function maybe_extract_const_bool(c::Conditional)
130+
maybe_extract_const_bool(c::Const) = (val = c.val; isa(val, Bool)) ? val : nothing
131+
function maybe_extract_const_bool(c::AnyConditional)
120132
(c.vtype === Bottom && !(c.elsetype === Bottom)) && return false
121133
(c.elsetype === Bottom && !(c.vtype === Bottom)) && return true
122134
nothing
@@ -145,14 +157,14 @@ function ⊑(@nospecialize(a), @nospecialize(b))
145157
b === Union{} && return false
146158
@assert !isa(a, TypeVar) "invalid lattice item"
147159
@assert !isa(b, TypeVar) "invalid lattice item"
148-
if isa(a, Conditional)
149-
if isa(b, Conditional)
160+
if isa(a, AnyConditional)
161+
if isa(b, AnyConditional)
150162
return issubconditional(a, b)
151163
elseif isa(b, Const) && isa(b.val, Bool)
152164
return maybe_extract_const_bool(a) === b.val
153165
end
154166
a = Bool
155-
elseif isa(b, Conditional)
167+
elseif isa(b, AnyConditional)
156168
return false
157169
end
158170
if isa(a, PartialStruct)
@@ -251,7 +263,7 @@ function is_lattice_equal(@nospecialize(a), @nospecialize(b))
251263
return a b && b a
252264
end
253265

254-
widenconst(c::Conditional) = Bool
266+
widenconst(c::AnyConditional) = Bool
255267
function widenconst(c::Const)
256268
if isa(c.val, Type)
257269
if isvarargtype(c.val)
@@ -286,7 +298,7 @@ end
286298
@inline schanged(@nospecialize(n), @nospecialize(o)) = (n !== o) && (o === NOT_FOUND || (n !== NOT_FOUND && !issubstate(n, o)))
287299

288300
widenconditional(@nospecialize typ) = typ
289-
function widenconditional(typ::Conditional)
301+
function widenconditional(typ::AnyConditional)
290302
if typ.vtype === Union{}
291303
return Const(false)
292304
elseif typ.elsetype === Union{}

base/compiler/typelimits.jl

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ function tmerge(@nospecialize(typea), @nospecialize(typeb))
334334
end
335335
end
336336
if isa(typea, Conditional) && isa(typeb, Conditional)
337-
if typea.var === typeb.var
337+
if is_same_conditionals(typea, typeb)
338338
vtype = tmerge(typea.vtype, typeb.vtype)
339339
elsetype = tmerge(typea.elsetype, typeb.elsetype)
340340
if vtype != elsetype
@@ -347,6 +347,36 @@ function tmerge(@nospecialize(typea), @nospecialize(typeb))
347347
end
348348
return Bool
349349
end
350+
# type-lattice for InterConditional wrapper, InterConditional will never be merged with Conditional
351+
if isa(typea, InterConditional) && isa(typeb, Const)
352+
if typeb.val === true
353+
typeb = InterConditional(typea.slot, Any, Union{})
354+
elseif typeb.val === false
355+
typeb = InterConditional(typea.slot, Union{}, Any)
356+
end
357+
end
358+
if isa(typeb, InterConditional) && isa(typea, Const)
359+
if typea.val === true
360+
typea = InterConditional(typeb.slot, Any, Union{})
361+
elseif typea.val === false
362+
typea = InterConditional(typeb.slot, Union{}, Any)
363+
end
364+
end
365+
if isa(typea, InterConditional) && isa(typeb, InterConditional)
366+
if is_same_conditionals(typea, typeb)
367+
vtype = tmerge(typea.vtype, typeb.vtype)
368+
elsetype = tmerge(typea.elsetype, typeb.elsetype)
369+
if vtype != elsetype
370+
return InterConditional(typea.slot, vtype, elsetype)
371+
end
372+
end
373+
val = maybe_extract_const_bool(typea)
374+
if val isa Bool && val === maybe_extract_const_bool(typeb)
375+
return Const(val)
376+
end
377+
return Bool
378+
end
379+
# type-lattice for Const and PartialStruct wrappers
350380
if (isa(typea, PartialStruct) || isa(typea, Const)) &&
351381
(isa(typeb, PartialStruct) || isa(typeb, Const)) &&
352382
widenconst(typea) === widenconst(typeb)

src/builtins.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1616,6 +1616,7 @@ void jl_init_primitives(void) JL_GC_DISABLED
16161616
add_builtin("Const", (jl_value_t*)jl_const_type);
16171617
add_builtin("PartialStruct", (jl_value_t*)jl_partial_struct_type);
16181618
add_builtin("PartialOpaque", (jl_value_t*)jl_partial_opaque_type);
1619+
add_builtin("InterConditional", (jl_value_t*)jl_interconditional_type);
16191620
add_builtin("MethodMatch", (jl_value_t*)jl_method_match_type);
16201621
add_builtin("IntrinsicFunction", (jl_value_t*)jl_intrinsic_type);
16211622
add_builtin("Function", (jl_value_t*)jl_function_type);

src/jl_exported_data.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
XX(jl_number_type) \
7373
XX(jl_partial_struct_type) \
7474
XX(jl_partial_opaque_type) \
75+
XX(jl_interconditional_type) \
7576
XX(jl_phicnode_type) \
7677
XX(jl_phinode_type) \
7778
XX(jl_pinode_type) \

src/jltypes.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2308,6 +2308,10 @@ void jl_init_types(void) JL_GC_DISABLED
23082308
jl_perm_symsvec(2, "typ", "fields"),
23092309
jl_svec2(jl_any_type, jl_array_any_type), 0, 0, 2);
23102310

2311+
jl_interconditional_type = jl_new_datatype(jl_symbol("InterConditional"), core, jl_any_type, jl_emptysvec,
2312+
jl_perm_symsvec(3, "slot", "vtype", "elsetype"),
2313+
jl_svec(3, jl_long_type, jl_any_type, jl_any_type), 0, 0, 3);
2314+
23112315
jl_method_match_type = jl_new_datatype(jl_symbol("MethodMatch"), core, jl_any_type, jl_emptysvec,
23122316
jl_perm_symsvec(4, "spec_types", "sparams", "method", "fully_covers"),
23132317
jl_svec(4, jl_type_type, jl_simplevector_type, jl_method_type, jl_bool_type), 0, 0, 4);

0 commit comments

Comments
 (0)