diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 9a474a4cbd878..351f241878e7d 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2030,7 +2030,13 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs else thentype = form_partially_defined_struct(argtype2, argtypes[3]) if thentype !== nothing - return Conditional(a, thentype, argtype2) + elsetype = argtype2 + if rt === Const(false) + thentype = Bottom + elseif rt === Const(true) + elsetype = Bottom + end + return Conditional(a, thentype, elsetype) end end end @@ -2045,10 +2051,16 @@ function form_partially_defined_struct(@nospecialize(obj), @nospecialize(name)) name isa Const || return nothing objt0 = widenconst(obj) objt = unwrap_unionall(objt0) + objt isa DataType || return nothing isabstracttype(objt) && return nothing fldidx = try_compute_fieldidx(objt, name.val) fldidx === nothing && return nothing - fldidx โ‰ค datatype_min_ninitialized(objt) && return nothing + nminfld = datatype_min_ninitialized(objt) + if ismutabletype(objt) + fldidx == nminfld+1 || return nothing + else + fldidx > nminfld || return nothing + end return PartialStruct(objt0, Any[fieldtype(objt0, i) for i = 1:fldidx]) end @@ -3111,7 +3123,8 @@ end @nospecializeinfer function widenreturn_partials(๐•ƒแตข::PartialsLattice, @nospecialize(rt), info::BestguessInfo) if isa(rt, PartialStruct) fields = copy(rt.fields) - anyrefine = !isvarargtype(rt.fields[end]) && length(rt.fields) > datatype_min_ninitialized(rt.typ) + anyrefine = !isvarargtype(rt.fields[end]) && + length(rt.fields) > datatype_min_ninitialized(unwrap_unionall(rt.typ)) ๐•ƒ = typeinf_lattice(info.interp) โŠ = strictpartialorder(๐•ƒ) for i in 1:length(fields) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index e3cf031770c36..89874b9a6df10 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -419,7 +419,7 @@ end else return Bottom end - if 1 <= idx <= datatype_min_ninitialized(a1) + if 1 โ‰ค idx โ‰ค datatype_min_ninitialized(a1) return Const(true) elseif a1.name === _NAMEDTUPLE_NAME if isconcretetype(a1) @@ -427,22 +427,19 @@ end else ns = a1.parameters[1] if isa(ns, Tuple) - return Const(1 <= idx <= length(ns)) + return Const(1 โ‰ค idx โ‰ค length(ns)) end end - elseif idx <= 0 || (!isvatuple(a1) && idx > fieldcount(a1)) + elseif idx โ‰ค 0 || (!isvatuple(a1) && idx > fieldcount(a1)) return Const(false) elseif isa(arg1, Const) if !ismutabletype(a1) || isconst(a1, idx) return Const(isdefined(arg1.val, idx)) end elseif isa(arg1, PartialStruct) - nflds = length(arg1.fields) if !isvarargtype(arg1.fields[end]) - if 1 โ‰ค idx โ‰ค nflds + if 1 โ‰ค idx โ‰ค length(arg1.fields) return Const(true) - elseif !ismutabletype(a1) || isconst(a1, idx) - return Const(false) end end elseif !isvatuple(a1) @@ -1005,7 +1002,7 @@ end nflds = nfields(sv) ismod = sv isa Module elseif isa(s00, PartialStruct) - sty = s00.typ + sty = unwrap_unionall(s00.typ) nflds = fieldcount_noerror(sty) ismod = false else diff --git a/base/compiler/typelattice.jl b/base/compiler/typelattice.jl index a84608ba72b1e..7565740338a1d 100644 --- a/base/compiler/typelattice.jl +++ b/base/compiler/typelattice.jl @@ -34,7 +34,7 @@ with `Int` values. If `typ` is a struct, `fields` represents the fields of the struct that are guaranteed to be initialized. For instance, if the length of `fields` of `PartialStruct` representing a -struct with 4 fields is 3, the 4th field may be uninitialized. If the length is four, all +struct with 4 fields is 3, the 4th field may not be initialized. If the length is 4, all fields are guaranteed to be initialized. If `typ` is a tuple, the last element of `fields` may be `Vararg`. In this case, it is diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index 318ac0b5c27e5..1a1aa7a35e840 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -328,6 +328,9 @@ const issimpleenoughtupleelem = issimpleenoughtype typea === typeb && return true if typea isa PartialStruct aty = widenconst(typea) + if length(typea.fields) > datatype_min_ninitialized(unwrap_unionall(aty)) + return false # TODO more accuracy here? + end for i = 1:length(typea.fields) ai = unwrapva(typea.fields[i]) bi = fieldtype(aty, i) @@ -572,34 +575,43 @@ end # N.B. This can also be called with both typea::Const and typeb::Const to # to recover PartialStruct from `Const`s with overlapping fields. -@nospecializeinfer function tmerge_partial_struct(lattice::PartialsLattice, @nospecialize(typea), @nospecialize(typeb)) +@nospecializeinfer function tmerge_partial_struct(๐•ƒ::PartialsLattice, @nospecialize(typea), @nospecialize(typeb)) aty = widenconst(typea) bty = widenconst(typeb) if aty === bty # must have egal here, since we do not create PartialStruct for non-concrete types - typea_nfields = nfields_tfunc(lattice, typea) - typeb_nfields = nfields_tfunc(lattice, typeb) + typea_nfields = nfields_tfunc(๐•ƒ, typea) + typeb_nfields = nfields_tfunc(๐•ƒ, typeb) isa(typea_nfields, Const) || return nothing isa(typeb_nfields, Const) || return nothing type_nfields = typea_nfields.val::Int - type_nfields === typeb_nfields.val::Int || return nothing + type_nfields == typeb_nfields.val::Int || return nothing type_nfields == 0 && return nothing + if typea isa PartialStruct + if typeb isa PartialStruct + length(typea.fields) == length(typeb.fields) || return nothing + else + length(typea.fields) == type_nfields || return nothing + end + elseif typeb isa PartialStruct + length(typeb.fields) == type_nfields || return nothing + end fields = Vector{Any}(undef, type_nfields) anyrefine = false for i = 1:type_nfields - ai = getfield_tfunc(lattice, typea, Const(i)) - bi = getfield_tfunc(lattice, typeb, Const(i)) + ai = getfield_tfunc(๐•ƒ, typea, Const(i)) + bi = getfield_tfunc(๐•ƒ, typeb, Const(i)) # N.B.: We're assuming here that !isType(aty), because that case # only arises when typea === typeb, which should have been caught # before calling this. ft = fieldtype(aty, i) - if is_lattice_equal(lattice, ai, bi) || is_lattice_equal(lattice, ai, ft) + if is_lattice_equal(๐•ƒ, ai, bi) || is_lattice_equal(๐•ƒ, ai, ft) # Since ai===bi, the given type has no restrictions on complexity. # and can be used to refine ft tyi = ai - elseif is_lattice_equal(lattice, bi, ft) + elseif is_lattice_equal(๐•ƒ, bi, ft) tyi = bi - elseif (tyiโ€ฒ = tmerge_field(lattice, ai, bi); tyiโ€ฒ !== nothing) + elseif (tyiโ€ฒ = tmerge_field(๐•ƒ, ai, bi); tyiโ€ฒ !== nothing) # allow external lattice implementation to provide a custom field-merge strategy tyi = tyiโ€ฒ else @@ -621,8 +633,8 @@ end end fields[i] = tyi if !anyrefine - anyrefine = has_nontrivial_extended_info(lattice, tyi) || # extended information - โ‹ค(lattice, tyi, ft) # just a type-level information, but more precise than the declared type + anyrefine = has_nontrivial_extended_info(๐•ƒ, tyi) || # extended information + โ‹ค(๐•ƒ, tyi, ft) # just a type-level information, but more precise than the declared type end end anyrefine && return PartialStruct(aty, fields) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index cfe6712075b2f..b5c1321c94eb3 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1538,7 +1538,7 @@ let nfields_tfunc(@nospecialize xs...) = @test sizeof_nothrow(String) @test !sizeof_nothrow(Type{String}) @test sizeof_tfunc(Type{Union{Int64, Int32}}) == Const(Core.sizeof(Union{Int64, Int32})) - let PT = Core.Compiler.PartialStruct(Tuple{Int64,UInt64}, Any[Const(10), UInt64]) + let PT = Core.PartialStruct(Tuple{Int64,UInt64}, Any[Const(10), UInt64]) @test sizeof_tfunc(PT) === Const(16) @test nfields_tfunc(PT) === Const(2) @test sizeof_nothrow(PT) @@ -4743,32 +4743,40 @@ end # issue #43784 @testset "issue #43784" begin - init = Base.ImmutableDict{Any,Any}() - a = Const(init) - b = Core.PartialStruct(typeof(init), Any[Const(init), Any, Any]) - c = Core.Compiler.tmerge(a, b) - @test โŠ‘(a, c) - @test โŠ‘(b, c) - - init = Base.ImmutableDict{Number,Number}() - a = Const(init) - b = Core.Compiler.PartialStruct(typeof(init), Any[Const(init), Any, ComplexF64]) - c = Core.Compiler.tmerge(a, b) - @test โŠ‘(a, c) && โŠ‘(b, c) - @test c === typeof(init) - - a = Core.Compiler.PartialStruct(typeof(init), Any[Const(init), ComplexF64, ComplexF64]) - c = Core.Compiler.tmerge(a, b) - @test โŠ‘(a, c) && โŠ‘(b, c) - @test c.fields[2] === Any # or Number - @test c.fields[3] === ComplexF64 - - b = Core.Compiler.PartialStruct(typeof(init), Any[Const(init), ComplexF32, Union{ComplexF32,ComplexF64}]) - c = Core.Compiler.tmerge(a, b) - @test โŠ‘(a, c) - @test โŠ‘(b, c) - @test c.fields[2] === Complex - @test c.fields[3] === Complex + โŠ‘ = Core.Compiler.partialorder(Core.Compiler.fallback_lattice) + โŠ” = Core.Compiler.join(Core.Compiler.fallback_lattice) + Const, PartialStruct = Core.Const, Core.PartialStruct + + let init = Base.ImmutableDict{Any,Any}() + a = Const(init) + b = PartialStruct(typeof(init), Any[Const(init), Any, Any]) + c = a โŠ” b + @test a โŠ‘ c && b โŠ‘ c + @test c === typeof(init) + end + let init = Base.ImmutableDict{Number,Number}() + a = Const(init) + b = PartialStruct(typeof(init), Any[Const(init), Number, ComplexF64]) + c = a โŠ” b + @test a โŠ‘ c && b โŠ‘ c + @test c === typeof(init) + end + let init = Base.ImmutableDict{Number,Number}() + a = PartialStruct(typeof(init), Any[Const(init), ComplexF64, ComplexF64]) + b = PartialStruct(typeof(init), Any[Const(init), Number, ComplexF64]) + c = a โŠ” b + @test a โŠ‘ c && b โŠ‘ c + @test c.fields[2] === Number + @test c.fields[3] === ComplexF64 + end + let init = Base.ImmutableDict{Number,Number}() + a = PartialStruct(typeof(init), Any[Const(init), ComplexF64, ComplexF64]) + b = PartialStruct(typeof(init), Any[Const(init), ComplexF32, Union{ComplexF32,ComplexF64}]) + c = a โŠ” b + @test a โŠ‘ c && b โŠ‘ c + @test c.fields[2] === Complex + @test c.fields[3] === Complex + end global const ginit43784 = Base.ImmutableDict{Any,Any}() @test Base.return_types() do @@ -5887,7 +5895,11 @@ end end == Val{true} @test Base.infer_return_type((Any,Any,)) do a, b Val(isdefined(PartiallyInitialized1(a, b), :c)) -end == Val{false} +end >: Val{false} +@test Base.infer_return_type((PartiallyInitialized1,)) do x + @assert isdefined(x, :a) + return Val(isdefined(x, :c)) +end == Val @test Base.infer_return_type((Any,Any,Any)) do a, b, c Val(isdefined(PartiallyInitialized1(a, b, c), :c)) end == Val{true} @@ -5949,6 +5961,13 @@ end |> Core.Compiler.is_nothrow return x[] end end |> Core.Compiler.is_nothrow +@test Base.infer_effects((Any,Any); optimize=false) do a, c + x = PartiallyInitialized2(a) + x.c = c + if isdefined(x, :c) + return x.b + end +end |> !Core.Compiler.is_nothrow # End to end test case for the partially initialized struct with `PartialStruct` @noinline broadcast_noescape1(a) = (broadcast(identity, a); nothing) diff --git a/test/tuple.jl b/test/tuple.jl index b1894bd2bb6ce..355ad965f9584 100644 --- a/test/tuple.jl +++ b/test/tuple.jl @@ -533,7 +533,7 @@ end @test ntuple(identity, Val(n)) == ntuple(identity, n) end - @test Core.Compiler.return_type(ntuple, Tuple{typeof(identity), Val}) == Tuple{Vararg{Int}} + @test Base.infer_return_type(ntuple, Tuple{typeof(identity), Val}) == Tuple{Vararg{Int}} end struct A_15703{N} @@ -835,8 +835,8 @@ end @test @inferred(Base.circshift(t3, 7)) == ('b', 'c', 'd', 'a') @test @inferred(Base.circshift(t3, -1)) == ('b', 'c', 'd', 'a') @test_throws MethodError circshift(t1, 'a') - @test Core.Compiler.return_type(circshift, Tuple{Tuple,Integer}) <: Tuple - @test Core.Compiler.return_type(circshift, Tuple{Tuple{Vararg{Any,10}},Integer}) <: Tuple{Vararg{Any,10}} + @test Base.infer_return_type(circshift, Tuple{Tuple,Integer}) <: Tuple + @test Base.infer_return_type(circshift, Tuple{Tuple{Vararg{Any,10}},Integer}) <: Tuple{Vararg{Any,10}} for len โˆˆ 0:5 v = 1:len t = Tuple(v)