Note [Replacement vs keeping]
When we have two Given constraints both of type (C tys), say, which should
we keep? More subtle than you might think! This is all implemented in
solveOneFromTheOther.
1) Constraints come from different levels (different_level_strategy)
- For implicit parameters we want to keep the innermost (deepest)
one, so that it overrides the outer one.
See Note [Shadowing of implicit parameters] in GHC.Tc.Solver.Dict
- For everything else, we want to keep the outermost one. Reason: that
makes it more likely that the inner one will turn out to be unused,
and can be reported as redundant. See Note [Tracking redundant constraints]
in GHC.Tc.Solver.
It transpires that using the outermost one is responsible for an
8% performance improvement in nofib cryptarithm2, compared to
just rolling the dice. I didn't investigate why.
2) Constraints coming from the same level (i.e. same implication)
(a) If both are GivenSCOrigin, choose the one that is unblocked if possible
according to Note [Solving superclass constraints] in GHC.Tc.TyCl.Instance.
(b) Prefer constraints that are not superclass selections. See
(TRC3) in Note [Tracking redundant constraints] in GHC.Tc.Solver.
(c) If both are GivenSCOrigin, chooose the one with the shallower
superclass-selection depth, in the hope of identifying more correct
redundant constraints. This is really a generalization of point (b),
because the superclass depth of a non-superclass constraint is 0.
(If the levels differ, we definitely won't have both with GivenSCOrigin.)
(d) Finally, when there is still a choice, use KeepInert rather than
KeepWork, for two reasons:
- to avoid unnecessary munging of the inert set.
- to cut off superclass loops; see Note [Superclass loops] in GHC.Tc.Solver.Dict
Doing the level-check for implicit parameters, rather than making the work item
always override, is important. Consider
data T a where { T1 :: (?x::Int) => T Int; T2 :: T a }
f :: (?x::a) => T a -> Int
f T1 = ?x
f T2 = 3
We have a [G] (?x::a) in the inert set, and at the pattern match on T1 we add
two new givens in the work-list: [G] (?x::Int)
[G] (a ~ Int)
Now consider these steps
- process a~Int, kicking out (?x::a)
- process (?x::Int), the inner given, adding to inert set
- process (?x::a), the outer given, overriding the inner given
Wrong! The level-check ensures that the inner implicit parameter wins.
(Actually I think that the order in which the work-list is processed means
that this chain of events won't happen, but that's very fragile.) References 4
- Shadowing of implicit parameters GHC.Tc.Solver.Dict
- Superclass loops GHC.Tc.Solver.Dict
- Tracking redundant constraints GHC.Tc.Solver.Solve
- Solving superclass constraints GHC.Tc.TyCl.Instance
Referenced by 11
- GHC.Tc.Solver.InertSet call site ×6
- Use only the best matching quantified constraint GHC.Tc.Solver.Dict
- Superclass loops GHC.Tc.Solver.Dict
- GHC.Tc.Solver.Dict call site
- Tracking redundant constraints GHC.Tc.Solver.Solve
- GHC.Tc.Types.Origin call site