Apoorv Ingle pushed to branch wip/ani/T27156 at Glasgow Haskell Compiler / GHC
Commits:
-
3bd74519
by Apoorv Ingle at 2026-04-09T18:15:00-05:00
7 changed files:
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Rename/Expr.hs
- compiler/GHC/Rename/Utils.hs
- compiler/GHC/Tc/Gen/Expand.hs
- compiler/GHC/Tc/Types/Origin.hs
- testsuite/tests/parser/should_fail/RecordDotSyntaxFail8.stderr
- testsuite/tests/parser/should_fail/RecordDotSyntaxFail9.stderr
Changes:
| ... | ... | @@ -340,13 +340,13 @@ type instance XLHsRecUpdLabels GhcTc = DataConCantHappen |
| 340 | 340 | type instance XLHsOLRecUpdLabels p = NoExtField
|
| 341 | 341 | |
| 342 | 342 | type instance XGetField GhcPs = NoExtField
|
| 343 | -type instance XGetField GhcRn = NoExtField
|
|
| 343 | +type instance XGetField GhcRn = Name
|
|
| 344 | 344 | type instance XGetField GhcTc = DataConCantHappen
|
| 345 | 345 | -- HsGetField is eliminated by the renamer. See [Handling overloaded
|
| 346 | 346 | -- and rebindable constructs].
|
| 347 | 347 | |
| 348 | 348 | type instance XProjection GhcPs = AnnProjection
|
| 349 | -type instance XProjection GhcRn = NoExtField
|
|
| 349 | +type instance XProjection GhcRn = (Name, Name)
|
|
| 350 | 350 | type instance XProjection GhcTc = DataConCantHappen
|
| 351 | 351 | -- HsProjection is eliminated by the renamer. See [Handling overloaded
|
| 352 | 352 | -- and rebindable constructs].
|
| ... | ... | @@ -72,7 +72,7 @@ import qualified GHC.LanguageExtensions as LangExt |
| 72 | 72 | import Control.Monad
|
| 73 | 73 | import qualified Data.Foldable as Partial (maximum)
|
| 74 | 74 | import Data.List (unzip4)
|
| 75 | -import Data.List.NonEmpty ( NonEmpty(..), head, init, last, nonEmpty, scanl, tail )
|
|
| 75 | +import Data.List.NonEmpty ( NonEmpty(..), head, nonEmpty, scanl, tail )
|
|
| 76 | 76 | import Control.Arrow (first)
|
| 77 | 77 | import Data.Ord
|
| 78 | 78 | import Data.Array
|
| ... | ... | @@ -419,19 +419,13 @@ rnExpr (HsGetField _ e f) |
| 419 | 419 | = do { (getField, fv_getField) <- lookupSyntaxName getFieldName
|
| 420 | 420 | ; (e, fv_e) <- rnLExpr e
|
| 421 | 421 | ; let f' = rnDotFieldOcc <$> f
|
| 422 | - ; return ( mkExpandedExpr
|
|
| 423 | - (HsGetField noExtField e f')
|
|
| 424 | - (mkGetField getField e (fmap (unLoc . dfoLabel) f'))
|
|
| 425 | - , fv_e `plusFN` fv_getField ) }
|
|
| 422 | + ; return (HsGetField getField e f', fv_e `plusFN` fv_getField) }
|
|
| 426 | 423 | |
| 427 | 424 | rnExpr (HsProjection _ fs)
|
| 428 | 425 | = do { (getField, fv_getField) <- lookupSyntaxName getFieldName
|
| 429 | 426 | ; circ <- lookupOccRn WL_TermVariable compose_RDR
|
| 430 | 427 | ; let fs' = NE.map rnDotFieldOcc fs
|
| 431 | - ; return ( mkExpandedExpr
|
|
| 432 | - (HsProjection noExtField fs')
|
|
| 433 | - (mkProjection getField circ $ NE.map (unLoc . dfoLabel) fs')
|
|
| 434 | - , unitFN circ `plusFN` fv_getField) }
|
|
| 428 | + ; return (HsProjection (getField , circ) fs', unitFN circ `plusFN` fv_getField) }
|
|
| 435 | 429 | |
| 436 | 430 | ------------------------------------------
|
| 437 | 431 | -- Template Haskell extensions
|
| ... | ... | @@ -717,14 +711,12 @@ rnSection :: HsExpr GhcPs -> RnM (HsExpr GhcRn, FreeNames) |
| 717 | 711 | -- Also see Note [Handling overloaded and rebindable constructs]
|
| 718 | 712 | |
| 719 | 713 | rnSection section@(SectionR x op expr)
|
| 720 | - -- See Note [Left and right sections]
|
|
| 721 | 714 | = do { (op', fvs_op) <- rnLExpr op
|
| 722 | 715 | ; (expr', fvs_expr) <- rnLExpr expr
|
| 723 | 716 | ; checkSectionPrec InfixR section op' expr'
|
| 724 | 717 | ; return $ (SectionR x op' expr' , fvs_op `plusFN` fvs_expr) }
|
| 725 | 718 | |
| 726 | 719 | rnSection section@(SectionL x expr op)
|
| 727 | - -- See Note [Left and right sections]
|
|
| 728 | 720 | = do { (expr', fvs_expr) <- rnLExpr expr
|
| 729 | 721 | ; (op', fvs_op) <- rnLExpr op
|
| 730 | 722 | ; checkSectionPrec InfixL section op' expr'
|
| ... | ... | @@ -732,129 +724,7 @@ rnSection section@(SectionL x expr op) |
| 732 | 724 | |
| 733 | 725 | rnSection other = pprPanic "rnSection" (ppr other)
|
| 734 | 726 | |
| 735 | -{- Note [Left and right sections]
|
|
| 736 | -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 737 | -Dealing with left sections (x *) and right sections (* x) is
|
|
| 738 | -surprisingly fiddly. We expand like this
|
|
| 739 | - (`op` e) ==> rightSection op e
|
|
| 740 | - (e `op`) ==> leftSection (op e)
|
|
| 741 | - |
|
| 742 | -Using an auxiliary function in this way avoids the awkwardness of
|
|
| 743 | -generating a lambda, esp if `e` is a redex, so we *don't* want
|
|
| 744 | -to generate `(\x -> op x e)`. See Historical
|
|
| 745 | -Note [Desugaring operator sections]
|
|
| 746 | - |
|
| 747 | -Here are their definitions:
|
|
| 748 | - leftSection :: forall r1 r2 n (a::TYPE r1) (b::TYPE r2).
|
|
| 749 | - (a %n-> b) -> a %n-> b
|
|
| 750 | - leftSection f x = f x
|
|
| 751 | - |
|
| 752 | - rightSection :: forall r1 r2 r3 n1 n2 (a::TYPE r1) (b::TYPE r2) (c::TYPE r3).
|
|
| 753 | - (a %n1 -> b %n2-> c) -> b %n2-> a %n1-> c
|
|
| 754 | - rightSection f y x = f x y
|
|
| 755 | - |
|
| 756 | -Note the wrinkles:
|
|
| 757 | - |
|
| 758 | -* We do /not/ use lookupSyntaxName, which would make left and right
|
|
| 759 | - section fall under RebindableSyntax. Reason: it would be a user-
|
|
| 760 | - facing change, and there are some tricky design choices (#19354).
|
|
| 761 | - Plus, infix operator applications would be trickier to make
|
|
| 762 | - rebindable, so it'd be inconsistent to do so for sections.
|
|
| 763 | - |
|
| 764 | - TL;DR: we still use the renamer-expansion mechanism for operator
|
|
| 765 | - sections, but only to eliminate special-purpose code paths in the
|
|
| 766 | - renamer and desugarer.
|
|
| 767 | - |
|
| 768 | -* leftSection and rightSection must be representation-polymorphic, to allow
|
|
| 769 | - (+# 4#) and (4# +#) to work. See
|
|
| 770 | - Note [Wired-in Ids for rebindable syntax] in GHC.Types.Id.Make.
|
|
| 771 | - |
|
| 772 | -* leftSection and rightSection must be multiplicity-polymorphic.
|
|
| 773 | - (Test linear/should_compile/OldList showed this up.)
|
|
| 774 | - |
|
| 775 | -* Because they are representation-polymorphic, we have to define them
|
|
| 776 | - as wired-in Ids, with compulsory inlining. See
|
|
| 777 | - GHC.Types.Id.Make.leftSectionId, rightSectionId.
|
|
| 778 | - |
|
| 779 | -* leftSection is just ($) really; but unlike ($) it is
|
|
| 780 | - representation-polymorphic in the result type, so we can write
|
|
| 781 | - `(x +#)`, say.
|
|
| 782 | - |
|
| 783 | -* The type of leftSection must have an arrow in its first argument,
|
|
| 784 | - because (x `ord`) should be rejected, because ord does not take two
|
|
| 785 | - arguments
|
|
| 786 | - |
|
| 787 | -* It's important that we define leftSection in an eta-expanded way,
|
|
| 788 | - (i.e. not leftSection f = f), so that
|
|
| 789 | - (True `undefined`) `seq` ()
|
|
| 790 | - = (leftSection (undefined True) `seq` ())
|
|
| 791 | - evaluates to () and not undefined
|
|
| 792 | - |
|
| 793 | -* If PostfixOperators is ON, then we expand a left section like this:
|
|
| 794 | - (e `op`) ==> op e
|
|
| 795 | - with no auxiliary function at all. Simple!
|
|
| 796 | - |
|
| 797 | -* leftSection and rightSection switch on ImpredicativeTypes locally,
|
|
| 798 | - during Quick Look; see GHC.Tc.Gen.App.wantQuickLook. Consider
|
|
| 799 | - test DeepSubsumption08:
|
|
| 800 | - type Setter st t a b = forall f. Identical f => blah
|
|
| 801 | - (.~) :: Setter s t a b -> b -> s -> t
|
|
| 802 | - clear :: Setter a a' b (Maybe b') -> a -> a'
|
|
| 803 | - clear = (.~ Nothing)
|
|
| 804 | - The expansion look like (rightSection (.~) Nothing). So we must
|
|
| 805 | - instantiate `rightSection` first type argument to a polytype!
|
|
| 806 | - Hence the special magic in App.wantQuickLook.
|
|
| 807 | - |
|
| 808 | -Historical Note [Desugaring operator sections]
|
|
| 809 | -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 810 | -This Note explains some historical trickiness in desugaring left and
|
|
| 811 | -right sections. That trickiness has completely disappeared now that
|
|
| 812 | -we desugar to calls to 'leftSection` and `rightSection`, but I'm
|
|
| 813 | -leaving it here to remind us how nice the new story is.
|
|
| 814 | - |
|
| 815 | -Desugaring left sections with -XPostfixOperators is straightforward: convert
|
|
| 816 | -(expr `op`) to (op expr).
|
|
| 817 | - |
|
| 818 | -Without -XPostfixOperators it's a bit more tricky. At first it looks as if we
|
|
| 819 | -can convert
|
|
| 820 | - |
|
| 821 | - (expr `op`)
|
|
| 822 | - |
|
| 823 | -naively to
|
|
| 824 | - |
|
| 825 | - \x -> op expr x
|
|
| 826 | - |
|
| 827 | -But no! expr might be a redex, and we can lose laziness badly this
|
|
| 828 | -way. Consider
|
|
| 829 | - |
|
| 830 | - map (expr `op`) xs
|
|
| 831 | - |
|
| 832 | -for example. If expr were a redex then eta-expanding naively would
|
|
| 833 | -result in multiple evaluations where the user might only have expected one.
|
|
| 834 | - |
|
| 835 | -So we convert instead to
|
|
| 836 | - |
|
| 837 | - let y = expr in \x -> op y x
|
|
| 838 | - |
|
| 839 | -Also, note that we must do this for both right and (perhaps surprisingly) left
|
|
| 840 | -sections. Why are left sections necessary? Consider the program (found in #18151),
|
|
| 841 | - |
|
| 842 | - seq (True `undefined`) ()
|
|
| 843 | - |
|
| 844 | -according to the Haskell Report this should reduce to () (as it specifies
|
|
| 845 | -desugaring via eta expansion). However, if we fail to eta expand we will rather
|
|
| 846 | -bottom. Consequently, we must eta expand even in the case of a left section.
|
|
| 847 | - |
|
| 848 | -If `expr` is actually just a variable, say, then the simplifier
|
|
| 849 | -will inline `y`, eliminating the redundant `let`.
|
|
| 850 | - |
|
| 851 | -Note that this works even in the case that `expr` is unlifted. In this case
|
|
| 852 | -bindNonRec will automatically do the right thing, giving us:
|
|
| 853 | - |
|
| 854 | - case expr of y -> (\x -> op y x)
|
|
| 855 | - |
|
| 856 | -See #18151.
|
|
| 857 | - |
|
| 727 | +{-
|
|
| 858 | 728 | Note [Reporting unbound names]
|
| 859 | 729 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 860 | 730 | Faced with an out-of-scope `RdrName` there are two courses of action
|
| ... | ... | @@ -2841,64 +2711,6 @@ rnHsIf p b1 b2 |
| 2841 | 2711 | fvs = plusFNs [fvs_if, unitFN ite_name]
|
| 2842 | 2712 | ; return (mkExpandedExpr rn_if ds_if, fvs) } }
|
| 2843 | 2713 | |
| 2844 | ------------------------------------------
|
|
| 2845 | --- Bits and pieces for RecordDotSyntax.
|
|
| 2846 | ---
|
|
| 2847 | --- See Note [Overview of record dot syntax] in GHC.Hs.Expr.
|
|
| 2848 | - |
|
| 2849 | --- mkGetField arg field calculates a get_field @field arg expression.
|
|
| 2850 | --- e.g. z.x = mkGetField z x = get_field @x z
|
|
| 2851 | -mkGetField :: Name -> LHsExpr GhcRn -> LocatedAn NoEpAnns FieldLabelString -> HsExpr GhcRn
|
|
| 2852 | -mkGetField get_field arg field = unLoc (head $ mkGet get_field (arg :| []) field)
|
|
| 2853 | - |
|
| 2854 | --- mkSetField a field b calculates a set_field @field expression.
|
|
| 2855 | --- e.g mkSetSetField a field b = set_field @"field" a b (read as "set field 'field' to a on b").
|
|
| 2856 | --- NB: the order of aruments is specified by GHC Proposal 583: HasField redesign.
|
|
| 2857 | -mkSetField :: Name -> LHsExpr GhcRn -> LocatedAn NoEpAnns FieldLabelString -> LHsExpr GhcRn -> HsExpr GhcRn
|
|
| 2858 | -mkSetField set_field a (L _ (FieldLabelString field)) b =
|
|
| 2859 | - genHsApp (genHsApp (genHsVar set_field `genAppType` genHsTyLit field) b) a
|
|
| 2860 | - |
|
| 2861 | -mkGet :: Name -> NonEmpty (LHsExpr GhcRn) -> LocatedAn NoEpAnns FieldLabelString -> NonEmpty (LHsExpr GhcRn)
|
|
| 2862 | -mkGet get_field l@(r :| _) (L _ (FieldLabelString field)) =
|
|
| 2863 | - wrapGenSpan (genHsApp (genHsVar get_field `genAppType` genHsTyLit field) r) NE.<| l
|
|
| 2864 | - |
|
| 2865 | -mkSet :: Name -> LHsExpr GhcRn -> (LocatedAn NoEpAnns FieldLabelString, LHsExpr GhcRn) -> LHsExpr GhcRn
|
|
| 2866 | -mkSet set_field acc (field, g) = wrapGenSpan (mkSetField set_field g field acc)
|
|
| 2867 | - |
|
| 2868 | --- mkProjection fields calculates a projection.
|
|
| 2869 | --- e.g. .x = mkProjection [x] = getField @"x"
|
|
| 2870 | --- .x.y = mkProjection [.x, .y] = (.y) . (.x) = getField @"y" . getField @"x"
|
|
| 2871 | -mkProjection :: Name -> Name -> NonEmpty FieldLabelString -> HsExpr GhcRn
|
|
| 2872 | -mkProjection getFieldName circName (field :| fields) = foldl' f (proj field) fields
|
|
| 2873 | - where
|
|
| 2874 | - f :: HsExpr GhcRn -> FieldLabelString -> HsExpr GhcRn
|
|
| 2875 | - f acc field = genHsApps circName $ map wrapGenSpan [proj field, acc]
|
|
| 2876 | - |
|
| 2877 | - proj :: FieldLabelString -> HsExpr GhcRn
|
|
| 2878 | - proj (FieldLabelString f) = genHsVar getFieldName `genAppType` genHsTyLit f
|
|
| 2879 | - |
|
| 2880 | --- mkProjUpdateSetField calculates functions representing dot notation record updates.
|
|
| 2881 | --- e.g. Suppose an update like foo.bar = 1.
|
|
| 2882 | --- We calculate the function \a -> setField @"foo" a (setField @"bar" (getField @"foo" a) 1).
|
|
| 2883 | -mkProjUpdateSetField :: Name -> Name -> LHsRecProj GhcRn (LHsExpr GhcRn) -> (LHsExpr GhcRn -> LHsExpr GhcRn)
|
|
| 2884 | -mkProjUpdateSetField get_field set_field (L _ (HsFieldBind { hfbLHS = (L _ (FieldLabelStrings flds')), hfbRHS = arg } ))
|
|
| 2885 | - = let {
|
|
| 2886 | - ; flds = NE.map (fmap (unLoc . dfoLabel)) flds'
|
|
| 2887 | - ; final = last flds -- quux
|
|
| 2888 | - ; fields = init flds -- [foo, bar, baz]
|
|
| 2889 | - ; getters = \a -> foldl' (mkGet get_field) (a :| []) fields -- Ordered from deep to shallow.
|
|
| 2890 | - -- [getField@"baz"(getField@"bar"(getField@"foo" a), getField@"bar"(getField@"foo" a), getField@"foo" a, a]
|
|
| 2891 | - ; zips = \a -> (final, head (getters a)) : zip (reverse fields) (tail (getters a)) -- Ordered from deep to shallow.
|
|
| 2892 | - -- [("quux", getField@"baz"(getField@"bar"(getField@"foo" a)), ("baz", getField@"bar"(getField@"foo" a)), ("bar", getField@"foo" a), ("foo", a)]
|
|
| 2893 | - }
|
|
| 2894 | - in (\a -> foldl' (mkSet set_field) arg (zips a))
|
|
| 2895 | - -- setField@"foo" (a) (setField@"bar" (getField @"foo" (a))(setField@"baz" (getField @"bar" (getField @"foo" (a)))(setField@"quux" (getField @"baz" (getField @"bar" (getField @"foo" (a))))(quux))))
|
|
| 2896 | - |
|
| 2897 | -mkRecordDotUpd :: Name -> Name -> LHsExpr GhcRn -> [LHsRecUpdProj GhcRn] -> HsExpr GhcRn
|
|
| 2898 | -mkRecordDotUpd get_field set_field exp updates = foldl' fieldUpdate (unLoc exp) updates
|
|
| 2899 | - where
|
|
| 2900 | - fieldUpdate :: HsExpr GhcRn -> LHsRecUpdProj GhcRn -> HsExpr GhcRn
|
|
| 2901 | - fieldUpdate acc lpu = unLoc $ (mkProjUpdateSetField get_field set_field lpu) (wrapGenSpan acc)
|
|
| 2902 | 2714 | |
| 2903 | 2715 | rnHsUpdProjs :: [LHsRecUpdProj GhcPs] -> RnM ([LHsRecUpdProj GhcRn], FreeNames)
|
| 2904 | 2716 | rnHsUpdProjs us = do
|
| ... | ... | @@ -24,6 +24,8 @@ module GHC.Rename.Utils ( |
| 24 | 24 | genSimpleFunBind, genFunBind,
|
| 25 | 25 | genHsLamDoExp, genHsCaseAltDoExp, genSimpleMatch, genHsLet,
|
| 26 | 26 | |
| 27 | + mkGetField, mkSetField, mkProjection, mkRecordDotUpd,
|
|
| 28 | + |
|
| 27 | 29 | mkExpandedRn, mkExpandedExpr, mkExpandedStmt, mkExpandedLExpr, mkExpandedTc, mkExpandedExprTc,
|
| 28 | 30 | |
| 29 | 31 | mkRnSyntaxExpr,
|
| ... | ... | @@ -41,7 +43,7 @@ module GHC.Rename.Utils ( |
| 41 | 43 | where
|
| 42 | 44 | |
| 43 | 45 | |
| 44 | -import GHC.Prelude
|
|
| 46 | +import GHC.Prelude hiding (init, last, scanl, tail)
|
|
| 45 | 47 | |
| 46 | 48 | import GHC.Core.Type
|
| 47 | 49 | import GHC.Hs
|
| ... | ... | @@ -71,6 +73,7 @@ import GHC.Iface.Load |
| 71 | 73 | import qualified GHC.LanguageExtensions as LangExt
|
| 72 | 74 | |
| 73 | 75 | import qualified Data.List.NonEmpty as NE
|
| 76 | +import Data.List.NonEmpty ( NonEmpty(..), init, last, tail )
|
|
| 74 | 77 | import Data.Foldable (for_)
|
| 75 | 78 | import Data.Maybe
|
| 76 | 79 | |
| ... | ... | @@ -867,3 +870,63 @@ mkExpandedTc |
| 867 | 870 | -> LHsExpr GhcTc -- ^ expanded typechecked expression
|
| 868 | 871 | -> HsExpr GhcTc -- ^ suitably wrapped 'XXExprGhcTc'
|
| 869 | 872 | mkExpandedTc o e = XExpr (ExpandedThingTc (HSE o e))
|
| 873 | + |
|
| 874 | + |
|
| 875 | +-----------------------------------------
|
|
| 876 | +-- Bits and pieces for RecordDotSyntax.
|
|
| 877 | +--
|
|
| 878 | +-- See Note [Overview of record dot syntax] in GHC.Hs.Expr.
|
|
| 879 | + |
|
| 880 | +-- mkGetField arg field calculates a get_field @field arg expression.
|
|
| 881 | +-- e.g. z.x = mkGetField z x = get_field @x z
|
|
| 882 | +mkGetField :: Name -> LHsExpr GhcRn -> LocatedAn NoEpAnns FieldLabelString -> HsExpr GhcRn
|
|
| 883 | +mkGetField get_field arg field = unLoc (NE.head $ mkGet get_field (arg :| []) field)
|
|
| 884 | + |
|
| 885 | +-- mkSetField a field b calculates a set_field @field expression.
|
|
| 886 | +-- e.g mkSetSetField a field b = set_field @"field" a b (read as "set field 'field' to a on b").
|
|
| 887 | +-- NB: the order of aruments is specified by GHC Proposal 583: HasField redesign.
|
|
| 888 | +mkSetField :: Name -> LHsExpr GhcRn -> LocatedAn NoEpAnns FieldLabelString -> LHsExpr GhcRn -> HsExpr GhcRn
|
|
| 889 | +mkSetField set_field a (L _ (FieldLabelString field)) b =
|
|
| 890 | + genHsApp (genHsApp (genHsVar set_field `genAppType` genHsTyLit field) b) a
|
|
| 891 | + |
|
| 892 | +mkGet :: Name -> NonEmpty (LHsExpr GhcRn) -> LocatedAn NoEpAnns FieldLabelString -> NonEmpty (LHsExpr GhcRn)
|
|
| 893 | +mkGet get_field l@(r :| _) (L _ (FieldLabelString field)) =
|
|
| 894 | + wrapGenSpan (genHsApp (genHsVar get_field `genAppType` genHsTyLit field) r) NE.<| l
|
|
| 895 | + |
|
| 896 | +mkSet :: Name -> LHsExpr GhcRn -> (LocatedAn NoEpAnns FieldLabelString, LHsExpr GhcRn) -> LHsExpr GhcRn
|
|
| 897 | +mkSet set_field acc (field, g) = wrapGenSpan (mkSetField set_field g field acc)
|
|
| 898 | + |
|
| 899 | +-- mkProjection fields calculates a projection.
|
|
| 900 | +-- e.g. .x = mkProjection [x] = getField @"x"
|
|
| 901 | +-- .x.y = mkProjection [.x, .y] = (.y) . (.x) = getField @"y" . getField @"x"
|
|
| 902 | +mkProjection :: Name -> Name -> NonEmpty FieldLabelString -> HsExpr GhcRn
|
|
| 903 | +mkProjection getFieldName circName (field :| fields) = foldl' f (proj field) fields
|
|
| 904 | + where
|
|
| 905 | + f :: HsExpr GhcRn -> FieldLabelString -> HsExpr GhcRn
|
|
| 906 | + f acc field = genHsApps circName $ map wrapGenSpan [proj field, acc]
|
|
| 907 | + |
|
| 908 | + proj :: FieldLabelString -> HsExpr GhcRn
|
|
| 909 | + proj (FieldLabelString f) = genHsVar getFieldName `genAppType` genHsTyLit f
|
|
| 910 | + |
|
| 911 | +-- mkProjUpdateSetField calculates functions representing dot notation record updates.
|
|
| 912 | +-- e.g. Suppose an update like foo.bar = 1.
|
|
| 913 | +-- We calculate the function \a -> setField @"foo" a (setField @"bar" (getField @"foo" a) 1).
|
|
| 914 | +mkProjUpdateSetField :: Name -> Name -> LHsRecProj GhcRn (LHsExpr GhcRn) -> (LHsExpr GhcRn -> LHsExpr GhcRn)
|
|
| 915 | +mkProjUpdateSetField get_field set_field (L _ (HsFieldBind { hfbLHS = (L _ (FieldLabelStrings flds')), hfbRHS = arg } ))
|
|
| 916 | + = let {
|
|
| 917 | + ; flds = NE.map (fmap (unLoc . dfoLabel)) flds'
|
|
| 918 | + ; final = last flds -- quux
|
|
| 919 | + ; fields = init flds -- [foo, bar, baz]
|
|
| 920 | + ; getters = \a -> foldl' (mkGet get_field) (a :| []) fields -- Ordered from deep to shallow.
|
|
| 921 | + -- [getField@"baz"(getField@"bar"(getField@"foo" a), getField@"bar"(getField@"foo" a), getField@"foo" a, a]
|
|
| 922 | + ; zips = \a -> (final, NE.head (getters a)) : zip (reverse fields) (tail (getters a)) -- Ordered from deep to shallow.
|
|
| 923 | + -- [("quux", getField@"baz"(getField@"bar"(getField@"foo" a)), ("baz", getField@"bar"(getField@"foo" a)), ("bar", getField@"foo" a), ("foo", a)]
|
|
| 924 | + }
|
|
| 925 | + in (\a -> foldl' (mkSet set_field) arg (zips a))
|
|
| 926 | + -- setField@"foo" (a) (setField@"bar" (getField @"foo" (a))(setField@"baz" (getField @"bar" (getField @"foo" (a)))(setField@"quux" (getField @"baz" (getField @"bar" (getField @"foo" (a))))(quux))))
|
|
| 927 | + |
|
| 928 | +mkRecordDotUpd :: Name -> Name -> LHsExpr GhcRn -> [LHsRecUpdProj GhcRn] -> HsExpr GhcRn
|
|
| 929 | +mkRecordDotUpd get_field set_field exp updates = foldl' fieldUpdate (unLoc exp) updates
|
|
| 930 | + where
|
|
| 931 | + fieldUpdate :: HsExpr GhcRn -> LHsRecUpdProj GhcRn -> HsExpr GhcRn
|
|
| 932 | + fieldUpdate acc lpu = unLoc $ (mkProjUpdateSetField get_field set_field lpu) (wrapGenSpan acc) |
| ... | ... | @@ -17,8 +17,10 @@ import GHC.Tc.Utils.Monad |
| 17 | 17 | import GHC.Tc.Types.ErrCtxt
|
| 18 | 18 | |
| 19 | 19 | import GHC.Types.Id.Make
|
| 20 | +import GHC.Types.SrcLoc
|
|
| 20 | 21 | |
| 21 | 22 | import GHC.Rename.Utils
|
| 23 | +import qualified Data.List.NonEmpty as NE (map)
|
|
| 22 | 24 | |
| 23 | 25 | import qualified GHC.LanguageExtensions as LangExt
|
| 24 | 26 | |
| ... | ... | @@ -105,7 +107,8 @@ Wrinkle (TBE1) |
| 105 | 107 | |
| 106 | 108 | -}
|
| 107 | 109 | |
| 108 | ----------------
|
|
| 110 | +------------------------------------------
|
|
| 111 | +-- Operator Applications
|
|
| 109 | 112 | tcExpand :: HsExpr GhcRn -> TcM (Maybe (HsExpansion GhcRn))
|
| 110 | 113 | tcExpand e@(OpApp _ arg1 op arg2)
|
| 111 | 114 | = return $ Just $
|
| ... | ... | @@ -114,14 +117,18 @@ tcExpand e@(OpApp _ arg1 op arg2) |
| 114 | 117 | where
|
| 115 | 118 | ap f a = wrapGenSpan (HsApp noExtField f a)
|
| 116 | 119 | |
| 120 | +------------------------------------------
|
|
| 121 | +-- Left and Right Sections
|
|
| 122 | + |
|
| 117 | 123 | tcExpand e@(SectionR _ op expr)
|
| 124 | + -- See Note [Left and right sections]
|
|
| 118 | 125 | = return $ Just $
|
| 119 | 126 | HSE { hse_ctxt = ExprCtxt e
|
| 120 | 127 | , hse_exp = wrapGenSpan $ genHsApps rightSectionName [op, expr] }
|
| 121 | 128 | |
| 122 | 129 | tcExpand e@(SectionL _ expr op)
|
| 130 | + -- Note [Left and right sections]
|
|
| 123 | 131 | = do { postfix_ops <- xoptM LangExt.PostfixOperators
|
| 124 | - -- Note [Left and right sections]
|
|
| 125 | 132 | ; let ds_section
|
| 126 | 133 | | postfix_ops = HsApp noExtField op expr
|
| 127 | 134 | | otherwise = genHsApps leftSectionName
|
| ... | ... | @@ -130,10 +137,26 @@ tcExpand e@(SectionL _ expr op) |
| 130 | 137 | HSE { hse_ctxt = ExprCtxt e
|
| 131 | 138 | , hse_exp = wrapGenSpan ds_section } }
|
| 132 | 139 | |
| 140 | +------------------------------------------
|
|
| 141 | +-- Record dot syntax
|
|
| 142 | + |
|
| 143 | +tcExpand e@(HsGetField getFieldName expr f)
|
|
| 144 | + = return $ Just $
|
|
| 145 | + HSE { hse_ctxt = ExprCtxt e
|
|
| 146 | + , hse_exp = wrapGenSpan $ (mkGetField getFieldName expr (fmap (unLoc . dfoLabel) f)) }
|
|
| 147 | + |
|
| 148 | +tcExpand e@(HsProjection (getFieldName, circName) fs)
|
|
| 149 | + = return$ Just $
|
|
| 150 | + HSE { hse_ctxt = ExprCtxt e
|
|
| 151 | + , hse_exp = wrapGenSpan $ (mkProjection getFieldName circName $ NE.map (unLoc . dfoLabel) fs) }
|
|
| 133 | 152 | |
| 153 | +---------
|
|
| 134 | 154 | tcExpand (XExpr (ExpandedThingRn hse))
|
| 135 | 155 | = return (Just hse)
|
| 136 | 156 | |
| 157 | +------------------------------------------
|
|
| 158 | +-- Template Haskell Splices
|
|
| 159 | + |
|
| 137 | 160 | tcExpand e@(HsUntypedSplice splice_res _)
|
| 138 | 161 | -- See Note [Looking through Template Haskell splices in splitHsApps]
|
| 139 | 162 | = do { fun <- getUntypedSpliceBody splice_res
|
| ... | ... | @@ -142,3 +165,132 @@ tcExpand e@(HsUntypedSplice splice_res _) |
| 142 | 165 | , hse_exp = wrapGenSpan fun } }
|
| 143 | 166 | |
| 144 | 167 | tcExpand _ = return Nothing
|
| 168 | + |
|
| 169 | + |
|
| 170 | + |
|
| 171 | + |
|
| 172 | + |
|
| 173 | + |
|
| 174 | +{- Note [Left and right sections]
|
|
| 175 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 176 | +Dealing with left sections (x *) and right sections (* x) is
|
|
| 177 | +surprisingly fiddly. We expand like this
|
|
| 178 | + (`op` e) ==> rightSection op e
|
|
| 179 | + (e `op`) ==> leftSection (op e)
|
|
| 180 | + |
|
| 181 | +Using an auxiliary function in this way avoids the awkwardness of
|
|
| 182 | +generating a lambda, esp if `e` is a redex, so we *don't* want
|
|
| 183 | +to generate `(\x -> op x e)`. See Historical
|
|
| 184 | +Note [Desugaring operator sections]
|
|
| 185 | + |
|
| 186 | +Here are their definitions:
|
|
| 187 | + leftSection :: forall r1 r2 n (a::TYPE r1) (b::TYPE r2).
|
|
| 188 | + (a %n-> b) -> a %n-> b
|
|
| 189 | + leftSection f x = f x
|
|
| 190 | + |
|
| 191 | + rightSection :: forall r1 r2 r3 n1 n2 (a::TYPE r1) (b::TYPE r2) (c::TYPE r3).
|
|
| 192 | + (a %n1 -> b %n2-> c) -> b %n2-> a %n1-> c
|
|
| 193 | + rightSection f y x = f x y
|
|
| 194 | + |
|
| 195 | +Note the wrinkles:
|
|
| 196 | + |
|
| 197 | +* We do /not/ use lookupSyntaxName, which would make left and right
|
|
| 198 | + section fall under RebindableSyntax. Reason: it would be a user-
|
|
| 199 | + facing change, and there are some tricky design choices (#19354).
|
|
| 200 | + Plus, infix operator applications would be trickier to make
|
|
| 201 | + rebindable, so it'd be inconsistent to do so for sections.
|
|
| 202 | + |
|
| 203 | + TL;DR: we still use the renamer-expansion mechanism for operator
|
|
| 204 | + sections, but only to eliminate special-purpose code paths in the
|
|
| 205 | + renamer and desugarer.
|
|
| 206 | + |
|
| 207 | +* leftSection and rightSection must be representation-polymorphic, to allow
|
|
| 208 | + (+# 4#) and (4# +#) to work. See
|
|
| 209 | + Note [Wired-in Ids for rebindable syntax] in GHC.Types.Id.Make.
|
|
| 210 | + |
|
| 211 | +* leftSection and rightSection must be multiplicity-polymorphic.
|
|
| 212 | + (Test linear/should_compile/OldList showed this up.)
|
|
| 213 | + |
|
| 214 | +* Because they are representation-polymorphic, we have to define them
|
|
| 215 | + as wired-in Ids, with compulsory inlining. See
|
|
| 216 | + GHC.Types.Id.Make.leftSectionId, rightSectionId.
|
|
| 217 | + |
|
| 218 | +* leftSection is just ($) really; but unlike ($) it is
|
|
| 219 | + representation-polymorphic in the result type, so we can write
|
|
| 220 | + `(x +#)`, say.
|
|
| 221 | + |
|
| 222 | +* The type of leftSection must have an arrow in its first argument,
|
|
| 223 | + because (x `ord`) should be rejected, because ord does not take two
|
|
| 224 | + arguments
|
|
| 225 | + |
|
| 226 | +* It's important that we define leftSection in an eta-expanded way,
|
|
| 227 | + (i.e. not leftSection f = f), so that
|
|
| 228 | + (True `undefined`) `seq` ()
|
|
| 229 | + = (leftSection (undefined True) `seq` ())
|
|
| 230 | + evaluates to () and not undefined
|
|
| 231 | + |
|
| 232 | +* If PostfixOperators is ON, then we expand a left section like this:
|
|
| 233 | + (e `op`) ==> op e
|
|
| 234 | + with no auxiliary function at all. Simple!
|
|
| 235 | + |
|
| 236 | +* leftSection and rightSection switch on ImpredicativeTypes locally,
|
|
| 237 | + during Quick Look; see GHC.Tc.Gen.App.wantQuickLook. Consider
|
|
| 238 | + test DeepSubsumption08:
|
|
| 239 | + type Setter st t a b = forall f. Identical f => blah
|
|
| 240 | + (.~) :: Setter s t a b -> b -> s -> t
|
|
| 241 | + clear :: Setter a a' b (Maybe b') -> a -> a'
|
|
| 242 | + clear = (.~ Nothing)
|
|
| 243 | + The expansion look like (rightSection (.~) Nothing). So we must
|
|
| 244 | + instantiate `rightSection` first type argument to a polytype!
|
|
| 245 | + Hence the special magic in App.wantQuickLook.
|
|
| 246 | + |
|
| 247 | +Historical Note [Desugaring operator sections]
|
|
| 248 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 249 | +This Note explains some historical trickiness in desugaring left and
|
|
| 250 | +right sections. That trickiness has completely disappeared now that
|
|
| 251 | +we desugar to calls to 'leftSection` and `rightSection`, but I'm
|
|
| 252 | +leaving it here to remind us how nice the new story is.
|
|
| 253 | + |
|
| 254 | +Desugaring left sections with -XPostfixOperators is straightforward: convert
|
|
| 255 | +(expr `op`) to (op expr).
|
|
| 256 | + |
|
| 257 | +Without -XPostfixOperators it's a bit more tricky. At first it looks as if we
|
|
| 258 | +can convert
|
|
| 259 | + |
|
| 260 | + (expr `op`)
|
|
| 261 | + |
|
| 262 | +naively to
|
|
| 263 | + |
|
| 264 | + \x -> op expr x
|
|
| 265 | + |
|
| 266 | +But no! expr might be a redex, and we can lose laziness badly this
|
|
| 267 | +way. Consider
|
|
| 268 | + |
|
| 269 | + map (expr `op`) xs
|
|
| 270 | + |
|
| 271 | +for example. If expr were a redex then eta-expanding naively would
|
|
| 272 | +result in multiple evaluations where the user might only have expected one.
|
|
| 273 | + |
|
| 274 | +So we convert instead to
|
|
| 275 | + |
|
| 276 | + let y = expr in \x -> op y x
|
|
| 277 | + |
|
| 278 | +Also, note that we must do this for both right and (perhaps surprisingly) left
|
|
| 279 | +sections. Why are left sections necessary? Consider the program (found in #18151),
|
|
| 280 | + |
|
| 281 | + seq (True `undefined`) ()
|
|
| 282 | + |
|
| 283 | +according to the Haskell Report this should reduce to () (as it specifies
|
|
| 284 | +desugaring via eta expansion). However, if we fail to eta expand we will rather
|
|
| 285 | +bottom. Consequently, we must eta expand even in the case of a left section.
|
|
| 286 | + |
|
| 287 | +If `expr` is actually just a variable, say, then the simplifier
|
|
| 288 | +will inline `y`, eliminating the redundant `let`.
|
|
| 289 | + |
|
| 290 | +Note that this works even in the case that `expr` is unlifted. In this case
|
|
| 291 | +bindNonRec will automatically do the right thing, giving us:
|
|
| 292 | + |
|
| 293 | + case expr of y -> (\x -> op y x)
|
|
| 294 | + |
|
| 295 | +See #18151.
|
|
| 296 | +-} |
| ... | ... | @@ -634,6 +634,7 @@ exprCtOrigin (XExpr (ExpandedThingRn (HSE o _))) = hsCtxtCtOrigin o |
| 634 | 634 | exprCtOrigin (XExpr (HsRecSelRn f)) = OccurrenceOfRecSel $ L (getLoc $ foLabel f) (foExt f)
|
| 635 | 635 | |
| 636 | 636 | hsCtxtCtOrigin :: HsCtxt -> CtOrigin
|
| 637 | +hsCtxtCtOrigin (ExprCtxt (ExprWithTySig _ (L _ e) _)) = exprCtOrigin e
|
|
| 637 | 638 | hsCtxtCtOrigin (ExprCtxt e) = exprCtOrigin e
|
| 638 | 639 | hsCtxtCtOrigin (FunAppCtxt (FunAppCtxtExpr _ e) _) = exprCtOrigin e
|
| 639 | 640 | hsCtxtCtOrigin (StmtErrCtxt{}) = DoStmtOrigin
|
| ... | ... | @@ -2,11 +2,11 @@ RecordDotSyntaxFail8.hs:47:17: error: [GHC-39999] |
| 2 | 2 | • No instance for ‘HasField "quux1" Quux Quux’
|
| 3 | 3 | arising from selecting the field ‘quux1’
|
| 4 | 4 | NB: ‘HasField’ is not the built-in ‘GHC.Internal.Records.HasField’ class.
|
| 5 | - • In the second argument of ‘($)’, namely ‘....bar.baz.quux1’
|
|
| 6 | - In a stmt of a 'do' block: print @Quux $ ....baz.quux1
|
|
| 5 | + • In the second argument of ‘($)’, namely ‘a.foo.bar.baz.quux1’
|
|
| 6 | + In a stmt of a 'do' block: print @Quux $ a.foo.bar.baz.quux1
|
|
| 7 | 7 | In the expression:
|
| 8 | 8 | do let a = Foo {foo = ...}
|
| 9 | - print @Quux $ ....quux1
|
|
| 9 | + print @Quux $ ....bar.baz.quux1
|
|
| 10 | 10 | let b = myQuux
|
| 11 | 11 | print @Quux $ b.quux2
|
| 12 | 12 | let c = Foo {foo = ...}
|
| ... | ... | @@ -20,7 +20,7 @@ RecordDotSyntaxFail8.hs:50:17: error: [GHC-39999] |
| 20 | 20 | In a stmt of a 'do' block: print @Quux $ b.quux2
|
| 21 | 21 | In the expression:
|
| 22 | 22 | do let a = Foo {foo = ...}
|
| 23 | - print @Quux $ ....quux1
|
|
| 23 | + print @Quux $ ....bar.baz.quux1
|
|
| 24 | 24 | let b = myQuux
|
| 25 | 25 | print @Quux $ b.quux2
|
| 26 | 26 | let c = Foo {foo = ...}
|
| ... | ... | @@ -30,11 +30,11 @@ RecordDotSyntaxFail8.hs:53:17: error: [GHC-39999] |
| 30 | 30 | • No instance for ‘HasField "quux3" Quux r0’
|
| 31 | 31 | arising from selecting the field ‘quux3’
|
| 32 | 32 | NB: ‘HasField’ is not the built-in ‘GHC.Internal.Records.HasField’ class.
|
| 33 | - • In the second argument of ‘($)’, namely ‘....baz.quux3.wob’
|
|
| 34 | - In a stmt of a 'do' block: print @Bool $ ....quux3.wob
|
|
| 33 | + • In the second argument of ‘($)’, namely ‘a.foo.bar.baz.quux3.wob’
|
|
| 34 | + In a stmt of a 'do' block: print @Bool $ a.foo.bar.baz.quux3.wob
|
|
| 35 | 35 | In the expression:
|
| 36 | 36 | do let a = Foo {foo = ...}
|
| 37 | - print @Quux $ ....quux1
|
|
| 37 | + print @Quux $ ....bar.baz.quux1
|
|
| 38 | 38 | let b = myQuux
|
| 39 | 39 | print @Quux $ b.quux2
|
| 40 | 40 | let c = Foo {foo = ...}
|
| 1 | 1 | RecordDotSyntaxFail9.hs:7:11: error: [GHC-18872]
|
| 2 | 2 | • Couldn't match type ‘Int’ with ‘[Char]’
|
| 3 | 3 | arising from selecting the field ‘foo’
|
| 4 | - • In the expression: a.foo
|
|
| 4 | + • In the expression: a.foo :: String
|
|
| 5 | 5 | In a pattern binding: _ = a.foo :: String
|
| 6 | 6 | In a stmt of a 'do' block: let _ = a.foo :: String
|
| 7 | 7 |