Zubin pushed to branch wip/27627 at Glasgow Haskell Compiler / GHC
Commits:
-
226d0699
by Zubin Duggal at 2026-08-14T16:33:12+05:30
17 changed files:
- compiler/GHC/Core/Lint.hs
- compiler/GHC/Core/Make.hs
- compiler/GHC/Core/Opt/WorkWrap/Utils.hs
- compiler/GHC/Core/Type.hs
- compiler/GHC/Core/Utils.hs
- compiler/GHC/Types/Demand.hs
- compiler/GHC/Types/Id/Make.hs
- + testsuite/tests/core-to-stg/T27627a/Callee.hs
- + testsuite/tests/core-to-stg/T27627a/Caller.hs
- + testsuite/tests/core-to-stg/T27627a/Main.hs
- + testsuite/tests/core-to-stg/T27627a/T27627a.stdout
- + testsuite/tests/core-to-stg/T27627a/all.T
- + testsuite/tests/core-to-stg/T27627b/Callee.hs
- + testsuite/tests/core-to-stg/T27627b/Caller.hs
- + testsuite/tests/core-to-stg/T27627b/Main.hs
- + testsuite/tests/core-to-stg/T27627b/T27627b.stdout
- + testsuite/tests/core-to-stg/T27627b/all.T
Changes:
| ... | ... | @@ -566,8 +566,8 @@ lintLetBind top_lvl rec_flag binder rhs rhs_ty |
| 566 | 566 | -- Check that we have not bound bottom at a type whose values are
|
| 567 | 567 | -- assumed to be non-bottom, such as a dictionary. (See #24934, #25924, #27627).
|
| 568 | 568 | -- A deferred type error is an exception.
|
| 569 | - -- See (4) in Note [NON-BOTTOM-DICTS invariant] in GHC.Core
|
|
| 570 | - ; checkL (not (isTerminatingType binder_ty)
|
|
| 569 | + -- See (5) in Note [NON-BOTTOM-DICTS invariant] in GHC.Core
|
|
| 570 | + ; checkL (not (mightBeTerminatingType binder_ty)
|
|
| 571 | 571 | || not (exprIsDeadEnd rhs)
|
| 572 | 572 | || isDeferredTypeError rhs)
|
| 573 | 573 | (mkBottomTerminatingTyMsg binder)
|
| ... | ... | @@ -3827,7 +3827,7 @@ isDeferredTypeError :: CoreExpr -> Bool |
| 3827 | 3827 | -- ^ True if it contains a call to `typeError` like -fdefer-type-errors and its
|
| 3828 | 3828 | -- relatives insert, such as
|
| 3829 | 3829 | -- case typeError @LiftedRep @() "No instance for ..."# of {}
|
| 3830 | --- See (4) in Note [NON-BOTTOM-DICTS invariant] in GHC.Core
|
|
| 3830 | +-- See (5) in Note [NON-BOTTOM-DICTS invariant] in GHC.Core
|
|
| 3831 | 3831 | isDeferredTypeError = go
|
| 3832 | 3832 | where
|
| 3833 | 3833 | go (Case scrut _ _ []) = go scrut
|
| ... | ... | @@ -3838,7 +3838,7 @@ isDeferredTypeError = go |
| 3838 | 3838 | go _ = False
|
| 3839 | 3839 | |
| 3840 | 3840 | mkBottomTerminatingTyMsg :: Id -> SDoc
|
| 3841 | --- See (4) in Note [NON-BOTTOM-DICTS invariant] in GHC.Core
|
|
| 3841 | +-- See (5) in Note [NON-BOTTOM-DICTS invariant] in GHC.Core
|
|
| 3842 | 3842 | mkBottomTerminatingTyMsg binder
|
| 3843 | 3843 | = vcat [ text "Binder of a terminating type is bound to bottom:"
|
| 3844 | 3844 | , nest 2 (ppr binder <+> dcolon <+> ppr (idType binder))
|
| ... | ... | @@ -227,7 +227,7 @@ mkLitRubbish :: Type -> Maybe CoreExpr |
| 227 | 227 | -- Fail (returning Nothing) if
|
| 228 | 228 | -- * the RuntimeRep of the Type is not monomorphic;
|
| 229 | 229 | -- * the type is (a ~# b), the type of coercion
|
| 230 | --- * the type is terminating (isTerminatingType), e.g. a dictionary
|
|
| 230 | +-- * the type might be terminating (mightBeTerminatingType), e.g. a dictionary
|
|
| 231 | 231 | -- See INVARIANT 1, 2 and 3 of item (2) in Note [Rubbish literals]
|
| 232 | 232 | -- in GHC.Types.Literal
|
| 233 | 233 | mkLitRubbish ty
|
| ... | ... | @@ -235,7 +235,7 @@ mkLitRubbish ty |
| 235 | 235 | = Nothing -- Satisfy INVARIANT 1
|
| 236 | 236 | | isEqPred ty
|
| 237 | 237 | = Nothing -- Satisfy INVARIANT 2
|
| 238 | - | isTerminatingType ty
|
|
| 238 | + | mightBeTerminatingType ty
|
|
| 239 | 239 | = Nothing -- Satisfy INVARIANT 3
|
| 240 | 240 | | otherwise
|
| 241 | 241 | = Just (Lit (LitRubbish torc rep) `mkTyApps` [ty])
|
| ... | ... | @@ -1068,11 +1068,11 @@ unbox_one_arg opts arg_var |
| 1068 | 1068 | -- same type as @id@. Otherwise, no suitable filler could be found.
|
| 1069 | 1069 | mkAbsentFiller :: Module -> Id -> StrictnessMark -> Maybe CoreExpr
|
| 1070 | 1070 | mkAbsentFiller mod arg str
|
| 1071 | - -- We never make a filler for a terminating type: it might be speculatively
|
|
| 1072 | - -- evaluated or have a field projected out of it.
|
|
| 1071 | + -- We never make a filler for a type that might be terminating: it might be
|
|
| 1072 | + -- speculatively evaluated or have a field projected out of it.
|
|
| 1073 | 1073 | -- See (AF4) in Note [Absent fillers], and
|
| 1074 | 1074 | -- Note [Don't make fillers for terminating types].
|
| 1075 | - | isTerminatingType arg_ty
|
|
| 1075 | + | mightBeTerminatingType arg_ty
|
|
| 1076 | 1076 | = Nothing
|
| 1077 | 1077 | |
| 1078 | 1078 | -- The lifted case: bind 'absentError'. See (AF1) in Note [Absent fillers]
|
| ... | ... | @@ -132,7 +132,9 @@ module GHC.Core.Type ( |
| 132 | 132 | mightBeLiftedType, mightBeUnliftedType,
|
| 133 | 133 | definitelyLiftedType, definitelyUnliftedType,
|
| 134 | 134 | isAlgType, isDataFamilyApp, isSatTyFamApp,
|
| 135 | - isPrimitiveType, isStrictType, isTerminatingType,
|
|
| 135 | + isPrimitiveType, isStrictType,
|
|
| 136 | + IsTerminating(..), typeTerminating,
|
|
| 137 | + definitelyTerminatingType, mightBeTerminatingType,
|
|
| 136 | 138 | unwrapUnaryClasses,
|
| 137 | 139 | isLevityTy, isLevityVar,
|
| 138 | 140 | isRuntimeRepTy, isRuntimeRepVar, isRuntimeRepKindedTy,
|
| ... | ... | @@ -2458,22 +2460,46 @@ isAlgType ty |
| 2458 | 2460 | isStrictType :: HasDebugCallStack => Type -> Bool
|
| 2459 | 2461 | isStrictType = isUnliftedType
|
| 2460 | 2462 | |
| 2461 | -isTerminatingType :: HasDebugCallStack => Type -> Bool
|
|
| 2462 | --- ^ True <=> a term of this type cannot be bottom
|
|
| 2463 | --- This identifies the types described by
|
|
| 2464 | --- Note [NON-BOTTOM-DICTS invariant] in GHC.Core
|
|
| 2463 | +-- | Returned by 'typeTerminating'.
|
|
| 2464 | +-- See Note [NON-BOTTOM-DICTS invariant] in GHC.Core
|
|
| 2465 | +data IsTerminating
|
|
| 2466 | + = SurelyTerminating -- ^ A term of this type is never bottom
|
|
| 2467 | + | SurelyNotTerminating -- ^ A term of this type may well be bottom
|
|
| 2468 | + | MaybeTerminating -- ^ We cannot tell; see (4) in the Note
|
|
| 2469 | + deriving (Eq, Show)
|
|
| 2470 | + |
|
| 2471 | +instance Outputable IsTerminating where
|
|
| 2472 | + ppr = text . show
|
|
| 2473 | + |
|
| 2474 | +typeTerminating :: HasDebugCallStack => Type -> IsTerminating
|
|
| 2475 | +-- ^ Says whether a term of this type can be bottom.
|
|
| 2476 | +-- See Note [NON-BOTTOM-DICTS invariant] in GHC.Core
|
|
| 2465 | 2477 | -- NB: unlifted types are not terminating types!
|
| 2466 | 2478 | -- e.g. you can write a term (loop 1)::Int# that diverges.
|
| 2467 | -isTerminatingType ty = case tyConAppTyCon_maybe (unwrapUnaryClasses id ty) of
|
|
| 2468 | - Just tc -> isClassTyCon tc && not (isUnaryClassTyCon tc)
|
|
| 2469 | - -- We ask about the type that represents the dictionary, not the
|
|
| 2470 | - -- type we were handed, because a unary class dictionary **is** the
|
|
| 2471 | - -- field it wraps. A non-unary class TyCon is terminating; a
|
|
| 2472 | - -- unary one is left here only when there is a cylce,
|
|
| 2473 | - -- and then the dictionary really can be bottom.
|
|
| 2474 | - -- See (1) and (3) in Note [NON-BOTTOM-DICTS invariant] in
|
|
| 2475 | - -- GHC.Core, and (UCM3) in Note [Unary class magic]
|
|
| 2476 | - _ -> False
|
|
| 2479 | +typeTerminating ty
|
|
| 2480 | + | Just tc <- tyConAppTyCon_maybe unwrapped
|
|
| 2481 | + , isClassTyCon tc
|
|
| 2482 | + = if isUnaryClassTyCon tc
|
|
| 2483 | + then SurelyNotTerminating -- Only left here by a cycle; see (3)
|
|
| 2484 | + else SurelyTerminating
|
|
| 2485 | + |
|
| 2486 | + | ConstraintLike <- typeTypeOrConstraint unwrapped
|
|
| 2487 | + = MaybeTerminating -- See (4)
|
|
| 2488 | + |
|
| 2489 | + | otherwise
|
|
| 2490 | + = SurelyNotTerminating
|
|
| 2491 | + where
|
|
| 2492 | + unwrapped = dropForAlls (unwrapUnaryClasses dropForAlls ty)
|
|
| 2493 | + |
|
| 2494 | +definitelyTerminatingType :: HasDebugCallStack => Type -> Bool
|
|
| 2495 | +-- ^ For callers that will evaluate a term of this type.
|
|
| 2496 | +-- See (2) in Note [NON-BOTTOM-DICTS invariant] in GHC.Core
|
|
| 2497 | +definitelyTerminatingType ty = typeTerminating ty == SurelyTerminating
|
|
| 2498 | + |
|
| 2499 | +mightBeTerminatingType :: HasDebugCallStack => Type -> Bool
|
|
| 2500 | +-- ^ For callers that will put a bottoming term at this type.
|
|
| 2501 | +-- See (2) in Note [NON-BOTTOM-DICTS invariant] in GHC.Core
|
|
| 2502 | +mightBeTerminatingType ty = typeTerminating ty /= SurelyNotTerminating
|
|
| 2477 | 2503 | |
| 2478 | 2504 | -- | Look through unary classes, and return the first type
|
| 2479 | 2505 | -- that is not a unary class. For
|
| ... | ... | @@ -2275,7 +2275,7 @@ app_ok fun_ok primop_ok fun args |
| 2275 | 2275 | |
| 2276 | 2276 | _other -- Unlifted and terminating types;
|
| 2277 | 2277 | -- Also c.f. the Var case of exprIsHNF
|
| 2278 | - | isTerminatingType fun_ty -- See Note [exprOkForSpeculation and type classes]
|
|
| 2278 | + | definitelyTerminatingType fun_ty -- See Note [exprOkForSpeculation and type classes]
|
|
| 2279 | 2279 | || definitelyUnliftedType fun_ty
|
| 2280 | 2280 | -> assertPpr (n_val_args == 0) (ppr fun $$ ppr args)
|
| 2281 | 2281 | True -- Both terminating types (e.g. Eq a), and unlifted types (e.g. Int#)
|
| ... | ... | @@ -91,7 +91,7 @@ import GHC.Types.Unique.FM |
| 91 | 91 | import GHC.Types.Basic
|
| 92 | 92 | import GHC.Data.Maybe ( orElse )
|
| 93 | 93 | |
| 94 | -import GHC.Core.Type ( Type, isTerminatingType )
|
|
| 94 | +import GHC.Core.Type ( Type, definitelyTerminatingType )
|
|
| 95 | 95 | import GHC.Core.DataCon ( splitDataProductType_maybe, StrictnessMark, isMarkedStrict )
|
| 96 | 96 | import GHC.Core.Multiplicity ( scaledThing )
|
| 97 | 97 | |
| ... | ... | @@ -1064,7 +1064,7 @@ strictifyDmd = plusDmd seqDmd |
| 1064 | 1064 | strictifyDictDmd :: Type -> Demand -> Demand
|
| 1065 | 1065 | strictifyDictDmd ty (n :* Prod b ds)
|
| 1066 | 1066 | | not (isAbs n)
|
| 1067 | - , isTerminatingType ty
|
|
| 1067 | + , definitelyTerminatingType ty
|
|
| 1068 | 1068 | , Just (_tc, _arg_tys, _data_con, field_tys) <- splitDataProductType_maybe ty
|
| 1069 | 1069 | = C_1N :* mkProd b (zipWith strictifyDictDmd (map scaledThing field_tys) ds)
|
| 1070 | 1070 | -- main idea: ensure it's strict
|
| ... | ... | @@ -498,7 +498,7 @@ mkDictSelId name clas |
| 498 | 498 | mkFunctionType ManyTy pred_ty res_ty
|
| 499 | 499 | -- See Note [Type classes and linear types]
|
| 500 | 500 | |
| 501 | - terminating = isTerminatingType res_ty || definitelyUnliftedType res_ty
|
|
| 501 | + terminating = definitelyTerminatingType res_ty || definitelyUnliftedType res_ty
|
|
| 502 | 502 | -- If the field is unlifted, it can't be bottom
|
| 503 | 503 | -- Ditto if it's a terminating type
|
| 504 | 504 |
| 1 | +{-# LANGUAGE GADTs, ConstraintKinds, ScopedTypeVariables, TypeFamilies #-}
|
|
| 2 | +{-# LANGUAGE UndecidableInstances, UndecidableSuperClasses, FlexibleInstances #-}
|
|
| 3 | +{-# OPTIONS_GHC -fno-worker-wrapper #-}
|
|
| 4 | +module Callee where
|
|
| 5 | + |
|
| 6 | +import Data.Kind (Constraint)
|
|
| 7 | + |
|
| 8 | +-- A Constraint-kinded type family. It always reduces to (TC a), but
|
|
| 9 | +-- unwrapUnaryClasses does not reduce type families, so it cannot walk
|
|
| 10 | +-- through this field. See (4) in Note [NON-BOTTOM-DICTS invariant].
|
|
| 11 | +type family F a :: Constraint
|
|
| 12 | +type instance F a = TC a
|
|
| 13 | + |
|
| 14 | +-- Not unary: a superclass field and a method field.
|
|
| 15 | +class Eq a => TC a where
|
|
| 16 | + tcDummy :: a -> Int
|
|
| 17 | + |
|
| 18 | +-- Unary: a single superclass field, whose type is the unreduced family
|
|
| 19 | +-- application (F a).
|
|
| 20 | +class F a => UC a
|
|
| 21 | + |
|
| 22 | +instance TC Int where tcDummy _ = 0
|
|
| 23 | +instance UC Int
|
|
| 24 | + |
|
| 25 | +data Dict c where
|
|
| 26 | + Dict :: c => Dict c
|
|
| 27 | + |
|
| 28 | +{-# NOINLINE discard #-}
|
|
| 29 | +discard :: Dict c -> Int
|
|
| 30 | +discard _ = 42
|
|
| 31 | + |
|
| 32 | +{-# NOINLINE b #-}
|
|
| 33 | +b :: forall a. UC a => a -> Int
|
|
| 34 | +b _ = discard (Dict :: Dict (Eq a)) |
| 1 | +module Caller where
|
|
| 2 | + |
|
| 3 | +import Callee
|
|
| 4 | + |
|
| 5 | +-- b does not use its dictionary, so a's dictionary is absent. Worker/wrapper
|
|
| 6 | +-- must not replace it with an error thunk: at runtime a (UC t) dictionary is a
|
|
| 7 | +-- (TC t) dictionary, and Callee speculates a superclass selection out of it.
|
|
| 8 | +{-# NOINLINE a #-}
|
|
| 9 | +a :: UC t => t -> Int
|
|
| 10 | +a x = b x + 1 |
| 1 | +module Main where
|
|
| 2 | + |
|
| 3 | +import Caller
|
|
| 4 | + |
|
| 5 | +main :: IO ()
|
|
| 6 | +main = print (a (3 :: Int)) |
| 1 | +43 |
| 1 | +test('T27627a',
|
|
| 2 | + [extra_files(['Main.hs', 'Caller.hs', 'Callee.hs'])],
|
|
| 3 | + multimod_compile_and_run,
|
|
| 4 | + ['Main', '-O']) |
| 1 | +{-# LANGUAGE GADTs, ConstraintKinds, ScopedTypeVariables, QuantifiedConstraints #-}
|
|
| 2 | +{-# LANGUAGE UndecidableInstances, FlexibleInstances, RankNTypes #-}
|
|
| 3 | +{-# OPTIONS_GHC -fno-worker-wrapper #-}
|
|
| 4 | +module Callee where
|
|
| 5 | + |
|
| 6 | +-- Not unary: a superclass field and a method field, so (TC a) is terminating.
|
|
| 7 | +class Eq a => TC a where
|
|
| 8 | + tcDummy :: a -> Int
|
|
| 9 | + |
|
| 10 | +-- Unary: one superclass field, of type (forall a. TC (f a)).
|
|
| 11 | +-- unwrapUnaryClasses cannot walk through a forall, so it cannot reach the
|
|
| 12 | +-- class underneath. See (4) in Note [NON-BOTTOM-DICTS invariant].
|
|
| 13 | +class (forall a. TC (f a)) => UQ f
|
|
| 14 | + |
|
| 15 | +newtype Id a = MkId a
|
|
| 16 | +instance Eq (Id a) where _ == _ = True
|
|
| 17 | +instance TC (Id a) where tcDummy _ = 0
|
|
| 18 | +instance UQ Id
|
|
| 19 | + |
|
| 20 | +data Dict c where
|
|
| 21 | + Dict :: c => Dict c
|
|
| 22 | + |
|
| 23 | +{-# NOINLINE discard #-}
|
|
| 24 | +discard :: Dict c -> Int
|
|
| 25 | +discard _ = 42
|
|
| 26 | + |
|
| 27 | +{-# NOINLINE b #-}
|
|
| 28 | +b :: forall f. UQ f => f Int -> Int
|
|
| 29 | +b _ = discard (Dict :: Dict (Eq (f Int))) |
| 1 | +module Caller where
|
|
| 2 | + |
|
| 3 | +import Callee
|
|
| 4 | + |
|
| 5 | +-- b does not use its dictionary, so a's dictionary is absent. Worker/wrapper
|
|
| 6 | +-- must not replace it with an error thunk: Callee speculates a superclass
|
|
| 7 | +-- selection out of it.
|
|
| 8 | +{-# NOINLINE a #-}
|
|
| 9 | +a :: UQ f => f Int -> Int
|
|
| 10 | +a x = b x + 1 |
| 1 | +module Main where
|
|
| 2 | + |
|
| 3 | +import Callee
|
|
| 4 | +import Caller
|
|
| 5 | + |
|
| 6 | +main :: IO ()
|
|
| 7 | +main = print (a (MkId 3 :: Id Int)) |
| 1 | +43 |
| 1 | +test('T27627b',
|
|
| 2 | + [extra_files(['Main.hs', 'Caller.hs', 'Callee.hs'])],
|
|
| 3 | + multimod_compile_and_run,
|
|
| 4 | + ['Main', '-O']) |