[Git][ghc/ghc][master] Care with implicit-parameter superclasses
Marge Bot
gitlab at gitlab.haskell.org
Tue Sep 15 19:19:54 UTC 2020
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
c7182a5c by Simon Peyton Jones at 2020-09-15T15:19:44-04:00
Care with implicit-parameter superclasses
Two bugs, #18627 and #18649, had the same cause: we were not
account for the fact that a constaint tuple might hide an implicit
parameter.
The solution is not hard: look for implicit parameters in
superclasses. See Note [Local implicit parameters] in
GHC.Core.Predicate.
Then we use this new function in two places
* The "short-cut solver" in GHC.Tc.Solver.Interact.shortCutSolver
which simply didn't handle implicit parameters properly at all.
This fixes #18627
* The specialiser, which should not specialise on implicit parameters
This fixes #18649
There are some lingering worries (see Note [Local implicit
parameters]) but things are much better.
- - - - -
16 changed files:
- compiler/GHC/Core/Opt/Specialise.hs
- compiler/GHC/Core/Predicate.hs
- compiler/GHC/Tc/Instance/Class.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/Solver.hs
- compiler/GHC/Tc/Solver/Canonical.hs
- compiler/GHC/Tc/Solver/Interact.hs
- compiler/GHC/Tc/Solver/Monad.hs
- compiler/GHC/Tc/TyCl/Instance.hs
- compiler/GHC/Tc/Utils/TcType.hs
- + testsuite/tests/simplCore/should_compile/T18649.hs
- + testsuite/tests/simplCore/should_compile/T18649.stderr
- testsuite/tests/simplCore/should_compile/all.T
- + testsuite/tests/typecheck/should_run/T18627.hs
- + testsuite/tests/typecheck/should_run/T18627.stdout
- testsuite/tests/typecheck/should_run/all.T
Changes:
=====================================
compiler/GHC/Core/Opt/Specialise.hs
=====================================
@@ -2514,9 +2514,12 @@ mkCallUDs' env f args
-- we decide on a case by case basis if we want to specialise
-- on this argument; if so, SpecDict, if not UnspecArg
mk_spec_arg arg (Anon InvisArg pred)
- | type_determines_value (scaledThing pred)
- , interestingDict env arg -- Note [Interesting dictionary arguments]
+ | not (isIPLikePred (scaledThing pred))
+ -- See Note [Type determines value]
+ , interestingDict env arg
+ -- See Note [Interesting dictionary arguments]
= SpecDict arg
+
| otherwise = UnspecArg
mk_spec_arg _ (Anon VisArg _)
@@ -2529,41 +2532,18 @@ mkCallUDs' env f args
-- in specImports
-- Use 'realIdUnfolding' to ignore the loop-breaker flag!
- type_determines_value pred -- See Note [Type determines value]
- = case classifyPredType pred of
- ClassPred cls _ -> not (isIPClass cls) -- Superclasses can't be IPs
- EqPred {} -> True
- IrredPred {} -> True -- Things like (D []) where D is a
- -- Constraint-ranged family; #7785
- ForAllPred {} -> True
-
-{-
-Note [Type determines value]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Only specialise on non-IP *class* params, because these are the ones
-whose *type* determines their *value*. In particular, with implicit
-params, the type args *don't* say what the value of the implicit param
-is! See #7101.
+{- Note [Type determines value]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Only specialise on non-impicit-parameter predicates, because these
+are the ones whose *type* determines their *value*. In particular,
+with implicit params, the type args *don't* say what the value of the
+implicit param is! See #7101.
So we treat implicit params just like ordinary arguments for the
purposes of specialisation. Note that we still want to specialise
functions with implicit params if they have *other* dicts which are
class params; see #17930.
-One apparent additional complexity involves type families. For
-example, consider
- type family D (v::*->*) :: Constraint
- type instance D [] = ()
- f :: D v => v Char -> Int
-If we see a call (f "foo"), we'll pass a "dictionary"
- () |> (g :: () ~ D [])
-and it's good to specialise f at this dictionary.
-
-So the question is: can an implicit parameter "hide inside" a
-type-family constraint like (D a). Well, no. We don't allow
- type instance D Maybe = ?x:Int
-Hence the IrredPred case in type_determines_value. See #7785.
-
Note [Interesting dictionary arguments]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Consider this
=====================================
compiler/GHC/Core/Predicate.hs
=====================================
@@ -20,9 +20,10 @@ module GHC.Core.Predicate (
mkClassPred, isDictTy,
isClassPred, isEqPredClass, isCTupleClass,
getClassPredTys, getClassPredTys_maybe,
+ classMethodTy, classMethodInstTy,
-- Implicit parameters
- isIPPred, isIPPred_maybe, isIPTyCon, isIPClass, hasIPPred,
+ isIPLikePred, hasIPSuperClasses, isIPTyCon, isIPClass,
-- Evidence variables
DictId, isEvVar, isDictId
@@ -39,12 +40,10 @@ import GHC.Core.Multiplicity ( scaledThing )
import GHC.Builtin.Names
-import GHC.Data.FastString
import GHC.Utils.Outputable
import GHC.Utils.Misc
import GHC.Utils.Panic
-import Control.Monad ( guard )
-- | A predicate in the solver. The solver tries to prove Wanted predicates
-- from Given ones.
@@ -95,6 +94,26 @@ getClassPredTys_maybe ty = case splitTyConApp_maybe ty of
Just (tc, tys) | Just clas <- tyConClass_maybe tc -> Just (clas, tys)
_ -> Nothing
+classMethodTy :: Id -> Type
+-- Takes a class selector op :: forall a. C a => meth_ty
+-- and returns the type of its method, meth_ty
+-- The selector can be a superclass selector, in which case
+-- you get back a superclass
+classMethodTy sel_id
+ = funResultTy $ -- meth_ty
+ dropForAlls $ -- C a => meth_ty
+ varType sel_id -- forall a. C n => meth_ty
+
+classMethodInstTy :: Id -> [Type] -> Type
+-- Takes a class selector op :: forall a b. C a b => meth_ty
+-- and the types [ty1, ty2] at which it is instantiated,
+-- returns the instantiated type of its method, meth_ty[t1/a,t2/b]
+-- The selector can be a superclass selector, in which case
+-- you get back a superclass
+classMethodInstTy sel_id arg_tys
+ = funResultTy $
+ piResultTys (varType sel_id) arg_tys
+
-- --------------------- Equality predicates ---------------------------------
-- | A choice of equality relation. This is separate from the type 'Role'
@@ -170,7 +189,7 @@ isEqPredClass :: Class -> Bool
isEqPredClass cls = cls `hasKey` eqTyConKey
|| cls `hasKey` heqTyConKey
-isClassPred, isEqPred, isEqPrimPred, isIPPred :: PredType -> Bool
+isClassPred, isEqPred, isEqPrimPred :: PredType -> Bool
isClassPred ty = case tyConAppTyCon_maybe ty of
Just tyCon | isClassTyCon tyCon -> True
_ -> False
@@ -186,9 +205,15 @@ isEqPred ty -- True of (a ~ b) and (a ~~ b)
isEqPrimPred ty = isCoVarType ty
-- True of (a ~# b) (a ~R# b)
-isIPPred ty = case tyConAppTyCon_maybe ty of
- Just tc -> isIPTyCon tc
- _ -> False
+isCTupleClass :: Class -> Bool
+isCTupleClass cls = isTupleTyCon (classTyCon cls)
+
+
+{- *********************************************************************
+* *
+ Implicit parameters
+* *
+********************************************************************* -}
isIPTyCon :: TyCon -> Bool
isIPTyCon tc = tc `hasKey` ipClassKey
@@ -197,31 +222,103 @@ isIPTyCon tc = tc `hasKey` ipClassKey
isIPClass :: Class -> Bool
isIPClass cls = cls `hasKey` ipClassKey
-isCTupleClass :: Class -> Bool
-isCTupleClass cls = isTupleTyCon (classTyCon cls)
+isIPLikePred :: Type -> Bool
+-- See Note [Local implicit parameters]
+isIPLikePred = is_ip_like_pred initIPRecTc
-isIPPred_maybe :: Type -> Maybe (FastString, Type)
-isIPPred_maybe ty =
- do (tc,[t1,t2]) <- splitTyConApp_maybe ty
- guard (isIPTyCon tc)
- x <- isStrLitTy t1
- return (x,t2)
-
-hasIPPred :: PredType -> Bool
-hasIPPred pred
- = case classifyPredType pred of
- ClassPred cls tys
- | isIPClass cls -> True
- | isCTupleClass cls -> any hasIPPred tys
- _other -> False
-{-
-************************************************************************
+is_ip_like_pred :: RecTcChecker -> Type -> Bool
+is_ip_like_pred rec_clss ty
+ | Just (tc, tys) <- splitTyConApp_maybe ty
+ , Just rec_clss' <- if isTupleTyCon tc -- Tuples never cause recursion
+ then Just rec_clss
+ else checkRecTc rec_clss tc
+ , Just cls <- tyConClass_maybe tc
+ = isIPClass cls || has_ip_super_classes rec_clss' cls tys
+
+ | otherwise
+ = False -- Includes things like (D []) where D is
+ -- a Constraint-ranged family; #7785
+
+hasIPSuperClasses :: Class -> [Type] -> Bool
+-- See Note [Local implicit parameters]
+hasIPSuperClasses = has_ip_super_classes initIPRecTc
+
+has_ip_super_classes :: RecTcChecker -> Class -> [Type] -> Bool
+has_ip_super_classes rec_clss cls tys
+ = any ip_ish (classSCSelIds cls)
+ where
+ -- Check that the type of a superclass determines its value
+ -- sc_sel_id :: forall a b. C a b -> <superclass type>
+ ip_ish sc_sel_id = is_ip_like_pred rec_clss $
+ classMethodInstTy sc_sel_id tys
+
+initIPRecTc :: RecTcChecker
+initIPRecTc = setRecTcMaxBound 1 initRecTc
+
+{- Note [Local implicit parameters]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The function isIPLikePred tells if this predicate, or any of its
+superclasses, is an implicit parameter.
+
+Why are implicit parameters special? Unlike normal classes, we can
+have local instances for implicit parameters, in the form of
+ let ?x = True in ...
+So in various places we must be careful not to assume that any value
+of the right type will do; we must carefully look for the innermost binding.
+So isIPLikePred checks whether this is an implicit parameter, or has
+a superclass that is an implicit parameter.
+
+Several wrinkles
+
+* We must be careful with superclasses, as #18649 showed. Haskell
+ doesn't allow an implicit parameter as a superclass
+ class (?x::a) => C a where ...
+ but with a constraint tuple we might have
+ (% Eq a, ?x::Int %)
+ and /its/ superclasses, namely (Eq a) and (?x::Int), /do/ include an
+ implicit parameter.
+
+ With ConstraintKinds this can apply to /any/ class, e.g.
+ class sc => C sc where ...
+ Then (C (?x::Int)) has (?x::Int) as a superclass. So we must
+ instantiate and check each superclass, one by one, in
+ hasIPSuperClasses.
+
+* With -XRecursiveSuperClasses, the superclass hunt can go on forever,
+ so we need a RecTcChecker to cut it off.
+
+* Another apparent additional complexity involves type families. For
+ example, consider
+ type family D (v::*->*) :: Constraint
+ type instance D [] = ()
+ f :: D v => v Char -> Int
+ If we see a call (f "foo"), we'll pass a "dictionary"
+ () |> (g :: () ~ D [])
+ and it's good to specialise f at this dictionary.
+
+So the question is: can an implicit parameter "hide inside" a
+type-family constraint like (D a). Well, no. We don't allow
+ type instance D Maybe = ?x:Int
+Hence the umbrella 'otherwise' case in is_ip_like_pred. See #7785.
+
+Small worries (Sept 20):
+* I don't see what stops us having that 'type instance'. Indeed I
+ think nothing does.
+* I'm a little concerned about type variables; such a variable might
+ be instantiated to an implicit parameter. I don't think this
+ matters in the cases for which isIPLikePred is used, and it's pretty
+ obscure anyway.
+* The superclass hunt stops when it encounters the same class again,
+ but in principle we could have the same class, differently instantiated,
+ and the second time it could have an implicit parameter
+I'm going to treat these as problems for another day. They are all exotic. -}
+
+{- *********************************************************************
* *
Evidence variables
* *
-************************************************************************
--}
+********************************************************************* -}
isEvVar :: Var -> Bool
isEvVar var = isEvVarType (varType var)
=====================================
compiler/GHC/Tc/Instance/Class.hs
=====================================
@@ -388,10 +388,9 @@ makeLitDict clas ty et
| Just (_, co_dict) <- tcInstNewTyCon_maybe (classTyCon clas) [ty]
-- co_dict :: KnownNat n ~ SNat n
, [ meth ] <- classMethods clas
- , Just tcRep <- tyConAppTyCon_maybe -- SNat
- $ funResultTy -- SNat n
- $ dropForAlls -- KnownNat n => SNat n
- $ idType meth -- forall n. KnownNat n => SNat n
+ , Just tcRep <- tyConAppTyCon_maybe (classMethodTy meth)
+ -- If the method type is forall n. KnownNat n => SNat n
+ -- then tcRep is SNat
, Just (_, co_rep) <- tcInstNewTyCon_maybe tcRep [ty]
-- SNat n ~ Integer
, let ev_tm = mkEvCast et (mkTcSymCo (mkTcTransCo co_dict co_rep))
=====================================
compiler/GHC/Tc/Module.hs
=====================================
@@ -65,9 +65,10 @@ import GHC.Builtin.Types ( unitTy, mkListTy )
import GHC.Driver.Plugins
import GHC.Driver.Session
import GHC.Hs
-import GHC.Iface.Syntax ( ShowSub(..), showToHeader )
-import GHC.Iface.Type ( ShowForAllFlag(..) )
-import GHC.Core.PatSyn( pprPatSynType )
+import GHC.Iface.Syntax ( ShowSub(..), showToHeader )
+import GHC.Iface.Type ( ShowForAllFlag(..) )
+import GHC.Core.PatSyn ( pprPatSynType )
+import GHC.Core.Predicate ( classMethodTy )
import GHC.Builtin.Names
import GHC.Builtin.Utils
import GHC.Types.Name.Reader
@@ -1014,10 +1015,8 @@ checkBootTyCon is_boot tc1 tc2
name2 = idName id2
pname1 = quotes (ppr name1)
pname2 = quotes (ppr name2)
- (_, rho_ty1) = splitForAllTys (idType id1)
- op_ty1 = funResultTy rho_ty1
- (_, rho_ty2) = splitForAllTys (idType id2)
- op_ty2 = funResultTy rho_ty2
+ op_ty1 = classMethodTy id1
+ op_ty2 = classMethodTy id2
eqAT (ATI tc1 def_ats1) (ATI tc2 def_ats2)
= checkBootTyCon is_boot tc1 tc2 `andThenCheck`
=====================================
compiler/GHC/Tc/Solver.hs
=====================================
@@ -1374,7 +1374,7 @@ growThetaTyVars theta tcvs
| otherwise = transCloVarSet mk_next seed_tcvs
where
seed_tcvs = tcvs `unionVarSet` tyCoVarsOfTypes ips
- (ips, non_ips) = partition isIPPred theta
+ (ips, non_ips) = partition isIPLikePred theta
-- See Note [Inheriting implicit parameters] in GHC.Tc.Utils.TcType
mk_next :: VarSet -> VarSet -- Maps current set to newly-grown ones
=====================================
compiler/GHC/Tc/Solver/Canonical.hs
=====================================
@@ -29,7 +29,7 @@ import GHC.Core.Multiplicity
import GHC.Core.TyCo.Rep -- cleverly decomposes types, good for completeness checking
import GHC.Core.Coercion
import GHC.Core
-import GHC.Types.Id( idType, mkTemplateLocals )
+import GHC.Types.Id( mkTemplateLocals )
import GHC.Core.FamInstEnv ( FamInstEnvs )
import GHC.Tc.Instance.Family ( tcTopNormaliseNewTypeTF_maybe )
import GHC.Types.Var
@@ -542,7 +542,7 @@ mk_strict_superclasses rec_clss (CtGiven { ctev_evar = evar, ctev_loc = loc })
mk_given_desc sel_id sc_pred
; mk_superclasses rec_clss given_ev tvs theta sc_pred }
where
- sc_pred = funResultTy (piResultTys (idType sel_id) tys)
+ sc_pred = classMethodInstTy sel_id tys
-- See Note [Nested quantified constraint superclasses]
mk_given_desc :: Id -> PredType -> (PredType, EvTerm)
=====================================
compiler/GHC/Tc/Solver/Interact.hs
=====================================
@@ -566,10 +566,10 @@ solveOneFromTheOther ev_i ev_w
ev_id_w = ctEvEvId ev_w
different_level_strategy -- Both Given
- | isIPPred pred = if lvl_w > lvl_i then KeepWork else KeepInert
- | otherwise = if lvl_w > lvl_i then KeepInert else KeepWork
+ | isIPLikePred pred = if lvl_w > lvl_i then KeepWork else KeepInert
+ | otherwise = if lvl_w > lvl_i then KeepInert else KeepWork
-- See Note [Replacement vs keeping] (the different-level bullet)
- -- For the isIPPred case see Note [Shadowing of Implicit Parameters]
+ -- For the isIPLikePred case see Note [Shadowing of Implicit Parameters]
same_level_strategy binds -- Both Given
| GivenOrigin (InstSC s_i) <- ctLocOrigin loc_i
@@ -1071,6 +1071,8 @@ shortCutSolver dflags ev_w ev_i
-- programs should typecheck regardless of whether we take this step or
-- not. See Note [Shortcut solving]
+ && not (isIPLikePred (ctEvPred ev_w)) -- Not for implicit parameters (#18627)
+
&& not (xopt LangExt.IncoherentInstances dflags)
-- If IncoherentInstances is on then we cannot rely on coherence of proofs
-- in order to justify this optimization: The proof provided by the
@@ -1079,6 +1081,7 @@ shortCutSolver dflags ev_w ev_i
&& gopt Opt_SolveConstantDicts dflags
-- Enabled by the -fsolve-constant-dicts flag
+
= do { ev_binds_var <- getTcEvBindsVar
; ev_binds <- ASSERT2( not (isCoEvBindsVar ev_binds_var ), ppr ev_w )
getTcEvBindsMap ev_binds_var
=====================================
compiler/GHC/Tc/Solver/Monad.hs
=====================================
@@ -2527,8 +2527,7 @@ emptyDictMap = emptyTcAppMap
findDict :: DictMap a -> CtLoc -> Class -> [Type] -> Maybe a
findDict m loc cls tys
- | isCTupleClass cls
- , any hasIPPred tys -- See Note [Tuples hiding implicit parameters]
+ | hasIPSuperClasses cls tys -- See Note [Tuples hiding implicit parameters]
= Nothing
| Just {} <- isCallStackPred cls tys
=====================================
compiler/GHC/Tc/TyCl/Instance.hs
=====================================
@@ -54,6 +54,7 @@ import GHC.Core.Make ( nO_METHOD_BINDING_ERROR_ID )
import GHC.Core.Unfold.Make ( mkInlineUnfoldingWithArity, mkDFunUnfolding )
import GHC.Core.Type
import GHC.Core.SimpleOpt
+import GHC.Core.Predicate( classMethodInstTy )
import GHC.Tc.Types.Evidence
import GHC.Core.TyCon
import GHC.Core.Coercion.Axiom
@@ -1634,7 +1635,7 @@ tcMethods dfun_id clas tyvars dfun_ev_vars inst_tys
nO_METHOD_BINDING_ERROR_ID
error_msg dflags = L inst_loc (HsLit noExtField (HsStringPrim NoSourceText
(unsafeMkByteString (error_string dflags))))
- meth_tau = funResultTy (piResultTys (idType sel_id) inst_tys)
+ meth_tau = classMethodInstTy sel_id inst_tys
error_string dflags = showSDoc dflags
(hcat [ppr inst_loc, vbar, ppr sel_id ])
lam_wrapper = mkWpTyLams tyvars <.> mkWpLams dfun_ev_vars
=====================================
compiler/GHC/Tc/Utils/TcType.hs
=====================================
@@ -79,7 +79,7 @@ module GHC.Tc.Utils.TcType (
isFloatingTy, isDoubleTy, isFloatTy, isIntTy, isWordTy, isStringTy,
isIntegerTy, isNaturalTy,
isBoolTy, isUnitTy, isCharTy, isCallStackTy, isCallStackPred,
- hasIPPred, isTauTy, isTauTyCon, tcIsTyVarTy, tcIsForAllTy,
+ isTauTy, isTauTyCon, tcIsTyVarTy, tcIsForAllTy,
isPredTy, isTyVarClassPred, isTyVarHead, isInsolubleOccursCheck,
checkValidClsArgs, hasTyVarHead,
isRigidTy, isAlmostFunctionFree,
@@ -141,7 +141,7 @@ module GHC.Tc.Utils.TcType (
mkTyConTy, mkTyVarTy, mkTyVarTys,
mkTyCoVarTy, mkTyCoVarTys,
- isClassPred, isEqPrimPred, isIPPred, isEqPred, isEqPredClass,
+ isClassPred, isEqPrimPred, isIPLikePred, isEqPred, isEqPredClass,
mkClassPred,
tcSplitDFunTy, tcSplitDFunHead, tcSplitMethodTy,
isRuntimeRepVar, isKindLevPoly,
@@ -1747,7 +1747,7 @@ pickCapturedPreds
pickCapturedPreds qtvs theta
= filter captured theta
where
- captured pred = isIPPred pred || (tyCoVarsOfType pred `intersectsVarSet` qtvs)
+ captured pred = isIPLikePred pred || (tyCoVarsOfType pred `intersectsVarSet` qtvs)
-- Superclasses
=====================================
testsuite/tests/simplCore/should_compile/T18649.hs
=====================================
@@ -0,0 +1,26 @@
+{-# LANGUAGE ImplicitParams #-}
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE FlexibleContexts #-}
+
+module Test where
+
+import Prelude
+
+type Hidden a =
+ ( ?enable :: a
+ , Eq a -- removing this "fixes" the issue
+ )
+
+{-# NOINLINE a #-}
+a :: Hidden Bool => Integer -> Bool
+a _ = ?enable
+
+system :: Hidden Bool => Bool
+system = a 0
+
+topEntity :: Bool -> Bool
+topEntity ena = let ?enable = ena
+ in system
+
+someVar = let ?enable = True
+ in system
=====================================
testsuite/tests/simplCore/should_compile/T18649.stderr
=====================================
@@ -0,0 +1,4 @@
+
+==================== Tidy Core rules ====================
+
+
=====================================
testsuite/tests/simplCore/should_compile/all.T
=====================================
@@ -333,3 +333,7 @@ test('T18347', normal, compile, ['-dcore-lint -O'])
test('T18355', [ grep_errmsg(r'OneShot') ], compile, ['-O -ddump-simpl -dsuppress-uniques'])
test('T18399', normal, compile, ['-dcore-lint -O'])
test('T18589', normal, compile, ['-dcore-lint -O'])
+
+# T18649 should /not/ generate a specialisation rule
+test('T18649', normal, compile, ['-O -ddump-rules -Wno-simplifiable-class-constraints'])
+
=====================================
testsuite/tests/typecheck/should_run/T18627.hs
=====================================
@@ -0,0 +1,16 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE ImplicitParams #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+module Main where
+
+import GHC.Classes
+
+instance IP "x" Int where
+ ip = 21
+
+baz :: (?x :: Int) => Int
+baz = ?x
+
+main :: IO ()
+main = let ?x = 42
+ in print baz
=====================================
testsuite/tests/typecheck/should_run/T18627.stdout
=====================================
@@ -0,0 +1 @@
+42
=====================================
testsuite/tests/typecheck/should_run/all.T
=====================================
@@ -146,3 +146,4 @@ test('UnliftedNewtypesDependentFamilyRun', normal, compile_and_run, [''])
test('UnliftedNewtypesIdentityRun', normal, compile_and_run, [''])
test('UnliftedNewtypesCoerceRun', normal, compile_and_run, [''])
test('T17104', normal, compile_and_run, [''])
+test('T18627', normal, compile_and_run, ['-O']) # Optimisation shows up the bug
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/c7182a5c67fe8b5bd256cb8eb805562636853ea2
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/c7182a5c67fe8b5bd256cb8eb805562636853ea2
You're receiving this email because of your account on gitlab.haskell.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20200915/22712f4f/attachment-0001.html>
More information about the ghc-commits
mailing list