[Git][ghc/ghc][wip/romes/eqsat-pmc] Compiler working with e-graph

Rodrigo Mesquita (@alt-romes) gitlab at gitlab.haskell.org
Sun Jun 11 19:52:00 UTC 2023



Rodrigo Mesquita pushed to branch wip/romes/eqsat-pmc at Glasgow Haskell Compiler / GHC


Commits:
d0575935 by Rodrigo Mesquita at 2023-06-11T21:51:42+02:00
Compiler working with e-graph

- - - - -


4 changed files:

- compiler/GHC/HsToCore/Pmc/Solver.hs
- compiler/GHC/HsToCore/Pmc/Solver/Types.hs
- compiler/GHC/Types/Unique/SDFM.hs
- libraries/hegg


Changes:

=====================================
compiler/GHC/HsToCore/Pmc/Solver.hs
=====================================
@@ -49,7 +49,6 @@ import GHC.Data.Bag
 import GHC.Types.CompleteMatch
 import GHC.Types.Unique.Set
 import GHC.Types.Unique.DSet
-import GHC.Types.Unique.SDFM
 import GHC.Types.Id
 import GHC.Types.Name
 import GHC.Types.Var      (EvVar)
@@ -99,6 +98,11 @@ import Data.List     (sortBy, find)
 import qualified Data.List.NonEmpty as NE
 import Data.Ord      (comparing)
 
+import Data.Functor.Const
+import Data.Equality.Graph (EGraph)
+import Data.Equality.Utils (Fix(..))
+import qualified Data.Equality.Graph as EG
+
 --
 -- * Main exports
 --
@@ -685,32 +689,38 @@ filterUnliftedFields con args =
 -- ⊥.
 addBotCt :: Nabla -> Id -> MaybeT DsM Nabla
 addBotCt nabla at MkNabla{ nabla_tm_st = ts at TmSt{ ts_facts=env } } x = do
-  let (y, vi at VI { vi_bot = bot }) = lookupVarInfoNT (nabla_tm_st nabla) x
-  case bot of
-    IsNotBot -> mzero      -- There was x ≁ ⊥. Contradiction!
-    IsBot    -> pure nabla -- There already is x ~ ⊥. Nothing left to do
-    MaybeBot               -- We add x ~ ⊥
-      | definitelyUnliftedType (idType x)
-      -- Case (3) in Note [Strict fields and variables of unlifted type]
-      -> mzero -- unlifted vars can never be ⊥
-      | otherwise
-      -> do
-          let vi' = vi{ vi_bot = IsBot }
-          pure nabla{ nabla_tm_st = ts{ts_facts = addToUSDFM env y vi' } }
+  let (xid, env') = EG.represent (Fix $ Const x) env
+  env'' <- EG.adjustF go xid env'
+  pure nabla{nabla_tm_st = ts{ts_facts = env''}}
+  where
+    go :: VarInfo -> MaybeT DsM VarInfo
+    go vi at VI { vi_bot = bot }
+      = case bot of
+          IsNotBot -> mzero      -- There was x ≁ ⊥. Contradiction!
+          IsBot    -> pure vi    -- There already is x ~ ⊥. Nothing left to do
+          MaybeBot               -- We add x ~ ⊥
+            | definitelyUnliftedType (idType x)
+            -- Case (3) in Note [Strict fields and variables of unlifted type]
+            -> mzero -- unlifted vars can never be ⊥
+            | otherwise
+            -> do
+              pure vi{ vi_bot = IsBot }
 
 -- | Adds the constraint @x ~/ ⊥@ to 'Nabla'. Quite similar to 'addNotConCt',
 -- but only cares for the ⊥ "constructor".
 addNotBotCt :: Nabla -> Id -> MaybeT DsM Nabla
 addNotBotCt nabla at MkNabla{ nabla_tm_st = ts at TmSt{ts_facts=env} } x = do
-  let (y, vi at VI { vi_bot = bot }) = lookupVarInfoNT (nabla_tm_st nabla) x
+  let (xid, env') = EG.represent (Fix $ Const x) env
+  -- ROMES:TODO: This could be all be a function passed to adjust
+  let (y, vi at VI { vi_bot = bot }, TmSt{ts_facts=env''}) = lookupVarInfoNT (ts{ts_facts=env'}) x -- ROMES:TODO: this will represent x again (quite cheap still), but whatever for now
   case bot of
     IsBot    -> mzero      -- There was x ~ ⊥. Contradiction!
-    IsNotBot -> pure nabla -- There already is x ≁ ⊥. Nothing left to do
+    IsNotBot -> pure nabla -- There already is x ≁ ⊥. Nothing left to do (ROMES:TODO missing env')
     MaybeBot -> do         -- We add x ≁ ⊥ and test if x is still inhabited
       -- Mark dirty for a delayed inhabitation test
       let vi' = vi{ vi_bot = IsNotBot}
       pure $ markDirty y
-           $ nabla{ nabla_tm_st = ts{ ts_facts = addToUSDFM env y vi' } }
+           $ nabla{ nabla_tm_st = ts{ ts_facts = EG.adjust (const vi') xid env''} }
 
 -- | Record a @x ~/ K@ constraint, e.g. that a particular 'Id' @x@ can't
 -- take the shape of a 'PmAltCon' @K@ in the 'Nabla' and return @Nothing@ if
@@ -769,7 +779,10 @@ hasRequiredTheta _                 = False
 -- See Note [TmState invariants].
 addConCt :: Nabla -> Id -> PmAltCon -> [TyVar] -> [Id] -> MaybeT DsM Nabla
 addConCt nabla at MkNabla{ nabla_tm_st = ts at TmSt{ ts_facts=env } } x alt tvs args = do
-  let vi@(VI _ pos neg bot _) = lookupVarInfo ts x
+  let (xid, env') = EG.represent (Fix $ Const x) env
+  -- ROMES:TODO: Omssions of updates on ts_facts on nabla are fine, but not perfect. Get it consistent
+  -- ROMES:TODO: Also looks like a function on varinfo (adjust)
+  let (vi@(VI _ pos neg bot _), TmSt{ts_facts=env''}) = lookupVarInfo (ts{ts_facts=env'}) x
   -- First try to refute with a negative fact
   guard (not (elemPmAltConSet alt neg))
   -- Then see if any of the other solutions (remember: each of them is an
@@ -788,7 +801,7 @@ addConCt nabla at MkNabla{ nabla_tm_st = ts at TmSt{ ts_facts=env } } x alt tvs args =
     Nothing -> do
       let pos' = PACA alt tvs args : pos
       let nabla_with bot' =
-            nabla{ nabla_tm_st = ts{ts_facts = addToUSDFM env x (vi{vi_pos = pos', vi_bot = bot'})} }
+            nabla{ nabla_tm_st = ts{ts_facts = EG.adjust (const (vi{vi_pos = pos', vi_bot = bot'})) xid env''} }
       -- Do (2) in Note [Coverage checking Newtype matches]
       case (alt, args) of
         (PmAltConLike (RealDataCon dc), [y]) | isNewDataCon dc ->
@@ -817,11 +830,13 @@ equateTys ts us =
 --
 -- See Note [TmState invariants].
 addVarCt :: Nabla -> Id -> Id -> MaybeT DsM Nabla
+-- This is where equality-graphs really come into play.
 addVarCt nabla at MkNabla{ nabla_tm_st = ts at TmSt{ ts_facts = env } } x y =
-  case equateUSDFM env x y of
-    (Nothing,   env') -> pure (nabla{ nabla_tm_st = ts{ ts_facts = env' } })
+  -- ROMES:TODO: equate auxiliary var that finds both vars, and lookups up the domain associated. However, I think we no longer should have Just/Nothing but rather always store emptyVarInfo for new e-nodes
+  -- equate should also update e-graph, basically re-implement "equateUSDFM" in terms of the e-graph, or inline it or so
+  case equate env x y of
     -- Add the constraints we had for x to y
-    (Just vi_x, env') -> do
+    (vi_x, env') -> do
       let nabla_equated = nabla{ nabla_tm_st = ts{ts_facts = env'} }
       -- and then gradually merge every positive fact we have on x into y
       let add_pos nabla (PACA cl tvs args) = addConCt nabla y cl tvs args
@@ -829,6 +844,25 @@ addVarCt nabla at MkNabla{ nabla_tm_st = ts at TmSt{ ts_facts = env } } x y =
       -- Do the same for negative info
       let add_neg nabla nalt = addNotConCt nabla y nalt
       foldlM add_neg nabla_pos (pmAltConSetElems (vi_neg vi_x))
+  where
+    -- @equate env x y@ makes @x@ and @y@ point to the same entry,
+    -- thereby merging @x@'s class with @y@'s.
+    -- If both @x@ and @y@ are in the domain of the map, then @y@'s entry will be
+    -- chosen as the new entry and @x@'s old entry will be returned.
+    --
+    -- Examples in terms of the model (see 'UniqSDFM'):
+    -- >>> equate [] u1 u2 == (Nothing, [({u1,u2}, Nothing)])
+    -- >>> equate [({u1,u3}, Just ele1)] u3 u4 == (Nothing, [({u1,u3,u4}, Just ele1)])
+    -- >>> equate [({u1,u3}, Just ele1)] u4 u3 == (Nothing, [({u1,u3,u4}, Just ele1)])
+    -- >>> equate [({u1,u3}, Just ele1), ({u2}, Just ele2)] u3 u2 == (Just ele1, [({u2,u1,u3}, Just ele2)])
+    equate :: EGraph VarInfo (Const Id) -> Id -> Id -> (VarInfo, EGraph VarInfo (Const Id))
+    equate eg x y = do
+      let (xid, eg')  = EG.represent (Fix $ Const x) eg
+          (yid, eg'') = EG.represent (Fix $ Const y) eg'
+          (_, eg''')  = EG.merge xid yid eg''
+       in (EG.lookup xid eg', eg''')
+       -- Note: lookup in eg', because it's before the merge.
+
 
 -- | Inspects a 'PmCoreCt' @let x = e@ by recording constraints for @x@ based
 -- on the shape of the 'CoreExpr' @e at . Examples:
@@ -955,6 +989,7 @@ modifyT f = StateT $ fmap ((,) ()) . f
 -- there weren't any such constraints.
 representCoreExpr :: Nabla -> CoreExpr -> DsM (Id, Nabla)
 representCoreExpr nabla at MkNabla{ nabla_tm_st = ts at TmSt{ ts_reps = reps } } e
+-- ROMES:TODO: Represent
   | Just rep <- lookupCoreMap reps key = pure (rep, nabla)
   | otherwise = do
       rep <- mkPmId (exprType e)
@@ -1280,13 +1315,15 @@ traverseDirty f ts at TmSt{ts_facts = env, ts_dirty = dirty} =
   go (uniqDSetToList dirty) env
   where
     go []     env  = pure ts{ts_facts=env}
-    go (x:xs) !env = do
-      vi' <- f (lookupVarInfo ts x)
-      go xs (addToUSDFM env x vi')
+    go (x:xs) !_env = do
+      let (vi, TmSt{ts_facts=env'}) = lookupVarInfo ts x
+      vi' <- f vi -- todo: lookupvar should really return the xid
+      let (xid,env'') = EG.represent (Fix $ Const x) env' -- ROMES:TODO: really, a helper functoin for representing Ids
+      go xs (EG.adjust (const vi') xid env'')
 
 traverseAll :: Monad m => (VarInfo -> m VarInfo) -> TmState -> m TmState
 traverseAll f ts at TmSt{ts_facts = env} = do
-  env' <- traverseUSDFM f env
+  env' <- EG.traverseAnalysisData f env
   pure ts{ts_facts = env'}
 
 -- | Makes sure the given 'Nabla' is still inhabited, by trying to instantiate
@@ -1321,6 +1358,9 @@ inhabitationTest fuel  old_ty_st nabla at MkNabla{ nabla_tm_st = ts } = {-# SCC "in
           instantiate (fuel-1) nabla_not_dirty vi
         _ -> pure vi
 
+-- ROMES:TODO: The dirty shortcutting bit seems like the bookeeping on nodes to
+-- upward merge, perhaps we can rid of it too
+
 -- | Checks whether the given 'VarInfo' needs to be tested for inhabitants.
 -- Returns `False` when we can skip the inhabitation test, presuming it would
 -- say "yes" anyway. See Note [Shortcutting the inhabitation test].
@@ -1393,7 +1433,7 @@ instCompleteSets fuel nabla vi = {-# SCC "instCompleteSets" #-} do
   let x = vi_id vi
   (rcm, nabla) <- lift (addNormalisedTypeMatches nabla x)
   nabla <- foldM (\nabla cls -> instCompleteSet fuel nabla x cls) nabla (getRcm rcm)
-  pure (lookupVarInfo (nabla_tm_st nabla) x)
+  pure (fst $ lookupVarInfo (nabla_tm_st nabla) x)
 
 anyConLikeSolution :: (ConLike -> Bool) -> [PmAltConApp] -> Bool
 anyConLikeSolution p = any (go . paca_con)
@@ -1422,7 +1462,7 @@ instCompleteSet fuel nabla x cs
   | otherwise
   = {-# SCC "instCompleteSet" #-} go nabla (sorted_candidates cs)
   where
-    vi = lookupVarInfo (nabla_tm_st nabla) x
+    (vi, _env') = lookupVarInfo (nabla_tm_st nabla) x
 
     sorted_candidates :: CompleteMatch -> [ConLike]
     sorted_candidates cm
@@ -1911,7 +1951,7 @@ generateInhabitingPatterns _    _      0 _     = pure []
 generateInhabitingPatterns _    []     _ nabla = pure [nabla]
 generateInhabitingPatterns mode (x:xs) n nabla = do
   tracePm "generateInhabitingPatterns" (ppr mode <+> ppr n <+> ppr (x:xs) $$ ppr nabla)
-  let VI _ pos neg _ _ = lookupVarInfo (nabla_tm_st nabla) x
+  let (VI _ pos neg _ _, _env') = lookupVarInfo (nabla_tm_st nabla) x
   case pos of
     _:_ -> do
       -- Example for multiple solutions (must involve a PatSyn):
@@ -1949,7 +1989,7 @@ generateInhabitingPatterns mode (x:xs) n nabla = do
       case mb_stuff of
         Nothing -> pure []
         Just (y, newty_nabla) -> do
-          let vi = lookupVarInfo (nabla_tm_st newty_nabla) y
+          let (vi, _env) = lookupVarInfo (nabla_tm_st newty_nabla) y
           env <- dsGetFamInstEnvs
           rcm <- case splitReprTyConApp_maybe env rep_ty of
             Just (tc, _, _) -> addTyConMatches tc (vi_rcm vi)


=====================================
compiler/GHC/HsToCore/Pmc/Solver/Types.hs
=====================================
@@ -1,3 +1,6 @@
+{-# OPTIONS_GHC -Wno-orphans #-} -- Oh god, ROMES:TODO
+{-# LANGUAGE FlexibleInstances   #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE ApplicativeDo       #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE ViewPatterns        #-}
@@ -44,7 +47,6 @@ import GHC.Data.FastString
 import GHC.Types.Id
 import GHC.Types.Var.Set
 import GHC.Types.Unique.DSet
-import GHC.Types.Unique.SDFM
 import GHC.Types.Name
 import GHC.Core.DataCon
 import GHC.Core.ConLike
@@ -75,6 +77,12 @@ import Data.Ratio
 import GHC.Real (Ratio(..))
 import qualified Data.Semigroup as Semi
 
+import Data.Functor.Const
+import Data.Equality.Analysis (Analysis(..))
+import Data.Equality.Graph (EGraph)
+import Data.Equality.Utils (Fix(..))
+import qualified Data.Equality.Graph as EG
+
 -- import GHC.Driver.Ppr
 
 --
@@ -138,18 +146,23 @@ initTyState = TySt 0 emptyInert
 -- See Note [TmState invariants] in "GHC.HsToCore.Pmc.Solver".
 data TmState
   = TmSt
-  { ts_facts :: !(UniqSDFM Id VarInfo)
+  { ts_facts :: !(EGraph VarInfo (Const Id)) -- ROMES:TODO: The Id here is because we don't merge yet ts_reps into the e-graph; so we simply have Ids as E-nodes
   -- ^ Facts about term variables. Deterministic env, so that we generate
   -- deterministic error messages.
+-- ROMES:TODO: ts_reps perhaps too as well... but a first iteration should map CoreMap to ClassId, and replace just ts_facts.
   , ts_reps  :: !(CoreMap Id)
   -- ^ An environment for looking up whether we already encountered semantically
   -- equivalent expressions that we want to represent by the same 'Id'
   -- representative.
+-- ROMES:TODO: ts_dirty looks a bit to me like the bookeeping needed to know
+-- which nodes to upward merge, perhaps we can get rid of it too.
   , ts_dirty :: !DIdSet
   -- ^ Which 'VarInfo' needs to be checked for inhabitants because of new
   -- negative constraints (e.g. @x ≁ ⊥@ or @x ≁ K@).
   }
 
+instance EG.Language (Const Id)
+
 -- | Information about an 'Id'. Stores positive ('vi_pos') facts, like @x ~ Just 42@,
 -- and negative ('vi_neg') facts, like "x is not (:)".
 -- Also caches the type ('vi_ty'), the 'ResidualCompleteMatches' of a COMPLETE set
@@ -202,6 +215,18 @@ data VarInfo
   -- to recognise completion of a COMPLETE set efficiently for large enums.
   }
 
+-- | This instance is seriously wrong for general purpose, it's just required for instancing Analysis.
+-- There ought to be a better way.
+instance Eq VarInfo where
+  (==) _ _ = False
+instance Analysis VarInfo (Const Id) where
+  {-# INLINE makeA #-}
+  {-# INLINE joinA #-}
+  makeA (Const id) = emptyVarInfo id
+  -- romes: so currently, variables are joined in 'addVarCt' manually by getting the old value of $x$ and assuming the value of $y$ was chosen.
+  -- That's obviously bad now, it'd be much more clearer to do it here. It's just the nabla threading that's more trouble
+  joinA _a b = b
+
 data PmAltConApp
   = PACA
   { paca_con :: !PmAltCon
@@ -227,7 +252,7 @@ instance Outputable BotInfo where
 
 -- | Not user-facing.
 instance Outputable TmState where
-  ppr (TmSt state reps dirty) = ppr state $$ ppr reps $$ ppr dirty
+  ppr (TmSt _state reps dirty) = text "<e-graph>" $$ ppr reps $$ ppr dirty
 
 -- | Not user-facing.
 instance Outputable VarInfo where
@@ -248,7 +273,7 @@ instance Outputable VarInfo where
 
 -- | Initial state of the term oracle.
 initTmState :: TmState
-initTmState = TmSt emptyUSDFM emptyCoreMap emptyDVarSet
+initTmState = TmSt EG.emptyEGraph emptyCoreMap emptyDVarSet
 
 -- | A data type that caches for the 'VarInfo' of @x@ the results of querying
 -- 'dsGetCompleteMatches' and then striking out all occurrences of @K@ for
@@ -300,9 +325,17 @@ emptyVarInfo x
   , vi_rcm = emptyRCM
   }
 
-lookupVarInfo :: TmState -> Id -> VarInfo
--- (lookupVarInfo tms x) tells what we know about 'x'
-lookupVarInfo (TmSt env _ _) x = fromMaybe (emptyVarInfo x) (lookupUSDFM env x)
+-- | @lookupVarInfo tms x@ tells what we know about 'x'
+--- romes: TODO: lookupVarInfo should also return the ClassId the Id was represented in..., that'd make things better
+lookupVarInfo :: TmState -> Id -> (VarInfo, TmState)
+lookupVarInfo tm@(TmSt env _ _) x
+  -- = fromMaybe (emptyVarInfo x) (lookupUSDFM env x)
+  -- ROMES:TODO Kind of an issue here, we could have a lookup operation on e-graphs but it'd be good to make it faster
+  -- We will want to assume every Id is mapped to VarInfo, with emptyVarInfo as the default rather than Maybe
+  -- I'm just unsure if the Id always exists or not.
+  -- Then again this shouldn't be Id, but rather ClassId§
+  = let (i,env') = EG.represent (Fix $ Const x) env
+     in (EG.lookup i env', tm{ts_facts=env'})
 
 -- | Like @lookupVarInfo ts x@, but @lookupVarInfo ts x = (y, vi)@ also looks
 -- through newtype constructors. We have @x ~ N1 (... (Nk y))@ such that the
@@ -314,22 +347,27 @@ lookupVarInfo (TmSt env _ _) x = fromMaybe (emptyVarInfo x) (lookupUSDFM env x)
 -- modulo type normalisation!
 --
 -- See also Note [Coverage checking Newtype matches] in GHC.HsToCore.Pmc.Solver.
-lookupVarInfoNT :: TmState -> Id -> (Id, VarInfo)
+lookupVarInfoNT :: TmState -> Id -> (Id, VarInfo, TmState)
 lookupVarInfoNT ts x = case lookupVarInfo ts x of
-  VI{ vi_pos = as_newtype -> Just y } -> lookupVarInfoNT ts y
-  res                                 -> (x, res)
+  (VI{ vi_pos = as_newtype -> Just y },ts')
+    -> lookupVarInfoNT ts' y
+  (res,ts')
+    -> (x, res, ts')
   where
     as_newtype = listToMaybe . mapMaybe go
     go PACA{paca_con = PmAltConLike (RealDataCon dc), paca_ids = [y]}
       | isNewDataCon dc = Just y
     go _                = Nothing
 
+-- ROMES:TODO: What does this do, how to update?
 trvVarInfo :: Functor f => (VarInfo -> f (a, VarInfo)) -> Nabla -> Id -> f (a, Nabla)
-trvVarInfo f nabla at MkNabla{ nabla_tm_st = ts at TmSt{ts_facts = env} } x
-  = set_vi <$> f (lookupVarInfo ts x)
+trvVarInfo f nabla at MkNabla{ nabla_tm_st = ts at TmSt{ts_facts = _env} } x
+  -- ROMES:TODO: adjust on the EG, instead of fetching? the (a,) bit is not trivial
+  = let (vi, ts'@TmSt{ts_facts = env'}) = lookupVarInfo ts x
+        set_vi (a, vi') =
+          (a, nabla{ nabla_tm_st = ts'{ ts_facts = let (i,env'') = EG.represent (Fix $ Const $ vi_id vi') env' in EG.adjust (const vi') i env'' } })
+     in set_vi <$> f vi
   where
-    set_vi (a, vi') =
-      (a, nabla{ nabla_tm_st = ts{ ts_facts = addToUSDFM env (vi_id vi') vi' } })
 
 ------------------------------------------------
 -- * Exported utility functions querying 'Nabla'
@@ -338,7 +376,9 @@ lookupRefuts :: Nabla -> Id -> [PmAltCon]
 -- Unfortunately we need the extra bit of polymorphism and the unfortunate
 -- duplication of lookupVarInfo here.
 lookupRefuts MkNabla{ nabla_tm_st = ts } x =
-  pmAltConSetElems $ vi_neg $ lookupVarInfo ts x
+  -- bimap (pmAltConSetElems . vi_neg) (\ts' -> nabla{nabla_tm_st=ts'}) $ lookupVarInfo ts x
+  -- ROMES:TODO: It's a bit unfortunate we forget the representation of $x$, but OK
+  pmAltConSetElems $ vi_neg $ fst $ lookupVarInfo ts x
 
 isDataConSolution :: PmAltConApp -> Bool
 isDataConSolution PACA{paca_con = PmAltConLike (RealDataCon _)} = True
@@ -347,11 +387,12 @@ isDataConSolution _                                             = False
 -- @lookupSolution nabla x@ picks a single solution ('vi_pos') of @x@ from
 -- possibly many, preferring 'RealDataCon' solutions whenever possible.
 lookupSolution :: Nabla -> Id -> Maybe PmAltConApp
-lookupSolution nabla x = case vi_pos (lookupVarInfo (nabla_tm_st nabla) x) of
-  []                                         -> Nothing
-  pos@(x:_)
-    | Just sol <- find isDataConSolution pos -> Just sol
-    | otherwise                              -> Just x
+lookupSolution nabla x = case vi_pos $ fst $ lookupVarInfo (nabla_tm_st nabla) x of
+  -- ROMES:TODO: It's a bit unfortunate we forget the representation of $x$, but OK
+    []                                         -> Nothing
+    pos@(x:_)
+      | Just sol <- find isDataConSolution pos -> Just sol
+      | otherwise                              -> Just x
 
 --------------------------------------------------------------------------------
 -- The rest is just providing an IR for (overloaded!) literals and AltCons that


=====================================
compiler/GHC/Types/Unique/SDFM.hs
=====================================
@@ -82,6 +82,7 @@ lookupUSDFM usdfm x = snd (lookupReprAndEntryUSDFM usdfm x)
 -- >>> equateUSDFM [({u1,u3}, Just ele1)] u3 u4 == (Nothing, [({u1,u3,u4}, Just ele1)])
 -- >>> equateUSDFM [({u1,u3}, Just ele1)] u4 u3 == (Nothing, [({u1,u3,u4}, Just ele1)])
 -- >>> equateUSDFM [({u1,u3}, Just ele1), ({u2}, Just ele2)] u3 u2 == (Just ele1, [({u2,u1,u3}, Just ele2)])
+-- ROMES:TODO: Are all USDFM functions just for the PMC Nabla TM?
 equateUSDFM
   :: Uniquable key => UniqSDFM key ele -> key -> key -> (Maybe ele, UniqSDFM key ele)
 equateUSDFM usdfm@(USDFM env) x y =


=====================================
libraries/hegg
=====================================
@@ -1 +1 @@
-Subproject commit ec0f55ce20bee83738ed29dcd5fb4159f9e90df4
+Subproject commit 67453e7735fdfc9e6212c607ba3ed855d525d349



View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/d0575935ff754d76fd358ba6229e7fc6c798801b

-- 
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/d0575935ff754d76fd358ba6229e7fc6c798801b
You're receiving this email because of your account on gitlab.haskell.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20230611/48d9cee1/attachment-0001.html>


More information about the ghc-commits mailing list