[Git][ghc/ghc][wip/24279] 3 commits: Remove the `CoreBindings` constructor from `LinkablePart`
Simon Peyton Jones pushed to branch wip/24279 at Glasgow Haskell Compiler / GHC Commits: b253013e by Georgios Karachalias at 2025-11-07T17:21:57-05:00 Remove the `CoreBindings` constructor from `LinkablePart` Adjust HscRecompStatus to disallow unhydrated WholeCoreBindings from being passed as input to getLinkDeps (which would previously panic in this case). Fixes #26497 - - - - - ac7b737e by Sylvain Henry at 2025-11-07T17:22:51-05:00 Testsuite: pass ext-interp test way (#26552) Note that some tests are still marked as broken with the ext-interp way (see #26552 and #14335) - - - - - b44163d3 by Simon Peyton Jones at 2025-11-10T16:44:15+00:00 Make TYPE and CONSTRAINT apart again This patch finally fixes #24279. * The story started with #11715 * Then #21623 articulated a plan, which made Type and Constraint not-apart; a horrible hack but it worked. The main patch was commit 778c6adca2c995cd8a1b84394d4d5ca26b915dac Author: Simon Peyton Jones <simonpj@microsoft.com> Date: Wed Nov 9 10:33:22 2022 +0000 Type vs Constraint: finally nailed * #24279 reported a bug in the above big commit; this small patch fixes it commit af6932d6c068361c6ae300d52e72fbe13f8e1f18 Author: Simon Peyton Jones <simon.peytonjones@gmail.com> Date: Mon Jan 8 10:49:49 2024 +0000 Make TYPE and CONSTRAINT not-apart Issue #24279 showed up a bug in the logic in GHC.Core.Unify.unify_ty which is supposed to make TYPE and CONSTRAINT be not-apart. * Then !10479 implemented "unary classes". * That change in turn allows us to make Type and Constraint apart again, cleaning up the compiler and allowing a little bit more expressiveness. It fixes the original hope in #24279, namely that `Type` and `Constraint` should be distinct throughout. - - - - - 25 changed files: - compiler/GHC/Builtin/Types/Prim.hs - compiler/GHC/Core/Coercion.hs - compiler/GHC/Core/Lint.hs - compiler/GHC/Core/RoughMap.hs - compiler/GHC/Core/Type.hs - compiler/GHC/Core/Unify.hs - compiler/GHC/Driver/Main.hs - compiler/GHC/Driver/Pipeline.hs - compiler/GHC/Linker/Deps.hs - compiler/GHC/Linker/Types.hs - compiler/GHC/Tc/Instance/Class.hs - compiler/GHC/Unit/Home/ModInfo.hs - compiler/GHC/Unit/Module/Status.hs - compiler/GHC/Unit/Module/WholeCoreBindings.hs - libraries/base/tests/all.T - testsuite/driver/testlib.py - testsuite/tests/driver/T20696/all.T - testsuite/tests/driver/fat-iface/all.T - testsuite/tests/indexed-types/should_fail/T21092.hs - − testsuite/tests/indexed-types/should_fail/T21092.stderr - testsuite/tests/indexed-types/should_fail/all.T - testsuite/tests/splice-imports/all.T - testsuite/tests/typecheck/should_fail/T24279.hs - − testsuite/tests/typecheck/should_fail/T24279.stderr - testsuite/tests/typecheck/should_fail/all.T Changes: ===================================== compiler/GHC/Builtin/Types/Prim.hs ===================================== @@ -752,8 +752,9 @@ Specifically (a ~# b) :: CONSTRAINT (TupleRep []) Wrinkles -(W1) Type and Constraint are considered distinct throughout GHC. But they - are not /apart/: see Note [Type and Constraint are not apart] +(W1) Type and Constraint are considered distinct throughout GHC. + That wasn't always the case: + see Historical Note [Type and Constraint are not apart] (W2) We need two absent-error Ids, aBSENT_ERROR_ID for types of kind Type, and aBSENT_CONSTRAINT_ERROR_ID for types of kind Constraint. @@ -768,8 +769,24 @@ Wrinkles of type TYPE rr. See (CPR2) in Note [Which types are unboxed?] in GHC.Core.Opt.WorkWrap.Utils. -Note [Type and Constraint are not apart] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------------------------------------------- +Historical Note [Type and Constraint are not apart] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Nov 2025: + In the past, Type and Constraint were carefully coonsiderd to be + not /apart/. But the necessity for that vanished with unary classes + (see Note [Unary class magic]), done in + + commit 9bd7fcc518111a1549c98720c222cdbabd32ed46 + Author: Simon Peyton Jones <simon.peytonjones@gmail.com> + Date: Tue Apr 15 17:43:46 2025 +0100 + Implement unary classes + + So now Type and Constraint are simply distinct type constructors, just as + much as Int and Bool. + + The rest of this Note is preserved for historical interest. + Type and Constraint are not equal (eqType) but they are not /apart/ either. Reason (c.f. #7451): @@ -841,6 +858,9 @@ Wrinkles So in GHC.Tc.Instance.Class.matchTypeable, Type and Constraint are treated as separate TyCons; i.e. given no special treatment. +End of Historical Note +------------------------------------------------------------- + Note [RuntimeRep polymorphism] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Generally speaking, you can't be polymorphic in `RuntimeRep`. E.g ===================================== compiler/GHC/Core/Coercion.hs ===================================== @@ -641,11 +641,6 @@ eqTyConRole tc -- | Given a coercion `co :: (t1 :: TYPE r1) ~ (t2 :: TYPE r2)` -- produce a coercion `rep_co :: r1 ~ r2` --- But actually it is possible that --- co :: (t1 :: CONSTRAINT r1) ~ (t2 :: CONSTRAINT r2) --- or co :: (t1 :: TYPE r1) ~ (t2 :: CONSTRAINT r2) --- or co :: (t1 :: CONSTRAINT r1) ~ (t2 :: TYPE r2) --- See Note [mkRuntimeRepCo] mkRuntimeRepCo :: HasDebugCallStack => Coercion -> Coercion mkRuntimeRepCo co = assert (isTYPEorCONSTRAINT k1 && isTYPEorCONSTRAINT k2) $ @@ -654,26 +649,6 @@ mkRuntimeRepCo co kind_co = mkKindCo co -- kind_co :: TYPE r1 ~ TYPE r2 Pair k1 k2 = coercionKind kind_co -{- Note [mkRuntimeRepCo] -~~~~~~~~~~~~~~~~~~~~~~~~ -Given - class C a where { op :: Maybe a } -we will get an axiom - axC a :: (C a :: CONSTRAINT r1) ~ (Maybe a :: TYPE r2) -(See Note [Type and Constraint are not apart] in GHC.Builtin.Types.Prim.) - -Then we may call mkRuntimeRepCo on (axC ty), and that will return - mkSelCo (SelTyCon 0 Nominal) (Kind (axC ty)) :: r1 ~ r2 - -So mkSelCo needs to be happy with decomposing a coercion of kind - CONSTRAINT r1 ~ TYPE r2 - -Hence the use of `tyConIsTYPEorCONSTRAINT` in the assertion `good_call` -in `mkSelCo`. See #23018 for a concrete example. (In this context it's -important that TYPE and CONSTRAINT have the same arity and kind, not -merely that they are not-apart; otherwise SelCo would not make sense.) --} - isReflCoVar_maybe :: Var -> Maybe Coercion -- If cv :: t~t then isReflCoVar_maybe cv = Just (Refl t) -- Works on all kinds of Vars, not just CoVars @@ -1305,8 +1280,7 @@ mkSelCo_maybe cs co , Just (tc2, tys2) <- splitTyConApp_maybe ty2 , let { len1 = length tys1 ; len2 = length tys2 } - = (tc1 == tc2 || (tyConIsTYPEorCONSTRAINT tc1 && tyConIsTYPEorCONSTRAINT tc2)) - -- tyConIsTYPEorCONSTRAINT: see Note [mkRuntimeRepCo] + = tc1 == tc2 && len1 == len2 && n < len1 && r == tyConRole (coercionRole co) tc1 n ===================================== compiler/GHC/Core/Lint.hs ===================================== @@ -2891,13 +2891,9 @@ lint_branch ax_tc (CoAxBranch { cab_tvs = tvs, cab_cvs = cvs hang (text "Inhomogeneous axiom") 2 (text "lhs:" <+> ppr lhs <+> dcolon <+> ppr lhs_kind $$ text "rhs:" <+> ppr rhs <+> dcolon <+> ppr rhs_kind) } - -- Type and Constraint are not Apart, so this test allows - -- the newtype axiom for a single-method class. Indeed the - -- whole reason Type and Constraint are not Apart is to allow - -- such axioms! --- these checks do not apply to newtype axioms lint_family_branch :: TyCon -> CoAxBranch -> LintM () +-- These checks do not apply to newtype axioms lint_family_branch fam_tc br@(CoAxBranch { cab_tvs = tvs , cab_eta_tvs = eta_tvs , cab_cvs = cvs ===================================== compiler/GHC/Core/RoughMap.hs ===================================== @@ -36,7 +36,6 @@ import GHC.Core.Type import GHC.Utils.Outputable import GHC.Types.Name import GHC.Types.Name.Env -import GHC.Builtin.Types.Prim( cONSTRAINTTyConName, tYPETyConName ) import Control.Monad (join) import Data.Data (Data) @@ -347,16 +346,7 @@ typeToRoughMatchTc ty roughMatchTyConName :: TyCon -> Name roughMatchTyConName tc - | tc_name == cONSTRAINTTyConName - = tYPETyConName -- TYPE and CONSTRAINT are not apart, so they must use - -- the same rough-map key. We arbitrarily use TYPE. - -- See Note [Type and Constraint are not apart] - -- wrinkle (W1) in GHC.Builtin.Types.Prim - | otherwise - = assertPpr (isGenerativeTyCon tc Nominal) (ppr tc) tc_name - where - tc_name = tyConName tc - + = assertPpr (isGenerativeTyCon tc Nominal) (ppr tc) (tyConName tc) -- | Trie of @[RoughMatchTc]@ -- ===================================== compiler/GHC/Core/Type.hs ===================================== @@ -1421,8 +1421,6 @@ piResultTy ty arg = case piResultTy_maybe ty arg of Nothing -> pprPanic "piResultTy" (ppr ty $$ ppr arg) piResultTy_maybe :: Type -> Type -> Maybe Type --- We don't need a 'tc' version, because --- this function behaves the same for Type and Constraint piResultTy_maybe ty arg = case coreFullView ty of FunTy { ft_res = res } -> Just res ===================================== compiler/GHC/Core/Unify.hs ===================================== @@ -27,7 +27,6 @@ import GHC.Prelude import GHC.Types.Var import GHC.Types.Var.Env import GHC.Types.Var.Set -import GHC.Builtin.Names( tYPETyConKey, cONSTRAINTTyConKey ) import GHC.Core.Type hiding ( getTvSubstEnv ) import GHC.Core.Coercion hiding ( getCvSubstEnv ) import GHC.Core.Predicate( scopedSort ) @@ -98,8 +97,6 @@ of ways. Here we summarise, but see Note [Specification of unification]. See Note [Apartness and type families] * MARInfinite (occurs check): See Note [Infinitary substitutions] - * MARTypeVsConstraint: - See Note [Type and Constraint are not apart] in GHC.Builtin.Types.Prim * MARCast (obscure): See (KCU2) in Note [Kind coercions in Unify] @@ -997,16 +994,12 @@ data UnifyResultM a = Unifiable a -- the subst that unifies the types -- | Why are two types 'MaybeApart'? 'MARInfinite' takes precedence: -- This is used (only) in Note [Infinitary substitution in lookup] in GHC.Core.InstEnv --- As of Feb 2022, we never differentiate between MARTypeFamily and MARTypeVsConstraint; --- it's really only MARInfinite that's interesting here. +-- It's really only MARInfinite that's interesting here. data MaybeApartReason = MARTypeFamily -- ^ matching e.g. F Int ~? Bool | MARInfinite -- ^ matching e.g. a ~? Maybe a - | MARTypeVsConstraint -- ^ matching Type ~? Constraint or the arrow types - -- See Note [Type and Constraint are not apart] in GHC.Builtin.Types.Prim - | MARCast -- ^ Very obscure. -- See (KCU2) in Note [Kind coercions in Unify] @@ -1015,13 +1008,11 @@ combineMAR :: MaybeApartReason -> MaybeApartReason -> MaybeApartReason -- See (UR1) in Note [Unification result] for why MARInfinite wins combineMAR MARInfinite _ = MARInfinite -- MARInfinite wins combineMAR MARTypeFamily r = r -- Otherwise it doesn't really matter -combineMAR MARTypeVsConstraint r = r combineMAR MARCast r = r instance Outputable MaybeApartReason where ppr MARTypeFamily = text "MARTypeFamily" ppr MARInfinite = text "MARInfinite" - ppr MARTypeVsConstraint = text "MARTypeVsConstraint" ppr MARCast = text "MARCast" instance Semigroup MaybeApartReason where @@ -1729,30 +1720,6 @@ unify_ty env ty1 ty2 kco ; unify_tc_app env tc1 tys1 tys2 } - -- TYPE and CONSTRAINT are not Apart - -- See Note [Type and Constraint are not apart] in GHC.Builtin.Types.Prim - -- NB: at this point we know that the two TyCons do not match - | Just (tc1,_) <- mb_tc_app1, let u1 = tyConUnique tc1 - , Just (tc2,_) <- mb_tc_app2, let u2 = tyConUnique tc2 - , (u1 == tYPETyConKey && u2 == cONSTRAINTTyConKey) || - (u2 == tYPETyConKey && u1 == cONSTRAINTTyConKey) - = maybeApart MARTypeVsConstraint - -- We don't bother to look inside; wrinkle (W3) in GHC.Builtin.Types.Prim - -- Note [Type and Constraint are not apart] - - -- The arrow types are not Apart - -- See Note [Type and Constraint are not apart] in GHC.Builtin.Types.Prim - -- wrinkle (W2) - -- NB1: at this point we know that the two TyCons do not match - -- NB2: In the common FunTy/FunTy case you might wonder if we want to go via - -- splitTyConApp_maybe. But yes we do: we need to look at those implied - -- kind argument in order to satisfy (Unification Kind Invariant) - | FunTy {} <- ty1 - , FunTy {} <- ty2 - = maybeApart MARTypeVsConstraint - -- We don't bother to look inside; wrinkle (W3) in GHC.Builtin.Types.Prim - -- Note [Type and Constraint are not apart] - where mb_tc_app1 = splitTyConApp_maybe ty1 mb_tc_app2 = splitTyConApp_maybe ty2 ===================================== compiler/GHC/Driver/Main.hs ===================================== @@ -277,6 +277,7 @@ import Data.Data hiding (Fixity, TyCon) import Data.Functor ((<&>)) import Data.List ( nub, isPrefixOf, partition ) import qualified Data.List.NonEmpty as NE +import Data.Traversable (for) import Control.Monad import Data.IORef import System.FilePath as FilePath @@ -850,11 +851,11 @@ hscRecompStatus if | not (backendGeneratesCode (backend lcl_dflags)) -> do -- No need for a linkable, we're good to go msg UpToDate - return $ HscUpToDate checked_iface emptyHomeModInfoLinkable + return $ HscUpToDate checked_iface emptyRecompLinkables | not (backendGeneratesCodeForHsBoot (backend lcl_dflags)) , IsBoot <- isBootSummary mod_summary -> do msg UpToDate - return $ HscUpToDate checked_iface emptyHomeModInfoLinkable + return $ HscUpToDate checked_iface emptyRecompLinkables -- Always recompile with the JS backend when TH is enabled until -- #23013 is fixed. @@ -883,7 +884,7 @@ hscRecompStatus let just_o = justObjects <$> obj_linkable bytecode_or_object_code - | gopt Opt_WriteByteCode lcl_dflags = justBytecode <$> definitely_bc + | gopt Opt_WriteByteCode lcl_dflags = justBytecode . Left <$> definitely_bc | otherwise = (justBytecode <$> maybe_bc) `choose` just_o @@ -900,13 +901,13 @@ hscRecompStatus definitely_bc = bc_obj_linkable `prefer` bc_in_memory_linkable -- If not -fwrite-byte-code, then we could use core bindings or object code if that's available. - maybe_bc = bc_in_memory_linkable `choose` - bc_obj_linkable `choose` - bc_core_linkable + maybe_bc = (Left <$> bc_in_memory_linkable) `choose` + (Left <$> bc_obj_linkable) `choose` + (Right <$> bc_core_linkable) bc_result = if gopt Opt_WriteByteCode lcl_dflags -- If the byte-code artifact needs to be produced, then we certainly need bytecode. - then definitely_bc + then Left <$> definitely_bc else maybe_bc trace_if (hsc_logger hsc_env) @@ -1021,14 +1022,13 @@ checkByteCodeFromObject hsc_env mod_sum = do -- | Attempt to load bytecode from whole core bindings in the interface if they exist. -- This is a legacy code-path, these days it should be preferred to use the bytecode object linkable. -checkByteCodeFromIfaceCoreBindings :: HscEnv -> ModIface -> ModSummary -> IO (MaybeValidated Linkable) +checkByteCodeFromIfaceCoreBindings :: HscEnv -> ModIface -> ModSummary -> IO (MaybeValidated WholeCoreBindingsLinkable) checkByteCodeFromIfaceCoreBindings _hsc_env iface mod_sum = do let this_mod = ms_mod mod_sum if_date = fromJust $ ms_iface_date mod_sum case iface_core_bindings iface (ms_location mod_sum) of - Just fi -> do - return (UpToDateItem (Linkable if_date this_mod (NE.singleton (CoreBindings fi)))) + Just fi -> return $ UpToDateItem (Linkable if_date this_mod fi) _ -> return $ outOfDateItemBecause MissingBytecode Nothing -------------------------------------------------------------- @@ -1142,20 +1142,22 @@ initWholeCoreBindings :: HscEnv -> ModIface -> ModDetails -> - Linkable -> - IO Linkable -initWholeCoreBindings hsc_env iface details (Linkable utc_time this_mod uls) = do - Linkable utc_time this_mod <$> mapM (go hsc_env) uls + RecompLinkables -> + IO HomeModLinkable +initWholeCoreBindings hsc_env iface details (RecompLinkables bc o) = do + bc' <- go bc + pure $ HomeModLinkable bc' o where - go hsc_env' = \case - CoreBindings wcb -> do + type_env = md_types details + + go :: RecompBytecodeLinkable -> IO (Maybe Linkable) + go (NormalLinkable l) = pure l + go (WholeCoreBindingsLinkable wcbl) = + fmap Just $ for wcbl $ \wcb -> do add_iface_to_hpt iface details hsc_env bco <- unsafeInterleaveIO $ - compileWholeCoreBindings hsc_env' type_env wcb - pure (DotGBC bco) - l -> pure l - - type_env = md_types details + compileWholeCoreBindings hsc_env type_env wcb + pure $ NE.singleton (DotGBC bco) -- | Hydrate interface Core bindings and compile them to bytecode. -- ===================================== compiler/GHC/Driver/Pipeline.hs ===================================== @@ -109,6 +109,7 @@ import GHC.Unit.Env import GHC.Unit.Finder import GHC.Unit.Module.ModSummary import GHC.Unit.Module.ModIface +import GHC.Unit.Module.Status import GHC.Unit.Home.ModInfo import GHC.Unit.Home.PackageTable @@ -249,8 +250,8 @@ compileOne' mHscMessage (iface, linkable) <- runPipeline (hsc_hooks plugin_hsc_env) pipeline -- See Note [ModDetails and --make mode] details <- initModDetails plugin_hsc_env iface - linkable' <- traverse (initWholeCoreBindings plugin_hsc_env iface details) (homeMod_bytecode linkable) - return $! HomeModInfo iface details (linkable { homeMod_bytecode = linkable' }) + linkable' <- initWholeCoreBindings plugin_hsc_env iface details linkable + return $! HomeModInfo iface details linkable' where lcl_dflags = ms_hspp_opts summary location = ms_location summary @@ -759,7 +760,7 @@ preprocessPipeline pipe_env hsc_env input_fn = do $ phaseIfFlag hsc_env flag def action -- | The complete compilation pipeline, from start to finish -fullPipeline :: P m => PipeEnv -> HscEnv -> FilePath -> HscSource -> m (ModIface, HomeModLinkable) +fullPipeline :: P m => PipeEnv -> HscEnv -> FilePath -> HscSource -> m (ModIface, RecompLinkables) fullPipeline pipe_env hsc_env pp_fn src_flavour = do (dflags, input_fn) <- preprocessPipeline pipe_env hsc_env pp_fn let hsc_env' = hscSetFlags dflags hsc_env @@ -768,7 +769,7 @@ fullPipeline pipe_env hsc_env pp_fn src_flavour = do hscPipeline pipe_env (hsc_env_with_plugins, mod_sum, hsc_recomp_status) -- | Everything after preprocess -hscPipeline :: P m => PipeEnv -> ((HscEnv, ModSummary, HscRecompStatus)) -> m (ModIface, HomeModLinkable) +hscPipeline :: P m => PipeEnv -> (HscEnv, ModSummary, HscRecompStatus) -> m (ModIface, RecompLinkables) hscPipeline pipe_env (hsc_env_with_plugins, mod_sum, hsc_recomp_status) = do case hsc_recomp_status of HscUpToDate iface mb_linkable -> return (iface, mb_linkable) @@ -777,7 +778,7 @@ hscPipeline pipe_env (hsc_env_with_plugins, mod_sum, hsc_recomp_status) = do hscBackendAction <- use (T_HscPostTc hsc_env_with_plugins mod_sum tc_result warnings mb_old_hash ) hscBackendPipeline pipe_env hsc_env_with_plugins mod_sum hscBackendAction -hscBackendPipeline :: P m => PipeEnv -> HscEnv -> ModSummary -> HscBackendAction -> m (ModIface, HomeModLinkable) +hscBackendPipeline :: P m => PipeEnv -> HscEnv -> ModSummary -> HscBackendAction -> m (ModIface, RecompLinkables) hscBackendPipeline pipe_env hsc_env mod_sum result = if backendGeneratesCode (backend (hsc_dflags hsc_env)) then do @@ -796,15 +797,15 @@ hscBackendPipeline pipe_env hsc_env mod_sum result = return res else case result of - HscUpdate iface -> return (iface, emptyHomeModInfoLinkable) - HscRecomp {} -> (,) <$> liftIO (mkFullIface hsc_env (hscs_partial_iface result) Nothing Nothing NoStubs []) <*> pure emptyHomeModInfoLinkable + HscUpdate iface -> return (iface, emptyRecompLinkables) + HscRecomp {} -> (,) <$> liftIO (mkFullIface hsc_env (hscs_partial_iface result) Nothing Nothing NoStubs []) <*> pure emptyRecompLinkables hscGenBackendPipeline :: P m => PipeEnv -> HscEnv -> ModSummary -> HscBackendAction - -> m (ModIface, HomeModLinkable) + -> m (ModIface, RecompLinkables) hscGenBackendPipeline pipe_env hsc_env mod_sum result = do let mod_name = moduleName (ms_mod mod_sum) src_flavour = (ms_hsc_src mod_sum) @@ -812,7 +813,7 @@ hscGenBackendPipeline pipe_env hsc_env mod_sum result = do (fos, miface, mlinkable, o_file) <- use (T_HscBackend pipe_env hsc_env mod_name src_flavour location result) final_fp <- hscPostBackendPipeline pipe_env hsc_env (ms_hsc_src mod_sum) (backend (hsc_dflags hsc_env)) (Just location) o_file final_linkable <- - case final_fp of + safeCastHomeModLinkable <$> case final_fp of -- No object file produced, bytecode or NoBackend Nothing -> return mlinkable Just o_fp -> do @@ -936,7 +937,7 @@ pipelineStart pipe_env hsc_env input_fn mb_phase = as :: P m => Bool -> m (Maybe FilePath) as use_cpp = asPipeline use_cpp pipe_env hsc_env Nothing input_fn - objFromLinkable (_, homeMod_object -> Just (Linkable _ _ (DotO lnk _ :| []))) = Just lnk + objFromLinkable (_, recompLinkables_object -> Just (Linkable _ _ (DotO lnk _ :| []))) = Just lnk objFromLinkable _ = Nothing fromPhase :: P m => Phase -> m (Maybe FilePath) ===================================== compiler/GHC/Linker/Deps.hs ===================================== @@ -33,7 +33,6 @@ import GHC.Utils.Error import GHC.Unit.Env import GHC.Unit.Finder import GHC.Unit.Module -import GHC.Unit.Module.WholeCoreBindings import GHC.Unit.Home.ModInfo import GHC.Iface.Errors.Types @@ -206,10 +205,7 @@ get_link_deps opts pls maybe_normal_osuf span mods = do DotO file ForeignObject -> pure (DotO file ForeignObject) DotA fp -> panic ("adjust_ul DotA " ++ show fp) DotDLL fp -> panic ("adjust_ul DotDLL " ++ show fp) - DotGBC {} -> pure part - CoreBindings WholeCoreBindings {wcb_module} -> - pprPanic "Unhydrated core bindings" (ppr wcb_module) - + DotGBC {} -> pure part {- ===================================== compiler/GHC/Linker/Types.hs ===================================== @@ -1,5 +1,6 @@ {-# LANGUAGE TypeApplications #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE DeriveTraversable #-} ----------------------------------------------------------------------------- -- @@ -30,7 +31,9 @@ module GHC.Linker.Types , PkgsLoaded -- * Linkable - , Linkable(..) + , Linkable + , WholeCoreBindingsLinkable + , LinkableWith(..) , mkModuleByteCodeLinkable , LinkablePart(..) , LinkableObjectSort (..) @@ -254,7 +257,7 @@ instance Outputable LoadedPkgInfo where -- | Information we can use to dynamically link modules into the compiler -data Linkable = Linkable +data LinkableWith parts = Linkable { linkableTime :: !UTCTime -- ^ Time at which this linkable was built -- (i.e. when the bytecodes were produced, @@ -263,9 +266,13 @@ data Linkable = Linkable , linkableModule :: !Module -- ^ The linkable module itself - , linkableParts :: NonEmpty LinkablePart + , linkableParts :: parts -- ^ Files and chunks of code to link. - } + } deriving (Functor, Traversable, Foldable) + +type Linkable = LinkableWith (NonEmpty LinkablePart) + +type WholeCoreBindingsLinkable = LinkableWith WholeCoreBindings type LinkableSet = ModuleEnv Linkable @@ -282,7 +289,7 @@ unionLinkableSet = plusModuleEnv_C go | linkableTime l1 > linkableTime l2 = l1 | otherwise = l2 -instance Outputable Linkable where +instance Outputable a => Outputable (LinkableWith a) where ppr (Linkable when_made mod parts) = (text "Linkable" <+> parens (text (show when_made)) <+> ppr mod) $$ nest 3 (ppr parts) @@ -318,11 +325,6 @@ data LinkablePart | DotDLL FilePath -- ^ Dynamically linked library file (.so, .dll, .dylib) - | CoreBindings WholeCoreBindings - -- ^ Serialised core which we can turn into BCOs (or object files), or - -- used by some other backend See Note [Interface Files with Core - -- Definitions] - | DotGBC ModuleByteCode -- ^ A byte-code object, lives only in memory. @@ -350,7 +352,6 @@ instance Outputable LinkablePart where ppr (DotA path) = text "DotA" <+> text path ppr (DotDLL path) = text "DotDLL" <+> text path ppr (DotGBC bco) = text "DotGBC" <+> ppr bco - ppr (CoreBindings {}) = text "CoreBindings" -- | Return true if the linkable only consists of native code (no BCO) linkableIsNativeCodeOnly :: Linkable -> Bool @@ -391,7 +392,6 @@ isNativeCode = \case DotA {} -> True DotDLL {} -> True DotGBC {} -> False - CoreBindings {} -> False -- | Is the part a native library? (.so/.dll) isNativeLib :: LinkablePart -> Bool @@ -400,7 +400,6 @@ isNativeLib = \case DotA {} -> True DotDLL {} -> True DotGBC {} -> False - CoreBindings {} -> False -- | Get the FilePath of linkable part (if applicable) linkablePartPath :: LinkablePart -> Maybe FilePath @@ -408,7 +407,6 @@ linkablePartPath = \case DotO fn _ -> Just fn DotA fn -> Just fn DotDLL fn -> Just fn - CoreBindings {} -> Nothing DotGBC {} -> Nothing -- | Return the paths of all object code files (.o, .a, .so) contained in this @@ -418,7 +416,6 @@ linkablePartNativePaths = \case DotO fn _ -> [fn] DotA fn -> [fn] DotDLL fn -> [fn] - CoreBindings {} -> [] DotGBC {} -> [] -- | Return the paths of all object files (.o) contained in this 'LinkablePart'. @@ -427,7 +424,6 @@ linkablePartObjectPaths = \case DotO fn _ -> [fn] DotA _ -> [] DotDLL _ -> [] - CoreBindings {} -> [] DotGBC bco -> gbc_foreign_files bco -- | Retrieve the compiled byte-code from the linkable part. @@ -444,12 +440,11 @@ linkableFilter f linkable = do Just linkable {linkableParts = new} linkablePartNative :: LinkablePart -> [LinkablePart] -linkablePartNative = \case - u@DotO {} -> [u] - u@DotA {} -> [u] - u@DotDLL {} -> [u] +linkablePartNative u = case u of + DotO {} -> [u] + DotA {} -> [u] + DotDLL {} -> [u] DotGBC bco -> [DotO f ForeignObject | f <- gbc_foreign_files bco] - _ -> [] linkablePartByteCode :: LinkablePart -> [LinkablePart] linkablePartByteCode = \case ===================================== compiler/GHC/Tc/Instance/Class.hs ===================================== @@ -963,11 +963,6 @@ matchTypeable clas [k,t] -- clas = Typeable | k `eqType` naturalTy = doTyLit knownNatClassName t | k `eqType` typeSymbolKind = doTyLit knownSymbolClassName t | k `eqType` charTy = doTyLit knownCharClassName t - - -- TyCon applied to its kind args - -- No special treatment of Type and Constraint; they get distinct TypeReps - -- see wrinkle (W4) of Note [Type and Constraint are not apart] - -- in GHC.Builtin.Types.Prim. | Just (tc, ks) <- splitTyConApp_maybe t -- See Note [Typeable (T a b c)] , onlyNamedBndrsApplied tc ks = doTyConApp clas t tc ks ===================================== compiler/GHC/Unit/Home/ModInfo.hs ===================================== @@ -3,13 +3,10 @@ module GHC.Unit.Home.ModInfo ( HomeModInfo (..) - , HomeModLinkable(..) + , HomeModLinkable (..) , homeModInfoObject , homeModInfoByteCode , emptyHomeModInfoLinkable - , justBytecode - , justObjects - , bytecodeAndObjects ) where @@ -18,11 +15,9 @@ import GHC.Prelude import GHC.Unit.Module.ModIface import GHC.Unit.Module.ModDetails -import GHC.Linker.Types ( Linkable(..), linkableIsNativeCodeOnly ) +import GHC.Linker.Types ( Linkable ) import GHC.Utils.Outputable -import GHC.Utils.Panic - -- | Information about modules in the package being compiled data HomeModInfo = HomeModInfo @@ -68,22 +63,6 @@ data HomeModLinkable = HomeModLinkable { homeMod_bytecode :: !(Maybe Linkable) instance Outputable HomeModLinkable where ppr (HomeModLinkable l1 l2) = ppr l1 $$ ppr l2 -justBytecode :: Linkable -> HomeModLinkable -justBytecode lm = - assertPpr (not (linkableIsNativeCodeOnly lm)) (ppr lm) - $ emptyHomeModInfoLinkable { homeMod_bytecode = Just lm } - -justObjects :: Linkable -> HomeModLinkable -justObjects lm = - assertPpr (linkableIsNativeCodeOnly lm) (ppr lm) - $ emptyHomeModInfoLinkable { homeMod_object = Just lm } - -bytecodeAndObjects :: Linkable -> Linkable -> HomeModLinkable -bytecodeAndObjects bc o = - assertPpr (not (linkableIsNativeCodeOnly bc) && linkableIsNativeCodeOnly o) (ppr bc $$ ppr o) - (HomeModLinkable (Just bc) (Just o)) - - {- Note [Home module build products] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ===================================== compiler/GHC/Unit/Module/Status.hs ===================================== @@ -1,22 +1,35 @@ +{-# LANGUAGE LambdaCase #-} + module GHC.Unit.Module.Status - ( HscBackendAction(..), HscRecompStatus (..) + ( HscBackendAction(..) + , HscRecompStatus (..) + , RecompLinkables (..) + , RecompBytecodeLinkable (..) + , emptyRecompLinkables + , justBytecode + , justObjects + , bytecodeAndObjects + , safeCastHomeModLinkable ) where import GHC.Prelude import GHC.Unit +import GHC.Unit.Home.ModInfo import GHC.Unit.Module.ModGuts import GHC.Unit.Module.ModIface +import GHC.Linker.Types ( Linkable, WholeCoreBindingsLinkable, linkableIsNativeCodeOnly ) + import GHC.Utils.Fingerprint import GHC.Utils.Outputable -import GHC.Unit.Home.ModInfo +import GHC.Utils.Panic -- | Status of a module in incremental compilation data HscRecompStatus -- | Nothing to do because code already exists. - = HscUpToDate ModIface HomeModLinkable + = HscUpToDate ModIface RecompLinkables -- | Recompilation of module, or update of interface is required. Optionally -- pass the old interface hash to avoid updating the existing interface when -- it has not changed. @@ -41,6 +54,16 @@ data HscBackendAction -- changed. } +-- | Linkables produced by @hscRecompStatus@. Might contain serialized core +-- which can be turned into BCOs (or object files), or used by some other +-- backend. See Note [Interface Files with Core Definitions]. +data RecompLinkables = RecompLinkables { recompLinkables_bytecode :: !RecompBytecodeLinkable + , recompLinkables_object :: !(Maybe Linkable) } + +data RecompBytecodeLinkable + = NormalLinkable !(Maybe Linkable) + | WholeCoreBindingsLinkable !WholeCoreBindingsLinkable + instance Outputable HscRecompStatus where ppr HscUpToDate{} = text "HscUpToDate" ppr HscRecompNeeded{} = text "HscRecompNeeded" @@ -48,3 +71,37 @@ instance Outputable HscRecompStatus where instance Outputable HscBackendAction where ppr (HscUpdate mi) = text "Update:" <+> (ppr (mi_module mi)) ppr (HscRecomp _ ml _mi _mf) = text "Recomp:" <+> ppr ml + +instance Outputable RecompLinkables where + ppr (RecompLinkables l1 l2) = ppr l1 $$ ppr l2 + +instance Outputable RecompBytecodeLinkable where + ppr (NormalLinkable lm) = text "NormalLinkable:" <+> ppr lm + ppr (WholeCoreBindingsLinkable lm) = text "WholeCoreBindingsLinkable:" <+> ppr lm + +emptyRecompLinkables :: RecompLinkables +emptyRecompLinkables = RecompLinkables (NormalLinkable Nothing) Nothing + +safeCastHomeModLinkable :: HomeModLinkable -> RecompLinkables +safeCastHomeModLinkable (HomeModLinkable bc o) = RecompLinkables (NormalLinkable bc) o + +justBytecode :: Either Linkable WholeCoreBindingsLinkable -> RecompLinkables +justBytecode = \case + Left lm -> + assertPpr (not (linkableIsNativeCodeOnly lm)) (ppr lm) + $ emptyRecompLinkables { recompLinkables_bytecode = NormalLinkable (Just lm) } + Right lm -> emptyRecompLinkables { recompLinkables_bytecode = WholeCoreBindingsLinkable lm } + +justObjects :: Linkable -> RecompLinkables +justObjects lm = + assertPpr (linkableIsNativeCodeOnly lm) (ppr lm) + $ emptyRecompLinkables { recompLinkables_object = Just lm } + +bytecodeAndObjects :: Either Linkable WholeCoreBindingsLinkable -> Linkable -> RecompLinkables +bytecodeAndObjects either_bc o = case either_bc of + Left bc -> + assertPpr (not (linkableIsNativeCodeOnly bc) && linkableIsNativeCodeOnly o) (ppr bc $$ ppr o) + $ RecompLinkables (NormalLinkable (Just bc)) (Just o) + Right bc -> + assertPpr (linkableIsNativeCodeOnly o) (ppr o) + $ RecompLinkables (WholeCoreBindingsLinkable bc) (Just o) ===================================== compiler/GHC/Unit/Module/WholeCoreBindings.hs ===================================== @@ -130,6 +130,9 @@ data WholeCoreBindings = WholeCoreBindings , wcb_foreign :: IfaceForeign } +instance Outputable WholeCoreBindings where + ppr (WholeCoreBindings {}) = text "WholeCoreBindings" + {- Note [Foreign stubs and TH bytecode linking] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ===================================== libraries/base/tests/all.T ===================================== @@ -80,7 +80,7 @@ test('length001', # excessive amounts of stack space. So we specifically set a low # stack limit and mark it as failing under a few conditions. [extra_run_opts('+RTS -K8m -RTS'), - expect_fail_for(['normal', 'threaded1', 'llvm', 'nonmoving', 'nonmoving_thr', 'nonmoving_thr_ghc']), + expect_fail_for(['normal', 'threaded1', 'llvm', 'nonmoving', 'nonmoving_thr', 'nonmoving_thr_ghc', 'ext-interp']), # JS doesn't support stack limit so the test sometimes passes just fine. Therefore the test is # marked as fragile. when(js_arch(), fragile(22921))], ===================================== testsuite/driver/testlib.py ===================================== @@ -352,6 +352,9 @@ def req_plugins( name, opts ): """ req_interp(name, opts) + # Plugins aren't supported with the external interpreter (#14335) + expect_broken_for(14335,['ext-interp'])(name,opts) + if config.cross: opts.skip = True ===================================== testsuite/tests/driver/T20696/all.T ===================================== @@ -1,4 +1,5 @@ test('T20696', [extra_files(['A.hs', 'B.hs', 'C.hs']) + , expect_broken_for(26552, ['ext-interp']) , unless(ghc_dynamic(), skip)], multimod_compile, ['A', '']) test('T20696-static', [extra_files(['A.hs', 'B.hs', 'C.hs']) , when(ghc_dynamic(), skip)], multimod_compile, ['A', '']) ===================================== testsuite/tests/driver/fat-iface/all.T ===================================== @@ -9,12 +9,12 @@ test('fat010', [req_th,extra_files(['THA.hs', 'THB.hs', 'THC.hs']), copy_files], # Check linking works when using -fbyte-code-and-object-code test('fat011', [req_th, extra_files(['FatMain.hs', 'FatTH.hs', 'FatQuote.hs'])], multimod_compile, ['FatMain', '-fbyte-code-and-object-code -fprefer-byte-code']) # Check that we use interpreter rather than enable dynamic-too if needed for TH -test('fat012', [req_th, unless(ghc_dynamic(), skip), extra_files(['FatTH.hs', 'FatQuote.hs'])], multimod_compile, ['FatTH', '-fprefer-byte-code']) +test('fat012', [req_th, expect_broken_for(26552, ['ext-interp']), unless(ghc_dynamic(), skip), extra_files(['FatTH.hs', 'FatQuote.hs'])], multimod_compile, ['FatTH', '-fprefer-byte-code']) # Check that no objects are generated if using -fno-code and -fprefer-byte-code test('fat013', [req_th, req_bco, extra_files(['FatTH.hs', 'FatQuote.hs'])], multimod_compile, ['FatTH', '-fno-code -fprefer-byte-code']) # When using interpreter should not produce objects test('fat014', [req_th, extra_files(['FatTH.hs', 'FatQuote.hs']), extra_run_opts('-fno-code')], ghci_script, ['fat014.script']) -test('fat015', [req_th, unless(ghc_dynamic(), skip), extra_files(['FatQuote.hs', 'FatQuote1.hs', 'FatQuote2.hs', 'FatTH1.hs', 'FatTH2.hs', 'FatTHTop.hs'])], multimod_compile, ['FatTHTop', '-fno-code -fwrite-interface']) +test('fat015', [req_th, expect_broken_for(26552, ['ext-interp']), unless(ghc_dynamic(), skip), extra_files(['FatQuote.hs', 'FatQuote1.hs', 'FatQuote2.hs', 'FatTH1.hs', 'FatTH2.hs', 'FatTHTop.hs'])], multimod_compile, ['FatTHTop', '-fno-code -fwrite-interface']) test('T22807', [req_th, unless(ghc_dynamic(), skip), extra_files(['T22807A.hs', 'T22807B.hs'])] , makefile_test, ['T22807']) test('T22807_ghci', [req_th, unless(ghc_dynamic(), skip), extra_files(['T22807_ghci.hs'])] ===================================== testsuite/tests/indexed-types/should_fail/T21092.hs ===================================== @@ -7,3 +7,5 @@ type family F a type instance F Type = Int type instance F Constraint = Bool + +-- Nov 2025: Type and Constraint are now Apart (#24279) ===================================== testsuite/tests/indexed-types/should_fail/T21092.stderr deleted ===================================== @@ -1,5 +0,0 @@ - -T21092.hs:8:15: error: [GHC-34447] - Conflicting family instance declarations: - F (*) = Int -- Defined at T21092.hs:8:15 - F Constraint = Bool -- Defined at T21092.hs:9:15 ===================================== testsuite/tests/indexed-types/should_fail/all.T ===================================== @@ -107,7 +107,7 @@ test('T8368', normal, compile_fail, ['']) test('T8368a', normal, compile_fail, ['']) test('T8518', normal, compile_fail, ['']) test('T9036', normal, compile_fail, ['']) -test('T21092', normal, compile_fail, ['']) +test('T21092', normal, compile, ['']) # Now compiles fine test('T9167', normal, compile_fail, ['']) test('T9171', normal, compile_fail, ['']) test('T9097', normal, compile_fail, ['']) ===================================== testsuite/tests/splice-imports/all.T ===================================== @@ -9,7 +9,7 @@ test('SI03', [extra_files(["SI01A.hs"])], multimod_compile_fail, ['SI03', '-v0'] test('SI04', [extra_files(["SI01A.hs"])], multimod_compile, ['SI04', '-v0']) test('SI05', [extra_files(["SI01A.hs"])], multimod_compile_fail, ['SI05', '-v0']) test('SI06', [extra_files(["SI01A.hs"])], multimod_compile, ['SI06', '-v0']) -test('SI07', [unless(ghc_dynamic(), skip), extra_files(["SI05A.hs"])], multimod_compile, ['SI07', '-fwrite-interface -fno-code']) +test('SI07', [expect_broken_for(26552, ['ext-interp']), unless(ghc_dynamic(), skip), extra_files(["SI05A.hs"])], multimod_compile, ['SI07', '-fwrite-interface -fno-code']) # Instance tests test('SI08', [extra_files(["ClassA.hs", "InstanceA.hs"])], multimod_compile_fail, ['SI08', '-v0']) test('SI09', [extra_files(["ClassA.hs", "InstanceA.hs"])], multimod_compile, ['SI09', '-v0']) ===================================== testsuite/tests/typecheck/should_fail/T24279.hs ===================================== @@ -13,7 +13,7 @@ type G :: Type -> RuntimeRep -> Type type family G a where G (a b) = a --- Should be rejected +-- Now (Nov 2025) accepted foo :: (F (G Constraint)) -> Bool foo x = x @@ -22,10 +22,10 @@ type family H a b where H a a = Int H a b = Bool --- Should be rejected -bar1 :: H TYPE CONSTRAINT -> Int +-- Now (Nov 2025) accepted +bar1 :: H TYPE CONSTRAINT -> Bool bar1 x = x --- Should be rejected -bar2 :: H Type Constraint -> Int +-- Now (Nov 2025) accepted +bar2 :: H Type Constraint -> Bool bar2 x = x ===================================== testsuite/tests/typecheck/should_fail/T24279.stderr deleted ===================================== @@ -1,19 +0,0 @@ - -T24279.hs:18:9: error: [GHC-83865] - • Couldn't match type ‘F CONSTRAINT’ with ‘Bool’ - Expected: Bool - Actual: F (G Constraint) - • In the expression: x - In an equation for ‘foo’: foo x = x - -T24279.hs:27:10: error: [GHC-83865] - • Couldn't match expected type ‘Int’ - with actual type ‘H TYPE CONSTRAINT’ - • In the expression: x - In an equation for ‘bar1’: bar1 x = x - -T24279.hs:31:10: error: [GHC-83865] - • Couldn't match expected type ‘Int’ - with actual type ‘H (*) Constraint’ - • In the expression: x - In an equation for ‘bar2’: bar2 x = x ===================================== testsuite/tests/typecheck/should_fail/all.T ===================================== @@ -718,7 +718,7 @@ test('T24064', normal, compile_fail, ['']) test('T24090a', normal, compile_fail, ['']) test('T24090b', normal, compile, ['']) # scheduled to become an actual error in GHC 9.16 test('T24298', normal, compile_fail, ['']) -test('T24279', normal, compile_fail, ['']) +test('T24279', normal, compile, ['']) # Now accepted (Nov 2025) test('T24318', normal, compile_fail, ['']) # all the various do expansion fail messages View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/0b211ff84d9a34fb60b51188d479b1b... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/0b211ff84d9a34fb60b51188d479b1b... You're receiving this email because of your account on gitlab.haskell.org.
participants (1)
-
Simon Peyton Jones (@simonpj)