Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
-
e8a196c6
by sheaf at 2026-04-16T13:31:19-04:00
11 changed files:
- + changelog.d/T27124.md
- compiler/GHC/CoreToStg/Prep.hs
- compiler/GHC/HsToCore/Pmc/Solver/Types.hs
- compiler/GHC/Tc/Errors.hs
- + testsuite/tests/overloadedstrings/should_fail/T25926.hs
- + testsuite/tests/overloadedstrings/should_fail/T25926.stderr
- + testsuite/tests/overloadedstrings/should_fail/T27124.hs
- + testsuite/tests/overloadedstrings/should_fail/T27124.stderr
- + testsuite/tests/overloadedstrings/should_fail/all.T
- + testsuite/tests/overloadedstrings/should_run/T27124a.hs
- testsuite/tests/overloadedstrings/should_run/all.T
Changes:
| 1 | +section: compiler
|
|
| 2 | +issues: #25926 #27124
|
|
| 3 | +mrs: !15895
|
|
| 4 | +synopsis:
|
|
| 5 | + Fix "failed to detect OverLit" panic in the pattern-match checker.
|
|
| 6 | +description:
|
|
| 7 | + Fixed an issue in which overloaded literals (e.g. numeric literals, overloaded
|
|
| 8 | + strings with -XOverloadedStrings, overloaded lists, etc) could cause a GHC
|
|
| 9 | + crash when using -fdefer-type-errors, with an error message of the form
|
|
| 10 | + "failed to detect OverLit". |
| ... | ... | @@ -1055,6 +1055,9 @@ cpeApp top_env expr |
| 1055 | 1055 | || f `hasKey` nospecIdKey -- Replace (nospec a) with a
|
| 1056 | 1056 | -- See Note [nospecId magic] in GHC.Types.Id.Make
|
| 1057 | 1057 | |
| 1058 | + -- NB: keep this in sync with GHC.HsToCore.Pmc.Solver.Types.coreExprAsPmLit,
|
|
| 1059 | + -- as that also needs to see through these magic Ids.
|
|
| 1060 | + |
|
| 1058 | 1061 | -- Consider the code:
|
| 1059 | 1062 | --
|
| 1060 | 1063 | -- lazy (f x) y
|
| ... | ... | @@ -694,6 +694,15 @@ coreExprAsPmLit :: CoreExpr -> Maybe PmLit |
| 694 | 694 | coreExprAsPmLit (Tick _t e) = coreExprAsPmLit e
|
| 695 | 695 | coreExprAsPmLit (Lit l) = literalToPmLit (literalType l) l
|
| 696 | 696 | coreExprAsPmLit e = case collectArgs e of
|
| 697 | + |
|
| 698 | + -- Look through nospec, noinline and lazy, which are only eliminated by Core Prep.
|
|
| 699 | + -- See Note [coreExprAsPmLit and nospec]
|
|
| 700 | + (Var x, Type _ : inner : rest_args)
|
|
| 701 | + | x `hasKey` nospecIdKey
|
|
| 702 | + || x `hasKey` noinlineIdKey
|
|
| 703 | + || x `hasKey` lazyIdKey
|
|
| 704 | + -> coreExprAsPmLit (mkApps inner rest_args)
|
|
| 705 | + |
|
| 697 | 706 | (Var x, [Lit l])
|
| 698 | 707 | | Just dc <- isDataConWorkId_maybe x
|
| 699 | 708 | , dc `elem` [intDataCon, wordDataCon, charDataCon, floatDataCon, doubleDataCon]
|
| ... | ... | @@ -834,6 +843,34 @@ with large exponents case. This will return a `PmLitOverRat` literal. |
| 834 | 843 | Which is then passed to overloadPmLit which simply returns it as-is since
|
| 835 | 844 | it's already overloaded.
|
| 836 | 845 | |
| 846 | +Note [coreExprAsPmLit and nospec]
|
|
| 847 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 848 | +For coverage checking, we need to analyse overloaded literal patterns to figure
|
|
| 849 | +out which literals they correspond to; this is what 'coreExprAsPmLit' does.
|
|
| 850 | +For example, the literal pattern "fromString" (with -XOverloadedStrings)
|
|
| 851 | +will turn into an equality check against the **expression**
|
|
| 852 | + |
|
| 853 | + fromString @T $dFromString "hello"#
|
|
| 854 | + |
|
| 855 | +and 'coreExprAsPmLit' recovers the string by taking apart this application.
|
|
| 856 | + |
|
| 857 | +However, when $dFromString is non-canonical (e.g. when an INCOHERENT
|
|
| 858 | +instance was discarded during resolution of the typeclass constraint, or when
|
|
| 859 | +the dictionary comes from 'withDict'), the desugarer wraps 'fromString' in
|
|
| 860 | +'nospec' (as per Note [nospecId magic] in GHC.Types.Id.Make and
|
|
| 861 | +Note [Desugaring non-canonical evidence] in GHC.HsToCore.Expr):
|
|
| 862 | + |
|
| 863 | + nospec @(IsString a => String -> Maybe a) fromString @T $dFromString "hello"#
|
|
| 864 | + |
|
| 865 | +(For a full example, see test case T27124a.)
|
|
| 866 | + |
|
| 867 | +The 'nospec' mechanism only exists for the specialiser; it should be transparent
|
|
| 868 | +to everything else. 'coreExprAsPmLit' must thus look through the 'nospec'
|
|
| 869 | +application in order obtain the string "hello". If it doesn't, we can't do
|
|
| 870 | +pattern match checking (in fact GHC.HsToCore.Pmc.Desugar.desugarPat is liable
|
|
| 871 | +to crash!).
|
|
| 872 | + |
|
| 873 | +The same reasoning applies to `noinline` and `lazy`.
|
|
| 837 | 874 | -}
|
| 838 | 875 | |
| 839 | 876 | instance Outputable PmLitValue where
|
| ... | ... | @@ -1360,11 +1360,11 @@ addDeferredBinding ctxt supp hints msg (EI { ei_evdest = Just dest |
| 1360 | 1360 | |
| 1361 | 1361 | ; case dest of
|
| 1362 | 1362 | EvVarDest evar
|
| 1363 | - -> addTcEvBind ev_binds_var $ mkWantedEvBind evar EvNonCanonical err_tm
|
|
| 1363 | + -> addTcEvBind ev_binds_var $ mkWantedEvBind evar EvCanonical err_tm
|
|
| 1364 | 1364 | HoleDest hole
|
| 1365 | 1365 | -> do { -- See Note [Deferred errors for coercion holes]
|
| 1366 | 1366 | let co_var = coHoleCoVar hole
|
| 1367 | - ; addTcEvBind ev_binds_var $ mkWantedEvBind co_var EvNonCanonical err_tm
|
|
| 1367 | + ; addTcEvBind ev_binds_var $ mkWantedEvBind co_var EvCanonical err_tm
|
|
| 1368 | 1368 | ; fillCoercionHole hole (CPH { cph_co = mkCoVarCo co_var
|
| 1369 | 1369 | , cph_holes = emptyCoHoleSet }) } }
|
| 1370 | 1370 | addDeferredBinding _ _ _ _ _ = return () -- Do not set any evidence for Given
|
| 1 | +module T25926 where
|
|
| 2 | + |
|
| 3 | +f () 0 = ()
|
|
| 4 | +f 'a' _ = () |
| 1 | +T25926.hs:4:3: warning: [GHC-83865] [-Wdeferred-type-errors (in -Wdefault)]
|
|
| 2 | + • Couldn't match expected type ‘()’ with actual type ‘Char’
|
|
| 3 | + • In the pattern: 'a'
|
|
| 4 | + In an equation for ‘f’: f 'a' _ = ()
|
|
| 5 | + |
| 1 | +{-# LANGUAGE OverloadedStrings #-}
|
|
| 2 | + |
|
| 3 | +module T27124 where
|
|
| 4 | + |
|
| 5 | +foo :: [String] -> Bool
|
|
| 6 | +foo "HI" = True
|
|
| 7 | +foo _ = False
|
|
| 8 | + |
|
| 9 | +main = pure () |
| 1 | +T27124.hs:6:5: warning: [GHC-18872] [-Wdeferred-type-errors (in -Wdefault)]
|
|
| 2 | + • Couldn't match type ‘[Char]’ with ‘Char’
|
|
| 3 | + arising from the literal ‘"HI"’
|
|
| 4 | + • In the pattern: "HI"
|
|
| 5 | + In an equation for ‘foo’: foo "HI" = True
|
|
| 6 | + |
| 1 | +test('T25926', normal, compile, ['-fdefer-type-errors'])
|
|
| 2 | +test('T27124', normal, compile, ['-fdefer-type-errors']) |
| 1 | +{-# LANGUAGE FlexibleInstances #-}
|
|
| 2 | +{-# LANGUAGE OverloadedStrings #-}
|
|
| 3 | + |
|
| 4 | +module T27124a where
|
|
| 5 | + |
|
| 6 | +import Data.String (IsString(..))
|
|
| 7 | + |
|
| 8 | +newtype Wrap a = Wrap a deriving (Eq, Show)
|
|
| 9 | + |
|
| 10 | +instance IsString a => IsString (Wrap a) where
|
|
| 11 | + fromString = Wrap . fromString
|
|
| 12 | + |
|
| 13 | +instance {-# INCOHERENT #-} IsString (Wrap Bool) where
|
|
| 14 | + fromString _ = Wrap False
|
|
| 15 | + |
|
| 16 | +f :: (Eq a, IsString a) => Wrap a -> Bool
|
|
| 17 | +f "hello" = True
|
|
| 18 | +f _ = False
|
|
| 19 | + |
|
| 20 | +main :: IO ()
|
|
| 21 | +main = do
|
|
| 22 | + print (f (Wrap ("hello" :: String)))
|
|
| 23 | + print (f (Wrap ("world" :: String))) |
| 1 | 1 | test('overloadedstringsrun01', normal, compile_and_run, [''])
|
| 2 | +test('T27124a', normal, compile, ['-fno-specialise-incoherents']) |