-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AssertionError(msg="Union{}
typed field should be strictly undefined")
#57673
Comments
The following case seems to still be unsupported: julia> struct A{T}; x::Any; y::T; A{T}(x) where {T} = new{T}(x); end
julia> Core.PartialStruct(A{Union{}}, Union{Nothing,Bool}[false, nothing], Any[Int, Union{}])
ERROR: AssertionError: `Union{}` typed field should be strictly undefined
Stacktrace:
[1] Core.PartialStruct(typ::Type, undefs::Vector{Union{Nothing, Bool}}, fields::Vector{Any})
@ Base ./coreir.jl:62 Perhaps we should adjust the check to also allow While a field with type |
I'm open to loosening the assertion if we need to, but I'd really like to understand why it's failing in the first place. When I ran the Oscar tests locally, I hit an inference stack overflow error and couldn't even get to the point where the assertion fails. Do we have a reliable way to reproduce this? |
I can try to reproduce and see if we can get to an understanding about what's going on |
Here it goes: julia> using Oscar, Oscar.IntersectionTheory
julia> code_typed(degeneracy_locus, (Oscar.IntersectionTheory.AbstractBundle{Oscar.IntersectionTheory.AbstractVariety}, Oscar.IntersectionTheory.AbstractBundle{Oscar.IntersectionTheory.AbstractVariety}, Int64))
ERROR: AssertionError: `Union{}` typed field should be strictly undefined
Stacktrace:
[1] Core.PartialStruct(typ::Type, undefs::Vector{Union{Nothing, Bool}}, fields::Vector{Any})
@ Base ./coreir.jl:62
[2] Core.PartialStruct(::Compiler.InferenceLattice{Compiler.ConditionalsLattice{…}}, typ::Any, undefs::Vector{Union{…}}, fields::Vector{Any})
@ Compiler ./../usr/share/julia/Compiler/src/typelattice.jl:765
[3] form_partially_defined_struct(𝕃ᵢ::Compiler.InferenceLattice{Compiler.ConditionalsLattice{Compiler.PartialsLattice{Compiler.ConstsLattice}}}, obj::Any, name::Any)
@ Compiler ~/.julia/juliaup/julia-nightly/share/julia/Compiler/src/abstractinterpretation.jl:2162
[4] abstract_call_builtin(interp::Compiler.NativeInterpreter, f::Core.Builtin, ::Compiler.ArgInfo, sv::Compiler.InferenceState)
@ Compiler ~/.julia/juliaup/julia-nightly/share/julia/Compiler/src/abstractinterpretation.jl:2118
[5] abstract_call_known(interp::Compiler.NativeInterpreter, f::Any, arginfo::Compiler.ArgInfo, si::Compiler.StmtInfo, sv::Compiler.InferenceState, max_methods::Int64)
@ Compiler ~/.julia/juliaup/julia-nightly/share/julia/Compiler/src/abstractinterpretation.jl:2664
[...] (truncated) It errors when constructing a objt0 = Oscar.MPolyAnyMap{<:AbstractAlgebra.MPolyRing{T}, Union{}, Nothing} where T<:AbstractAlgebra.FieldElem
undefs = Union{Nothing, Bool}[0, 0, 0, 0, nothing, nothing, 0]
fields = Any[AbstractAlgebra.MPolyRing{T} where T<:AbstractAlgebra.FieldElem, Union{}, Nothing, Vector, Any, Vector{Int64}, Dict{Symbol, Any}] where the We can see it being instantiated with a Therefore, the fix I suggested would not even be enough. Even if we may wonder why it occurs, isn't that technically a valid Julia type that we may see in inference? (even if it would never be constructed at runtime) I guess a possible fix would be to look at |
Thanks so much for creating the reproducer! From your explanation, it seems like the inference for julia/Compiler/src/abstractinterpretation.jl Lines 3092 to 3093 in d369c10
Because of that, an object type that shouldn't exist at runtime is being propagated around. This is likely causing However, I still can't reproduce the issue on my end though: julia> using Oscar, Oscar.IntersectionTheory
___ ____ ____ _ ____
/ _ \ / ___| / ___| / \ | _ \ | Combining ANTIC, GAP, Polymake, Singular
| | | |\___ \| | / _ \ | |_) | | Type "?Oscar" for more information
| |_| | ___) | |___ / ___ \| _ < | Manual: https://docs.oscar-system.org
\___/ |____/ \____/_/ \_\_| \_\ | Version 1.3.0
julia> code_typed(degeneracy_locus, (Oscar.IntersectionTheory.AbstractBundle{Oscar.IntersectionTheory.AbstractVariety}, Oscar.IntersectionTheory.AbstractBundle{Oscar.IntersectionTheory.AbstractVariety}, Int64))
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
ERROR: StackOverflowError:
For fixing the root cause, I think |
@aviatesk that's odd. We had to apply a change to our code to work around a stack overflow in code inference in Julia (yet another regression on master), but this workaround should be in the Oscar 1.3.0 release... But perhaps just in case, try with latest Oscar |
Sorry, it looks like the issue was caused by changes I had locally (I didn't notice because those changes didn't cause any error when running the base tests). I looked into the problem, and there's nothing wrong with |
It worked for me with Oscar I managed to reproduce the pattern that generates an invalid type: julia> struct A{C<:Union{Int,Float64}} val::C end
julia> f(a::A{<:String}) = a
f (generic function with 1 method)
julia> code_typed(f, (A,))
1-element Vector{Any}:
CodeInfo(
1 ─ return a
) => A{Union{}} which happens in Oscar when dispatching to this function with an instance of this type, which eventually branches on # make it mutable with at least one possibly undefined field so we later form a PartialStruct
julia> mutable struct MA{C<:Union{Int,String}} a::C; b::Any; MA(a::T) where {T} = new{T}(a); end
julia> f(a::MA{<:Symbol}) = isdefined(a, :b) ? invokelatest(identity, a) : a.a
f (generic function with 1 method)
julia> code_typed(f, (MA,))
ERROR: AssertionError: `Union{}` typed field should be strictly undefined
Stacktrace:
[1] Core.PartialStruct(typ::Type, undefs::Vector{Union{Nothing, Bool}}, fields::Vector{Any})
@ Base ./coreir.jl:62
[2] Core.PartialStruct(::Compiler.InferenceLattice{Compiler.ConditionalsLattice{…}}, typ::Any, undefs::Vector{Union{…}}, fields::Vector{Any})
@ Compiler ~/.julia/juliaup/julia-nightly/share/julia/Compiler/src/typelattice.jl:765
[3] form_partially_defined_struct(𝕃ᵢ::Compiler.InferenceLattice{Compiler.ConditionalsLattice{Compiler.PartialsLattice{Compiler.ConstsLattice}}}, obj::Any, name::Any)
@ Compiler ~/.julia/juliaup/julia-nightly/share/julia/Compiler/src/abstractinterpretation.jl:2164
[4] abstract_call_builtin(interp::Compiler.NativeInterpreter, f::Core.Builtin, ::Compiler.ArgInfo, sv::Compiler.InferenceState)
@ Compiler ~/.julia/juliaup/julia-nightly/share/julia/Compiler/src/abstractinterpretation.jl:2121
[5] abstract_call_known(interp::Compiler.NativeInterpreter, f::Any, arginfo::Compiler.ArgInfo, si::Compiler.StmtInfo, sv::Compiler.InferenceState, max_methods::Int64)
[...] IIUC the root cause is that |
Specifically, when `form_partially_defined_struct` tries to create a `PartialStruct`, this commit adds extra checks to prevent creating it if it doesn't exist at runtime. While it would be better to avoid propagating these runtime-invalid object types altogether, that would require a major overhaul of the dispatch system. As a fix within the current dispatch system, this commit is proposed. - closes #57673 Co-authored-by: Cédric Belmant <[email protected]>
Yea, it seems like the dispatch can sometimes narrow down signatures in a way that creates types that are invalid at runtime. I'm not an expert on the dispatch system, but ideally, we should prevent dispatch from matching when it would create these runtime-invalid types. I'm not sure if that's even possible, though. As a temporary fix, I've created #57720. @vtjnash, we're discussing whether we can prevent method matching in cases where it would generate runtime-invalid object types like below. My understanding is that it's quite difficult to do (since it means the dispatch system needs to recognize min-field-count for each possible object types). What are your thoughts? julia> mutable struct Issue57673{C<:Union{Int,Float64}}
c::C
d
Issue57673(c::C, d) where C = new{C}(c, d)
Issue57673(c::C) where C = new{C}(c)
end
julia> function issue57673(x::Issue57673{<:AbstractString})
x
end
issue57673 (generic function with 1 method)
julia> methods(issue57673, (Issue57673,)) # we ideally want to have method error for this case?
# 1 method for generic function "issue57673" from Main:
[1] issue57673(x::Issue57673{<:AbstractString})
@ REPL[41]:1
julia> code_typed(issue57673, (Issue57673,); optimize=false)
1-element Vector{Any}:
CodeInfo(
1 ─ %1 = x::Issue57673{Union{}}
└── return %1
) => Issue57673{Union{}} |
This is about inference, so dispatch has nothing to do with this issue. We have that predicate already, julia> typeintersect(Issue57673{<:AbstractString}, Issue57673)
Issue57673{Union{}}
julia> Base.Compiler.has_concrete_subtype(ans)
false
julia> Base.Compiler.valid_as_lattice(Issue57673{Union{}}, true)
false and we already forbid inference from exploring those methods itself (unless you force it with julia/Compiler/src/abstractinterpretation.jl Lines 612 to 613 in 3f4eda6
And that is also called from here: function abstract_eval_new(interp::AbstractInterpreter, e::Expr, sstate::StatementState,
sv::AbsIntState)
𝕃ᵢ = typeinf_lattice(interp)
rt, isexact = instanceof_tfunc(abstract_eval_value(interp, e.args[1], sstate, sv), true) It is a conservative query of course, but it isn't immediately obvious how it would have missed such an easy case where the field type is simply |
Thanks for the input. It seems that we may have a bug then: julia> T = Oscar.MPolyAnyMap{<:Oscar.MPolyRing{<:Oscar.FieldElem}, Union{}, Nothing}
Oscar.MPolyAnyMap{<:MPolyRing{<:FieldElem}, Union{}, Nothing}
julia> fieldtype(T, 2)
Union{}
julia> Compiler.valid_as_lattice(T, true)
true where julia> Compiler.unwrap_unionall(T).flags
0x0021 |
Here is a smaller example which captures the same behavior: julia> struct A{X,Y}
x::X
y::Y
end
julia> Compiler.valid_as_lattice(A{Int,Union{}}, true)
false
julia> Compiler.valid_as_lattice(A{<:Int,Union{}}, true)
true
julia> Base.unwrap_unionall(A{Int,Union{}}).flags
0x0052
julia> Base.unwrap_unionall(A{<:Int,Union{}}).flags
0x0021 The second call should return |
I suspect it hasn't yet computed the fieldtypes for that UnionAll, so it is unaware yet as to whether they are possible |
Actually So I agree with Cédric making the checks that |
I ran Oscar's test suite on the latest nightly build and confirmed that the error reported in this issue no longer occurs at least. There still seem to be other assertion errors, but they appear unrelated to this particular issue. |
Field types are already precomputed here for this reason: julia/Compiler/src/typeutils.jl Line 84 in c9008ff
But even after computing a julia> T = A{<:Int,Union{}}
A{<:Int64, Union{}}
julia> T0 = Base.unwrap_unionall(T)
A{var"#s4"<:Int64, Union{}}
julia> Base.datatype_fieldtypes(T0)
svec(var"#s4"<:Int64, Union{})
julia> Compiler.has_concrete_subtype(T0)
true
julia> T0.flags
0x0021 |
That isn't very useful to be there, since we are often unlikely to get there, bailing here instead: Line 353 in b9ac28a
|
Would you suggest to move the computation of |
It can probably be moved above that conditional, so that we always call it just after computing the field types |
I prototyped a fix in #57764, comments are welcome. |
The Oscar.jl tests seem to be running into this assertion now after #57541 tried to fix oscar-system/Oscar.jl#4648:
https://github.com/oscar-system/Oscar.jl/actions/runs/13611555768/job/38059503585#step:11:677
Originally posted by @benlorenz in #57541 (comment)
cc @benlorenz @fingolfin
ping @aviatesk @serenity4 @topolarity as you guys worked on #57541
The text was updated successfully, but these errors were encountered: