Sjoerd Visscher pushed to branch wip/T18570 at Glasgow Haskell Compiler / GHC
Commits:
-
8dc4ca41
by Sjoerd Visscher at 2025-05-12T15:24:20+02:00
9 changed files:
- compiler/GHC/Core/DataCon.hs
- compiler/GHC/Tc/TyCl/PatSyn.hs
- compiler/GHC/Tc/TyCl/Utils.hs
- docs/users_guide/9.14.1-notes.rst
- + testsuite/tests/linear/should_compile/LinearRecordSelector.hs
- testsuite/tests/linear/should_compile/all.T
- + testsuite/tests/linear/should_fail/LinearRecordSelectorFail.hs
- + testsuite/tests/linear/should_fail/LinearRecordSelectorFail.stderr
- testsuite/tests/linear/should_fail/all.T
Changes:
| ... | ... | @@ -44,6 +44,7 @@ module GHC.Core.DataCon ( |
| 44 | 44 | dataConInstOrigArgTys, dataConRepArgTys, dataConResRepTyArgs,
|
| 45 | 45 | dataConInstUnivs,
|
| 46 | 46 | dataConFieldLabels, dataConFieldType, dataConFieldType_maybe,
|
| 47 | + dataConOtherFieldsAllMultMany,
|
|
| 47 | 48 | dataConSrcBangs,
|
| 48 | 49 | dataConSourceArity, dataConRepArity,
|
| 49 | 50 | dataConIsInfix,
|
| ... | ... | @@ -1405,6 +1406,15 @@ dataConFieldType_maybe :: DataCon -> FieldLabelString |
| 1405 | 1406 | dataConFieldType_maybe con label
|
| 1406 | 1407 | = find ((== label) . flLabel . fst) (dcFields con `zip` (scaledThing <$> dcOrigArgTys con))
|
| 1407 | 1408 | |
| 1409 | +-- | Check if all the fields of the 'DataCon' have multiplicity 'Many',
|
|
| 1410 | +-- except for the given labelled field. In this case the selector
|
|
| 1411 | +-- of the given field can be a linear function, since it is allowed
|
|
| 1412 | +-- to discard all the other fields.
|
|
| 1413 | +dataConOtherFieldsAllMultMany :: DataCon -> FieldLabelString -> Bool
|
|
| 1414 | +dataConOtherFieldsAllMultMany con label
|
|
| 1415 | + = all (\(fld, mult) -> flLabel fld == label || isManyTy mult)
|
|
| 1416 | + (dcFields con `zip` (scaledMult <$> dcOrigArgTys con))
|
|
| 1417 | + |
|
| 1408 | 1418 | -- | Strictness/unpack annotations, from user; or, for imported
|
| 1409 | 1419 | -- DataCons, from the interface file
|
| 1410 | 1420 | -- The list is in one-to-one correspondence with the arity of the 'DataCon'
|
| ... | ... | @@ -725,7 +725,8 @@ tc_patsyn_finish lname dir is_infix lpat' prag_fn |
| 725 | 725 | |
| 726 | 726 | -- Selectors
|
| 727 | 727 | ; has_sel <- xopt_FieldSelectors <$> getDynFlags
|
| 728 | - ; let rn_rec_sel_binds = mkPatSynRecSelBinds patSyn (patSynFieldLabels patSyn) has_sel
|
|
| 728 | + -- ; linearEnabled <- xoptM LangExt.LinearTypes
|
|
| 729 | + ; let rn_rec_sel_binds = mkPatSynRecSelBinds True patSyn (patSynFieldLabels patSyn) has_sel
|
|
| 729 | 730 | tything = AConLike (PatSynCon patSyn)
|
| 730 | 731 | ; tcg_env <- tcExtendGlobalEnv [tything] $
|
| 731 | 732 | tcRecSelBinds rn_rec_sel_binds
|
| ... | ... | @@ -835,12 +836,13 @@ tcPatSynMatcher (L loc ps_name) lpat prag_fn |
| 835 | 836 | |
| 836 | 837 | ; return ((matcher_name, matcher_sigma, is_unlifted), matcher_bind) }
|
| 837 | 838 | |
| 838 | -mkPatSynRecSelBinds :: PatSyn
|
|
| 839 | +mkPatSynRecSelBinds :: Bool
|
|
| 840 | + -> PatSyn
|
|
| 839 | 841 | -> [FieldLabel] -- ^ Visible field labels
|
| 840 | 842 | -> FieldSelectors
|
| 841 | 843 | -> [(Id, LHsBind GhcRn)]
|
| 842 | -mkPatSynRecSelBinds ps fields has_sel
|
|
| 843 | - = [ mkOneRecordSelector [PatSynCon ps] (RecSelPatSyn ps) fld_lbl has_sel
|
|
| 844 | +mkPatSynRecSelBinds linearEnabled ps fields has_sel
|
|
| 845 | + = [ mkOneRecordSelector linearEnabled [PatSynCon ps] (RecSelPatSyn ps) fld_lbl has_sel
|
|
| 844 | 846 | | fld_lbl <- fields ]
|
| 845 | 847 | |
| 846 | 848 | isUnidirectional :: HsPatSynDir a -> Bool
|
| ... | ... | @@ -32,7 +32,7 @@ import GHC.Tc.Utils.Env |
| 32 | 32 | import GHC.Tc.Gen.Bind( tcValBinds )
|
| 33 | 33 | import GHC.Tc.Utils.TcType
|
| 34 | 34 | |
| 35 | -import GHC.Builtin.Types( unitTy )
|
|
| 35 | +import GHC.Builtin.Types( unitTy, manyDataConTy, multiplicityTy )
|
|
| 36 | 36 | import GHC.Builtin.Uniques ( mkBuiltinUnique )
|
| 37 | 37 | |
| 38 | 38 | import GHC.Hs
|
| ... | ... | @@ -71,6 +71,7 @@ import GHC.Types.Name.Env |
| 71 | 71 | import GHC.Types.Name.Reader ( mkRdrUnqual )
|
| 72 | 72 | import GHC.Types.Id
|
| 73 | 73 | import GHC.Types.Id.Info
|
| 74 | +import GHC.Types.Var (mkTyVar)
|
|
| 74 | 75 | import GHC.Types.Var.Env
|
| 75 | 76 | import GHC.Types.Var.Set
|
| 76 | 77 | import GHC.Types.Unique.Set
|
| ... | ... | @@ -765,7 +766,8 @@ addTyConsToGblEnv tyclss |
| 765 | 766 | do { traceTc "tcAddTyCons" $ vcat
|
| 766 | 767 | [ text "tycons" <+> ppr tyclss
|
| 767 | 768 | , text "implicits" <+> ppr implicit_things ]
|
| 768 | - ; gbl_env <- tcRecSelBinds (mkRecSelBinds tyclss)
|
|
| 769 | + ; linearEnabled <- xoptM LangExt.LinearTypes
|
|
| 770 | + ; gbl_env <- tcRecSelBinds (mkRecSelBinds linearEnabled tyclss)
|
|
| 769 | 771 | ; th_bndrs <- tcTyThBinders implicit_things
|
| 770 | 772 | ; return (gbl_env, th_bndrs)
|
| 771 | 773 | }
|
| ... | ... | @@ -848,24 +850,24 @@ tcRecSelBinds sel_bind_prs |
| 848 | 850 | , let loc = getSrcSpan sel_id ]
|
| 849 | 851 | binds = [(NonRecursive, [bind]) | (_, bind) <- sel_bind_prs]
|
| 850 | 852 | |
| 851 | -mkRecSelBinds :: [TyCon] -> [(Id, LHsBind GhcRn)]
|
|
| 853 | +mkRecSelBinds :: Bool -> [TyCon] -> [(Id, LHsBind GhcRn)]
|
|
| 852 | 854 | -- NB We produce *un-typechecked* bindings, rather like 'deriving'
|
| 853 | 855 | -- This makes life easier, because the later type checking will add
|
| 854 | 856 | -- all necessary type abstractions and applications
|
| 855 | -mkRecSelBinds tycons
|
|
| 856 | - = map mkRecSelBind [ (tc,fld) | tc <- tycons
|
|
| 857 | - , fld <- tyConFieldLabels tc ]
|
|
| 857 | +mkRecSelBinds linearEnabled tycons
|
|
| 858 | + = [ mkRecSelBind linearEnabled tc fld | tc <- tycons
|
|
| 859 | + , fld <- tyConFieldLabels tc ]
|
|
| 858 | 860 | |
| 859 | -mkRecSelBind :: (TyCon, FieldLabel) -> (Id, LHsBind GhcRn)
|
|
| 860 | -mkRecSelBind (tycon, fl)
|
|
| 861 | - = mkOneRecordSelector all_cons (RecSelData tycon) fl
|
|
| 861 | +mkRecSelBind :: Bool -> TyCon -> FieldLabel -> (Id, LHsBind GhcRn)
|
|
| 862 | +mkRecSelBind linearEnabled tycon fl
|
|
| 863 | + = mkOneRecordSelector linearEnabled all_cons (RecSelData tycon) fl
|
|
| 862 | 864 | FieldSelectors -- See Note [NoFieldSelectors and naughty record selectors]
|
| 863 | 865 | where
|
| 864 | 866 | all_cons = map RealDataCon (tyConDataCons tycon)
|
| 865 | 867 | |
| 866 | -mkOneRecordSelector :: [ConLike] -> RecSelParent -> FieldLabel -> FieldSelectors
|
|
| 868 | +mkOneRecordSelector :: Bool -> [ConLike] -> RecSelParent -> FieldLabel -> FieldSelectors
|
|
| 867 | 869 | -> (Id, LHsBind GhcRn)
|
| 868 | -mkOneRecordSelector all_cons idDetails fl has_sel
|
|
| 870 | +mkOneRecordSelector linearEnabled all_cons idDetails fl has_sel
|
|
| 869 | 871 | = (sel_id, L (noAnnSrcSpan loc) sel_bind)
|
| 870 | 872 | where
|
| 871 | 873 | loc = getSrcSpan sel_name
|
| ... | ... | @@ -916,17 +918,23 @@ mkOneRecordSelector all_cons idDetails fl has_sel |
| 916 | 918 | -- thus suppressing making a binding
|
| 917 | 919 | -- A slight hack!
|
| 918 | 920 | |
| 921 | + all_other_fields_unrestricted = all all_other_unrestricted all_cons
|
|
| 922 | + where
|
|
| 923 | + all_other_unrestricted PatSynCon{} = False
|
|
| 924 | + all_other_unrestricted (RealDataCon dc) = dataConOtherFieldsAllMultMany dc lbl
|
|
| 925 | + |
|
| 919 | 926 | sel_ty | is_naughty = unitTy -- See Note [Naughty record selectors]
|
| 920 | - | otherwise = mkForAllTys (tyVarSpecToBinders sel_tvbs) $
|
|
| 927 | + | otherwise = mkForAllTys (tyVarSpecToBinders (sel_tvbs ++ mult_tvb)) $
|
|
| 921 | 928 | -- Urgh! See Note [The stupid context] in GHC.Core.DataCon
|
| 922 | - mkPhiTy (conLikeStupidTheta con1) $
|
|
| 929 | + mkPhiTy (conLikeStupidTheta con1) $
|
|
| 923 | 930 | -- req_theta is empty for normal DataCon
|
| 924 | - mkPhiTy req_theta $
|
|
| 925 | - mkVisFunTyMany data_ty $
|
|
| 926 | - -- Record selectors are always typed with Many. We
|
|
| 927 | - -- could improve on it in the case where all the
|
|
| 928 | - -- fields in all the constructor have multiplicity Many.
|
|
| 931 | + mkPhiTy req_theta $
|
|
| 932 | + mkVisFunTy sel_mult data_ty $
|
|
| 929 | 933 | field_ty
|
| 934 | + (mult_tvb, sel_mult) = if linearEnabled && not_partial && all_other_fields_unrestricted
|
|
| 935 | + then ([mkForAllTyBinder InferredSpec mult_var], mkTyVarTy mult_var)
|
|
| 936 | + else ([], manyDataConTy)
|
|
| 937 | + mult_var = mkTyVar (mkSysTvName (mkBuiltinUnique 1) (fsLit "m")) multiplicityTy
|
|
| 930 | 938 | |
| 931 | 939 | -- make the binding: sel (C2 { fld = x }) = x
|
| 932 | 940 | -- sel (C7 { fld = x }) = x
|
| ... | ... | @@ -952,7 +960,7 @@ mkOneRecordSelector all_cons idDetails fl has_sel |
| 952 | 960 | -- Add catch-all default case unless the case is exhaustive
|
| 953 | 961 | -- We do this explicitly so that we get a nice error message that
|
| 954 | 962 | -- mentions this particular record selector
|
| 955 | - deflt | all dealt_with all_cons = []
|
|
| 963 | + deflt | not_partial = []
|
|
| 956 | 964 | | otherwise = [mkSimpleMatch match_ctxt (wrapGenSpan [genWildPat])
|
| 957 | 965 | (genLHsApp
|
| 958 | 966 | (genHsVar (getName rEC_SEL_ERROR_ID))
|
| ... | ... | @@ -967,6 +975,7 @@ mkOneRecordSelector all_cons idDetails fl has_sel |
| 967 | 975 | -- data instance T Int a where
|
| 968 | 976 | -- A :: { fld :: Int } -> T Int Bool
|
| 969 | 977 | -- B :: { fld :: Int } -> T Int Char
|
| 978 | + not_partial = all dealt_with all_cons
|
|
| 970 | 979 | dealt_with :: ConLike -> Bool
|
| 971 | 980 | dealt_with (PatSynCon _) = False -- We can't predict overlap
|
| 972 | 981 | dealt_with con@(RealDataCon dc)
|
| ... | ... | @@ -78,6 +78,11 @@ Language |
| 78 | 78 | |
| 79 | 79 | This causes the constructor to have type ``Rec :: Int %'Many -> Char %1 -> Record``.
|
| 80 | 80 | |
| 81 | + Also record selector functions are now multiplicity-polymorphic when possible.
|
|
| 82 | + In the above example the selector function ``y`` now has type
|
|
| 83 | + ``y :: Record %m -> Char``, because the ``x`` field is allowed to be discarded.
|
|
| 84 | + In particular this applies to the selector of a newtype wrapper.
|
|
| 85 | + |
|
| 81 | 86 | Compiler
|
| 82 | 87 | ~~~~~~~~
|
| 83 | 88 |
| 1 | +{-# LANGUAGE LinearTypes, DataKinds, OverloadedRecordDot, RebindableSyntax #-}
|
|
| 2 | +module LinearRecordSelector where
|
|
| 3 | + |
|
| 4 | +import GHC.Exts (Multiplicity(..))
|
|
| 5 | +import Prelude
|
|
| 6 | + |
|
| 7 | +getField :: ()
|
|
| 8 | +getField = ()
|
|
| 9 | + |
|
| 10 | +data Test = A { test :: Int, test2 %Many :: String } | B { test %Many :: Int, test3 %Many :: Char }
|
|
| 11 | + |
|
| 12 | +test1 :: Test %1 -> Int
|
|
| 13 | +test1 a = test a
|
|
| 14 | + |
|
| 15 | +testM :: Test -> Int
|
|
| 16 | +testM a = test a
|
|
| 17 | + |
|
| 18 | +testX :: Test %m -> Int
|
|
| 19 | +testX = test
|
|
| 20 | + |
|
| 21 | +newtype NT = NT { unNT :: Int }
|
|
| 22 | + |
|
| 23 | +nt :: NT %m -> Int
|
|
| 24 | +nt a = unNT a
|
|
| 25 | + |
| ... | ... | @@ -36,6 +36,7 @@ test('LinearTH3', normal, compile, ['']) |
| 36 | 36 | test('LinearTH4', req_th, compile, [''])
|
| 37 | 37 | test('LinearHole', normal, compile, [''])
|
| 38 | 38 | test('LinearDataConSections', normal, compile, [''])
|
| 39 | +test('LinearRecordSelector', normal, compile, ['-dcore-lint'])
|
|
| 39 | 40 | test('T18731', normal, compile, [''])
|
| 40 | 41 | test('T19400', unless(compiler_debugged(), skip), compile, [''])
|
| 41 | 42 | test('T20023', normal, compile, [''])
|
| 1 | +{-# LANGUAGE LinearTypes, DataKinds, OverloadedRecordDot, RebindableSyntax #-}
|
|
| 2 | +module LinearRecordSelector where
|
|
| 3 | + |
|
| 4 | +import GHC.Exts (Multiplicity(..))
|
|
| 5 | +import Prelude
|
|
| 6 | + |
|
| 7 | +getField :: ()
|
|
| 8 | +getField = ()
|
|
| 9 | + |
|
| 10 | +data Test1 = A1 { testA11 :: Int, testA12 :: String }
|
|
| 11 | + |
|
| 12 | +-- Fails because testA12 is linear
|
|
| 13 | +test1 :: Test1 %1 -> Int
|
|
| 14 | +test1 a = testA11 a
|
|
| 15 | + |
|
| 16 | +data Test2 = A2 { testA2 :: Int } | B2 { testB2 %Many :: Char }
|
|
| 17 | + |
|
| 18 | +-- Fails because testA2 is partial
|
|
| 19 | +test2 :: Test2 %1 -> Int
|
|
| 20 | +test2 a = testA2 a |
|
| \ No newline at end of file |
| 1 | +LinearRecordSelectorFail.hs:14:7: error: [GHC-18872]
|
|
| 2 | + • Couldn't match type ‘Many’ with ‘One’
|
|
| 3 | + arising from multiplicity of ‘a’
|
|
| 4 | + • In an equation for ‘test1’: test1 a = testA11 a
|
|
| 5 | + |
|
| 6 | +LinearRecordSelectorFail.hs:20:7: error: [GHC-18872]
|
|
| 7 | + • Couldn't match type ‘Many’ with ‘One’
|
|
| 8 | + arising from multiplicity of ‘a’
|
|
| 9 | + • In an equation for ‘test2’: test2 a = testA2 a
|
|
| 10 | + |
| ... | ... | @@ -11,6 +11,7 @@ test('LinearNoExt', normal, compile_fail, ['']) |
| 11 | 11 | test('LinearNoExtU', normal, compile_fail, [''])
|
| 12 | 12 | test('LinearAsPat', normal, compile_fail, [''])
|
| 13 | 13 | test('LinearLazyPat', normal, compile_fail, [''])
|
| 14 | +test('LinearRecordSelectorFail', normal, compile_fail, [''])
|
|
| 14 | 15 | test('LinearRecordUpdate', normal, compile_fail, [''])
|
| 15 | 16 | test('LinearSeq', normal, compile_fail, [''])
|
| 16 | 17 | test('LinearViewPattern', normal, compile_fail, [''])
|