Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC

Commits:

11 changed files:

Changes:

  • changelog.d/T27124.md
    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".

  • compiler/GHC/CoreToStg/Prep.hs
    ... ... @@ -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
    

  • compiler/GHC/HsToCore/Pmc/Solver/Types.hs
    ... ... @@ -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
    

  • compiler/GHC/Tc/Errors.hs
    ... ... @@ -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
    

  • testsuite/tests/overloadedstrings/should_fail/T25926.hs
    1
    +module T25926 where
    
    2
    +
    
    3
    +f () 0 = ()
    
    4
    +f 'a' _ = ()

  • testsuite/tests/overloadedstrings/should_fail/T25926.stderr
    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
    +

  • testsuite/tests/overloadedstrings/should_fail/T27124.hs
    1
    +{-# LANGUAGE OverloadedStrings #-}
    
    2
    +
    
    3
    +module T27124 where
    
    4
    +
    
    5
    +foo :: [String] -> Bool
    
    6
    +foo "HI" = True
    
    7
    +foo _ = False
    
    8
    +
    
    9
    +main = pure ()

  • testsuite/tests/overloadedstrings/should_fail/T27124.stderr
    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
    +

  • testsuite/tests/overloadedstrings/should_fail/all.T
    1
    +test('T25926', normal, compile, ['-fdefer-type-errors'])
    
    2
    +test('T27124', normal, compile, ['-fdefer-type-errors'])

  • testsuite/tests/overloadedstrings/should_run/T27124a.hs
    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)))

  • testsuite/tests/overloadedstrings/should_run/all.T
    1 1
     test('overloadedstringsrun01', normal, compile_and_run, [''])
    
    2
    +test('T27124a', normal, compile, ['-fno-specialise-incoherents'])