[Git][ghc/ghc][wip/marge_bot_batch_merge_job] 5 commits: Remove Ptr example from roles docs

Marge Bot (@marge-bot) gitlab at gitlab.haskell.org
Tue Aug 22 13:11:19 UTC 2023



Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC


Commits:
76a4d11b by Jaro Reinders at 2023-08-22T08:08:13-04:00
Remove Ptr example from roles docs

- - - - -
069729d3 by Bryan Richter at 2023-08-22T08:08:49-04:00
Guard against duplicate pipelines in forks

- - - - -
f861423b by Rune K. Svendsen at 2023-08-22T08:09:35-04:00
dump-decls: fix "Ambiguous module name"-error

Fixes errors of the following kind, which happen when dump-decls is run on a package that contains a module name that clashes with that of another package.

```
dump-decls: <no location info>: error:
    Ambiguous module name `System.Console.ANSI.Types':
      it was found in multiple packages:
      ansi-terminal-0.11.4 ansi-terminal-types-0.11.5
```

- - - - -
cc742a0f by Krzysztof Gogolewski at 2023-08-22T09:11:06-04:00
Fix MultiWayIf linearity checking (#23814)

Co-authored-by: Thomas BAGREL <thomas.bagrel at tweag.io>

- - - - -
d5c835ca by konsumlamm at 2023-08-22T09:11:12-04:00
Update `Control.Concurrent.*` documentation

- - - - -


14 changed files:

- .gitlab-ci.yml
- compiler/GHC/Tc/Gen/Expr.hs
- docs/users_guide/exts/roles.rst
- libraries/base/Control/Concurrent/Chan.hs
- libraries/base/Control/Concurrent/MVar.hs
- libraries/base/Control/Concurrent/QSem.hs
- libraries/base/Control/Concurrent/QSemN.hs
- libraries/base/GHC/MVar.hs
- + testsuite/tests/linear/should_compile/T23814.hs
- testsuite/tests/linear/should_compile/all.T
- + testsuite/tests/linear/should_fail/T23814fail.hs
- + testsuite/tests/linear/should_fail/T23814fail.stderr
- testsuite/tests/linear/should_fail/all.T
- utils/dump-decls/Main.hs


Changes:

=====================================
.gitlab-ci.yml
=====================================
@@ -57,26 +57,45 @@ stages:
 # Note [The CI Story]
 # ~~~~~~~~~~~~~~~~~~~
 #
-# There are two different types of pipelines:
+# There are a few different types of pipelines. Among them:
 #
-#  - marge-bot merges to `master`. Here we perform an exhaustive validation
+# 1. marge-bot merges to `master`. Here we perform an exhaustive validation
 #    across all of the platforms which we support. In addition, we push
 #    performance metric notes upstream, providing a persistent record of the
 #    performance characteristics of the compiler.
 #
-#  - merge requests. Here we perform a slightly less exhaustive battery of
+# 2. merge requests. Here we perform a slightly less exhaustive battery of
 #    testing. Namely we omit some configurations (e.g. the unregisterised job).
 #    These use the merge request's base commit for performance metric
 #    comparisons.
 #
-
+# These and other pipelines are defined implicitly by the rules of individual
+# jobs.
+#
+# At the top level, however, we can declare that pipelines (of whatever type)
+# only run when:
+#
+# 1. Processing a merge request (as mentioned above)
+#
+# 2. Processing a tag
+#
+# 3. Pushing to master on the root ghc/ghc repo (as mentioned above)
+#
+# 4. Pushing to a release branch on the root ghc/ghc repo
+#
+# 5. Somebody manually triggers a pipeline from the GitLab UI
+#
+# In particular, note that pipelines don't automatically run just when changes
+# are pushed to a feature branch.
 workflow:
-  # N.B. Don't run on wip/ branches, instead on run on merge requests.
   rules:
     - if: $CI_MERGE_REQUEST_ID
     - if: $CI_COMMIT_TAG
-    - if: '$CI_COMMIT_BRANCH == "master"'
-    - if: '$CI_COMMIT_BRANCH =~ /ghc-[0-9]+\.[0-9]+/'
+    # N.B.: If we weren't explicit about CI_PROJECT_ID, the following rule would
+    # cause a duplicate pipeline for merge requests coming from the master
+    # branch of a fork.
+    - if: $CI_PROJECT_ID == "1" && $CI_COMMIT_BRANCH == "master"
+    - if: $CI_PROJECT_ID == "1" && $CI_COMMIT_BRANCH =~ /ghc-[0-9]+\.[0-9]+/
     - if: '$CI_PIPELINE_SOURCE == "web"'
 
 # which versions of GHC to allow bootstrap with


=====================================
compiler/GHC/Tc/Gen/Expr.hs
=====================================
@@ -396,9 +396,34 @@ tcExpr (HsIf x pred b1 b2) res_ty
        ; tcEmitBindingUsage (supUE u1 u2)
        ; return (HsIf x pred' b1' b2') }
 
+{-
+Note [MultiWayIf linearity checking]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Suppose we'd like to compute the usage environment for
+
+if | b1 -> e1
+   | b2 -> e2
+   | otherwise -> e3
+
+and let u1, u2, v1, v2, v3 denote the usage env for b1, b2, e1, e2, e3
+respectively.
+
+Since a multi-way if is mere sugar for nested if expressions, the usage
+environment should ideally be u1 + sup(v1, u2 + sup(v2, v3)).
+However, currently we don't support linear guards (#19193). All variables
+used in guards from u1 and u2 will have multiplicity Many.
+But in that case, we have equality u1 + sup(x,y) = sup(u1 + x, y),
+                      and likewise u2 + sup(x,y) = sup(u2 + x, y) for any x,y.
+Using this identity, we can just compute sup(u1 + v1, u2 + v2, v3) instead.
+This is simple to do, since we get u_i + v_i directly from tcGRHS.
+If we add linear guards, this code will have to be revisited.
+Not using 'sup' caused #23814.
+-}
+
 tcExpr (HsMultiIf _ alts) res_ty
-  = do { alts' <- mapM (wrapLocMA $ tcGRHS match_ctxt res_ty) alts
+  = do { (ues, alts') <- mapAndUnzipM (\alt -> tcCollectingUsage $ wrapLocMA (tcGRHS match_ctxt res_ty) alt) alts
        ; res_ty <- readExpType res_ty
+       ; tcEmitBindingUsage (supUEs ues)  -- See Note [MultiWayIf linearity checking]
        ; return (HsMultiIf res_ty alts') }
   where match_ctxt = MC { mc_what = IfAlt, mc_body = tcBody }
 


=====================================
docs/users_guide/exts/roles.rst
=====================================
@@ -155,26 +155,7 @@ Role annotations
     Allow role annotation syntax.
 
 Sometimes the programmer wants to constrain the inference process. For
-example, the base library contains the following definition: ::
-
-      data Ptr a = Ptr Addr#
-
-The idea is that ``a`` should really be a representational parameter,
-but role inference assigns it to phantom. This makes some level of
-sense: a pointer to an ``Int`` really is representationally the same as
-a pointer to a ``Bool``. But, that's not at all how we want to use
-``Ptr``\ s! So, we want to be able to say ::
-
-      type role Ptr representational
-      data Ptr a = Ptr Addr#
-
-The ``type role`` (enabled with :extension:`RoleAnnotations`) declaration
-forces the parameter ``a`` to be at role representational, not role
-phantom. GHC then checks the user-supplied roles to make sure they don't
-break any promises. It would be bad, for example, if the user could make
-``BadIdea``\'s role be representational.
-
-As another example, we can consider a type ``Set a`` that represents a
+example, we can consider a type ``Set a`` that represents a
 set of data, ordered according to ``a``\'s ``Ord`` instance. While it
 would generally be type-safe to consider ``a`` to be at role
 representational, it is possible that a ``newtype`` and its base type


=====================================
libraries/base/Control/Concurrent/Chan.hs
=====================================
@@ -13,9 +13,9 @@
 --
 -- Unbounded channels.
 --
--- The channels are implemented with @MVar at s and therefore inherit all the
+-- The channels are implemented with 'Control.Concurrent.MVar's and therefore inherit all the
 -- caveats that apply to @MVar at s (possibility of races, deadlocks etc). The
--- stm (software transactional memory) library has a more robust implementation
+-- @stm@ (software transactional memory) library has a more robust implementation
 -- of channels called @TChan at s.
 --
 -----------------------------------------------------------------------------
@@ -43,7 +43,7 @@ import Control.Exception (mask_)
 #define _UPK_(x) {-# UNPACK #-} !(x)
 
 -- A channel is represented by two @MVar at s keeping track of the two ends
--- of the channel contents,i.e.,  the read- and write ends. Empty @MVar at s
+-- of the channel contents, i.e., the read- and write ends. Empty @MVar at s
 -- are used to handle consumers trying to read from an empty channel.
 
 -- |'Chan' is an abstract type representing an unbounded FIFO channel.
@@ -59,13 +59,13 @@ data ChItem a = ChItem a _UPK_(Stream a)
   -- although it leads to higher allocation, the channel data takes up
   -- less space and is therefore quicker to GC.
 
--- See the Concurrent Haskell paper for a diagram explaining the
+-- See the Concurrent Haskell paper for a diagram explaining
 -- how the different channel operations proceed.
 
 -- @newChan@ sets up the read and write end of a channel by initialising
 -- these two @MVar at s with an empty @MVar at .
 
--- |Build and returns a new instance of 'Chan'.
+-- |Build and return a new instance of 'Chan'.
 newChan :: IO (Chan a)
 newChan = do
    hole  <- newEmptyMVar
@@ -113,7 +113,7 @@ readChan (Chan readVar _) =
     return (new_read_end, val)
 
 -- |Duplicate a 'Chan': the duplicate channel begins empty, but data written to
--- either channel from then on will be available from both.  Hence this creates
+-- either channel from then on will be available from both. Hence this creates
 -- a kind of broadcast channel, where data written by anyone is seen by
 -- everyone else.
 --


=====================================
libraries/base/Control/Concurrent/MVar.hs
=====================================
@@ -11,7 +11,7 @@
 -- Stability   :  stable
 -- Portability :  non-portable (concurrency)
 --
--- An @'MVar' t@ is mutable location that is either empty or contains a
+-- An @'MVar' t@ is a mutable location that is either empty or contains a
 -- value of type @t at .  It has two fundamental operations: 'putMVar'
 -- which fills an 'MVar' if it is empty and blocks otherwise, and
 -- 'takeMVar' which empties an 'MVar' if it is full and blocks
@@ -25,7 +25,7 @@
 --      wait and signal.
 --
 -- They were introduced in the paper
--- <https://www.haskell.org/ghc/docs/papers/concurrent-haskell.ps.gz "Concurrent Haskell">
+-- ["Concurrent Haskell"](https://www.microsoft.com/en-us/research/wp-content/uploads/1996/01/concurrent-haskell.pdf)
 -- by Simon Peyton Jones, Andrew Gordon and Sigbjorn Finne, though
 -- some details of their implementation have since then changed (in
 -- particular, a put on a full 'MVar' used to error, but now merely
@@ -35,7 +35,7 @@
 --
 -- 'MVar's offer more flexibility than 'Data.IORef.IORef's, but less flexibility
 -- than 'GHC.Conc.STM'.  They are appropriate for building synchronization
--- primitives and performing simple interthread communication; however
+-- primitives and performing simple inter-thread communication; however
 -- they are very simple and susceptible to race conditions, deadlocks or
 -- uncaught exceptions.  Do not use them if you need to perform larger
 -- atomic operations such as reading from multiple variables: use 'GHC.Conc.STM'
@@ -54,8 +54,8 @@
 -- No thread can be blocked indefinitely on an 'MVar' unless another
 -- thread holds that 'MVar' indefinitely.  One usual implementation of
 -- this fairness guarantee is that threads blocked on an 'MVar' are
--- served in a first-in-first-out fashion, but this is not guaranteed
--- in the semantics.
+-- served in a first-in-first-out fashion (this is what GHC does),
+-- but this is not guaranteed in the semantics.
 --
 -- === Gotchas
 --
@@ -64,7 +64,7 @@
 -- 'MVar', it will be evaluated by the thread that consumes it, not the
 -- thread that produced it.  Be sure to 'evaluate' values to be placed
 -- in an 'MVar' to the appropriate normal form, or utilize a strict
--- MVar provided by the strict-concurrency package.
+-- @MVar@ provided by the [strict-concurrency](https://hackage.haskell.org/package/strict-concurrency) package.
 --
 -- === Ordering
 --
@@ -89,33 +89,33 @@
 -- reader has not read yet, and empty otherwise.
 --
 -- @
---     data SkipChan a = SkipChan (MVar (a, [MVar ()])) (MVar ())
+-- data SkipChan a = SkipChan (MVar (a, [MVar ()])) (MVar ())
 --
---     newSkipChan :: IO (SkipChan a)
---     newSkipChan = do
---         sem <- newEmptyMVar
---         main <- newMVar (undefined, [sem])
---         return (SkipChan main sem)
+-- newSkipChan :: IO (SkipChan a)
+-- newSkipChan = do
+--     sem <- newEmptyMVar
+--     main <- newMVar (undefined, [sem])
+--     return (SkipChan main sem)
 --
---     putSkipChan :: SkipChan a -> a -> IO ()
---     putSkipChan (SkipChan main _) v = do
---         (_, sems) <- takeMVar main
---         putMVar main (v, [])
---         mapM_ (\sem -> putMVar sem ()) sems
+-- putSkipChan :: SkipChan a -> a -> IO ()
+-- putSkipChan (SkipChan main _) v = do
+--     (_, sems) <- takeMVar main
+--     putMVar main (v, [])
+--     mapM_ (\\sem -> putMVar sem ()) sems
 --
---     getSkipChan :: SkipChan a -> IO a
---     getSkipChan (SkipChan main sem) = do
---         takeMVar sem
---         (v, sems) <- takeMVar main
---         putMVar main (v, sem:sems)
---         return v
+-- getSkipChan :: SkipChan a -> IO a
+-- getSkipChan (SkipChan main sem) = do
+--     takeMVar sem
+--     (v, sems) <- takeMVar main
+--     putMVar main (v, sem : sems)
+--     return v
 --
---     dupSkipChan :: SkipChan a -> IO (SkipChan a)
---     dupSkipChan (SkipChan main _) = do
---         sem <- newEmptyMVar
---         (v, sems) <- takeMVar main
---         putMVar main (v, sem:sems)
---         return (SkipChan main sem)
+-- dupSkipChan :: SkipChan a -> IO (SkipChan a)
+-- dupSkipChan (SkipChan main _) = do
+--     sem <- newEmptyMVar
+--     (v, sems) <- takeMVar main
+--     putMVar main (v, sem : sems)
+--     return (SkipChan main sem)
 -- @
 --
 -- This example was adapted from the original Concurrent Haskell paper.
@@ -186,7 +186,7 @@ swapMVar mvar new =
 -}
 {-# INLINE withMVar #-}
 -- inlining has been reported to have dramatic effects; see
--- http://www.haskell.org//pipermail/haskell/2006-May/017907.html
+-- https://mail.haskell.org/pipermail/haskell/2006-May/017907.html
 withMVar :: MVar a -> (a -> IO b) -> IO b
 withMVar m io =
   mask $ \restore -> do
@@ -274,7 +274,7 @@ addMVarFinalizer :: MVar a -> IO () -> IO ()
 addMVarFinalizer = GHC.MVar.addMVarFinalizer
 
 -- | Make a 'Weak' pointer to an 'MVar', using the second argument as
--- a finalizer to run when 'MVar' is garbage-collected
+-- a finalizer to run when the 'MVar' is garbage-collected
 --
 -- @since 4.6.0.0
 mkWeakMVar :: MVar a -> IO () -> IO (Weak (MVar a))


=====================================
libraries/base/Control/Concurrent/QSem.hs
=====================================
@@ -34,7 +34,7 @@ import Data.Maybe
 --
 -- The pattern
 --
--- >   bracket_ waitQSem signalQSem (...)
+-- > bracket_ waitQSem signalQSem (...)
 --
 -- is safe; it never loses a unit of the resource.
 --
@@ -67,7 +67,7 @@ newQSem initial
       sem <- newMVar (initial, [], [])
       return (QSem sem)
 
--- |Wait for a unit to become available
+-- |Wait for a unit to become available.
 waitQSem :: QSem -> IO ()
 waitQSem (QSem m) =
   mask_ $ do
@@ -91,7 +91,7 @@ waitQSem (QSem m) =
                             else do putMVar b (); return (i,b1,b2)
                    putMVar m r')
 
--- |Signal that a unit of the 'QSem' is available
+-- |Signal that a unit of the 'QSem' is available.
 signalQSem :: QSem -> IO ()
 signalQSem (QSem m) =
   uninterruptibleMask_ $ do -- Note [signal uninterruptible]


=====================================
libraries/base/Control/Concurrent/QSemN.hs
=====================================
@@ -32,12 +32,12 @@ import Data.IORef (IORef, newIORef, atomicModifyIORef)
 import System.IO.Unsafe (unsafePerformIO)
 
 -- | 'QSemN' is a quantity semaphore in which the resource is acquired
--- and released in units of one. It provides guaranteed FIFO ordering
+-- and released in arbitrary amounts. It provides guaranteed FIFO ordering
 -- for satisfying blocked `waitQSemN` calls.
 --
 -- The pattern
 --
--- >   bracket_ (waitQSemN n) (signalQSemN n) (...)
+-- > bracket_ (waitQSemN n) (signalQSemN n) (...)
 --
 -- is safe; it never loses any of the resource.
 --
@@ -71,7 +71,7 @@ newQSemN initial
 -- An unboxed version of Maybe (MVar a)
 data MaybeMV a = JustMV !(MVar a) | NothingMV
 
--- |Wait for the specified quantity to become available
+-- |Wait for the specified quantity to become available.
 waitQSemN :: QSemN -> Int -> IO ()
 -- We need to mask here. Once we've enqueued our MVar, we need
 -- to be sure to wait for it. Otherwise, we could lose our


=====================================
libraries/base/GHC/MVar.hs
=====================================
@@ -42,9 +42,11 @@ as a box, which may be empty or full.
 -}
 
 -- pull in Eq (Mvar a) too, to avoid GHC.Conc being an orphan-instance module
--- | @since 4.1.0.0
+-- | Compares the underlying pointers.
+--
+-- @since 4.1.0.0
 instance Eq (MVar a) where
-        (MVar mvar1#) == (MVar mvar2#) = isTrue# (sameMVar# mvar1# mvar2#)
+    (MVar mvar1#) == (MVar mvar2#) = isTrue# (sameMVar# mvar1# mvar2#)
 
 {-
 M-Vars are rendezvous points for concurrent threads.  They begin
@@ -66,9 +68,9 @@ newEmptyMVar = IO $ \ s# ->
 
 -- |Create an 'MVar' which contains the supplied value.
 newMVar :: a -> IO (MVar a)
-newMVar value =
-    newEmptyMVar        >>= \ mvar ->
-    putMVar mvar value  >>
+newMVar value = do
+    mvar <- newEmptyMVar
+    putMVar mvar value
     return mvar
 
 -- |Return the contents of the 'MVar'.  If the 'MVar' is currently
@@ -95,6 +97,7 @@ takeMVar (MVar mvar#) = IO $ \ s# -> takeMVar# mvar# s#
 --
 -- 'readMVar' is multiple-wakeup, so when multiple readers are
 -- blocked on an 'MVar', all of them are woken up at the same time.
+-- The runtime guarantees that all woken threads complete their 'readMVar' operation.
 --
 -- /Compatibility note:/ Prior to base 4.7, 'readMVar' was a combination
 -- of 'takeMVar' and 'putMVar'.  This mean that in the presence of
@@ -104,12 +107,12 @@ takeMVar (MVar mvar#) = IO $ \ s# -> takeMVar# mvar# s#
 -- can be recovered by implementing 'readMVar as follows:
 --
 -- @
---  readMVar :: MVar a -> IO a
---  readMVar m =
---    mask_ $ do
---      a <- takeMVar m
---      putMVar m a
---      return a
+-- readMVar :: MVar a -> IO a
+-- readMVar m =
+--   mask_ $ do
+--     a <- takeMVar m
+--     putMVar m a
+--     return a
 -- @
 readMVar :: MVar a -> IO a
 readMVar (MVar mvar#) = IO $ \ s# -> readMVar# mvar# s#


=====================================
testsuite/tests/linear/should_compile/T23814.hs
=====================================
@@ -0,0 +1,17 @@
+{-# LANGUAGE LinearTypes #-}
+{-# LANGUAGE MultiWayIf #-}
+
+module T23814 where
+
+f :: Bool -> Int %1 -> Int
+f b x =
+  if
+    | b -> x
+    | otherwise -> x
+
+g :: Bool -> Bool -> Int %1 -> Int %1 -> (Int, Int)
+g b c x y =
+  if
+    | b -> (x,y)
+    | c -> (y,x)
+    | otherwise -> (x,y)


=====================================
testsuite/tests/linear/should_compile/all.T
=====================================
@@ -42,3 +42,4 @@ test('T20023', normal, compile, [''])
 test('T22546', normal, compile, [''])
 test('T23025', normal, compile, ['-dlinear-core-lint'])
 test('LinearRecUpd', normal, compile, [''])
+test('T23814', normal, compile, [''])


=====================================
testsuite/tests/linear/should_fail/T23814fail.hs
=====================================
@@ -0,0 +1,17 @@
+{-# LANGUAGE LinearTypes #-}
+{-# LANGUAGE MultiWayIf #-}
+
+module T23814fail where
+
+f' :: Bool -> Int %1 -> Int
+f' b x =
+  if
+    | b -> x
+    | otherwise -> 0
+
+g' :: Bool -> Bool -> Int %1 -> Int
+g' b c x =
+   if
+     | b -> x
+     | c -> 0
+     | otherwise -> 0


=====================================
testsuite/tests/linear/should_fail/T23814fail.stderr
=====================================
@@ -0,0 +1,17 @@
+
+T23814fail.hs:7:6: error: [GHC-18872]
+    • Couldn't match type ‘Many’ with ‘One’
+        arising from multiplicity of ‘x’
+    • In an equation for ‘f'’:
+          f' b x
+            = if | b -> x
+                 | otherwise -> 0
+
+T23814fail.hs:13:8: error: [GHC-18872]
+    • Couldn't match type ‘Many’ with ‘One’
+        arising from multiplicity of ‘x’
+    • In an equation for ‘g'’:
+          g' b c x
+            = if | b -> x
+                 | c -> 0
+                 | otherwise -> 0


=====================================
testsuite/tests/linear/should_fail/all.T
=====================================
@@ -41,3 +41,4 @@ test('T19120', normal, compile_fail, [''])
 test('T20083', normal, compile_fail, ['-XLinearTypes'])
 test('T19361', normal, compile_fail, [''])
 test('T21278', normal, compile_fail, ['-XLinearTypes'])
+test('T23814fail', normal, compile_fail, [''])


=====================================
utils/dump-decls/Main.hs
=====================================
@@ -6,7 +6,8 @@ import GHC.Core.Class (classMinimalDef)
 import GHC.Core.TyCo.FVs (tyConsOfType)
 import GHC.Driver.Ppr (showSDocForUser)
 import GHC.Unit.State (lookupUnitId, lookupPackageName)
-import GHC.Unit.Info (UnitInfo, unitExposedModules, PackageName(..))
+import GHC.Unit.Info (UnitInfo, unitExposedModules, unitId, PackageName(..))
+import GHC.Unit.Types (UnitId)
 import GHC.Data.FastString (fsLit)
 import GHC.Driver.Env (hsc_units, hscEPS)
 import GHC.Utils.Outputable
@@ -163,14 +164,14 @@ reportUnitDecls :: UnitInfo -> Ghc SDoc
 reportUnitDecls unit_info = do
     let exposed :: [ModuleName]
         exposed = map fst (unitExposedModules unit_info)
-    vcat <$> mapM reportModuleDecls exposed
+    vcat <$> mapM (reportModuleDecls $ unitId unit_info) exposed
 
-reportModuleDecls :: ModuleName -> Ghc SDoc
-reportModuleDecls modl_nm
+reportModuleDecls :: UnitId -> ModuleName -> Ghc SDoc
+reportModuleDecls unit_id modl_nm
   | modl_nm `elem` ignoredModules = do
       return $ vcat [ mod_header, text "-- ignored", text "" ]
   | otherwise = do
-    modl <- GHC.lookupQualifiedModule NoPkgQual modl_nm
+    modl <- GHC.lookupQualifiedModule (OtherPkg unit_id) modl_nm
     mb_mod_info <- GHC.getModuleInfo modl
     mod_info <- case mb_mod_info of
       Nothing -> fail "Failed to find module"



View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/765d6dfd4e573fbba57fe3ea4ed8dbe61fff8964...d5c835cafadae5fadfd9d69581efceb16b637407

-- 
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/765d6dfd4e573fbba57fe3ea4ed8dbe61fff8964...d5c835cafadae5fadfd9d69581efceb16b637407
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/20230822/752c874b/attachment-0001.html>


More information about the ghc-commits mailing list