Skip to content

Commit 58c7186

Browse files
authored
inference: propagate partially initialized mutable structs more (#55533)
1 parent 54142b7 commit 58c7186

File tree

3 files changed

+30
-4
lines changed

3 files changed

+30
-4
lines changed

base/compiler/abstractinterpretation.jl

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,7 +1027,7 @@ collect_const_args(arginfo::ArgInfo, start::Int) = collect_const_args(arginfo.ar
10271027
function collect_const_args(argtypes::Vector{Any}, start::Int)
10281028
return Any[ let a = widenslotwrapper(argtypes[i])
10291029
isa(a, Const) ? a.val :
1030-
isconstType(a) ? (a::DataType).parameters[1] :
1030+
isconstType(a) ? a.parameters[1] :
10311031
(a::DataType).instance
10321032
end for i = start:length(argtypes) ]
10331033
end
@@ -2063,11 +2063,21 @@ function form_partially_defined_struct(@nospecialize(obj), @nospecialize(name))
20632063
fldidx === nothing && return nothing
20642064
nminfld = datatype_min_ninitialized(objt)
20652065
if ismutabletype(objt)
2066-
fldidx == nminfld+1 || return nothing
2066+
# A mutable struct can have non-contiguous undefined fields, but `PartialStruct` cannot
2067+
# model such a state. So here `PartialStruct` can be used to represent only the
2068+
# objects where the field following the minimum initialized fields is also defined.
2069+
if fldidx nminfld+1
2070+
# if it is already represented as a `PartialStruct`, we can add one more
2071+
# `isdefined`-field information on top of those implied by its `fields`
2072+
if !(obj isa PartialStruct && fldidx == length(obj.fields)+1)
2073+
return nothing
2074+
end
2075+
end
20672076
else
20682077
fldidx > nminfld || return nothing
20692078
end
2070-
return PartialStruct(objt0, Any[fieldtype(objt0, i) for i = 1:fldidx])
2079+
return PartialStruct(objt0, Any[obj isa PartialStruct && ilength(obj.fields) ?
2080+
obj.fields[i] : fieldtype(objt0,i) for i = 1:fldidx])
20712081
end
20722082

20732083
function abstract_call_unionall(interp::AbstractInterpreter, argtypes::Vector{Any}, call::CallMeta)

base/compiler/typeutils.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ function hasuniquerep(@nospecialize t)
1818
iskindtype(typeof(t)) || return true # non-types are always compared by egal in the type system
1919
isconcretetype(t) && return true # these are also interned and pointer comparable
2020
if isa(t, DataType) && t.name !== Tuple.name && !isvarargtype(t) # invariant DataTypes
21-
return _all(hasuniquerep, t.parameters)
21+
return all(hasuniquerep, t.parameters)
2222
end
2323
return false
2424
end

test/compiler/inference.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6033,6 +6033,22 @@ end |> Core.Compiler.is_nothrow
60336033
return x.b
60346034
end
60356035
end |> !Core.Compiler.is_nothrow
6036+
@test Base.infer_effects((PartiallyInitialized2,); optimize=false) do x
6037+
if isdefined(x, :b)
6038+
if isdefined(x, :c)
6039+
return x.c
6040+
end
6041+
return x.b
6042+
end
6043+
return nothing
6044+
end |> Core.Compiler.is_nothrow
6045+
@test Base.infer_effects((Bool,Int,); optimize=false) do c, b
6046+
x = c ? PartiallyInitialized1(true) : PartiallyInitialized1(true, b)
6047+
if isdefined(x, :b)
6048+
return Val(x.a), x.b
6049+
end
6050+
return nothing
6051+
end |> Core.Compiler.is_nothrow
60366052

60376053
# End to end test case for the partially initialized struct with `PartialStruct`
60386054
@noinline broadcast_noescape1(a) = (broadcast(identity, a); nothing)

0 commit comments

Comments
 (0)