Simon Jakobi pushed to branch wip/sjakobi/lazy-field-anns at Glasgow Haskell Compiler / GHC

Commits:

16 changed files:

Changes:

  • changelog.d/lazy-field-annotations
    1
    +section: language
    
    2
    +synopsis: Add the `LazyFieldAnnotations` extension
    
    3
    +issues: #24455
    
    4
    +mrs: !16246
    
    5
    +
    
    6
    +description: {
    
    7
    +    The new :extension:`LazyFieldAnnotations` extension permits the prefix
    
    8
    +    ``~`` lazy field annotation on data and GADT constructor fields without
    
    9
    +    enabling :extension:`StrictData`. :extension:`StrictData` (and therefore
    
    10
    +    :extension:`Strict`) now implies :extension:`LazyFieldAnnotations` and
    
    11
    +    continues to control the default strictness of unannotated fields.
    
    12
    +}

  • compiler/GHC/Driver/Flags.hs
    ... ... @@ -261,6 +261,7 @@ extensionName = \case
    261 261
       LangExt.ExplicitLevelImports -> "ExplicitLevelImports"
    
    262 262
       LangExt.ImplicitStagePersistence -> "ImplicitStagePersistence"
    
    263 263
       LangExt.QualifiedStrings -> "QualifiedStrings"
    
    264
    +  LangExt.LazyFieldAnnotations -> "LazyFieldAnnotations"
    
    264 265
     
    
    265 266
     -- | Is this extension known by any other names? For example
    
    266 267
     -- -XGeneralizedNewtypeDeriving is accepted
    
    ... ... @@ -343,6 +344,7 @@ impliedXFlags
    343 344
     
    
    344 345
         , (LangExt.TemplateHaskell, On LangExt.TemplateHaskellQuotes)
    
    345 346
         , (LangExt.Strict, On LangExt.StrictData)
    
    347
    +    , (LangExt.StrictData, On LangExt.LazyFieldAnnotations)
    
    346 348
     
    
    347 349
         -- Historically only UnboxedTuples was required for unboxed sums to work.
    
    348 350
         -- To avoid breaking code, we make UnboxedTuples imply UnboxedSums.
    

  • compiler/GHC/Tc/Errors/Ppr.hs
    ... ... @@ -3231,7 +3231,7 @@ instance Diagnostic TcRnMessage where
    3231 3231
         TcRnHasFieldResolvedIncomplete{}
    
    3232 3232
           -> noHints
    
    3233 3233
         TcRnBadFieldAnnotation _ _ LazyFieldsDisabled
    
    3234
    -      -> [suggestExtension LangExt.StrictData]
    
    3234
    +      -> [suggestExtension LangExt.LazyFieldAnnotations]
    
    3235 3235
         TcRnBadFieldAnnotation{}
    
    3236 3236
           -> noHints
    
    3237 3237
         TcRnSuperclassCycle{}
    

  • compiler/GHC/Tc/Errors/Types.hs
    ... ... @@ -6355,7 +6355,7 @@ data PatSynInvalidRhsReason
    6355 6355
     
    
    6356 6356
     data BadFieldAnnotationReason where
    
    6357 6357
       {-| A lazy data type field annotation (~) was used without enabling the
    
    6358
    -    extension StrictData.
    
    6358
    +    extension LazyFieldAnnotations.
    
    6359 6359
     
    
    6360 6360
         Test cases:
    
    6361 6361
         LazyFieldsDisabled
    

  • compiler/GHC/Tc/TyCl.hs
    ... ... @@ -5186,7 +5186,7 @@ checkValidDataCon dflags existential_ok tc con
    5186 5186
             ; let check_bang :: Type -> HsSrcBang -> HsImplBang -> Int -> TcM ()
    
    5187 5187
                   check_bang orig_arg_ty bang rep_bang n
    
    5188 5188
                    | HsSrcBang _  _ SrcLazy <- bang
    
    5189
    -               , not (bang_opt_strict_data bang_opts)
    
    5189
    +               , not (xopt LangExt.LazyFieldAnnotations dflags)
    
    5190 5190
                    = addErrTc (bad_bang n LazyFieldsDisabled)
    
    5191 5191
     
    
    5192 5192
                    -- Warn about UNPACK without "!"
    

  • docs/users_guide/exts/strict.rst
    ... ... @@ -155,6 +155,44 @@ Note the following points:
    155 155
       for more discussion and examples.
    
    156 156
     
    
    157 157
     
    
    158
    +.. _lazy-field-annotations:
    
    159
    +
    
    160
    +Lazy field annotations
    
    161
    +----------------------
    
    162
    +
    
    163
    +.. extension:: LazyFieldAnnotations
    
    164
    +    :shortdesc: Allow the prefix ``~`` lazy field annotation on constructor fields.
    
    165
    +
    
    166
    +    :since: 10.2.1
    
    167
    +
    
    168
    +    Allow the prefix ``~`` lazy field annotation on data and GADT constructor
    
    169
    +    fields.
    
    170
    +
    
    171
    +``LazyFieldAnnotations`` allows a prefix ``~`` lazy annotation in every
    
    172
    +constructor-field position where a prefix ``!`` strict annotation is accepted:
    
    173
    +Haskell-98 prefix and record fields, and GADT argument types and record
    
    174
    +fields. ::
    
    175
    +
    
    176
    +  {-# LANGUAGE LazyFieldAnnotations #-}
    
    177
    +
    
    178
    +  data A = A ~Int Bool
    
    179
    +  data B = B { b1 :: ~Int, b2 :: !Bool }
    
    180
    +
    
    181
    +  data C where
    
    182
    +    C1 :: ~Int -> C
    
    183
    +    C2 :: { c1 :: ~Int, c2 :: !Bool } -> C
    
    184
    +
    
    185
    +A field annotated with ``~`` is lazy: it is not forced when the constructor is
    
    186
    +built, but held as a thunk — the same behaviour as an unannotated field.
    
    187
    +
    
    188
    +The ``~`` annotation must be written in prefix form::
    
    189
    +
    
    190
    +   data T = MkT ~Int   -- valid
    
    191
    +   data T = MkT ~ Int  -- invalid
    
    192
    +
    
    193
    +See `GHC Proposal #229 <https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0229-whitespace-bang-patterns.rst>`__
    
    194
    +for the precise rules.
    
    195
    +
    
    158 196
     .. _strict-data:
    
    159 197
     
    
    160 198
     Strict-by-default data types
    
    ... ... @@ -163,6 +201,8 @@ Strict-by-default data types
    163 201
     .. extension:: StrictData
    
    164 202
         :shortdesc: Treat datatype fields as strict by default.
    
    165 203
     
    
    204
    +    :implies: :extension:`LazyFieldAnnotations`
    
    205
    +
    
    166 206
         :since: 8.0.1
    
    167 207
     
    
    168 208
         Make fields of data types defined in the current module strict by default.
    
    ... ... @@ -183,13 +223,8 @@ we interpret it as if they had written ::
    183 223
     
    
    184 224
     The extension only affects definitions in this module.
    
    185 225
     
    
    186
    -The ``~`` annotation must be written in prefix form::
    
    187
    -
    
    188
    -   data T = MkT ~Int   -- valid
    
    189
    -   data T = MkT ~ Int  -- invalid
    
    190
    -
    
    191
    -See `GHC Proposal #229 <https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0229-whitespace-bang-patterns.rst>`__
    
    192
    -for the precise rules.
    
    226
    +The ``~`` lazy field annotation is provided by :extension:`LazyFieldAnnotations`
    
    227
    +(which ``StrictData`` implies); see :ref:`lazy-field-annotations`.
    
    193 228
     
    
    194 229
     .. _strict:
    
    195 230
     
    

  • libraries/ghc-internal/src/GHC/Internal/LanguageExtensions.hs
    ... ... @@ -169,6 +169,7 @@ data Extension
    169 169
        | ExplicitLevelImports
    
    170 170
        | ImplicitStagePersistence
    
    171 171
        | QualifiedStrings
    
    172
    +   | LazyFieldAnnotations
    
    172 173
        deriving (Eq, Enum, Show, Generic, Bounded)
    
    173 174
     -- 'Ord' and 'Bounded' are provided for GHC API users (see discussions
    
    174 175
     -- in https://gitlab.haskell.org/ghc/ghc/merge_requests/2707 and
    

  • testsuite/tests/deSugar/should_run/LazyFieldAnnotationsSemantics.hs
    1
    +{-# LANGUAGE LazyFieldAnnotations #-}
    
    2
    +
    
    3
    +-- | LazyFieldAnnotations does not change the default strictness of fields.
    
    4
    +-- Without StrictData an unannotated field stays lazy and a ~ field is lazy
    
    5
    +-- too; a ! field is still strict.
    
    6
    +module Main where
    
    7
    +
    
    8
    +import Control.Exception (try, evaluate, SomeException)
    
    9
    +
    
    10
    +data T = Plain  Int   -- unannotated: lazy
    
    11
    +       | Lazy   ~Int   -- explicit ~: lazy
    
    12
    +       | Strict !Int   -- explicit !: strict
    
    13
    +
    
    14
    +-- | Does building the constructor force the field?
    
    15
    +forcesField :: T -> IO Bool
    
    16
    +forcesField x = do
    
    17
    +  r <- try (evaluate x) :: IO (Either SomeException T)
    
    18
    +  pure (either (const True) (const False) r)
    
    19
    +
    
    20
    +main :: IO ()
    
    21
    +main = do
    
    22
    +  print =<< forcesField (Plain  undefined)  -- False: lazy
    
    23
    +  print =<< forcesField (Lazy   undefined)  -- False: lazy
    
    24
    +  print =<< forcesField (Strict undefined)  -- True:  strict

  • testsuite/tests/deSugar/should_run/LazyFieldAnnotationsSemantics.stdout
    1
    +False
    
    2
    +False
    
    3
    +True

  • testsuite/tests/deSugar/should_run/all.T
    ... ... @@ -76,3 +76,5 @@ test('T19680A', normal, compile_and_run, [''])
    76 76
     test('T20024', exit_code(1), compile_and_run, [''])
    
    77 77
     
    
    78 78
     test('Or5', exit_code(1), compile_and_run, [''])
    
    79
    +
    
    80
    +test('LazyFieldAnnotationsSemantics', normal, compile_and_run, [''])

  • testsuite/tests/typecheck/should_compile/LazyFieldAnnotations.hs
    1
    +{-# LANGUAGE LazyFieldAnnotations #-}
    
    2
    +{-# LANGUAGE GADTs #-}
    
    3
    +
    
    4
    +-- | LazyFieldAnnotations alone accepts the prefix ~ annotation in every
    
    5
    +-- constructor-field position, without enabling StrictData.
    
    6
    +module LazyFieldAnnotations where
    
    7
    +
    
    8
    +-- Haskell-98 prefix and record fields
    
    9
    +data A = A ~Int Bool
    
    10
    +data B = B { b1 :: ~Int, b2 :: !Bool }
    
    11
    +
    
    12
    +-- GADT argument types and record fields
    
    13
    +data C where
    
    14
    +  C1 :: ~Int -> C
    
    15
    +  C2 :: { c1 :: ~Int, c2 :: !Bool } -> C

  • testsuite/tests/typecheck/should_compile/all.T
    ... ... @@ -967,4 +967,5 @@ test('T26805a', normal, compile, [''])
    967 967
     test('T24464', normal, compile, [''])
    
    968 968
     test('ExpansionQLIm', normal, compile, [''])
    
    969 969
     test('T23135', normal, compile, [''])
    
    970
    +test('LazyFieldAnnotations', normal, compile, [''])
    
    970 971
     

  • testsuite/tests/typecheck/should_fail/LazyFieldsDisabled.stderr
    1
    -
    
    2 1
     LazyFieldsDisabled.hs:3:10: error: [GHC-81601]
    
    3 2
         • Lazy field annotations (~) are disabled
    
    4 3
             on the first argument of ‘A’
    
    5 4
         • In the definition of data constructor ‘A’
    
    6 5
           In the data type declaration for ‘A’
    
    7 6
         Suggested fix:
    
    8
    -      Perhaps you intended to use the ‘StrictData’ extension (implied by ‘Strict’)
    7
    +      Perhaps you intended to use the ‘LazyFieldAnnotations’ extension (implied by ‘StrictData’)
    
    8
    +

  • testsuite/tests/typecheck/should_fail/LazyFieldsDisabledStrictData.hs
    1
    +{-# LANGUAGE StrictData #-}
    
    2
    +{-# LANGUAGE NoLazyFieldAnnotations #-}
    
    3
    +
    
    4
    +-- | A later NoLazyFieldAnnotations overrides the implication from StrictData,
    
    5
    +-- so ~ is rejected even though unannotated fields remain strict.
    
    6
    +module LazyFieldsDisabledStrictData where
    
    7
    +
    
    8
    +data A = A ~Int

  • testsuite/tests/typecheck/should_fail/LazyFieldsDisabledStrictData.stderr
    1
    +LazyFieldsDisabledStrictData.hs:8:10: error: [GHC-81601]
    
    2
    +    • Lazy field annotations (~) are disabled
    
    3
    +        on the first argument of ‘A’
    
    4
    +    • In the definition of data constructor ‘A’
    
    5
    +      In the data type declaration for ‘A’
    
    6
    +    Suggested fix:
    
    7
    +      Perhaps you intended to use the ‘LazyFieldAnnotations’ extension (implied by ‘StrictData’)
    
    8
    +

  • testsuite/tests/typecheck/should_fail/all.T
    ... ... @@ -690,6 +690,7 @@ test('T21444', normal, compile_fail, [''])
    690 690
     test('T23308', normal, compile_fail, [''])
    
    691 691
     test('MultiAssocDefaults', normal, compile_fail, [''])
    
    692 692
     test('LazyFieldsDisabled', normal, compile_fail, [''])
    
    693
    +test('LazyFieldsDisabledStrictData', normal, compile_fail, [''])
    
    693 694
     test('TyfamsDisabled', normal, compile_fail, [''])
    
    694 695
     test('CommonFieldResultTypeMismatch', normal, compile_fail, [''])
    
    695 696
     test('CommonFieldTypeMismatch', normal, compile_fail, [''])