Simon Peyton Jones pushed to branch wip/T23162-spj at Glasgow Haskell Compiler / GHC
Commits:
-
46241879
by Simon Peyton Jones at 2025-07-07T16:05:46+01:00
-
e970a6b8
by Simon Peyton Jones at 2025-07-07T16:39:04+01:00
6 changed files:
- compiler/GHC/Tc/Solver/Dict.hs
- compiler/GHC/Tc/Solver/Equality.hs
- + compiler/GHC/Tc/Solver/FunDeps.hs
- compiler/GHC/Tc/Solver/Monad.hs
- compiler/GHC/Tc/Solver/Solve.hs
- compiler/ghc.cabal.in
Changes:
... | ... | @@ -8,9 +8,6 @@ module GHC.Tc.Solver.Dict ( |
8 | 8 | matchLocalInst, chooseInstance,
|
9 | 9 | makeSuperClasses, mkStrictSuperClasses,
|
10 | 10 | solveCallStack, -- For GHC.Tc.Solver
|
11 | - |
|
12 | - -- * Functional dependencies
|
|
13 | - doDictFunDepImprovement
|
|
14 | 11 | ) where
|
15 | 12 | |
16 | 13 | import GHC.Prelude
|
... | ... | @@ -1379,346 +1376,6 @@ with the least superclass depth (see Note [Replacement vs keeping]), |
1379 | 1376 | but that doesn't work for the example from #22216.
|
1380 | 1377 | -}
|
1381 | 1378 | |
1382 | -{- *********************************************************************
|
|
1383 | -* *
|
|
1384 | -* Functional dependencies, instantiation of equations
|
|
1385 | -* *
|
|
1386 | -************************************************************************
|
|
1387 | - |
|
1388 | -When we spot an equality arising from a functional dependency,
|
|
1389 | -we now use that equality (a "wanted") to rewrite the work-item
|
|
1390 | -constraint right away. This avoids two dangers
|
|
1391 | - |
|
1392 | - Danger 1: If we send the original constraint on down the pipeline
|
|
1393 | - it may react with an instance declaration, and in delicate
|
|
1394 | - situations (when a Given overlaps with an instance) that
|
|
1395 | - may produce new insoluble goals: see #4952
|
|
1396 | - |
|
1397 | - Danger 2: If we don't rewrite the constraint, it may re-react
|
|
1398 | - with the same thing later, and produce the same equality
|
|
1399 | - again --> termination worries.
|
|
1400 | - |
|
1401 | -To achieve this required some refactoring of GHC.Tc.Instance.FunDeps (nicer
|
|
1402 | -now!).
|
|
1403 | - |
|
1404 | -Note [FunDep and implicit parameter reactions]
|
|
1405 | -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
1406 | -Currently, our story of interacting two dictionaries (or a dictionary
|
|
1407 | -and top-level instances) for functional dependencies (including implicit
|
|
1408 | -parameters), is that we simply produce new Wanted equalities. So for example
|
|
1409 | - |
|
1410 | - class D a b | a -> b where ...
|
|
1411 | - Inert:
|
|
1412 | - [G] d1 : D Int Bool
|
|
1413 | - WorkItem:
|
|
1414 | - [W] d2 : D Int alpha
|
|
1415 | - |
|
1416 | - We generate the extra work item
|
|
1417 | - [W] cv : alpha ~ Bool
|
|
1418 | - where 'cv' is currently unused. However, this new item can perhaps be
|
|
1419 | - spontaneously solved to become given and react with d2,
|
|
1420 | - discharging it in favour of a new constraint d2' thus:
|
|
1421 | - [W] d2' : D Int Bool
|
|
1422 | - d2 := d2' |> D Int cv
|
|
1423 | - Now d2' can be discharged from d1
|
|
1424 | - |
|
1425 | -We could be more aggressive and try to *immediately* solve the dictionary
|
|
1426 | -using those extra equalities.
|
|
1427 | - |
|
1428 | -If that were the case with the same inert set and work item we might discard
|
|
1429 | -d2 directly:
|
|
1430 | - |
|
1431 | - [W] cv : alpha ~ Bool
|
|
1432 | - d2 := d1 |> D Int cv
|
|
1433 | - |
|
1434 | -But in general it's a bit painful to figure out the necessary coercion,
|
|
1435 | -so we just take the first approach. Here is a better example. Consider:
|
|
1436 | - class C a b c | a -> b
|
|
1437 | -And:
|
|
1438 | - [G] d1 : C T Int Char
|
|
1439 | - [W] d2 : C T beta Int
|
|
1440 | -In this case, it's *not even possible* to solve the wanted immediately.
|
|
1441 | -So we should simply output the functional dependency and add this guy
|
|
1442 | -[but NOT its superclasses] back in the worklist. Even worse:
|
|
1443 | - [G] d1 : C T Int beta
|
|
1444 | - [W] d2: C T beta Int
|
|
1445 | -Then it is solvable, but its very hard to detect this on the spot.
|
|
1446 | - |
|
1447 | -It's exactly the same with implicit parameters, except that the
|
|
1448 | -"aggressive" approach would be much easier to implement.
|
|
1449 | - |
|
1450 | -Note [Fundeps with instances, and equality orientation]
|
|
1451 | -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
1452 | -This Note describes a delicate interaction that constrains the orientation of
|
|
1453 | -equalities. This one is about fundeps, but the /exact/ same thing arises for
|
|
1454 | -type-family injectivity constraints: see Note [Improvement orientation].
|
|
1455 | - |
|
1456 | -doTopFunDepImprovement compares the constraint with all the instance
|
|
1457 | -declarations, to see if we can produce any equalities. E.g
|
|
1458 | - class C2 a b | a -> b
|
|
1459 | - instance C Int Bool
|
|
1460 | -Then the constraint (C Int ty) generates the equality [W] ty ~ Bool.
|
|
1461 | - |
|
1462 | -There is a nasty corner in #19415 which led to the typechecker looping:
|
|
1463 | - class C s t b | s -> t
|
|
1464 | - instance ... => C (T kx x) (T ky y) Int
|
|
1465 | - T :: forall k. k -> Type
|
|
1466 | - |
|
1467 | - work_item: dwrk :: C (T @ka (a::ka)) (T @kb0 (b0::kb0)) Char
|
|
1468 | - where kb0, b0 are unification vars
|
|
1469 | - |
|
1470 | - ==> {doTopFunDepImprovement: compare work_item with instance,
|
|
1471 | - generate /fresh/ unification variables kfresh0, yfresh0,
|
|
1472 | - emit a new Wanted, and add dwrk to inert set}
|
|
1473 | - |
|
1474 | - Suppose we emit this new Wanted from the fundep:
|
|
1475 | - [W] T kb0 (b0::kb0) ~ T kfresh0 (yfresh0::kfresh0)
|
|
1476 | - |
|
1477 | - ==> {solve that equality kb0 := kfresh0, b0 := yfresh0}
|
|
1478 | - Now kick out dwrk, since it mentions kb0
|
|
1479 | - But now we are back to the start! Loop!
|
|
1480 | - |
|
1481 | -NB1: This example relies on an instance that does not satisfy the
|
|
1482 | - coverage condition (although it may satisfy the weak coverage
|
|
1483 | - condition), and hence whose fundeps generate fresh unification
|
|
1484 | - variables. Not satisfying the coverage condition is known to
|
|
1485 | - lead to termination trouble, but in this case it's plain silly.
|
|
1486 | - |
|
1487 | -NB2: In this example, the third parameter to C ensures that the
|
|
1488 | - instance doesn't actually match the Wanted, so we can't use it to
|
|
1489 | - solve the Wanted
|
|
1490 | - |
|
1491 | -We solve the problem by (#21703):
|
|
1492 | - |
|
1493 | - carefully orienting the new Wanted so that all the
|
|
1494 | - freshly-generated unification variables are on the LHS.
|
|
1495 | - |
|
1496 | - Thus we call unifyWanteds on
|
|
1497 | - T kfresh0 (yfresh0::kfresh0) ~ T kb0 (b0::kb0)
|
|
1498 | - and /NOT/
|
|
1499 | - T kb0 (b0::kb0) ~ T kfresh0 (yfresh0::kfresh0)
|
|
1500 | - |
|
1501 | -Now we'll unify kfresh0:=kb0, yfresh0:=b0, and all is well. The general idea
|
|
1502 | -is that we want to preferentially eliminate those freshly-generated
|
|
1503 | -unification variables, rather than unifying older variables, which causes
|
|
1504 | -kick-out etc.
|
|
1505 | - |
|
1506 | -Keeping younger variables on the left also gives very minor improvement in
|
|
1507 | -the compiler performance by having less kick-outs and allocations (-0.1% on
|
|
1508 | -average). Indeed Historical Note [Eliminate younger unification variables]
|
|
1509 | -in GHC.Tc.Utils.Unify describes an earlier attempt to do so systematically,
|
|
1510 | -apparently now in abeyance.
|
|
1511 | - |
|
1512 | -But this is is a delicate solution. We must take care to /preserve/
|
|
1513 | -orientation during solving. Wrinkles:
|
|
1514 | - |
|
1515 | -(W1) We start with
|
|
1516 | - [W] T kfresh0 (yfresh0::kfresh0) ~ T kb0 (b0::kb0)
|
|
1517 | - Decompose to
|
|
1518 | - [W] kfresh0 ~ kb0
|
|
1519 | - [W] (yfresh0::kfresh0) ~ (b0::kb0)
|
|
1520 | - Preserve orientation when decomposing!!
|
|
1521 | - |
|
1522 | -(W2) Suppose we happen to tackle the second Wanted from (W1)
|
|
1523 | - first. Then in canEqCanLHSHetero we emit a /kind/ equality, as
|
|
1524 | - well as a now-homogeneous type equality
|
|
1525 | - [W] kco : kfresh0 ~ kb0
|
|
1526 | - [W] (yfresh0::kfresh0) ~ (b0::kb0) |> (sym kco)
|
|
1527 | - Preserve orientation in canEqCanLHSHetero!! (Failing to
|
|
1528 | - preserve orientation here was the immediate cause of #21703.)
|
|
1529 | - |
|
1530 | -(W3) There is a potential interaction with the swapping done by
|
|
1531 | - GHC.Tc.Utils.Unify.swapOverTyVars. We think it's fine, but it's
|
|
1532 | - a slight worry. See especially Note [TyVar/TyVar orientation] in
|
|
1533 | - that module.
|
|
1534 | - |
|
1535 | -The trouble is that "preserving orientation" is a rather global invariant,
|
|
1536 | -and sometimes we definitely do want to swap (e.g. Int ~ alpha), so we don't
|
|
1537 | -even have a precise statement of what the invariant is. The advantage
|
|
1538 | -of the preserve-orientation plan is that it is extremely cheap to implement,
|
|
1539 | -and apparently works beautifully.
|
|
1540 | - |
|
1541 | ---- Alternative plan (1) ---
|
|
1542 | -Rather than have an ill-defined invariant, another possiblity is to
|
|
1543 | -elminate those fresh unification variables at birth, when generating
|
|
1544 | -the new fundep-inspired equalities.
|
|
1545 | - |
|
1546 | -The key idea is to call `instFlexiX` in `emitFunDepWanteds` on only those
|
|
1547 | -type variables that are guaranteed to give us some progress. This means we
|
|
1548 | -have to locally (without calling emitWanteds) identify the type variables
|
|
1549 | -that do not give us any progress. In the above example, we _know_ that
|
|
1550 | -emitting the two wanteds `kco` and `co` is fruitless.
|
|
1551 | - |
|
1552 | - Q: How do we identify such no-ops?
|
|
1553 | - |
|
1554 | - 1. Generate a matching substitution from LHS to RHS
|
|
1555 | - ɸ = [kb0 :-> k0, b0 :-> y0]
|
|
1556 | - 2. Call `instFlexiX` on only those type variables that do not appear in the domain of ɸ
|
|
1557 | - ɸ' = instFlexiX ɸ (tvs - domain ɸ)
|
|
1558 | - 3. Apply ɸ' on LHS and then call emitWanteds
|
|
1559 | - unifyWanteds ... (subst ɸ' LHS) RHS
|
|
1560 | - |
|
1561 | -Why will this work? The matching substitution ɸ will be a best effort
|
|
1562 | -substitution that gives us all the easy solutions. It can be generated with
|
|
1563 | -modified version of `Core/Unify.unify_tys` where we run it in a matching mode
|
|
1564 | -and never generate `SurelyApart` and always return a `MaybeApart Subst`
|
|
1565 | -instead.
|
|
1566 | - |
|
1567 | -The same alternative plan would work for type-family injectivity constraints:
|
|
1568 | -see Note [Improvement orientation] in GHC.Tc.Solver.Equality.
|
|
1569 | ---- End of Alternative plan (1) ---
|
|
1570 | - |
|
1571 | ---- Alternative plan (2) ---
|
|
1572 | -We could have a new flavour of TcTyVar (like `TauTv`, `TyVarTv` etc; see GHC.Tc.Utils.TcType.MetaInfo)
|
|
1573 | -for the fresh unification variables introduced by functional dependencies. Say `FunDepTv`. Then in
|
|
1574 | -GHC.Tc.Utils.Unify.swapOverTyVars we could arrange to keep a `FunDepTv` on the left if possible.
|
|
1575 | -Looks possible, but it's one more complication.
|
|
1576 | ---- End of Alternative plan (2) ---
|
|
1577 | - |
|
1578 | - |
|
1579 | ---- Historical note: Failed Alternative Plan (3) ---
|
|
1580 | -Previously we used a flag `cc_fundeps` in `CDictCan`. It would flip to False
|
|
1581 | -once we used a fun dep to hint the solver to break and to stop emitting more
|
|
1582 | -wanteds. This solution was not complete, and caused a failures while trying
|
|
1583 | -to solve for transitive functional dependencies (test case: T21703)
|
|
1584 | --- End of Historical note: Failed Alternative Plan (3) --
|
|
1585 | - |
|
1586 | -Note [Do fundeps last]
|
|
1587 | -~~~~~~~~~~~~~~~~~~~~~~
|
|
1588 | -Consider T4254b:
|
|
1589 | - class FD a b | a -> b where { op :: a -> b }
|
|
1590 | - |
|
1591 | - instance FD Int Bool
|
|
1592 | - |
|
1593 | - foo :: forall a b. (a~Int,FD a b) => a -> Bool
|
|
1594 | - foo = op
|
|
1595 | - |
|
1596 | -(DFL1) Try local fundeps first.
|
|
1597 | - From the ambiguity check on the type signature we get
|
|
1598 | - [G] FD Int b
|
|
1599 | - [W] FD Int beta
|
|
1600 | - Interacting these gives beta:=b; then we start again and solve without
|
|
1601 | - trying fundeps between the new [W] FD Int b and the top-level instance.
|
|
1602 | - If we did, we'd generate [W] b ~ Bool, which fails.
|
|
1603 | - |
|
1604 | -(DFL2) Try solving from top-level instances before fundeps
|
|
1605 | - From the definition `foo = op` we get
|
|
1606 | - [G] FD Int b
|
|
1607 | - [W] FD Int Bool
|
|
1608 | - We solve this from the top level instance before even trying fundeps.
|
|
1609 | - If we did try fundeps, we'd generate [W] b ~ Bool, which fails.
|
|
1610 | - |
|
1611 | - |
|
1612 | -Note [Weird fundeps]
|
|
1613 | -~~~~~~~~~~~~~~~~~~~~
|
|
1614 | -Consider class Het a b | a -> b where
|
|
1615 | - het :: m (f c) -> a -> m b
|
|
1616 | - |
|
1617 | - class GHet (a :: * -> *) (b :: * -> *) | a -> b
|
|
1618 | - instance GHet (K a) (K [a])
|
|
1619 | - instance Het a b => GHet (K a) (K b)
|
|
1620 | - |
|
1621 | -The two instances don't actually conflict on their fundeps,
|
|
1622 | -although it's pretty strange. So they are both accepted. Now
|
|
1623 | -try [W] GHet (K Int) (K Bool)
|
|
1624 | -This triggers fundeps from both instance decls;
|
|
1625 | - [W] K Bool ~ K [a]
|
|
1626 | - [W] K Bool ~ K beta
|
|
1627 | -And there's a risk of complaining about Bool ~ [a]. But in fact
|
|
1628 | -the Wanted matches the second instance, so we never get as far
|
|
1629 | -as the fundeps.
|
|
1630 | - |
|
1631 | -#7875 is a case in point.
|
|
1632 | --}
|
|
1633 | - |
|
1634 | -doDictFunDepImprovement :: Cts -> TcS (Cts, Bool)
|
|
1635 | --- (doDictFunDepImprovement inst_envs cts)
|
|
1636 | --- * Generate the fundeps from interacting the
|
|
1637 | --- top-level `inst_envs` with the constraints `cts`
|
|
1638 | --- * Do the unifications and return any unsolved constraints
|
|
1639 | --- See Note [Fundeps with instances, and equality orientation]
|
|
1640 | -doDictFunDepImprovement unsolved_wanteds
|
|
1641 | - = do { inerts <- getInertCans -- The inert_dicts are all Givens
|
|
1642 | - ; inst_envs <- getInstEnvs
|
|
1643 | - ; (_, new_eqs, unifs) <- foldrM (do_one_dict inst_envs)
|
|
1644 | - (inert_dicts inerts, emptyBag, False)
|
|
1645 | - unsolved_wanteds
|
|
1646 | - ; return (new_eqs, unifs) }
|
|
1647 | - |
|
1648 | -do_one_dict :: InstEnvs -> Ct
|
|
1649 | - -> (DictMap DictCt, Cts, Bool)
|
|
1650 | - -> TcS (DictMap DictCt, Cts, Bool)
|
|
1651 | -do_one_dict inst_envs (CDictCan dict_ct) (local_dicts, new_eqs, unifs)
|
|
1652 | - = do { (new_eqs1, unifs1) <- do_one_top inst_envs dict_ct
|
|
1653 | - ; (local_dicts2, new_eqs2, unifs2) <- do_one_local local_dicts dict_ct
|
|
1654 | - ; return ( local_dicts2
|
|
1655 | - , new_eqs1 `unionBags` new_eqs2 `unionBags` new_eqs
|
|
1656 | - , unifs1 || unifs2 || unifs ) }
|
|
1657 | - |
|
1658 | -do_one_dict _ _ acc -- Non-DictCt constraints
|
|
1659 | - = return acc
|
|
1660 | - |
|
1661 | -do_one_top :: InstEnvs -> DictCt -> TcS (Cts, Bool)
|
|
1662 | -do_one_top inst_envs (DictCt { di_ev = ev, di_cls = cls, di_tys = xis })
|
|
1663 | - = unifyFunDepWanteds ev eqns
|
|
1664 | - where
|
|
1665 | - eqns :: [FunDepEqn (CtLoc, RewriterSet)]
|
|
1666 | - eqns = improveFromInstEnv inst_envs mk_ct_loc cls xis
|
|
1667 | - |
|
1668 | - dict_pred = mkClassPred cls xis
|
|
1669 | - dict_loc = ctEvLoc ev
|
|
1670 | - dict_origin = ctLocOrigin dict_loc
|
|
1671 | - dict_rewriters = ctEvRewriters ev
|
|
1672 | - |
|
1673 | - mk_ct_loc :: ClsInst -- The instance decl
|
|
1674 | - -> (CtLoc, RewriterSet)
|
|
1675 | - mk_ct_loc ispec
|
|
1676 | - = (dict_loc { ctl_origin = new_orig }, dict_rewriters)
|
|
1677 | - where
|
|
1678 | - inst_pred = mkClassPred cls (is_tys ispec)
|
|
1679 | - inst_loc = getSrcSpan (is_dfun ispec)
|
|
1680 | - new_orig = FunDepOrigin2 dict_pred dict_origin
|
|
1681 | - inst_pred inst_loc
|
|
1682 | - |
|
1683 | -do_one_local :: DictMap DictCt -> DictCt -> TcS (DictMap DictCt, Cts, Bool)
|
|
1684 | --- Using functional dependencies, interact the unsolved Wanteds
|
|
1685 | --- against each other and the inert Givens, to produce new equalities
|
|
1686 | -do_one_local locals dict_ct@(DictCt { di_cls = cls, di_ev = wanted_ev })
|
|
1687 | - -- locals contains all the Givens and earlier Wanteds
|
|
1688 | - = do { (new_eqs, unifs) <- foldrM do_interaction (emptyBag, False) $
|
|
1689 | - findDictsByClass locals cls
|
|
1690 | - ; return (addDict dict_ct locals, new_eqs, unifs) }
|
|
1691 | - where
|
|
1692 | - wanted_pred = ctEvPred wanted_ev
|
|
1693 | - wanted_loc = ctEvLoc wanted_ev
|
|
1694 | - |
|
1695 | - do_interaction :: DictCt -> (Cts,Bool) -> TcS (Cts,Bool)
|
|
1696 | - do_interaction (DictCt { di_ev = all_ev }) (new_eqs, unifs) -- This can be Given or Wanted
|
|
1697 | - = do { traceTcS "doLocalFunDepImprovement" $
|
|
1698 | - vcat [ ppr wanted_ev
|
|
1699 | - , pprCtLoc wanted_loc, ppr (isGivenLoc wanted_loc)
|
|
1700 | - , pprCtLoc all_loc, ppr (isGivenLoc all_loc)
|
|
1701 | - , pprCtLoc deriv_loc, ppr (isGivenLoc deriv_loc) ]
|
|
1702 | - |
|
1703 | - ; (new_eqs1, unifs1) <- unifyFunDepWanteds wanted_ev $
|
|
1704 | - improveFromAnother (deriv_loc, all_rewriters)
|
|
1705 | - all_pred wanted_pred
|
|
1706 | - ; return (new_eqs1 `unionBags` new_eqs, unifs1 || unifs) }
|
|
1707 | - where
|
|
1708 | - all_pred = ctEvPred all_ev
|
|
1709 | - all_loc = ctEvLoc all_ev
|
|
1710 | - all_rewriters = ctEvRewriters all_ev
|
|
1711 | - deriv_loc = wanted_loc { ctl_depth = deriv_depth
|
|
1712 | - , ctl_origin = deriv_origin }
|
|
1713 | - deriv_depth = ctl_depth wanted_loc `maxSubGoalDepth`
|
|
1714 | - ctl_depth all_loc
|
|
1715 | - deriv_origin = FunDepOrigin1 wanted_pred
|
|
1716 | - (ctLocOrigin wanted_loc)
|
|
1717 | - (ctLocSpan wanted_loc)
|
|
1718 | - all_pred
|
|
1719 | - (ctLocOrigin all_loc)
|
|
1720 | - (ctLocSpan all_loc)
|
|
1721 | - |
|
1722 | 1379 | |
1723 | 1380 | {- *********************************************************************
|
1724 | 1381 | * *
|
... | ... | @@ -14,6 +14,7 @@ import GHC.Tc.Solver.Irred( solveIrred ) |
14 | 14 | import GHC.Tc.Solver.Dict( matchLocalInst, chooseInstance )
|
15 | 15 | import GHC.Tc.Solver.Rewrite
|
16 | 16 | import GHC.Tc.Solver.Monad
|
17 | +import GHC.Tc.Solver.FunDeps( unifyAndEmitFunDepWanteds )
|
|
17 | 18 | import GHC.Tc.Solver.InertSet
|
18 | 19 | import GHC.Tc.Solver.Types( findFunEqsByTyCon )
|
19 | 20 | import GHC.Tc.Types.Evidence
|
... | ... | @@ -3108,13 +3109,17 @@ improve_wanted_top_fun_eqs fam_tc lhs_tys rhs_ty |
3108 | 3109 | | Just ops <- isBuiltInSynFamTyCon_maybe fam_tc
|
3109 | 3110 | = return (map snd $ tryInteractTopFam ops fam_tc lhs_tys rhs_ty)
|
3110 | 3111 | |
3112 | + -- ToDo: use ideas in #23162 for closed type families; injectivity only for open
|
|
3113 | + |
|
3111 | 3114 | -- See Note [Type inference for type families with injectivity]
|
3115 | + -- Open, so look for inj
|
|
3112 | 3116 | | Injective inj_args <- tyConInjectivityInfo fam_tc
|
3113 | 3117 | = do { fam_envs <- getFamInstEnvs
|
3114 | 3118 | ; top_eqns <- improve_injective_wanted_top fam_envs inj_args fam_tc lhs_tys rhs_ty
|
3115 | 3119 | ; let local_eqns = improve_injective_wanted_famfam inj_args fam_tc lhs_tys rhs_ty
|
3116 | 3120 | ; traceTcS "improve_wanted_top_fun_eqs" $
|
3117 | 3121 | vcat [ ppr fam_tc, text "local_eqns" <+> ppr local_eqns, text "top_eqns" <+> ppr top_eqns ]
|
3122 | + -- xxx ToDo: this does both local and top => bug?
|
|
3118 | 3123 | ; return (local_eqns ++ top_eqns) }
|
3119 | 3124 | |
3120 | 3125 | | otherwise -- No injectivity
|
1 | +{-# LANGUAGE DuplicateRecordFields #-}
|
|
2 | +{-# LANGUAGE MultiWayIf #-}
|
|
3 | + |
|
4 | +-- | Solving Class constraints CDictCan
|
|
5 | +module GHC.Tc.Solver.FunDeps (
|
|
6 | + unifyAndEmitFunDepWanteds,
|
|
7 | + doDictFunDepImprovement,
|
|
8 | + ImprovementResult, noImprovement
|
|
9 | + ) where
|
|
10 | + |
|
11 | +import GHC.Prelude
|
|
12 | + |
|
13 | +import GHC.Tc.Instance.FunDeps
|
|
14 | +import GHC.Tc.Types.Evidence
|
|
15 | +import GHC.Tc.Types.Constraint
|
|
16 | +import GHC.Tc.Types.CtLoc
|
|
17 | +import GHC.Tc.Types.Origin
|
|
18 | +import GHC.Tc.Solver.InertSet
|
|
19 | +import GHC.Tc.Solver.Monad
|
|
20 | +import GHC.Tc.Solver.Types
|
|
21 | +import GHC.Tc.Utils.TcType
|
|
22 | +import GHC.Tc.Utils.Unify( UnifyEnv(..) )
|
|
23 | +import GHC.Tc.Utils.Monad as TcM
|
|
24 | + |
|
25 | +import GHC.Core.Type
|
|
26 | +import GHC.Core.InstEnv ( InstEnvs, ClsInst(..) )
|
|
27 | +import GHC.Core.Coercion.Axiom( TypeEqn )
|
|
28 | + |
|
29 | +import GHC.Types.Name
|
|
30 | +import GHC.Types.Var.Set
|
|
31 | + |
|
32 | +import GHC.Utils.Outputable
|
|
33 | + |
|
34 | +import GHC.Data.Bag
|
|
35 | +import GHC.Data.Pair
|
|
36 | + |
|
37 | +import qualified Data.Semigroup as S
|
|
38 | + |
|
39 | +import Control.Monad
|
|
40 | + |
|
41 | +{- *********************************************************************
|
|
42 | +* *
|
|
43 | +* Functional dependencies, instantiation of equations
|
|
44 | +* *
|
|
45 | +************************************************************************
|
|
46 | + |
|
47 | +When we spot an equality arising from a functional dependency,
|
|
48 | +we now use that equality (a "wanted") to rewrite the work-item
|
|
49 | +constraint right away. This avoids two dangers
|
|
50 | + |
|
51 | + Danger 1: If we send the original constraint on down the pipeline
|
|
52 | + it may react with an instance declaration, and in delicate
|
|
53 | + situations (when a Given overlaps with an instance) that
|
|
54 | + may produce new insoluble goals: see #4952
|
|
55 | + |
|
56 | + Danger 2: If we don't rewrite the constraint, it may re-react
|
|
57 | + with the same thing later, and produce the same equality
|
|
58 | + again --> termination worries.
|
|
59 | + |
|
60 | +To achieve this required some refactoring of GHC.Tc.Instance.FunDeps (nicer
|
|
61 | +now!).
|
|
62 | + |
|
63 | +Note [FunDep and implicit parameter reactions]
|
|
64 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
65 | +Currently, our story of interacting two dictionaries (or a dictionary
|
|
66 | +and top-level instances) for functional dependencies (including implicit
|
|
67 | +parameters), is that we simply produce new Wanted equalities. So for example
|
|
68 | + |
|
69 | + class D a b | a -> b where ...
|
|
70 | + Inert:
|
|
71 | + [G] d1 : D Int Bool
|
|
72 | + WorkItem:
|
|
73 | + [W] d2 : D Int alpha
|
|
74 | + |
|
75 | + We generate the extra work item
|
|
76 | + [W] cv : alpha ~ Bool
|
|
77 | + where 'cv' is currently unused. However, this new item can perhaps be
|
|
78 | + spontaneously solved to become given and react with d2,
|
|
79 | + discharging it in favour of a new constraint d2' thus:
|
|
80 | + [W] d2' : D Int Bool
|
|
81 | + d2 := d2' |> D Int cv
|
|
82 | + Now d2' can be discharged from d1
|
|
83 | + |
|
84 | +We could be more aggressive and try to *immediately* solve the dictionary
|
|
85 | +using those extra equalities.
|
|
86 | + |
|
87 | +If that were the case with the same inert set and work item we might discard
|
|
88 | +d2 directly:
|
|
89 | + |
|
90 | + [W] cv : alpha ~ Bool
|
|
91 | + d2 := d1 |> D Int cv
|
|
92 | + |
|
93 | +But in general it's a bit painful to figure out the necessary coercion,
|
|
94 | +so we just take the first approach. Here is a better example. Consider:
|
|
95 | + class C a b c | a -> b
|
|
96 | +And:
|
|
97 | + [G] d1 : C T Int Char
|
|
98 | + [W] d2 : C T beta Int
|
|
99 | +In this case, it's *not even possible* to solve the wanted immediately.
|
|
100 | +So we should simply output the functional dependency and add this guy
|
|
101 | +[but NOT its superclasses] back in the worklist. Even worse:
|
|
102 | + [G] d1 : C T Int beta
|
|
103 | + [W] d2: C T beta Int
|
|
104 | +Then it is solvable, but its very hard to detect this on the spot.
|
|
105 | + |
|
106 | +It's exactly the same with implicit parameters, except that the
|
|
107 | +"aggressive" approach would be much easier to implement.
|
|
108 | + |
|
109 | +Note [Fundeps with instances, and equality orientation]
|
|
110 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
111 | +This Note describes a delicate interaction that constrains the orientation of
|
|
112 | +equalities. This one is about fundeps, but the /exact/ same thing arises for
|
|
113 | +type-family injectivity constraints: see Note [Improvement orientation].
|
|
114 | + |
|
115 | +doTopFunDepImprovement compares the constraint with all the instance
|
|
116 | +declarations, to see if we can produce any equalities. E.g
|
|
117 | + class C2 a b | a -> b
|
|
118 | + instance C Int Bool
|
|
119 | +Then the constraint (C Int ty) generates the equality [W] ty ~ Bool.
|
|
120 | + |
|
121 | +There is a nasty corner in #19415 which led to the typechecker looping:
|
|
122 | + class C s t b | s -> t
|
|
123 | + instance ... => C (T kx x) (T ky y) Int
|
|
124 | + T :: forall k. k -> Type
|
|
125 | + |
|
126 | + work_item: dwrk :: C (T @ka (a::ka)) (T @kb0 (b0::kb0)) Char
|
|
127 | + where kb0, b0 are unification vars
|
|
128 | + |
|
129 | + ==> {doTopFunDepImprovement: compare work_item with instance,
|
|
130 | + generate /fresh/ unification variables kfresh0, yfresh0,
|
|
131 | + emit a new Wanted, and add dwrk to inert set}
|
|
132 | + |
|
133 | + Suppose we emit this new Wanted from the fundep:
|
|
134 | + [W] T kb0 (b0::kb0) ~ T kfresh0 (yfresh0::kfresh0)
|
|
135 | + |
|
136 | + ==> {solve that equality kb0 := kfresh0, b0 := yfresh0}
|
|
137 | + Now kick out dwrk, since it mentions kb0
|
|
138 | + But now we are back to the start! Loop!
|
|
139 | + |
|
140 | +NB1: This example relies on an instance that does not satisfy the
|
|
141 | + coverage condition (although it may satisfy the weak coverage
|
|
142 | + condition), and hence whose fundeps generate fresh unification
|
|
143 | + variables. Not satisfying the coverage condition is known to
|
|
144 | + lead to termination trouble, but in this case it's plain silly.
|
|
145 | + |
|
146 | +NB2: In this example, the third parameter to C ensures that the
|
|
147 | + instance doesn't actually match the Wanted, so we can't use it to
|
|
148 | + solve the Wanted
|
|
149 | + |
|
150 | +We solve the problem by (#21703):
|
|
151 | + |
|
152 | + carefully orienting the new Wanted so that all the
|
|
153 | + freshly-generated unification variables are on the LHS.
|
|
154 | + |
|
155 | + Thus we call unifyWanteds on
|
|
156 | + T kfresh0 (yfresh0::kfresh0) ~ T kb0 (b0::kb0)
|
|
157 | + and /NOT/
|
|
158 | + T kb0 (b0::kb0) ~ T kfresh0 (yfresh0::kfresh0)
|
|
159 | + |
|
160 | +Now we'll unify kfresh0:=kb0, yfresh0:=b0, and all is well. The general idea
|
|
161 | +is that we want to preferentially eliminate those freshly-generated
|
|
162 | +unification variables, rather than unifying older variables, which causes
|
|
163 | +kick-out etc.
|
|
164 | + |
|
165 | +Keeping younger variables on the left also gives very minor improvement in
|
|
166 | +the compiler performance by having less kick-outs and allocations (-0.1% on
|
|
167 | +average). Indeed Historical Note [Eliminate younger unification variables]
|
|
168 | +in GHC.Tc.Utils.Unify describes an earlier attempt to do so systematically,
|
|
169 | +apparently now in abeyance.
|
|
170 | + |
|
171 | +But this is is a delicate solution. We must take care to /preserve/
|
|
172 | +orientation during solving. Wrinkles:
|
|
173 | + |
|
174 | +(W1) We start with
|
|
175 | + [W] T kfresh0 (yfresh0::kfresh0) ~ T kb0 (b0::kb0)
|
|
176 | + Decompose to
|
|
177 | + [W] kfresh0 ~ kb0
|
|
178 | + [W] (yfresh0::kfresh0) ~ (b0::kb0)
|
|
179 | + Preserve orientation when decomposing!!
|
|
180 | + |
|
181 | +(W2) Suppose we happen to tackle the second Wanted from (W1)
|
|
182 | + first. Then in canEqCanLHSHetero we emit a /kind/ equality, as
|
|
183 | + well as a now-homogeneous type equality
|
|
184 | + [W] kco : kfresh0 ~ kb0
|
|
185 | + [W] (yfresh0::kfresh0) ~ (b0::kb0) |> (sym kco)
|
|
186 | + Preserve orientation in canEqCanLHSHetero!! (Failing to
|
|
187 | + preserve orientation here was the immediate cause of #21703.)
|
|
188 | + |
|
189 | +(W3) There is a potential interaction with the swapping done by
|
|
190 | + GHC.Tc.Utils.Unify.swapOverTyVars. We think it's fine, but it's
|
|
191 | + a slight worry. See especially Note [TyVar/TyVar orientation] in
|
|
192 | + that module.
|
|
193 | + |
|
194 | +The trouble is that "preserving orientation" is a rather global invariant,
|
|
195 | +and sometimes we definitely do want to swap (e.g. Int ~ alpha), so we don't
|
|
196 | +even have a precise statement of what the invariant is. The advantage
|
|
197 | +of the preserve-orientation plan is that it is extremely cheap to implement,
|
|
198 | +and apparently works beautifully.
|
|
199 | + |
|
200 | +--- Alternative plan (1) ---
|
|
201 | +Rather than have an ill-defined invariant, another possiblity is to
|
|
202 | +elminate those fresh unification variables at birth, when generating
|
|
203 | +the new fundep-inspired equalities.
|
|
204 | + |
|
205 | +The key idea is to call `instFlexiX` in `emitFunDepWanteds` on only those
|
|
206 | +type variables that are guaranteed to give us some progress. This means we
|
|
207 | +have to locally (without calling emitWanteds) identify the type variables
|
|
208 | +that do not give us any progress. In the above example, we _know_ that
|
|
209 | +emitting the two wanteds `kco` and `co` is fruitless.
|
|
210 | + |
|
211 | + Q: How do we identify such no-ops?
|
|
212 | + |
|
213 | + 1. Generate a matching substitution from LHS to RHS
|
|
214 | + ɸ = [kb0 :-> k0, b0 :-> y0]
|
|
215 | + 2. Call `instFlexiX` on only those type variables that do not appear in the domain of ɸ
|
|
216 | + ɸ' = instFlexiX ɸ (tvs - domain ɸ)
|
|
217 | + 3. Apply ɸ' on LHS and then call emitWanteds
|
|
218 | + unifyWanteds ... (subst ɸ' LHS) RHS
|
|
219 | + |
|
220 | +Why will this work? The matching substitution ɸ will be a best effort
|
|
221 | +substitution that gives us all the easy solutions. It can be generated with
|
|
222 | +modified version of `Core/Unify.unify_tys` where we run it in a matching mode
|
|
223 | +and never generate `SurelyApart` and always return a `MaybeApart Subst`
|
|
224 | +instead.
|
|
225 | + |
|
226 | +The same alternative plan would work for type-family injectivity constraints:
|
|
227 | +see Note [Improvement orientation] in GHC.Tc.Solver.Equality.
|
|
228 | +--- End of Alternative plan (1) ---
|
|
229 | + |
|
230 | +--- Alternative plan (2) ---
|
|
231 | +We could have a new flavour of TcTyVar (like `TauTv`, `TyVarTv` etc; see GHC.Tc.Utils.TcType.MetaInfo)
|
|
232 | +for the fresh unification variables introduced by functional dependencies. Say `FunDepTv`. Then in
|
|
233 | +GHC.Tc.Utils.Unify.swapOverTyVars we could arrange to keep a `FunDepTv` on the left if possible.
|
|
234 | +Looks possible, but it's one more complication.
|
|
235 | +--- End of Alternative plan (2) ---
|
|
236 | + |
|
237 | + |
|
238 | +--- Historical note: Failed Alternative Plan (3) ---
|
|
239 | +Previously we used a flag `cc_fundeps` in `CDictCan`. It would flip to False
|
|
240 | +once we used a fun dep to hint the solver to break and to stop emitting more
|
|
241 | +wanteds. This solution was not complete, and caused a failures while trying
|
|
242 | +to solve for transitive functional dependencies (test case: T21703)
|
|
243 | +-- End of Historical note: Failed Alternative Plan (3) --
|
|
244 | + |
|
245 | +Note [Do fundeps last]
|
|
246 | +~~~~~~~~~~~~~~~~~~~~~~
|
|
247 | +Consider T4254b:
|
|
248 | + class FD a b | a -> b where { op :: a -> b }
|
|
249 | + |
|
250 | + instance FD Int Bool
|
|
251 | + |
|
252 | + foo :: forall a b. (a~Int,FD a b) => a -> Bool
|
|
253 | + foo = op
|
|
254 | + |
|
255 | +(DFL1) Try local fundeps first.
|
|
256 | + From the ambiguity check on the type signature we get
|
|
257 | + [G] FD Int b
|
|
258 | + [W] FD Int beta
|
|
259 | + If we ineract that Wanted with /both/ the t0p-level instance, /and/ the
|
|
260 | + local Given, we'll get
|
|
261 | + beta ~ Int and beta ~ b
|
|
262 | + respectively. That would generate (b~Bool), which would fai. I think
|
|
263 | + it doesn't matter which of the two we pick, but historically we have
|
|
264 | + picked teh local-fundeps firs.
|
|
265 | + |
|
266 | +(DFL2) Try solving from top-level instances before fundeps.
|
|
267 | + From the definition `foo = op` we get
|
|
268 | + [G] FD Int b
|
|
269 | + [W] FD Int Bool
|
|
270 | + We solve this from the top level instance before even trying fundeps.
|
|
271 | + If we did try fundeps, we'd generate [W] b ~ Bool, which fails.
|
|
272 | + |
|
273 | + (DFL2) is achieved by trying fundeps only on /unsolved/ Wanteds.
|
|
274 | + |
|
275 | + |
|
276 | +Note [Weird fundeps]
|
|
277 | +~~~~~~~~~~~~~~~~~~~~
|
|
278 | +Consider class Het a b | a -> b where
|
|
279 | + het :: m (f c) -> a -> m b
|
|
280 | + |
|
281 | + class GHet (a :: * -> *) (b :: * -> *) | a -> b
|
|
282 | + instance GHet (K a) (K [a])
|
|
283 | + instance Het a b => GHet (K a) (K b)
|
|
284 | + |
|
285 | +The two instances don't actually conflict on their fundeps,
|
|
286 | +although it's pretty strange. So they are both accepted. Now
|
|
287 | +try [W] GHet (K Int) (K Bool)
|
|
288 | +This triggers fundeps from both instance decls;
|
|
289 | + [W] K Bool ~ K [a]
|
|
290 | + [W] K Bool ~ K beta
|
|
291 | +And there's a risk of complaining about Bool ~ [a]. But in fact
|
|
292 | +the Wanted matches the second instance, so we never get as far
|
|
293 | +as the fundeps.
|
|
294 | + |
|
295 | +#7875 is a case in point.
|
|
296 | +-}
|
|
297 | + |
|
298 | +doDictFunDepImprovement :: Cts -> TcS ImprovementResult
|
|
299 | +-- (doDictFunDepImprovement inst_envs cts)
|
|
300 | +-- * Generate the fundeps from interacting the
|
|
301 | +-- top-level `inst_envs` with the constraints `cts`
|
|
302 | +-- * Do the unifications and return any unsolved constraints
|
|
303 | +-- See Note [Fundeps with instances, and equality orientation]
|
|
304 | +-- foldrM :: (Foldable t, Monad m) => (a -> b -> m b) -> b -> t a -> m b
|
|
305 | +doDictFunDepImprovement unsolved_wanteds
|
|
306 | + = do { inerts <- getInertCans -- The inert_dicts are all Givens
|
|
307 | + ; inst_envs <- getInstEnvs
|
|
308 | + ; (_, imp_res) <- foldM (do_one_dict inst_envs)
|
|
309 | + (inert_dicts inerts, noopImprovement)
|
|
310 | + unsolved_wanteds
|
|
311 | + ; return imp_res }
|
|
312 | + |
|
313 | +do_one_dict :: InstEnvs
|
|
314 | + -> (DictMap DictCt, ImprovementResult)
|
|
315 | + -> Ct
|
|
316 | + -> TcS (DictMap DictCt, ImprovementResult)
|
|
317 | +-- The `local_dicts` accumulator starts life as just the Givens, but
|
|
318 | +-- as we encounter each Wanted we augment it. Result: each Wanted
|
|
319 | +-- is interacted with all the Givens, and all prededing Wanteds.
|
|
320 | +-- This is worst-case quadratic because we have to compare each
|
|
321 | +-- constraint with all the others, to find all the pairwise interactions
|
|
322 | +do_one_dict inst_envs (local_dicts, imp_res) (CDictCan dict_ct)
|
|
323 | + = do { (local_dicts1, imp_res1) <- do_one_local local_dicts dict_ct
|
|
324 | + ; if noImprovement imp_res1
|
|
325 | + then do { imp_res2 <- do_one_top inst_envs dict_ct
|
|
326 | + ; return (local_dicts1, imp_res `plusImprovements` imp_res2) }
|
|
327 | + else return (local_dicts1, imp_res `plusImprovements` imp_res1) }
|
|
328 | + |
|
329 | +do_one_dict _ acc _ -- Non-DictCt constraints
|
|
330 | + = return acc
|
|
331 | + |
|
332 | +do_one_top :: InstEnvs -> DictCt -> TcS ImprovementResult
|
|
333 | +do_one_top inst_envs (DictCt { di_ev = ev, di_cls = cls, di_tys = xis })
|
|
334 | + = unifyFunDepWanteds ev eqns
|
|
335 | + where
|
|
336 | + eqns :: [FunDepEqn (CtLoc, RewriterSet)]
|
|
337 | + eqns = improveFromInstEnv inst_envs mk_ct_loc cls xis
|
|
338 | + |
|
339 | + dict_pred = mkClassPred cls xis
|
|
340 | + dict_loc = ctEvLoc ev
|
|
341 | + dict_origin = ctLocOrigin dict_loc
|
|
342 | + dict_rewriters = ctEvRewriters ev
|
|
343 | + |
|
344 | + mk_ct_loc :: ClsInst -- The instance decl
|
|
345 | + -> (CtLoc, RewriterSet)
|
|
346 | + mk_ct_loc ispec
|
|
347 | + = (dict_loc { ctl_origin = new_orig }, dict_rewriters)
|
|
348 | + where
|
|
349 | + inst_pred = mkClassPred cls (is_tys ispec)
|
|
350 | + inst_loc = getSrcSpan (is_dfun ispec)
|
|
351 | + new_orig = FunDepOrigin2 dict_pred dict_origin
|
|
352 | + inst_pred inst_loc
|
|
353 | + |
|
354 | +do_one_local :: DictMap DictCt -> DictCt -> TcS (DictMap DictCt, ImprovementResult)
|
|
355 | +-- Using functional dependencies, interact the unsolved Wanteds
|
|
356 | +-- against each other and the inert Givens, to produce new equalities
|
|
357 | +do_one_local locals dict_ct@(DictCt { di_cls = cls, di_ev = wanted_ev })
|
|
358 | + -- locals contains all the Givens and earlier Wanteds
|
|
359 | + = do { imp_res <- foldM do_interaction noopImprovement $
|
|
360 | + findDictsByClass locals cls
|
|
361 | + ; return (addDict dict_ct locals, imp_res) }
|
|
362 | + where
|
|
363 | + wanted_pred = ctEvPred wanted_ev
|
|
364 | + wanted_loc = ctEvLoc wanted_ev
|
|
365 | + |
|
366 | + do_interaction :: (Cts,Bool) -> DictCt -> TcS (Cts,Bool)
|
|
367 | + do_interaction (new_eqs, unifs) (DictCt { di_ev = all_ev }) -- This can be Given or Wanted
|
|
368 | + = do { traceTcS "doLocalFunDepImprovement" $
|
|
369 | + vcat [ ppr wanted_ev
|
|
370 | + , pprCtLoc wanted_loc, ppr (isGivenLoc wanted_loc)
|
|
371 | + , pprCtLoc all_loc, ppr (isGivenLoc all_loc)
|
|
372 | + , pprCtLoc deriv_loc, ppr (isGivenLoc deriv_loc) ]
|
|
373 | + |
|
374 | + ; (new_eqs1, unifs1) <- unifyFunDepWanteds wanted_ev $
|
|
375 | + improveFromAnother (deriv_loc, all_rewriters)
|
|
376 | + all_pred wanted_pred
|
|
377 | + ; return (new_eqs1 `unionBags` new_eqs, unifs1 || unifs) }
|
|
378 | + where
|
|
379 | + all_pred = ctEvPred all_ev
|
|
380 | + all_loc = ctEvLoc all_ev
|
|
381 | + all_rewriters = ctEvRewriters all_ev
|
|
382 | + deriv_loc = wanted_loc { ctl_depth = deriv_depth
|
|
383 | + , ctl_origin = deriv_origin }
|
|
384 | + deriv_depth = ctl_depth wanted_loc `maxSubGoalDepth`
|
|
385 | + ctl_depth all_loc
|
|
386 | + deriv_origin = FunDepOrigin1 wanted_pred
|
|
387 | + (ctLocOrigin wanted_loc)
|
|
388 | + (ctLocSpan wanted_loc)
|
|
389 | + all_pred
|
|
390 | + (ctLocOrigin all_loc)
|
|
391 | + (ctLocSpan all_loc)
|
|
392 | + |
|
393 | + |
|
394 | +{-
|
|
395 | +************************************************************************
|
|
396 | +* *
|
|
397 | + Emitting equalities arising from fundeps
|
|
398 | +* *
|
|
399 | +************************************************************************
|
|
400 | +-}
|
|
401 | + |
|
402 | +type ImprovementResult = (Cts, Bool)
|
|
403 | + -- The Cts are the new equality constraints
|
|
404 | + -- The Bool is True if we unified any meta-ty-vars on when
|
|
405 | + -- generating those new equality constraints
|
|
406 | + |
|
407 | +noopImprovement :: ImprovementResult
|
|
408 | +noopImprovement = (emptyBag, False)
|
|
409 | + |
|
410 | +noImprovement :: ImprovementResult -> Bool
|
|
411 | +noImprovement (cts,unifs) = not unifs && isEmptyBag cts
|
|
412 | + |
|
413 | +plusImprovements :: ImprovementResult -> ImprovementResult -> ImprovementResult
|
|
414 | +plusImprovements (cts1,unif1) (cts2,unif2)
|
|
415 | + = (cts1 `unionBags` cts2, unif1 || unif2)
|
|
416 | + |
|
417 | + |
|
418 | +unifyAndEmitFunDepWanteds :: CtEvidence -- The work item
|
|
419 | + -> [FunDepEqn (CtLoc, RewriterSet)]
|
|
420 | + -> TcS Bool -- True <=> some unification happened
|
|
421 | +unifyAndEmitFunDepWanteds ev fd_eqns
|
|
422 | + = do { (new_eqs, unifs) <- unifyFunDepWanteds ev fd_eqns
|
|
423 | + |
|
424 | + ; -- Emit the deferred constraints
|
|
425 | + -- See Note [Work-list ordering] in GHC.Tc.Solved.Equality
|
|
426 | + --
|
|
427 | + -- All the constraints in `cts` share the same rewriter set so,
|
|
428 | + -- rather than looking at it one by one, we pass it to
|
|
429 | + -- extendWorkListChildEqs; just a small optimisation.
|
|
430 | + ; unless (isEmptyBag new_eqs) $
|
|
431 | + updWorkListTcS (extendWorkListChildEqs ev new_eqs)
|
|
432 | + |
|
433 | + ; return unifs }
|
|
434 | + |
|
435 | +unifyFunDepWanteds :: CtEvidence -- The work item
|
|
436 | + -> [FunDepEqn (CtLoc, RewriterSet)]
|
|
437 | + -> TcS ImprovementResult
|
|
438 | + |
|
439 | +unifyFunDepWanteds _ [] = return noopImprovement -- common case noop
|
|
440 | +-- See Note [FunDep and implicit parameter reactions]
|
|
441 | + |
|
442 | +unifyFunDepWanteds ev fd_eqns
|
|
443 | + = do { (fresh_tvs_s, cts, unified_tvs) <- wrapUnifierX ev Nominal do_fundeps
|
|
444 | + |
|
445 | + -- Figure out if a "real" unification happened: See Note [unifyFunDeps]
|
|
446 | + ; let unif_happened = any is_old_tv unified_tvs
|
|
447 | + fresh_tvs = mkVarSet (concat fresh_tvs_s)
|
|
448 | + is_old_tv tv = not (tv `elemVarSet` fresh_tvs)
|
|
449 | + |
|
450 | + ; return (cts, unif_happened) }
|
|
451 | + where
|
|
452 | + do_fundeps :: UnifyEnv -> TcM [[TcTyVar]]
|
|
453 | + do_fundeps env = mapM (do_one env) fd_eqns
|
|
454 | + |
|
455 | + do_one :: UnifyEnv -> FunDepEqn (CtLoc, RewriterSet) -> TcM [TcTyVar]
|
|
456 | + do_one uenv (FDEqn { fd_qtvs = tvs, fd_eqs = eqs, fd_loc = (loc, rewriters) })
|
|
457 | + = do { (fresh_tvs, eqs') <- instantiateFunDepEqn tvs (reverse eqs)
|
|
458 | + -- (reverse eqs): See Note [Reverse order of fundep equations]
|
|
459 | + ; uPairsTcM env_one eqs'
|
|
460 | + ; return fresh_tvs }
|
|
461 | + where
|
|
462 | + env_one = uenv { u_rewriters = u_rewriters uenv S.<> rewriters
|
|
463 | + , u_loc = loc }
|
|
464 | + |
|
465 | +instantiateFunDepEqn :: [TyVar] -> [TypeEqn] -> TcM ([TcTyVar], [TypeEqn])
|
|
466 | +instantiateFunDepEqn tvs eqs
|
|
467 | + | null tvs
|
|
468 | + = return ([], eqs)
|
|
469 | + | otherwise
|
|
470 | + = do { TcM.traceTc "emitFunDepWanteds 2" (ppr tvs $$ ppr eqs)
|
|
471 | + ; (tvs', subst) <- instFlexiXTcM emptySubst tvs -- Takes account of kind substitution
|
|
472 | + ; return (tvs', map (subst_pair subst) eqs) }
|
|
473 | + where
|
|
474 | + subst_pair subst (Pair ty1 ty2)
|
|
475 | + = Pair (substTyUnchecked subst' ty1) ty2
|
|
476 | + -- ty2 does not mention fd_qtvs, so no need to subst it.
|
|
477 | + -- See GHC.Tc.Instance.Fundeps Note [Improving against instances]
|
|
478 | + -- Wrinkle (1)
|
|
479 | + where
|
|
480 | + subst' = extendSubstInScopeSet subst (tyCoVarsOfType ty1)
|
|
481 | + -- The free vars of ty1 aren't just fd_qtvs: ty1 is the result
|
|
482 | + -- of matching with the [W] constraint. So we add its free
|
|
483 | + -- vars to InScopeSet, to satisfy substTy's invariants, even
|
|
484 | + -- though ty1 will never (currently) be a poytype, so this
|
|
485 | + -- InScopeSet will never be looked at.
|
|
486 | + |
... | ... | @@ -105,10 +105,9 @@ module GHC.Tc.Solver.Monad ( |
105 | 105 | |
106 | 106 | -- Unification
|
107 | 107 | wrapUnifierX, wrapUnifierTcS, unifyFunDeps, uPairsTcM, unifyForAllBody,
|
108 | - unifyFunDepWanteds, unifyAndEmitFunDepWanteds,
|
|
109 | 108 | |
110 | 109 | -- MetaTyVars
|
111 | - newFlexiTcSTy, instFlexiX,
|
|
110 | + newFlexiTcSTy, instFlexiX, instFlexiXTcM,
|
|
112 | 111 | cloneMetaTyVar,
|
113 | 112 | tcInstSkolTyVarsX,
|
114 | 113 | |
... | ... | @@ -129,8 +128,7 @@ module GHC.Tc.Solver.Monad ( |
129 | 128 | pprEq,
|
130 | 129 | |
131 | 130 | -- Enforcing invariants for type equalities
|
132 | - checkTypeEq,
|
|
133 | - instantiateFunDepEqn
|
|
131 | + checkTypeEq
|
|
134 | 132 | ) where
|
135 | 133 | |
136 | 134 | import GHC.Prelude
|
... | ... | @@ -2235,83 +2233,6 @@ solverDepthError loc ty |
2235 | 2233 | where
|
2236 | 2234 | depth = ctLocDepth loc
|
2237 | 2235 | |
2238 | -{-
|
|
2239 | -************************************************************************
|
|
2240 | -* *
|
|
2241 | - Emitting equalities arising from fundeps
|
|
2242 | -* *
|
|
2243 | -************************************************************************
|
|
2244 | --}
|
|
2245 | - |
|
2246 | -unifyAndEmitFunDepWanteds :: CtEvidence -- The work item
|
|
2247 | - -> [FunDepEqn (CtLoc, RewriterSet)]
|
|
2248 | - -> TcS Bool -- True <=> some unification happened
|
|
2249 | -unifyAndEmitFunDepWanteds ev fd_eqns
|
|
2250 | - = do { (new_eqs, unifs) <- unifyFunDepWanteds ev fd_eqns
|
|
2251 | - |
|
2252 | - ; -- Emit the deferred constraints
|
|
2253 | - -- See Note [Work-list ordering] in GHC.Tc.Solved.Equality
|
|
2254 | - --
|
|
2255 | - -- All the constraints in `cts` share the same rewriter set so,
|
|
2256 | - -- rather than looking at it one by one, we pass it to
|
|
2257 | - -- extendWorkListChildEqs; just a small optimisation.
|
|
2258 | - ; unless (isEmptyBag new_eqs) $
|
|
2259 | - updWorkListTcS (extendWorkListChildEqs ev new_eqs)
|
|
2260 | - |
|
2261 | - ; return unifs }
|
|
2262 | - |
|
2263 | -unifyFunDepWanteds :: CtEvidence -- The work item
|
|
2264 | - -> [FunDepEqn (CtLoc, RewriterSet)]
|
|
2265 | - -> TcS (Cts, Bool) -- True <=> some unification happened
|
|
2266 | - |
|
2267 | -unifyFunDepWanteds _ [] = return (emptyBag, False) -- common case noop
|
|
2268 | --- See Note [FunDep and implicit parameter reactions]
|
|
2269 | - |
|
2270 | -unifyFunDepWanteds ev fd_eqns
|
|
2271 | - = do { (fresh_tvs_s, cts, unified_tvs) <- wrapUnifierX ev Nominal do_fundeps
|
|
2272 | - |
|
2273 | - -- Figure out if a "real" unification happened: See Note [unifyFunDeps]
|
|
2274 | - ; let unif_happened = any is_old_tv unified_tvs
|
|
2275 | - fresh_tvs = mkVarSet (concat fresh_tvs_s)
|
|
2276 | - is_old_tv tv = not (tv `elemVarSet` fresh_tvs)
|
|
2277 | - |
|
2278 | - ; return (cts, unif_happened) }
|
|
2279 | - where
|
|
2280 | - do_fundeps :: UnifyEnv -> TcM [[TcTyVar]]
|
|
2281 | - do_fundeps env = mapM (do_one env) fd_eqns
|
|
2282 | - |
|
2283 | - do_one :: UnifyEnv -> FunDepEqn (CtLoc, RewriterSet) -> TcM [TcTyVar]
|
|
2284 | - do_one uenv (FDEqn { fd_qtvs = tvs, fd_eqs = eqs, fd_loc = (loc, rewriters) })
|
|
2285 | - = do { (fresh_tvs, eqs') <- instantiateFunDepEqn tvs (reverse eqs)
|
|
2286 | - -- (reverse eqs): See Note [Reverse order of fundep equations]
|
|
2287 | - ; uPairsTcM env_one eqs'
|
|
2288 | - ; return fresh_tvs }
|
|
2289 | - where
|
|
2290 | - env_one = uenv { u_rewriters = u_rewriters uenv S.<> rewriters
|
|
2291 | - , u_loc = loc }
|
|
2292 | - |
|
2293 | -instantiateFunDepEqn :: [TyVar] -> [TypeEqn] -> TcM ([TcTyVar], [TypeEqn])
|
|
2294 | -instantiateFunDepEqn tvs eqs
|
|
2295 | - | null tvs
|
|
2296 | - = return ([], eqs)
|
|
2297 | - | otherwise
|
|
2298 | - = do { TcM.traceTc "emitFunDepWanteds 2" (ppr tvs $$ ppr eqs)
|
|
2299 | - ; (tvs', subst) <- instFlexiXTcM emptySubst tvs -- Takes account of kind substitution
|
|
2300 | - ; return (tvs', map (subst_pair subst) eqs) }
|
|
2301 | - where
|
|
2302 | - subst_pair subst (Pair ty1 ty2)
|
|
2303 | - = Pair (substTyUnchecked subst' ty1) ty2
|
|
2304 | - -- ty2 does not mention fd_qtvs, so no need to subst it.
|
|
2305 | - -- See GHC.Tc.Instance.Fundeps Note [Improving against instances]
|
|
2306 | - -- Wrinkle (1)
|
|
2307 | - where
|
|
2308 | - subst' = extendSubstInScopeSet subst (tyCoVarsOfType ty1)
|
|
2309 | - -- The free vars of ty1 aren't just fd_qtvs: ty1 is the result
|
|
2310 | - -- of matching with the [W] constraint. So we add its free
|
|
2311 | - -- vars to InScopeSet, to satisfy substTy's invariants, even
|
|
2312 | - -- though ty1 will never (currently) be a poytype, so this
|
|
2313 | - -- InScopeSet will never be looked at.
|
|
2314 | - |
|
2315 | 2236 | {-
|
2316 | 2237 | ************************************************************************
|
2317 | 2238 | * *
|
... | ... | @@ -15,6 +15,7 @@ module GHC.Tc.Solver.Solve ( |
15 | 15 | import GHC.Prelude
|
16 | 16 | |
17 | 17 | import GHC.Tc.Solver.Dict
|
18 | +import GHC.Tc.Solver.FunDeps( doDictFunDepImprovement )
|
|
18 | 19 | import GHC.Tc.Solver.Equality( solveEquality )
|
19 | 20 | import GHC.Tc.Solver.Irred( solveIrred )
|
20 | 21 | import GHC.Tc.Solver.Rewrite( rewrite, rewriteType )
|
... | ... | @@ -119,13 +120,13 @@ simplify_loop n limit definitely_redo_implications |
119 | 120 | , int (lengthBag simples) <+> text "simples to solve" ])
|
120 | 121 | ; traceTcS "simplify_loop: wc =" (ppr wc)
|
121 | 122 | |
122 | - ; (unifs1, wc1) <- reportUnifications $ -- See Note [Superclass iteration]
|
|
123 | - solveSimpleWanteds simples
|
|
123 | + ; (n_unifs, wc1) <- reportUnifications $ -- See Note [Superclass iteration]
|
|
124 | + solveSimpleWanteds simples
|
|
124 | 125 | -- Any insoluble constraints are in 'simples' and so get rewritten
|
125 | 126 | -- See Note [Rewrite insolubles] in GHC.Tc.Solver.InertSet
|
126 | 127 | |
127 | 128 | ; wc2 <- if not definitely_redo_implications -- See Note [Superclass iteration]
|
128 | - && unifs1 == 0 -- for this conditional
|
|
129 | + && n_unifs == 0 -- for this conditional
|
|
129 | 130 | && isEmptyBag (wc_impl wc1)
|
130 | 131 | then return (wc { wc_simple = wc_simple wc1 }) -- Short cut
|
131 | 132 | else do { implics2 <- solveNestedImplications $
|
... | ... | @@ -207,10 +208,19 @@ maybe_simplify_again n limit unif_happened wc@(WC { wc_simple = simples }) |
207 | 208 | |
208 | 209 | try_fundeps :: TcS (Maybe NextAction)
|
209 | 210 | try_fundeps
|
210 | - = do { (new_eqs, unifs) <- doDictFunDepImprovement simples
|
|
211 | - ; if null new_eqs && not unifs
|
|
211 | + = do { (new_eqs, unifs1) <- doDictFunDepImprovement simples
|
|
212 | + ; if null new_eqs && not unifs1
|
|
212 | 213 | then return Nothing
|
213 | - else return (Just (NA_TryAgain (wc `addSimples` new_eqs) unifs)) }
|
|
214 | + else
|
|
215 | + -- We solve new_eqs immediately, hoping to get some unifications
|
|
216 | + -- If instead we just added them to `wc` we'll iterate and (in case when
|
|
217 | + -- that doesn't solve it) we'll add the same constraint again... loop!
|
|
218 | + do { traceTcS "try_fundeps" (ppr unifs1 $$ ppr new_eqs)
|
|
219 | + ; (n_unifs2, _wc) <- reportUnifications $
|
|
220 | + solveSimpleWanteds new_eqs
|
|
221 | + ; if (unifs1 || n_unifs2 > 0)
|
|
222 | + then return (Just (NA_TryAgain wc True))
|
|
223 | + else return Nothing } }
|
|
214 | 224 | |
215 | 225 | {- Note [Superclass iteration]
|
216 | 226 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
... | ... | @@ -854,6 +854,7 @@ Library |
854 | 854 | GHC.Tc.Solver.Irred
|
855 | 855 | GHC.Tc.Solver.Equality
|
856 | 856 | GHC.Tc.Solver.Dict
|
857 | + GHC.Tc.Solver.FunDeps
|
|
857 | 858 | GHC.Tc.Solver.Monad
|
858 | 859 | GHC.Tc.Solver.Types
|
859 | 860 | GHC.Tc.TyCl
|