Special-case record fields ending with hash when deriving Read
authorRyan Scott <ryan.gl.scott@gmail.com>
Fri, 23 Mar 2018 15:40:02 +0000 (11:40 -0400)
committerRyan Scott <ryan.gl.scott@gmail.com>
Fri, 23 Mar 2018 15:40:02 +0000 (11:40 -0400)
Summary:
In commit dbd81f7e86514498218572b9d978373b1699cc5b, a
regression was inadvertently introduced which caused derived `Read`
instances for record data types with fields ending in a `#` symbol
(using `MagicHash`) would no longer parse on valid output. This
is ultimately due to the same reasons as #5041, as we cannot parse
a field name like `foo#` as a single identifier. We fix this issue
by employing the same workaround as in #5041: first parse the
identifier name `foo`, then then symbol `#`.

This is accomplished by the new `readFieldHash` function in
`GHC.Read`. This will likely warrant a `base-4.11.1.0` release.

Test Plan: make test TEST=T14918

Reviewers: tdammers, hvr, bgamari

Reviewed By: bgamari

Subscribers: rwbarton, thomie, carter

GHC Trac Issues: #14918

Differential Revision: https://phabricator.haskell.org/D4502

compiler/prelude/PrelNames.hs
compiler/typecheck/TcGenDeriv.hs
docs/users_guide/8.4.2-notes.rst [new file with mode: 0644]
docs/users_guide/index.rst
libraries/base/GHC/Read.hs
libraries/base/changelog.md
testsuite/tests/deriving/should_run/T14918.hs [new file with mode: 0644]
testsuite/tests/deriving/should_run/T14918.stdout [new file with mode: 0644]
testsuite/tests/deriving/should_run/all.T

index df13eaa..280f1ef 100644 (file)
@@ -745,8 +745,9 @@ choose_RDR              = varQual_RDR gHC_READ (fsLit "choose")
 lexP_RDR                = varQual_RDR gHC_READ (fsLit "lexP")
 expectP_RDR             = varQual_RDR gHC_READ (fsLit "expectP")
 
-readField_RDR, readSymField_RDR :: RdrName
+readField_RDR, readFieldHash_RDR, readSymField_RDR :: RdrName
 readField_RDR           = varQual_RDR gHC_READ (fsLit "readField")
+readFieldHash_RDR       = varQual_RDR gHC_READ (fsLit "readFieldHash")
 readSymField_RDR        = varQual_RDR gHC_READ (fsLit "readSymField")
 
 punc_RDR, ident_RDR, symbol_RDR :: RdrName
index 0a5c5aa..788e6d9 100644 (file)
@@ -1080,19 +1080,23 @@ gen_Read_binds get_fixity loc tycon
         [noLoc
           (mkBindStmt
             (nlVarPat a)
-            (nlHsApps
+            (nlHsApp
               read_field
-              [ nlHsLit (mkHsString lbl_str)
-              , nlHsVarApps reset_RDR [readPrec_RDR]
-              ]
+              (nlHsVarApps reset_RDR [readPrec_RDR])
             )
           )
         ]
         where
           lbl_str = unpackFS lbl
+          mk_read_field read_field_rdr lbl
+              = nlHsApps read_field_rdr [nlHsLit (mkHsString lbl)]
           read_field
-              | isSym lbl_str = readSymField_RDR
-              | otherwise = readField_RDR
+              | isSym lbl_str
+              = mk_read_field readSymField_RDR lbl_str
+              | Just (ss, '#') <- snocView lbl_str -- #14918
+              = mk_read_field readFieldHash_RDR ss
+              | otherwise
+              = mk_read_field readField_RDR lbl_str
 
 {-
 ************************************************************************
diff --git a/docs/users_guide/8.4.2-notes.rst b/docs/users_guide/8.4.2-notes.rst
new file mode 100644 (file)
index 0000000..7002caa
--- /dev/null
@@ -0,0 +1,88 @@
+.. _release-8-4-2:
+
+Release notes for version 8.4.2
+===============================
+
+TODO
+
+Highlights
+----------
+
+The highlights, since the 8.4.1 release, are:
+
+- TODO
+
+
+Full details
+------------
+
+
+Language
+~~~~~~~~
+
+- Fix a regression in which derived `Read` instances for record data types
+  with field names ending with `#` (by way of :ghc-flag:`-XMagicHash`) would
+  no longer parse valid output.
+
+Compiler
+~~~~~~~~
+
+
+Runtime system
+~~~~~~~~~~~~~~
+
+
+Template Haskell
+~~~~~~~~~~~~~~~~
+
+
+``ghc`` library
+~~~~~~~~~~~~~~~
+
+
+``base`` library
+~~~~~~~~~~~~~~~~
+
+- Add the `readFieldHash` function to `GHC.Read` which behaves like
+  `readField`, but for a field that ends with a `#` symbol.
+
+Build system
+~~~~~~~~~~~~
+
+
+Included libraries
+------------------
+
+The package database provided with this distribution also contains a number of
+packages other than GHC itself. See the changelogs provided with these packages
+for further change information.
+
+.. ghc-package-list::
+
+    libraries/array/array.cabal:             Dependency of ``ghc`` library
+    libraries/base/base.cabal:               Core library
+    libraries/binary/binary.cabal:           Dependency of ``ghc`` library
+    libraries/bytestring/bytestring.cabal:   Deppendency of ``ghc`` library
+    libraries/Cabal/Cabal/Cabal.cabal:       Dependency of ``ghc-pkg`` utility
+    libraries/containers/containers.cabal:   Dependency of ``ghc`` library
+    libraries/deepseq/deepseq.cabal:         Dependency of ``ghc`` library
+    libraries/directory/directory.cabal:     Dependency of ``ghc`` library
+    libraries/filepath/filepath.cabal:       Dependency of ``ghc`` library
+    compiler/ghc.cabal:                      The compiler itself
+    libraries/ghci/ghci.cabal:               The REPL interface
+    libraries/ghc-boot/ghc-boot.cabal:       Internal compiler library
+    libraries/ghc-compact/ghc-compact.cabal: Core library
+    libraries/ghc-prim/ghc-prim.cabal:       Core library
+    libraries/haskeline/haskeline.cabal:     Dependency of ``ghci`` executable
+    libraries/hpc/hpc.cabal:                 Dependency of ``hpc`` executable
+    libraries/integer-gmp/integer-gmp.cabal: Core library
+    libraries/mtl/mtl.cabal:                 Dependency of ``Cabal`` library
+    libraries/parsec/parsec.cabal:           Dependency of ``Cabal`` library
+    libraries/process/process.cabal:         Dependency of ``ghc`` library
+    libraries/template-haskell/template-haskell.cabal:     Core library
+    libraries/text/text.cabal:               Dependency of ``Cabal`` library
+    libraries/time/time.cabal:               Dependency of ``ghc`` library
+    libraries/transformers/transformers.cabal: Dependency of ``ghc`` library
+    libraries/unix/unix.cabal:               Dependency of ``ghc`` library
+    libraries/Win32/Win32.cabal:             Dependency of ``ghc`` library
+    libraries/xhtml/xhtml.cabal:             Dependency of ``haddock`` executable
index b57e37b..7f42dca 100644 (file)
@@ -12,8 +12,8 @@ Contents:
 
    license
    intro
-   8.2.1-notes
-   8.4.1-notes
+   8.4.2-notes
+   8.6.1-notes
    ghci
    runghc
    usage
index ad51c46..f7870a2 100644 (file)
@@ -37,6 +37,7 @@ module GHC.Read
   , readListDefault, readListPrecDefault
   , readNumber
   , readField
+  , readFieldHash
   , readSymField
 
   -- Temporary
@@ -376,6 +377,22 @@ readField fieldName readVal = do
 
 -- See Note [Why readField]
 
+-- | 'Read' parser for a record field, of the form @fieldName#=value@. That is,
+-- an alphanumeric identifier @fieldName@ followed by the symbol @#@. The
+-- second argument is a parser for the field value.
+--
+-- Note that 'readField' does not suffice for this purpose due to
+-- <https://ghc.haskell.org/trac/ghc/ticket/5041 Trac #5041>.
+readFieldHash :: String -> ReadPrec a -> ReadPrec a
+readFieldHash fieldName readVal = do
+        expectP (L.Ident fieldName)
+        expectP (L.Symbol "#")
+        expectP (L.Punc "=")
+        readVal
+{-# NOINLINE readFieldHash #-}
+
+-- See Note [Why readField]
+
 -- | 'Read' parser for a symbol record field, of the form @(###)=value@ (where
 -- @###@ is the field name). The field name must be a symbol (operator-style),
 -- e.g. @(#)@. For regular (alphanumeric) field names, use 'readField'. The
index 47fe011..16e183e 100644 (file)
@@ -9,6 +9,10 @@
   * Add `Applicative` (for `K1`), `Semigroup` and `Monoid` instances in
     `GHC.Generics`. (#14849)
 
+## 4.11.1.0 *TBA*
+  * Add the `readFieldHash` function to `GHC.Read` which behaves like
+    `readField`, but for a field that ends with a `#` symbol (#14918).
+
 ## 4.11.0.0 *TBA*
   * Bundled with GHC 8.4.1
 
diff --git a/testsuite/tests/deriving/should_run/T14918.hs b/testsuite/tests/deriving/should_run/T14918.hs
new file mode 100644 (file)
index 0000000..2ad2937
--- /dev/null
@@ -0,0 +1,18 @@
+{-# LANGUAGE MagicHash #-}
+module Main where
+
+data T  a = MkT  { runT  :: a, (##)  :: a } deriving (Read, Show)
+data T# a = MkT# { runT# :: a, (###) :: a } deriving (Read, Show)
+
+t1, t2 :: T Int
+t1 = MkT (-1) 1
+t2 = read $ show t1
+
+t1#, t2# :: T# Int
+t1# = MkT# (-1) 1
+t2# = read $ show t1#
+
+main :: IO ()
+main = do
+  print t2
+  print t2#
diff --git a/testsuite/tests/deriving/should_run/T14918.stdout b/testsuite/tests/deriving/should_run/T14918.stdout
new file mode 100644 (file)
index 0000000..b85e2a2
--- /dev/null
@@ -0,0 +1,2 @@
+MkT {runT = -1, (##) = 1}
+MkT# {runT# = -1, (###) = 1}
index c5605f6..cf0cb92 100644 (file)
@@ -45,3 +45,4 @@ test('T10598_bug', normal, compile_and_run, [''])
 test('T10598_run', normal, compile_and_run, [''])
 test('T11535', when(opsys('mingw32'), expect_broken_for(12210, ['ghci'])),
      compile_and_run, [''])
+test('T14918', normal, compile_and_run, [''])