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

Commits:

6 changed files:

Changes:

  • compiler/GHC/Core/Opt/Arity.hs
    ... ... @@ -2993,12 +2993,12 @@ pushCoValArg co
    2993 2993
         Pair tyL tyR = coercionKind co
    
    2994 2994
     
    
    2995 2995
     pushCoercionIntoLambda
    
    2996
    -    :: HasDebugCallStack => Subst -> InVar -> InExpr -> OutCoercionR -> Maybe (OutVar, OutExpr)
    
    2996
    +    :: HasDebugCallStack => InScopeSet -> Var -> CoreExpr -> CoercionR -> Maybe (Var, CoreExpr)
    
    2997 2997
     -- This implements the Push rule from the paper on coercions
    
    2998 2998
     --    (\x. e) |> co
    
    2999 2999
     -- ===>
    
    3000 3000
     --    (\x'. e |> co')
    
    3001
    -pushCoercionIntoLambda subst x e co
    
    3001
    +pushCoercionIntoLambda in_scope x e co
    
    3002 3002
         | assert (not (isTyVar x) && not (isCoVar x)) True
    
    3003 3003
         , Pair s1s2 t1t2 <- coercionKind co
    
    3004 3004
         , Just {}              <- splitFunTy_maybe s1s2
    
    ... ... @@ -3011,9 +3011,9 @@ pushCoercionIntoLambda subst x e co
    3011 3011
               -- Should we optimize the coercions here?
    
    3012 3012
               -- Otherwise they might not match too well
    
    3013 3013
               x' = x `setIdType` t1 `setIdMult` w1
    
    3014
    -          in_scope' = substInScopeSet subst `extendInScopeSet` x'
    
    3014
    +          in_scope' = in_scope `extendInScopeSet` x'
    
    3015 3015
               subst' =
    
    3016
    -            extendIdSubst (setInScope subst in_scope')
    
    3016
    +            extendIdSubst (setInScope emptySubst in_scope')
    
    3017 3017
                   x
    
    3018 3018
                   (mkCast (Var x') (mkSymCo co1))
    
    3019 3019
                 -- We substitute x' for x, except we need to preserve types.
    

  • compiler/GHC/Core/SimpleOpt.hs
    ... ... @@ -393,12 +393,19 @@ simple_app env e0@(Lam {}) as0@(_:_)
    393 393
           = wrapLet mb_pr $ do_beta env'' body as
    
    394 394
           where (env', b') = subst_opt_bndr env b
    
    395 395
     
    
    396
    -    do_beta env e@(Lam b body) as@(CastIt co:rest)
    
    397
    -      -- See Note [Desugaring unlifted newtypes]
    
    396
    +    -- See Note [Eliminate casts in function position]
    
    397
    +    do_beta env e@(Lam b _) as@(CastIt out_co:rest)
    
    398 398
           | isNonCoVarId b
    
    399
    -      , Just (b', body') <- pushCoercionIntoLambda (soe_subst env) b body co
    
    399
    +      -- Optimise the inner lambda to make it an 'OutExpr', which makes it
    
    400
    +      -- possible to call 'pushCoercionIntoLambda' with the 'OutCoercion' 'co'.
    
    401
    +      -- This is kind of horrible, as for nested casted lambdas with a big body,
    
    402
    +      -- we will repeatedly optimise the body (once for each binder). However,
    
    403
    +      -- we need to do this to avoid mixing 'InExpr' and 'OutExpr', or two
    
    404
    +      -- 'InExpr' with different environments (getting this wrong caused #26588 & #26589.)
    
    405
    +      , Lam out_b out_body <- simple_app env e []
    
    406
    +      , Just (b', body') <- pushCoercionIntoLambda (soeInScope env) out_b out_body out_co
    
    400 407
           = do_beta (soeZapSubst env) (Lam b' body') rest
    
    401
    -        -- soeZapSubst: pushCoercionIntoLambda applies the substitution
    
    408
    +        -- soeZapSubst: we've already optimised everything (the lambda and 'rest') by now.
    
    402 409
           | otherwise
    
    403 410
           = rebuild_app env (simple_opt_expr env e) as
    
    404 411
     
    
    ... ... @@ -511,7 +518,31 @@ TL;DR: To avoid the rest of the compiler pipeline seeing these bad lambas, we
    511 518
     rely on the simple optimiser to both inline the newtype unfolding and
    
    512 519
     subsequently deal with the resulting lambdas (either beta-reducing them
    
    513 520
     altogether or pushing coercions into them so that they satisfy the
    
    514
    -representation-polymorphism invariants).
    
    521
    +representation-polymorphism invariants). See Note [Eliminate casts in function position].
    
    522
    +
    
    523
    +[Alternative approach] (GHC ticket #26608)
    
    524
    +
    
    525
    +  We could instead, in the typechecker, emit a special form (a new constructor
    
    526
    +  of XXExprGhcTc) for instantiations of representation-polymorphic unlifted
    
    527
    +  newtypes (whether applied to a value argument or not):
    
    528
    +
    
    529
    +    UnliftedNT :: DataCon -> [Type] -> Coercion -> XXExprGhcTc
    
    530
    +
    
    531
    +  where "UnliftedNT nt_con [ty1, ...] co" represents the expression:
    
    532
    +
    
    533
    +    ( nt_con @ty1 ... ) |> co
    
    534
    +
    
    535
    +  The desugarer would then turn these AST nodes into appropriate Core, doing
    
    536
    +  what the simple optimiser does today:
    
    537
    +    - inline the compulsory unfolding of the newtype constructor
    
    538
    +    - apply it to its type arguments and beta reduce
    
    539
    +    - push the coercion into the resulting lambda
    
    540
    +
    
    541
    +  This would have several advantages:
    
    542
    +    - the desugarer would never produce "invalid" Core that needs to be
    
    543
    +      tidied up by the simple optimiser,
    
    544
    +    - the ugly and inefficient implementation described in
    
    545
    +      Note [Eliminate casts in function position] could be removed.
    
    515 546
     
    
    516 547
     Wrinkle [Unlifted newtypes with wrappers]
    
    517 548
     
    
    ... ... @@ -717,50 +748,49 @@ rhss here.
    717 748
     
    
    718 749
     Note [Eliminate casts in function position]
    
    719 750
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    720
    -Consider the following program:
    
    751
    +Due to the current implementation strategy for representation-polymorphic
    
    752
    +unlifted newtypes, as described in Note [Desugaring unlifted newtypes], we rely
    
    753
    +on the simple optimiser to push coercions into lambdas, such as in the following
    
    754
    +example:
    
    721 755
     
    
    722 756
       type R :: Type -> RuntimeRep
    
    723
    -  type family R a where { R Float = FloatRep; R Double = DoubleRep }
    
    724
    -  type F :: forall (a :: Type) -> TYPE (R a)
    
    725
    -  type family F a where { F Float = Float#  ; F Double = Double# }
    
    757
    +  type family R a where { R Int = IntRep }
    
    758
    +  type F :: forall a -> TYPE (R a)
    
    759
    +  type family F a where { F Int = Int# }
    
    726 760
     
    
    727
    -  type N :: forall (a :: Type) -> TYPE (R a)
    
    728 761
       newtype N a = MkN (F a)
    
    729 762
     
    
    730
    -As MkN is a newtype, its unfolding is a lambda which wraps its argument
    
    731
    -in a cast:
    
    732
    -
    
    733
    -  MkN :: forall (a :: Type). F a -> N a
    
    734
    -  MkN = /\a \(x::F a). x |> co_ax
    
    735
    -    -- recall that F a :: TYPE (R a)
    
    736
    -
    
    737
    -This is a representation-polymorphic lambda, in which the binder has an unknown
    
    738
    -representation (R a). We can't compile such a lambda on its own, but we can
    
    739
    -compile instantiations, such as `MkN @Float` or `MkN @Double`.
    
    763
    +Now, an instantiated occurrence of 'MkN', such as 'MkN @Int' (whether applied
    
    764
    +to a value argument or not) will lead, after inlining the compulsory unfolding
    
    765
    +of 'MkN', to a lambda fo the form:
    
    740 766
     
    
    741
    -Our strategy to avoid running afoul of the representation-polymorphism
    
    742
    -invariants of Note [Representation polymorphism invariants] in GHC.Core is thus:
    
    767
    +  ( \ ( x :: F Int ) -> body ) |> co
    
    743 768
     
    
    744
    -  1. Give the newtype a compulsory unfolding (it has no binding, as we can't
    
    745
    -     define lambdas with representation-polymorphic value binders in source Haskell).
    
    746
    -  2. Rely on the optimiser to beta-reduce away any representation-polymorphic
    
    747
    -     value binders.
    
    769
    +    where
    
    770
    +      co :: ( F Int -> res ) ~# ( Int# -> res )
    
    748 771
     
    
    749
    -For example, consider the application
    
    772
    +The problem is that we now have a lambda abstraction whose binder does not have a
    
    773
    +fixed RuntimeRep in the sense of Note [Fixed RuntimeRep] in GHC.Tc.Utils.Concrete.
    
    750 774
     
    
    751
    -    MkN @Float 34.0#
    
    775
    +However, if we use 'pushCoercionIntoLambda', we end up with:
    
    752 776
     
    
    753
    -After inlining MkN we'll get
    
    777
    +  ( \ ( x' :: Int# ) -> body' )
    
    754 778
     
    
    755
    -   ((/\a \(x:F a). x |> co_ax) @Float) |> co 34#
    
    779
    +which satisfies the representation-polymorphism invariants of
    
    780
    +Note [Representation polymorphism invariants] in GHC.Core.
    
    756 781
     
    
    757
    -where co :: (F Float -> N Float) ~ (Float# ~ N Float)
    
    782
    +In conclusion:
    
    758 783
     
    
    759
    -But to actually beta-reduce that lambda, we need to push the 'co'
    
    760
    -inside the `\x` with pushCoercionIntoLambda.  Hence the extra
    
    761
    -equation for Cast-of-Lam in simple_app.
    
    784
    +  1. The simple optimiser must push casts into lambdas.
    
    785
    +  2. It must also deal with a situation such as (MkN @Int) |> co, where we first
    
    786
    +     inline the compulsory unfolding of N. This means the simple optimiser must
    
    787
    +     "peel off" the casts and optimise the inner expression first, to determine
    
    788
    +     whether it is a lambda abstraction or not.
    
    762 789
     
    
    763
    -This is regrettably delicate.
    
    790
    +This is regrettably delicate. If we could make sure the typechecker/desugarer
    
    791
    +did not produce these bad lambdas in the first place (as described in
    
    792
    +[Alternative approach] in Note [Desugaring unlifted newtypes]), we could
    
    793
    +get rid of this ugly logic.
    
    764 794
     
    
    765 795
     Note [Preserve join-binding arity]
    
    766 796
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    ... ... @@ -1673,7 +1703,7 @@ exprIsLambda_maybe ise@(ISE in_scope_set _) (Cast casted_e co)
    1673 1703
         -- this implies that x is not in scope in gamma (makes this code simpler)
    
    1674 1704
         , not (isTyVar x) && not (isCoVar x)
    
    1675 1705
         , assert (not $ x `elemVarSet` tyCoVarsOfCo co) True
    
    1676
    -    , Just (x',e') <- pushCoercionIntoLambda (mkEmptySubst in_scope_set) x e co
    
    1706
    +    , Just (x',e') <- pushCoercionIntoLambda in_scope_set x e co
    
    1677 1707
         , let res = Just (x',e',ts)
    
    1678 1708
         = --pprTrace "exprIsLambda_maybe:Cast" (vcat [ppr casted_e,ppr co,ppr res)])
    
    1679 1709
           res
    

  • compiler/GHC/Tc/Gen/App.hs
    ... ... @@ -749,13 +749,13 @@ tcInstFun do_ql inst_final (tc_fun, fun_ctxt) fun_sigma rn_args
    749 749
         go1 _pos acc fun_ty []
    
    750 750
            | XExpr (ConLikeTc (RealDataCon dc)) <- tc_fun
    
    751 751
            , isNewDataCon dc
    
    752
    -       , [Scaled _ arg_ty] <- dataConOrigArgTys dc
    
    752
    +       , [Scaled _ orig_arg_ty] <- dataConOrigArgTys dc
    
    753 753
            , n_val_args == 0
    
    754 754
            -- If we're dealing with an unsaturated representation-polymorphic
    
    755 755
            -- UnliftedNewype, then perform a representation-polymorphism check.
    
    756 756
            -- See Note [Representation-polymorphism checks for unsaturated unlifted newtypes]
    
    757 757
            -- in GHC.Tc.Utils.Concrete.
    
    758
    -       , not $ typeHasFixedRuntimeRep arg_ty
    
    758
    +       , not $ typeHasFixedRuntimeRep orig_arg_ty
    
    759 759
            = do { (wrap_co, arg_ty, res_ty) <-
    
    760 760
                       matchActualFunTy (FRRRepPolyUnliftedNewtype dc)
    
    761 761
                         (Just $ HsExprTcThing tc_fun)
    

  • testsuite/tests/simplCore/should_compile/T26588.hs
    1
    +module T26588 ( getOptionSettingFromText ) where
    
    2
    +
    
    3
    +import           Control.Applicative ( Const(..) )
    
    4
    +import           Data.Map (Map)
    
    5
    +import qualified Data.Map.Strict as Map
    
    6
    +
    
    7
    +------------------------------------------------------------------------
    
    8
    +-- ConfigState
    
    9
    +
    
    10
    +data ConfigLeaf
    
    11
    +data ConfigTrie = ConfigTrie !(Maybe ConfigLeaf) !ConfigMap
    
    12
    +
    
    13
    +type ConfigMap = Map Int ConfigTrie
    
    14
    +
    
    15
    +freshLeaf :: [Int] -> ConfigLeaf -> ConfigTrie
    
    16
    +freshLeaf [] l     = ConfigTrie (Just l) mempty
    
    17
    +freshLeaf (a:as) l = ConfigTrie Nothing (Map.singleton a (freshLeaf as l))
    
    18
    +
    
    19
    +adjustConfigTrie :: Functor t => [Int] -> (Maybe ConfigLeaf -> t (Maybe ConfigLeaf)) -> Maybe (ConfigTrie) -> t (Maybe ConfigTrie)
    
    20
    +adjustConfigTrie     as f Nothing                 = fmap (freshLeaf as) <$> f Nothing
    
    21
    +adjustConfigTrie (a:as) f (Just (ConfigTrie x m)) = Just . ConfigTrie x <$> adjustConfigMap a as f m
    
    22
    +adjustConfigTrie     [] f (Just (ConfigTrie x m)) = g <$> f x
    
    23
    +  where g Nothing | Map.null m = Nothing
    
    24
    +        g x' = Just (ConfigTrie x' m)
    
    25
    +
    
    26
    +adjustConfigMap :: Functor t => Int -> [Int] -> (Maybe ConfigLeaf -> t (Maybe ConfigLeaf)) -> ConfigMap -> t ConfigMap
    
    27
    +adjustConfigMap a as f = Map.alterF (adjustConfigTrie as f) a
    
    28
    +
    
    29
    +getOptionSettingFromText :: Int -> [Int] -> ConfigMap -> IO ()
    
    30
    +getOptionSettingFromText p ps = getConst . adjustConfigMap p ps f
    
    31
    +  where
    
    32
    +    f _ = Const (return ())

  • testsuite/tests/simplCore/should_compile/T26589.hs
    1
    +module T26589 ( executeTest ) where
    
    2
    +
    
    3
    +-- base
    
    4
    +import Data.Coerce ( coerce )
    
    5
    +import Data.Foldable ( foldMap )
    
    6
    +
    
    7
    +--------------------------------------------------------------------------------
    
    8
    +
    
    9
    +newtype Traversal f = Traversal { getTraversal :: f () }
    
    10
    +
    
    11
    +instance Applicative f => Semigroup (Traversal f) where
    
    12
    +  Traversal f1 <> Traversal f2 = Traversal $ f1 *> f2
    
    13
    +instance Applicative f => Monoid (Traversal f) where
    
    14
    +  mempty = Traversal $ pure ()
    
    15
    +
    
    16
    +newtype Seq a = Seq (FingerTree (Elem a))
    
    17
    +newtype Elem a = Elem { getElem :: a }
    
    18
    +
    
    19
    +data FingerTree a
    
    20
    +    = EmptyT
    
    21
    +    | Deep !a (FingerTree a) !a
    
    22
    +
    
    23
    +executeTest :: Seq () -> IO ()
    
    24
    +executeTest fins = destroyResources
    
    25
    +  where
    
    26
    +    destroyResources :: IO ()
    
    27
    +    destroyResources =
    
    28
    +      getTraversal $
    
    29
    +        flip foldMap1 fins $ \ _ ->
    
    30
    +          Traversal $ return ()
    
    31
    +
    
    32
    +foldMap1 :: forall m a. Monoid m => (a -> m) -> Seq a -> m
    
    33
    +foldMap1 = coerce (foldMap2 :: (Elem a -> m) -> FingerTree (Elem a) -> m)
    
    34
    +
    
    35
    +foldMap2 :: Monoid m => (Elem a -> m) -> FingerTree (Elem a) -> m
    
    36
    +foldMap2 _ EmptyT = mempty
    
    37
    +foldMap2 f' (Deep pr' m' sf') = f' pr' <> foldMapTree f' m' <> f' sf'
    
    38
    +      where
    
    39
    +        foldMapTree :: Monoid m => (a -> m) -> FingerTree a -> m
    
    40
    +        foldMapTree _ EmptyT = mempty
    
    41
    +        foldMapTree f (Deep pr m sf) =
    
    42
    +            f pr <>
    
    43
    +            foldMapTree f m <>
    
    44
    +            f sf

  • testsuite/tests/simplCore/should_compile/all.T
    ... ... @@ -544,6 +544,9 @@ test('T25883b', normal, compile_grep_core, [''])
    544 544
     test('T25883c', normal, compile_grep_core, [''])
    
    545 545
     test('T25883d', [extra_files(['T25883d_import.hs'])], multimod_compile_filter, ['T25883d', '-O -ddump-simpl -dno-typeable-binds -dsuppress-all -dsuppress-uniques', r'grep -e "y ="'])
    
    546 546
     
    
    547
    +test('T26588', normal, compile, ['-package containers -O'])
    
    548
    +test('T26589', normal, compile, ['-O'])
    
    549
    +
    
    547 550
     test('T25976', [grep_errmsg('Dead Code')], compile, ['-O -ddump-simpl -dsuppress-uniques -dno-typeable-binds'])
    
    548 551
     
    
    549 552
     test('T25965', normal, compile, ['-O'])