Commit a9dc4277 authored by Simon Peyton Jones's avatar Simon Peyton Jones

Comments only, mainly on superclasses

This tidies up all the comments about recursive superclasses
and when to add superclasses.  Lots of duplicate and contradictory
comments removed!
parent 44b65fd8
......@@ -242,67 +242,95 @@ emitSuperclasses _ = panic "emit_superclasses of non-class!"
{-
Note [Adding superclasses]
~~~~~~~~~~~~~~~~~~~~~~~~~~
Since dictionaries are canonicalized only once in their lifetime, the
place to add their superclasses is canonicalisation (The alternative
would be to do it during constraint solving, but we'd have to be
extremely careful to not repeatedly introduced the same superclass in
our worklist). Here is what we do:
For Givens:
We add all their superclasses as Givens.
For Wanteds:
Generally speaking we want to be able to add superclasses of
wanteds for two reasons:
(1) Oportunities for improvement. Example:
class (a ~ b) => C a b
Wanted constraint is: C alpha beta
We'd like to simply have C alpha alpha. Similar
situations arise in relation to functional dependencies.
(2) To have minimal constraints to quantify over:
For instance, if our wanted constraint is (Eq a, Ord a)
we'd only like to quantify over Ord a.
To deal with (1) above we only add the superclasses of wanteds
which may lead to improvement, that is: equality superclasses or
superclasses with functional dependencies.
We deal with (2) completely independently in TcSimplify. See
Note [Minimize by SuperClasses] in TcSimplify.
Moreover, in all cases the extra improvement constraints are
Derived. Derived constraints have an identity (for now), but
we don't do anything with their evidence. For instance they
are never used to rewrite other constraints.
See also [New Wanted Superclass Work] in TcInteract.
For Deriveds:
We do nothing.
Since dictionaries are canonicalized only once in their lifetime, the
place to add their superclasses is canonicalisation. See Note [Add
superclasses only during canonicalisation]. Here is what we do:
Deriveds: Do nothing.
Givens: Add all their superclasses as Givens.
Wanteds: Add all their superclasses as Derived.
Not as Wanted: we don't need a proof.
Nor as Given: that leads to superclass loops.
We also want to ensure minimal constraints to quantify over. For
instance, if our wanted constraint is (Eq a, Ord a) we'd only like to
quantify over Ord a. But we deal with that completely independently
in TcSimplify. See Note [Minimize by SuperClasses] in TcSimplify.
Examples of how adding superclasses as Derived is useful
--- Example 1
class C a b | a -> b
Suppose we want to solve
[G] C a b
[W] C a beta
Then adding [D] beta~b will let us solve it.
-- Example 2 (similar but using a type-equality superclass)
class (F a ~ b) => C a b
And try to sllve:
[G] C a b
[W] C a beta
Follow the superclass rules to add
[G] F a ~ b
[D] F a ~ beta
Now we we get [D] beta ~ b, and can solve that.
Example of why adding superclass of a Wanted as a Given would
be terrible, see Note [Do not add superclasses of solved dictionaries]
in TcSMonad, which has this example:
class Ord a => C a where
instance Ord [a] => C [a] where ...
Suppose we are trying to solve
[G] d1 : Ord a
[W] d2 : C [a]
If we (bogusly) added the superclass of d2 as Gievn we'd have
[G] d1 : Ord a
[W] d2 : C [a]
[G] d3 : Ord [a] -- Superclass of d2, bogus
Then we'll use the instance decl to give
[G] d1 : Ord a Solved: d2 : C [a] = $dfCList d4
[G] d3 : Ord [a] -- Superclass of d2, bogus
[W] d4: Ord [a]
ANd now we could bogusly solve d4 from d3.
Note [Add superclasses only during canonicalisation]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We add superclasses only during canonicalisation, on the passage
from CNonCanonical to CDictCan. A class constraint can be repeatedly
rewritten, and there's no point in repeatedly adding its superclasses.
Here's an example that demonstrates why we chose to NOT add
superclasses during simplification: [Comes from ticket #4497]
Here's a serious, but now out-dated example, from Trac #4497:
class Num (RealOf t) => Normed t
type family RealOf x
Assume the generated wanted constraint is:
RealOf e ~ e, Normed e
[W] RealOf e ~ e
[W] Normed e
If we were to be adding the superclasses during simplification we'd get:
Num uf, Normed e, RealOf e ~ e, RealOf e ~ uf
[W] RealOf e ~ e
[W] Normed e
[D] RealOf e ~ fuv
[D] Num fuv
==>
e ~ uf, Num uf, Normed e, RealOf e ~ e
==> [Spontaneous solve]
Num uf, Normed uf, RealOf uf ~ uf
e := fuv, Num fuv, Normed fuv, RealOf fuv ~ fuv
While looks exactly like our original constraint. If we add the superclass again we'd loop.
By adding superclasses definitely only once, during canonicalisation, this situation can't
While looks exactly like our original constraint. If we add the
superclass of (Normed fuv) again we'd loop. By adding superclasses
definitely only once, during canonicalisation, this situation can't
happen.
-}
Mind you, now that Wanteds cannot rewrite Derived, I think this particular
situation can't happen.
-}
newSCWorkFromFlavored :: CtEvidence -> Class -> [Xi] -> TcS ()
-- Returns superclasses, see Note [Adding superclasses]
......
......@@ -1105,6 +1105,9 @@ See Trac #3731, #4809, #5751, #5913, #6117, #6161, which all
describe somewhat more complicated situations, but ones
encountered in practice.
See also tests tcrun020, tcrun021, tcrun033
----- THE PROBLEM --------
The problem is that it is all too easy to create a class whose
superclass is bottom when it should not be.
......@@ -1190,8 +1193,6 @@ that is *not* smaller than the target so we can't take *its*
superclasses. As a result the program is rightly rejected, unless you
add (Super (Fam a)) to the context of (i3).
Note [Silent superclass arguments] (historical interest)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NB1: this note describes our *old* solution to the
......
This diff is collapsed.
......@@ -378,6 +378,165 @@ Type-family equations, of form (ev : F tys ~ ty), live in three places
using w3 itself!
* The inert_funeqs are un-solved but fully processed and in the InertCans.
Note [Solved dictionaries]
~~~~~~~~~~~~~~~~~~~~~~~~~~
When we apply a top-level instance declararation, we add the "solved"
dictionary to the inert_solved_dicts. In general, we use it to avoid
creating a new EvVar when we have a new goal that we have solved in
the past.
But in particular, we can use it to create *recursive* dicationaries.
The simplest, degnerate case is
instance C [a] => C [a] where ...
If we have
[W] d1 :: C [x]
then we can apply the instance to get
d1 = $dfCList d
[W] d2 :: C [x]
Now 'd1' goes in inert_solved_dicts, and we can solve d2 directly from d1.
d1 = $dfCList d
d2 = d1
See Note [Example of recursive dictionaries]
Other notes about solved dictionaries
* See also Note [Do not add superclasses of solved dictionaries]
* The inert_solved_dicts field is not rewritten by equalities, so it may
get out of date.
Note [Do not add superclasses of solved dictionaries]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Every member of inert_solved_dicts is the result of applying a dictionary
function, NOT of applying superclass selection to anything.
Consider
class Ord a => C a where
instance Ord [a] => C [a] where ...
Suppose we are trying to solve
[G] d1 : Ord a
[W] d2 : C [a]
Then we'll use the instance decl to give
[G] d1 : Ord a Solved: d2 : C [a] = $dfCList d3
[W] d3 : Ord [a]
We must not add d4 : Ord [a] to the 'solved' set (by taking the
superclass of d2), otherwise we'll use it to solve d3, without ever
using d1, which would be a catastrophe.
Solution: when extending the solved dictionaries, do not add superclasses.
That's why each element of the inert_solved_dicts is the result of applying
a dictionary function.
Note [Example of recursive dictionaries]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--- Example 1
data D r = ZeroD | SuccD (r (D r));
instance (Eq (r (D r))) => Eq (D r) where
ZeroD == ZeroD = True
(SuccD a) == (SuccD b) = a == b
_ == _ = False;
equalDC :: D [] -> D [] -> Bool;
equalDC = (==);
We need to prove (Eq (D [])). Here's how we go:
[W] d1 : Eq (D [])
By instance decl of Eq (D r):
[W] d2 : Eq [D []] where d1 = dfEqD d2
By instance decl of Eq [a]:
[W] d3 : Eq (D []) where d2 = dfEqList d3
d1 = dfEqD d2
Now this wanted can interact with our "solved" d1 to get:
d3 = d1
-- Example 2:
This code arises in the context of "Scrap Your Boilerplate with Class"
class Sat a
class Data ctx a
instance Sat (ctx Char) => Data ctx Char -- dfunData1
instance (Sat (ctx [a]), Data ctx a) => Data ctx [a] -- dfunData2
class Data Maybe a => Foo a
instance Foo t => Sat (Maybe t) -- dfunSat
instance Data Maybe a => Foo a -- dfunFoo1
instance Foo a => Foo [a] -- dfunFoo2
instance Foo [Char] -- dfunFoo3
Consider generating the superclasses of the instance declaration
instance Foo a => Foo [a]
So our problem is this
[G] d0 : Foo t
[W] d1 : Data Maybe [t] -- Desired superclass
We may add the given in the inert set, along with its superclasses
Inert:
[G] d0 : Foo t
[G] d01 : Data Maybe t -- Superclass of d0
WorkList
[W] d1 : Data Maybe [t]
Solve d1 using instance dfunData2; d1 := dfunData2 d2 d3
Inert:
[G] d0 : Foo t
[G] d01 : Data Maybe t -- Superclass of d0
Solved:
d1 : Data Maybe [t]
WorkList:
[W] d2 : Sat (Maybe [t])
[W] d3 : Data Maybe t
Now, we may simplify d2 using dfunSat; d2 := dfunSat d4
Inert:
[G] d0 : Foo t
[G] d01 : Data Maybe t -- Superclass of d0
Solved:
d1 : Data Maybe [t]
d2 : Sat (Maybe [t])
WorkList:
[W] d3 : Data Maybe t
[W] d4 : Foo [t]
Now, we can just solve d3 from d01; d3 := d01
Inert
[G] d0 : Foo t
[G] d01 : Data Maybe t -- Superclass of d0
Solved:
d1 : Data Maybe [t]
d2 : Sat (Maybe [t])
WorkList
[W] d4 : Foo [t]
Now, solve d4 using dfunFoo2; d4 := dfunFoo2 d5
Inert
[G] d0 : Foo t
[G] d01 : Data Maybe t -- Superclass of d0
Solved:
d1 : Data Maybe [t]
d2 : Sat (Maybe [t])
d4 : Foo [t]
WorkList:
[W] d5 : Foo t
Now, d5 can be solved! d5 := d0
Result
d1 := dfunData2 d2 d3
d2 := dfunSat d4
d3 := d01
d4 := dfunFoo2 d5
d5 := d0
-}
-- All Given (fully known) or Wanted or Derived
......@@ -435,11 +594,8 @@ data InertSet
, inert_solved_dicts :: DictMap CtEvidence
-- Of form ev :: C t1 .. tn
-- Always the result of using a top-level instance declaration
-- - Used to avoid creating a new EvVar when we have a new goal
-- that we have solved in the past
-- - Stored not necessarily as fully rewritten
-- (ToDo: rewrite lazily when we lookup)
-- See Note [Solved dictionaries]
-- and Note [Do not add superclasses of solved dictionaries]
}
instance Outputable InertCans where
......@@ -514,6 +670,7 @@ insertInertItemTcS item
addSolvedDict :: CtEvidence -> Class -> [Type] -> TcS ()
-- Add a new item in the solved set of the monad
-- See Note [Solved dictionaries]
addSolvedDict item cls tys
| isIPPred (ctEvPred item) -- Never cache "solved" implicit parameters (not sure why!)
= return ()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment