Skip to content

Commit e58e076

Browse files
committed
lattice: Allow external lattice to have a say in how to widen
Currently, when e.g. a PartialStruct is not of interest to the lattice code, it just calls `widenconst` on it to pass it to the next lattice layer. This might be insufficient if an intermediate lattice layer has some other representation that is wider that the `PartialStruct`, but narrower than the corresponding `widenconst`. This adds `widenconst(::AbstractLatice, ::Any)` to allow the lattices to insert custom widening code. By default, it ends up calling `widenconst`, so there's no functional change in base, but custom lattices can make use of the extra hook. I'm not entirely sure that this is what we want the final interface to look like (I think it probably does too many type checks), but it works reasonably well and I think is good enough to experiment with.
1 parent 13c0060 commit e58e076

File tree

2 files changed

+33
-21
lines changed

2 files changed

+33
-21
lines changed

base/compiler/abstractlattice.jl

+18-14
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ extensions.
99
"""
1010
struct JLTypeLattice <: AbstractLattice; end
1111
widenlattice(::JLTypeLattice) = error("Type lattice is the least-precise lattice available")
12-
is_valid_lattice(::JLTypeLattice, @nospecialize(elem)) = isa(elem, Type)
12+
is_valid_lattice(lattice::JLTypeLattice, @nospecialize(elem)) = is_valid_lattice_norec(lattice, elem)
13+
is_valid_lattice_norec(::JLTypeLattice, @nospecialize(elem)) = isa(elem, Type)
1314

1415
"""
1516
struct ConstsLattice
@@ -18,8 +19,7 @@ A lattice extending `JLTypeLattice` and adjoining `Const` and `PartialTypeVar`.
1819
"""
1920
struct ConstsLattice <: AbstractLattice; end
2021
widenlattice(::ConstsLattice) = JLTypeLattice()
21-
is_valid_lattice(lattice::ConstsLattice, @nospecialize(elem)) =
22-
is_valid_lattice(widenlattice(lattice), elem) || isa(elem, Const) || isa(elem, PartialTypeVar)
22+
is_valid_lattice_norec(lattice::ConstsLattice, @nospecialize(elem)) = isa(elem, Const) || isa(elem, PartialTypeVar)
2323

2424
"""
2525
struct PartialsLattice{L}
@@ -30,9 +30,7 @@ struct PartialsLattice{L <: AbstractLattice} <: AbstractLattice
3030
parent::L
3131
end
3232
widenlattice(L::PartialsLattice) = L.parent
33-
is_valid_lattice(lattice::PartialsLattice, @nospecialize(elem)) =
34-
is_valid_lattice(widenlattice(lattice), elem) ||
35-
isa(elem, PartialStruct) || isa(elem, PartialOpaque)
33+
is_valid_lattice_norec(lattice::PartialsLattice, @nospecialize(elem)) = isa(elem, PartialStruct) || isa(elem, PartialOpaque)
3634

3735
"""
3836
struct ConditionalsLattice{L}
@@ -43,15 +41,13 @@ struct ConditionalsLattice{L <: AbstractLattice} <: AbstractLattice
4341
parent::L
4442
end
4543
widenlattice(L::ConditionalsLattice) = L.parent
46-
is_valid_lattice(lattice::ConditionalsLattice, @nospecialize(elem)) =
47-
is_valid_lattice(widenlattice(lattice), elem) || isa(elem, Conditional)
44+
is_valid_lattice_norec(lattice::ConditionalsLattice, @nospecialize(elem)) = isa(elem, Conditional)
4845

4946
struct InterConditionalsLattice{L <: AbstractLattice} <: AbstractLattice
5047
parent::L
5148
end
5249
widenlattice(L::InterConditionalsLattice) = L.parent
53-
is_valid_lattice(lattice::InterConditionalsLattice, @nospecialize(elem)) =
54-
is_valid_lattice(widenlattice(lattice), elem) || isa(elem, InterConditional)
50+
is_valid_lattice_norec(lattice::InterConditionalsLattice, @nospecialize(elem)) = isa(elem, InterConditional)
5551

5652
const AnyConditionalsLattice{L} = Union{ConditionalsLattice{L}, InterConditionalsLattice{L}}
5753
const BaseInferenceLattice = typeof(ConditionalsLattice(PartialsLattice(ConstsLattice())))
@@ -67,8 +63,7 @@ struct InferenceLattice{L} <: AbstractLattice
6763
parent::L
6864
end
6965
widenlattice(L::InferenceLattice) = L.parent
70-
is_valid_lattice(lattice::InferenceLattice, @nospecialize(elem)) =
71-
is_valid_lattice(widenlattice(lattice), elem) || isa(elem, LimitedAccuracy)
66+
is_valid_lattice_norec(lattice::InferenceLattice, @nospecialize(elem)) = isa(elem, LimitedAccuracy)
7267

7368
"""
7469
struct OptimizerLattice
@@ -81,8 +76,7 @@ struct OptimizerLattice{L} <: AbstractLattice
8176
end
8277
OptimizerLattice() = OptimizerLattice(BaseInferenceLattice.instance)
8378
widenlattice(L::OptimizerLattice) = L.parent
84-
is_valid_lattice(lattice::OptimizerLattice, @nospecialize(elem)) =
85-
is_valid_lattice(widenlattice(lattice), elem) || isa(elem, MaybeUndef)
79+
is_valid_lattice_norec(lattice::OptimizerLattice, @nospecialize(elem)) = isa(elem, MaybeUndef)
8680

8781
"""
8882
tmeet(lattice, a, b::Type)
@@ -174,3 +168,13 @@ tmerge(@nospecialize(a), @nospecialize(b)) = tmerge(fallback_lattice, a, b)
174168
(@nospecialize(a), @nospecialize(b)) = (fallback_lattice, a, b)
175169
(@nospecialize(a), @nospecialize(b)) = (fallback_lattice, a, b)
176170
is_lattice_equal(@nospecialize(a), @nospecialize(b)) = is_lattice_equal(fallback_lattice, a, b)
171+
172+
is_valid_lattice(lattice::AbstractLattice, @nospecialize(elem)) = is_valid_lattice_norec(lattice, elem) &&
173+
is_valid_lattice(widenlattice(lattice), elem)
174+
175+
# Widenlattice with argument
176+
widenlattice(::JLTypeLattice, @nospecialize(t)) = widenconst(t)
177+
function widenlattice(lattice::AbstractLattice, @nospecialize(t))
178+
is_valid_lattice_norec(lattice, t) && return t
179+
widenlattice(widenlattice(lattice), t)
180+
end

base/compiler/typelimits.jl

+15-7
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,8 @@ function tmerge(lattice::InterConditionalsLattice, @nospecialize(typea), @nospec
468468
end
469469
return Bool
470470
end
471+
typea = widenconditional(typea)
472+
typeb = widenconditional(typeb)
471473
return tmerge(widenlattice(lattice), typea, typeb)
472474
end
473475

@@ -524,10 +526,13 @@ function tmerge(lattice::PartialsLattice, @nospecialize(typea), @nospecialize(ty
524526
return anyrefine ? PartialStruct(aty, fields) : aty
525527
end
526528
end
529+
530+
527531
# Don't widen const here - external AbstractInterpreter might insert lattice
528532
# layers between us and `ConstsLattice`.
529-
isa(typea, PartialStruct) && (typea = widenconst(typea))
530-
isa(typeb, PartialStruct) && (typeb = widenconst(typeb))
533+
wl = widenlattice(lattice)
534+
isa(typea, PartialStruct) && (typea = widenlattice(wl, typea))
535+
isa(typeb, PartialStruct) && (typeb = widenlattice(wl, typeb))
531536

532537
# type-lattice for PartialOpaque wrapper
533538
apo = isa(typea, PartialOpaque)
@@ -540,24 +545,27 @@ function tmerge(lattice::PartialsLattice, @nospecialize(typea), @nospecialize(ty
540545
typea.parent === typeb.parent)
541546
return widenconst(typea)
542547
end
543-
return PartialOpaque(typea.typ, tmerge(typea.env, typeb.env),
548+
return PartialOpaque(typea.typ, tmerge(lattice, typea.env, typeb.env),
544549
typea.parent, typea.source)
545550
end
546551
typea = aty
547552
typeb = bty
548553
elseif apo
549-
typea = widenconst(typea)
554+
typea = widenlattice(wl, typea)
550555
elseif bpo
551-
typeb = widenconst(typeb)
556+
typeb = widenlattice(wl, typeb)
552557
end
553558

554-
return tmerge(widenlattice(lattice), typea, typeb)
559+
return tmerge(wl, typea, typeb)
555560
end
556561

557562
function tmerge(lattice::ConstsLattice, @nospecialize(typea), @nospecialize(typeb))
558563
# the equality of the constants can be checked here, but the equivalent check is usually
559564
# done by `tmerge_fast_path` at earlier lattice stage
560-
return tmerge(widenlattice(lattice), widenconst(typea), widenconst(typeb))
565+
wl = widenlattice(lattice)
566+
(isa(typea, Const) || isa(typea, PartialTypeVar)) && (typea = widenlattice(wl, typea))
567+
(isa(typeb, Const) || isa(typeb, PartialTypeVar)) && (typeb = widenlattice(wl, typeb))
568+
return tmerge(wl, typea, typeb)
561569
end
562570

563571
function tmerge(::JLTypeLattice, @nospecialize(typea::Type), @nospecialize(typeb::Type))

0 commit comments

Comments
 (0)