Matthew Pickering pushed to branch wip/splice-imports-2025 at Glasgow Haskell Compiler / GHC
Commits:
-
c319ea08
by Matthew Pickering at 2025-04-24T10:43:56+01:00
-
e443f033
by Matthew Pickering at 2025-04-24T10:45:07+01:00
-
b434257a
by Matthew Pickering at 2025-04-24T15:02:19+01:00
-
a676a4fa
by Matthew Pickering at 2025-04-24T15:02:32+01:00
-
c54ecffa
by Matthew Pickering at 2025-04-24T15:20:25+01:00
9 changed files:
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Tc/Deriv/Utils.hs
- compiler/GHC/Tc/Errors/Ppr.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/Solver/Monad.hs
- compiler/GHC/Types/Basic.hs
- compiler/GHC/Types/Error/Codes.hs
- testsuite/tests/splice-imports/SI14.stderr
- testsuite/tests/splice-imports/SI15.stderr
Changes:
... | ... | @@ -245,10 +245,14 @@ downsweepThunk hsc_env mod_summary = unsafeInterleaveIO $ do |
245 | 245 | -- | Construct a module graph starting from the interactive context.
|
246 | 246 | -- Produces, a thunk, which when forced will perform the downsweep.
|
247 | 247 | -- This graph contains the current interactive module, and its dependencies.
|
248 | - |
|
248 | +--
|
|
249 | +-- Invariant: The hsc_mod_graph already contains the relevant home modules which
|
|
250 | +-- might be imported by the interactive imports.
|
|
251 | +--
|
|
249 | 252 | -- This is a first approximation for this function.
|
250 | 253 | downsweepInteractiveImports :: HscEnv -> InteractiveContext -> IO ModuleGraph
|
251 | 254 | downsweepInteractiveImports hsc_env ic = unsafeInterleaveIO $ do
|
255 | + debugTraceMsg (hsc_logger hsc_env) 3 $ (text "Computing Interactive Module Graph thunk...")
|
|
252 | 256 | let imps = ic_imports (hsc_IC hsc_env)
|
253 | 257 | |
254 | 258 | let mn = icInteractiveModule ic
|
... | ... | @@ -256,9 +260,11 @@ downsweepInteractiveImports hsc_env ic = unsafeInterleaveIO $ do |
256 | 260 | let key = moduleToMnk mn NotBoot
|
257 | 261 | let node_type = ModuleNodeFixed key ml
|
258 | 262 | |
263 | + let cached_nodes = Map.fromList [ (mkNodeKey n, n) | n <- mg_mss (hsc_mod_graph hsc_env) ]
|
|
264 | + |
|
259 | 265 | let edges = map mkEdge imps
|
260 | 266 | let env = DownsweepEnv hsc_env DownsweepUseCompile mempty []
|
261 | - (module_edges, graph, _) <- runDownsweepM env $ loopImports edges M.empty Map.empty
|
|
267 | + (module_edges, graph) <- runDownsweepM env $ loopFromInteractive edges cached_nodes
|
|
262 | 268 | let node = ModuleNode module_edges node_type
|
263 | 269 | |
264 | 270 | let all_nodes = M.elems graph
|
... | ... | @@ -282,6 +288,36 @@ downsweepInteractiveImports hsc_env ic = unsafeInterleaveIO $ do |
282 | 288 | unitId = homeUnitId $ hsc_home_unit hsc_env
|
283 | 289 | in (unitId, lvl, mb_pkg, GWIB (noLoc wanted_mod) is_boot)
|
284 | 290 | |
291 | +loopFromInteractive :: [(UnitId, ImportLevel, PkgQual, GenWithIsBoot (Located ModuleName))]
|
|
292 | + -> M.Map NodeKey ModuleGraphNode
|
|
293 | + -> DownsweepM ([ModuleNodeEdge],M.Map NodeKey ModuleGraphNode)
|
|
294 | +loopFromInteractive [] cached_nodes = return ([], cached_nodes)
|
|
295 | +loopFromInteractive (edge:edges) cached_nodes = do
|
|
296 | + hsc_env <- asks downsweep_hsc_env
|
|
297 | + let (unitId, lvl, mb_pkg, GWIB wanted_mod is_boot) = edge
|
|
298 | + let home_unit = ue_unitHomeUnit unitId (hsc_unit_env hsc_env)
|
|
299 | + let k _ loc mod =
|
|
300 | + let key = moduleToMnk mod is_boot
|
|
301 | + in return $ FoundHome (ModuleNodeFixed key loc)
|
|
302 | + found <- liftIO $ summariseModuleDispatch k hsc_env home_unit is_boot wanted_mod mb_pkg []
|
|
303 | + case found of
|
|
304 | + -- Case 1: Home modules have to already be in the cache.
|
|
305 | + FoundHome (ModuleNodeFixed mod _) -> do
|
|
306 | + let edge = ModuleNodeEdge lvl (NodeKey_Module mod)
|
|
307 | + (edges, cached_nodes') <- loopFromInteractive edges cached_nodes
|
|
308 | + return (edge : edges, cached_nodes')
|
|
309 | + -- Case 2: External units may not be in the cache, if we haven't already initialised the
|
|
310 | + -- module graph.
|
|
311 | + External uid -> do
|
|
312 | + let hsc_env' = hscSetActiveHomeUnit home_unit hsc_env
|
|
313 | + cached_nodes' = loopUnit hsc_env' cached_nodes [uid]
|
|
314 | + edge = ModuleNodeEdge lvl (NodeKey_ExternalUnit uid)
|
|
315 | + (edges, cached_nodes') <- loopFromInteractive edges cached_nodes'
|
|
316 | + return (edge : edges, cached_nodes')
|
|
317 | + -- And if it's not found.. just carry on and hope.
|
|
318 | + _ -> loopFromInteractive edges cached_nodes
|
|
319 | + |
|
320 | + |
|
285 | 321 | -- | Create a module graph from a list of installed modules.
|
286 | 322 | -- This is used by the loader when we need to load modules but there
|
287 | 323 | -- isn't already an existing module graph. For example, when loading plugins
|
... | ... | @@ -927,6 +927,7 @@ stockSideConditions deriv_ctxt cls |
927 | 927 | cond_vanilla `andCond`
|
928 | 928 | cond_Representable1Ok)
|
929 | 929 | | sameUnique cls_key liftClassKey = Just (checkFlag LangExt.DeriveLift `andCond`
|
930 | + checkFlag LangExt.ImplicitStagePersistence `andCond`
|
|
930 | 931 | cond_vanilla `andCond`
|
931 | 932 | cond_args cls)
|
932 | 933 | | otherwise = Nothing
|
... | ... | @@ -2035,6 +2035,10 @@ instance Diagnostic TcRnMessage where |
2035 | 2035 | TcRnUnexpectedTypeSyntaxInTerms syntax -> mkSimpleDecorated $
|
2036 | 2036 | text "Unexpected" <+> pprTypeSyntaxName syntax
|
2037 | 2037 | |
2038 | + TcRnDeriveLiftWithoutImplicitStagePersistence{}
|
|
2039 | + -> mkSimpleDecorated $
|
|
2040 | + text "Deriving Lift is not possible when ImplicitStagePersistence is disabled."
|
|
2041 | + |
|
2038 | 2042 | diagnosticReason :: TcRnMessage -> DiagnosticReason
|
2039 | 2043 | diagnosticReason = \case
|
2040 | 2044 | TcRnUnknownMessage m
|
... | ... | @@ -2685,6 +2689,8 @@ instance Diagnostic TcRnMessage where |
2685 | 2689 | -> ErrorWithoutFlag
|
2686 | 2690 | TcRnUnexpectedTypeSyntaxInTerms{}
|
2687 | 2691 | -> ErrorWithoutFlag
|
2692 | + TcRnDeriveLiftWithoutImplicitStagePersistence{}
|
|
2693 | + -> ErrorWithoutFlag
|
|
2688 | 2694 | |
2689 | 2695 | diagnosticHints = \case
|
2690 | 2696 | TcRnUnknownMessage m
|
... | ... | @@ -3374,6 +3380,8 @@ instance Diagnostic TcRnMessage where |
3374 | 3380 | -> noHints
|
3375 | 3381 | TcRnUnexpectedTypeSyntaxInTerms syntax
|
3376 | 3382 | -> [suggestExtension (typeSyntaxExtension syntax)]
|
3383 | + TcRnDeriveLiftWithoutImplicitStagePersistence{}
|
|
3384 | + -> noHints
|
|
3377 | 3385 | |
3378 | 3386 | diagnosticCode = constructorCode @GHC
|
3379 | 3387 |
... | ... | @@ -4453,6 +4453,17 @@ data TcRnMessage where |
4453 | 4453 | Test cases: T24159_type_syntax_rn_fail
|
4454 | 4454 | -}
|
4455 | 4455 | TcRnUnexpectedTypeSyntaxInTerms :: TypeSyntax -> TcRnMessage
|
4456 | + |
|
4457 | + {-| TcRnDeriveLiftWithoutImplicitStagePersistence is an error indicating that
|
|
4458 | + someone tried to derive a Lift instance when ImplicitStagePersistence is enabled.
|
|
4459 | + |
|
4460 | + |
|
4461 | + Test cases:
|
|
4462 | + None yet
|
|
4463 | + -}
|
|
4464 | + TcRnDeriveLiftWithoutImplicitStagePersistence :: !Name -- ^ The type for which Lift is being derived
|
|
4465 | + -> TcRnMessage
|
|
4466 | + |
|
4456 | 4467 | deriving Generic
|
4457 | 4468 | |
4458 | 4469 | ----
|
... | ... | @@ -194,6 +194,8 @@ import GHC.Types.Var.Set |
194 | 194 | import GHC.Types.Unique.Supply
|
195 | 195 | import GHC.Types.Unique.Set( elementOfUniqSet )
|
196 | 196 | import GHC.Types.Id
|
197 | +import GHC.Types.Basic (allImportLevels)
|
|
198 | +import GHC.Types.ThLevelIndex (thLevelIndexFromImportLevel)
|
|
197 | 199 | |
198 | 200 | import GHC.Unit.Module
|
199 | 201 | import qualified GHC.Rename.Env as TcM
|
... | ... | @@ -1642,40 +1644,51 @@ checkCrossLevelClsInst dflags reason bind_lvls use_lvl_idx is_local |
1642 | 1644 | checkWellLevelledInstanceWhat :: HasCallStack => InstanceWhat -> TcS (Maybe (Set.Set ThLevelIndex, Bool))
|
1643 | 1645 | checkWellLevelledInstanceWhat what
|
1644 | 1646 | | TopLevInstance { iw_dfun_id = dfun_id } <- what
|
1645 | - = do
|
|
1646 | - -- MP: I am not sure if we have to only do this check for orphan instances.
|
|
1647 | - cur_mod <- extractModule <$> getGblEnv
|
|
1648 | - if nameIsLocalOrFrom cur_mod (idName dfun_id)
|
|
1649 | - then return $ Just ( (Set.singleton topLevelIndex, True) )
|
|
1650 | - else do
|
|
1651 | - hsc_env <- getTopEnv
|
|
1652 | - let q = mgQueryZero (hsc_mod_graph hsc_env)
|
|
1653 | - let mkKey s m = (Left (ModNodeKeyWithUid (GWIB (moduleName m) NotBoot) (moduleUnitId m), s))
|
|
1654 | - let scope_key s = mkKey s cur_mod
|
|
1655 | - let lkup :: ImportLevel -> Either (ModNodeKeyWithUid, ImportLevel) UnitId -> Bool
|
|
1656 | - lkup s k = q (scope_key s) k
|
|
1657 | - let splice_lvl = lkup SpliceLevel
|
|
1658 | - normal_lvl = lkup NormalLevel
|
|
1659 | - quote_lvl = lkup QuoteLevel
|
|
1660 | - |
|
1661 | - name_module = nameModule (idName dfun_id)
|
|
1662 | - instance_key = if moduleUnitId name_module `Set.member` hsc_all_home_unit_ids hsc_env
|
|
1663 | - then (mkKey NormalLevel name_module)
|
|
1664 | - else Right (moduleUnitId name_module)
|
|
1665 | - let lvls = [ spliceLevelIndex | splice_lvl instance_key]
|
|
1666 | - ++ [ topLevelIndex | normal_lvl instance_key]
|
|
1667 | - ++ [ quoteLevelIndex | quote_lvl instance_key]
|
|
1668 | - return $ Just ( Set.fromList lvls, False )
|
|
1669 | - |
|
1647 | + = Just <$> checkNameVisibleLevels (idName dfun_id)
|
|
1670 | 1648 | | BuiltinTypeableInstance tc <- what
|
1671 | - = do
|
|
1672 | - cur_mod <- extractModule <$> getGblEnv
|
|
1673 | - return $ Just (if nameIsLocalOrFrom cur_mod (tyConName tc)
|
|
1674 | - then (Set.singleton topLevelIndex, True)
|
|
1675 | - -- TODO, not correct, needs similar checks to normal instances
|
|
1676 | - else (Set.fromList [spliceLevelIndex, topLevelIndex], False))
|
|
1649 | + -- The typeable instance is always defined in the same module as the TyCon.
|
|
1650 | + = Just <$> checkNameVisibleLevels (tyConName tc)
|
|
1677 | 1651 | | otherwise = return Nothing
|
1678 | 1652 | |
1653 | +-- | Check the levels at which the given name is visible, including a boolean
|
|
1654 | +-- indicating if the name is local or not.
|
|
1655 | +checkNameVisibleLevels :: Name -> TcS (Set.Set ThLevelIndex, Bool)
|
|
1656 | +checkNameVisibleLevels name = do
|
|
1657 | + cur_mod <- extractModule <$> getGblEnv
|
|
1658 | + if nameIsLocalOrFrom cur_mod name
|
|
1659 | + then return (Set.singleton topLevelIndex, True)
|
|
1660 | + else do
|
|
1661 | + lvls <- checkModuleVisibleLevels (nameModule name)
|
|
1662 | + return (lvls, False)
|
|
1663 | + |
|
1664 | +-- | This function checks which levels the given module is visible at.
|
|
1665 | +-- It does this by querying the module graph, hence it is suitable for usage
|
|
1666 | +-- in instance checking, where the reason an instance is brought into scope is
|
|
1667 | +-- implicit.
|
|
1668 | +checkModuleVisibleLevels :: Module -> TcS (Set.Set ThLevelIndex)
|
|
1669 | +checkModuleVisibleLevels check_mod = do
|
|
1670 | + cur_mod <- extractModule <$> getGblEnv
|
|
1671 | + hsc_env <- getTopEnv
|
|
1672 | + |
|
1673 | + -- 0. The keys for the scope of the current module.
|
|
1674 | + let mkKey s m = (Left (moduleToMnk m NotBoot, s))
|
|
1675 | + cur_mod_scope_key s = mkKey s cur_mod
|
|
1676 | + |
|
1677 | + -- 1. is_visible checks that a specific key is visible from the given level in the
|
|
1678 | + -- current module.
|
|
1679 | + let is_visible :: ImportLevel -> Either (ModNodeKeyWithUid, ImportLevel) UnitId -> Bool
|
|
1680 | + is_visible s k = mgQueryZero (hsc_mod_graph hsc_env) (cur_mod_scope_key s) k
|
|
1681 | + |
|
1682 | + -- 2. The key we are looking for, either the module itself in the home package or the
|
|
1683 | + -- module unit id of the module we are checking.
|
|
1684 | + let instance_key = if moduleUnitId check_mod `Set.member` hsc_all_home_unit_ids hsc_env
|
|
1685 | + then mkKey NormalLevel check_mod
|
|
1686 | + else Right (moduleUnitId check_mod)
|
|
1687 | + |
|
1688 | + -- 3. For each level, check if the key is visible from that level.
|
|
1689 | + let lvls = [ thLevelIndexFromImportLevel lvl | lvl <- allImportLevels, is_visible lvl instance_key]
|
|
1690 | + return $ Set.fromList lvls
|
|
1691 | + |
|
1679 | 1692 | {-
|
1680 | 1693 | Note [Well-levelled instance evidence]
|
1681 | 1694 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
... | ... | @@ -123,7 +123,7 @@ module GHC.Types.Basic ( |
123 | 123 | |
124 | 124 | ForeignSrcLang (..),
|
125 | 125 | |
126 | - ImportLevel(..), convImportLevel, convImportLevelSpec,
|
|
126 | + ImportLevel(..), convImportLevel, convImportLevelSpec, allImportLevels
|
|
127 | 127 | ) where
|
128 | 128 | |
129 | 129 | import GHC.Prelude
|
... | ... | @@ -2442,7 +2442,7 @@ instance Outputable DefaultingStrategy where |
2442 | 2442 | |
2443 | 2443 | -- | ImportLevel
|
2444 | 2444 | |
2445 | -data ImportLevel = NormalLevel | SpliceLevel | QuoteLevel deriving (Eq, Ord, Data, Show, Enum)
|
|
2445 | +data ImportLevel = NormalLevel | SpliceLevel | QuoteLevel deriving (Eq, Ord, Data, Show, Enum, Bounded)
|
|
2446 | 2446 | |
2447 | 2447 | instance Outputable ImportLevel where
|
2448 | 2448 | ppr NormalLevel = text "normal"
|
... | ... | @@ -2451,6 +2451,9 @@ instance Outputable ImportLevel where |
2451 | 2451 | |
2452 | 2452 | deriving via (EnumBinary ImportLevel) instance Binary ImportLevel
|
2453 | 2453 | |
2454 | +allImportLevels :: [ImportLevel]
|
|
2455 | +allImportLevels = [minBound..maxBound]
|
|
2456 | + |
|
2454 | 2457 | convImportLevel :: ImportDeclLevelStyle -> ImportLevel
|
2455 | 2458 | convImportLevel (LevelStylePre level) = convImportLevelSpec level
|
2456 | 2459 | convImportLevel (LevelStylePost level) = convImportLevelSpec level
|
... | ... | @@ -700,6 +700,7 @@ type family GhcDiagnosticCode c = n | n -> c where |
700 | 700 | GhcDiagnosticCode "TcRnIllegalTypeExpr" = 35499
|
701 | 701 | GhcDiagnosticCode "TcRnUnexpectedTypeSyntaxInTerms" = 31244
|
702 | 702 | GhcDiagnosticCode "TcRnTypeApplicationsDisabled" = 23482
|
703 | + GhcDiagnosticCode "TcRnDeriveLiftWithoutImplicitStagePersistence" = 87906
|
|
703 | 704 | |
704 | 705 | -- TcRnIllegalInvisibleTypePattern
|
705 | 706 | GhcDiagnosticCode "InvisPatWithoutFlag" = 78249
|
1 | -SI14.hs:9:21: error: [GHC-28914]
|
|
2 | - • Level error: ‘A’ is bound at level 0 but used at level 1
|
|
3 | - Hint: quoting [| A |] or an enclosing expression
|
|
4 | - would allow the quotation to be used at an earlier level
|
|
5 | - • In the Template Haskell quotation: 'A
|
|
6 | - |
|
7 | -SI14.hs:9:21: error: [GHC-28914]
|
|
8 | - • Level error: ‘A’ is bound at level 0 but used at level 1
|
|
9 | - Hint: quoting [| A |] or an enclosing expression
|
|
10 | - would allow the quotation to be used at an earlier level
|
|
11 | - • In the Template Haskell quotation: 'A
|
|
1 | +SI14.hs:9:21: error: [GHC-86639]
|
|
2 | + • Can't make a derived instance of ‘Lift A’:
|
|
3 | + You need ImplicitStagePersistence to derive an instance for this class
|
|
4 | + • In the data type declaration for ‘A’
|
|
12 | 5 |
1 | -SI15.hs:9:21: error: [GHC-28914]
|
|
2 | - • Level error: ‘A’ is bound at level 0 but used at level 1
|
|
3 | - Hint: quoting [| A |] or an enclosing expression
|
|
4 | - would allow the quotation to be used at an earlier level
|
|
5 | - • In the Template Haskell quotation: 'A
|
|
6 | - |
|
7 | -SI15.hs:9:21: error: [GHC-28914]
|
|
8 | - • Level error: ‘A’ is bound at level 0 but used at level 1
|
|
9 | - Hint: quoting [| A |] or an enclosing expression
|
|
10 | - would allow the quotation to be used at an earlier level
|
|
11 | - • In the Template Haskell quotation: 'A
|
|
1 | +SI15.hs:9:21: error: [GHC-86639]
|
|
2 | + • Can't make a derived instance of ‘Lift A’:
|
|
3 | + You need ImplicitStagePersistence to derive an instance for this class
|
|
4 | + • In the data type declaration for ‘A’
|
|
12 | 5 |