summaryrefslogtreecommitdiff
path: root/testsuite
diff options
context:
space:
mode:
Diffstat (limited to 'testsuite')
-rw-r--r--testsuite/tests/driver/T4437.hs2
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxA.hs17
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail0.hs4
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail0.stderr2
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail1.hs5
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail1.stderr2
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail10.hs40
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail10.stderr13
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail11.hs8
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail11.stderr25
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail12.hs140
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail12.stderr36
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail13.hs26
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail13.stderr15
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail2.hs11
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail2.stderr2
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail3.hs24
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail3.stderr1
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail4.hs8
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail4.stderr2
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail5.hs17
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail5.stderr2
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail6.hs11
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail6.stderr5
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail7.hs9
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail7.stderr4
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail8.hs37
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail8.stderr25
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail9.hs8
-rw-r--r--testsuite/tests/parser/should_fail/RecordDotSyntaxFail9.stderr9
-rw-r--r--testsuite/tests/parser/should_fail/all.T14
-rw-r--r--testsuite/tests/parser/should_run/RecordDotSyntax1.hs141
-rw-r--r--testsuite/tests/parser/should_run/RecordDotSyntax1.stdout38
-rw-r--r--testsuite/tests/parser/should_run/RecordDotSyntax2.hs33
-rw-r--r--testsuite/tests/parser/should_run/RecordDotSyntax2.stdout12
-rw-r--r--testsuite/tests/parser/should_run/RecordDotSyntax3.hs14
-rw-r--r--testsuite/tests/parser/should_run/RecordDotSyntax3.stdout3
-rw-r--r--testsuite/tests/parser/should_run/RecordDotSyntax4.hs9
-rw-r--r--testsuite/tests/parser/should_run/RecordDotSyntax4.stdout1
-rw-r--r--testsuite/tests/parser/should_run/RecordDotSyntaxA.hs6
-rw-r--r--testsuite/tests/parser/should_run/all.T4
41 files changed, 785 insertions, 0 deletions
diff --git a/testsuite/tests/driver/T4437.hs b/testsuite/tests/driver/T4437.hs
index 27be970d22..cbcbefc573 100644
--- a/testsuite/tests/driver/T4437.hs
+++ b/testsuite/tests/driver/T4437.hs
@@ -41,6 +41,8 @@ expectedGhcOnlyExtensions =
, "AlternativeLayoutRule"
, "AlternativeLayoutRuleTransitional"
, "FieldSelectors"
+ , "OverloadedRecordDot"
+ , "OverloadedRecordUpdate"
]
expectedCabalOnlyExtensions :: [String]
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxA.hs b/testsuite/tests/parser/should_fail/RecordDotSyntaxA.hs
new file mode 100644
index 0000000000..22b5aed888
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxA.hs
@@ -0,0 +1,17 @@
+{-# LANGUAGE AllowAmbiguousTypes, FunctionalDependencies, ScopedTypeVariables, PolyKinds, TypeApplications, DataKinds #-}
+{-# LANGUAGE RecordWildCards, OverloadedRecordDot, OverloadedRecordUpdate #-}
+
+module RecordDotSyntaxA where
+
+class HasField x r a | x r -> a where
+ hasField :: r -> (a -> r, a)
+
+getField :: forall x r a . HasField x r a => r -> a
+getField = snd . hasField @x -- Note: a.x = is getField @"x" a.
+
+setField :: forall x r a . HasField x r a => r -> a -> r
+setField = fst . hasField @x -- Note : a{x = b} is setField @"x" a b.
+
+data Foo = Foo {foo :: Int}
+instance HasField "foo" Foo Int where
+ hasField r = (\x -> case r of Foo { .. } -> Foo { foo = x, .. }, foo r)
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail0.hs b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail0.hs
new file mode 100644
index 0000000000..f7692ec778
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail0.hs
@@ -0,0 +1,4 @@
+{-# LANGUAGE OverloadedRecordDot #-}
+
+no = Foo { bar.baz = 1 }
+ -- Syntax error: Can't use '.' in construction.
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail0.stderr b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail0.stderr
new file mode 100644
index 0000000000..6e4a3fbae6
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail0.stderr
@@ -0,0 +1,2 @@
+ RecordDotSyntaxFail0.hs:3:12:
+ Use of OverloadedRecordDot '.' not valid ('.' isn't allowed when constructing records or in record patterns)
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail1.hs b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail1.hs
new file mode 100644
index 0000000000..78b4f1072c
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail1.hs
@@ -0,0 +1,5 @@
+{-# LANGUAGE OverloadedRecordDot #-}
+
+no Foo { bar.baz = x } = undefined
+ -- Syntax error: Field selector syntax doesn't participate
+ -- in patterns
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail1.stderr b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail1.stderr
new file mode 100644
index 0000000000..f1ab2b9f95
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail1.stderr
@@ -0,0 +1,2 @@
+RecordDotSyntaxFail1.hs:3:10:
+ Use of OverloadedRecordDot '.' not valid ('.' isn't allowed when constructing records or in record patterns)
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail10.hs b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail10.hs
new file mode 100644
index 0000000000..cc76b469d5
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail10.hs
@@ -0,0 +1,40 @@
+{-# LANGUAGE AllowAmbiguousTypes, FunctionalDependencies, ScopedTypeVariables, PolyKinds, TypeApplications, DataKinds #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE OverloadedRecordDot, OverloadedRecordUpdate #-}
+{-# LANGUAGE RebindableSyntax #-}
+import Prelude
+
+class HasField x r a | x r -> a where
+ hasField :: r -> (a -> r, a)
+
+getField :: forall x r a . HasField x r a => r -> a
+getField = snd . hasField @x -- Note: a.x = is getField @"x" a.
+
+setField :: forall x r a . HasField x r a => r -> a -> r
+setField = fst . hasField @x -- Note : a{x = b} is setField @"x" a b.
+
+-- 'Foo' has 'foo' field of type 'Bar'
+data Foo = Foo { foo :: Bar } deriving (Show, Eq)
+instance HasField "foo" Foo Bar where
+ hasField r = (\x -> case r of Foo { .. } -> Foo { foo = x, .. }, foo r)
+
+-- 'Bar' has a 'bar' field of type 'Baz'
+data Bar = Bar { bar :: Baz } deriving (Show, Eq)
+instance HasField "bar" Bar Baz where
+ hasField r = (\x -> case r of Bar { .. } -> Bar { bar = x, .. }, bar r)
+
+-- 'Baz' has a 'baz' field of type 'Quux'
+data Baz = Baz { baz :: Quux } deriving (Show, Eq)
+instance HasField "baz" Baz Quux where
+ hasField r = (\x -> case r of Baz { .. } -> Baz { baz = x, .. }, baz r)
+
+-- 'Quux' has a 'quux' field of type 'Int'
+data Quux = Quux { quux :: Int } deriving (Show, Eq)
+instance HasField "quux" Quux Int where
+ hasField r = (\x -> case r of Quux { .. } -> Quux { quux = x, .. }, quux r)
+
+main = do
+ let a = Foo { foo = Bar{ bar = Baz { baz = Quux { quux = 42 } } } }
+ let quux = "Expecto patronum!"
+ print $ a{foo.bar.baz.quux} -- Type error. Does a{foo.bar.baz.quux} get underlined?
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail10.stderr b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail10.stderr
new file mode 100644
index 0000000000..38d9616489
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail10.stderr
@@ -0,0 +1,13 @@
+RecordDotSyntaxFail10.hs:40:11:
+ Couldn't match type ‘Int’ with ‘[Char]’
+ arising from a functional dependency between:
+ constraint ‘HasField "quux" Quux String’
+ arising from a use of ‘setField’
+ instance ‘HasField "quux" Quux Int’
+ at RecordDotSyntaxFail10.hs:34:10-33
+ In the second argument of ‘($)’, namely ‘a {foo.bar.baz.quux}’
+ In a stmt of a 'do' block: print $ a {foo.bar.baz.quux}
+ In the expression:
+ do let a = ...
+ let quux = "Expecto patronum!"
+ print $ a {foo.bar.baz.quux}
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail11.hs b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail11.hs
new file mode 100644
index 0000000000..62f9bd8f23
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail11.hs
@@ -0,0 +1,8 @@
+{-# LANGUAGE OverloadedRecordDot #-}
+
+data Foo = Foo { foo :: Bar }
+data Bar = Bar { bar :: Int }
+
+main = do
+ let a = Foo { foo = Bar { bar = 1 }}
+ print $ (.foo.bar.baz) a -- Oops, what is baz?
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail11.stderr b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail11.stderr
new file mode 100644
index 0000000000..4ca1005185
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail11.stderr
@@ -0,0 +1,25 @@
+RecordDotSyntaxFail11.hs:8:3:
+ Ambiguous type variable ‘a0’ arising from a use of ‘print’
+ prevents the constraint ‘(Show a0)’ from being solved.
+ Probable fix: use a type annotation to specify what ‘a0’ should be.
+ These potential instances exist:
+ instance Show Ordering -- Defined in ‘GHC.Show’
+ instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’
+ instance Show Integer -- Defined in ‘GHC.Show’
+ ...plus 23 others
+ ...plus N instances involving out-of-scope types
+ (use -fprint-potential-instances to see them all)
+ In the first argument of ‘($)’, namely ‘print’
+ In a stmt of a 'do' block: print $ (foo.bar.baz) a
+ In the expression:
+ do let a = ...
+ print $ (foo.bar.baz) a
+
+RecordDotSyntaxFail11.hs:8:11:
+ No instance for (GHC.Records.HasField "baz" Int a0)
+ arising from a use of ‘GHC.Records.getField’
+ In the second argument of ‘($)’, namely ‘(foo.bar.baz) a’
+ In a stmt of a 'do' block: print $ (foo.bar.baz) a
+ In the expression:
+ do let a = ...
+ print $ (foo.bar.baz) a
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail12.hs b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail12.hs
new file mode 100644
index 0000000000..ba7f7effed
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail12.hs
@@ -0,0 +1,140 @@
+{-# LANGUAGE AllowAmbiguousTypes, FunctionalDependencies, ScopedTypeVariables, PolyKinds, TypeApplications, DataKinds #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE OverloadedRecordDot, OverloadedRecordUpdate #-}
+-- For "higher kinded data" test.
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE FlexibleInstances #-}
+
+{-# LANGUAGE RebindableSyntax #-}
+import Prelude
+
+-- Choice (C2a).
+
+import Data.Function -- for &
+import Data.Functor.Identity
+
+class HasField x r a | x r -> a where
+ hasField :: r -> (a -> r, a)
+
+getField :: forall x r a . HasField x r a => r -> a
+getField = snd . hasField @x -- Note: a.x = is getField @"x" a.
+
+setField :: forall x r a . HasField x r a => r -> a -> r
+setField = fst . hasField @x -- Note : a{x = b} is setField @"x" a b.
+
+-- 'Foo' has 'foo' field of type 'Bar'
+data Foo = Foo { foo :: Bar } deriving (Show, Eq)
+instance HasField "foo" Foo Bar where
+ hasField r = (\x -> case r of Foo { .. } -> Foo { foo = x, .. }, foo r)
+
+-- 'Bar' has a 'bar' field of type 'Baz'
+data Bar = Bar { bar :: Baz } deriving (Show, Eq)
+instance HasField "bar" Bar Baz where
+ hasField r = (\x -> case r of Bar { .. } -> Bar { bar = x, .. }, bar r)
+
+-- 'Baz' has a 'baz' field of type 'Quux'
+data Baz = Baz { baz :: Quux } deriving (Show, Eq)
+instance HasField "baz" Baz Quux where
+ hasField r = (\x -> case r of Baz { .. } -> Baz { baz = x, .. }, baz r)
+
+-- 'Quux' has a 'quux' field of type 'Int'
+data Quux = Quux { quux :: Int } deriving (Show, Eq)
+instance HasField "quux" Quux Int where
+ hasField r = (\x -> case r of Quux { .. } -> Quux { quux = x, .. }, quux r)
+
+-- 'Corge' has a '&&&' field of type 'Int'
+data Corge = Corge { (&&&) :: Int } deriving (Show, Eq)
+instance HasField "&&&" Corge Int where
+ hasField r = (\x -> case r of Corge { .. } -> Corge { (&&&) = x, .. }, (&&&) r)
+-- Note : Dot notation is not available for fields with operator
+-- names.
+
+-- 'Grault' has two fields 'f' and 'g' of type 'Foo'.
+data Grault = Grault {f :: Foo, g :: Foo} deriving (Show, Eq)
+instance HasField "f" Grault Foo where
+ hasField r = (\x -> case r of Grault { .. } -> Grault { f = x, .. }, f r)
+instance HasField "g" Grault Foo where
+ hasField r = (\x -> case r of Grault { .. } -> Grault { g = x, .. }, g r)
+
+-- "Higher kinded data"
+-- (see https://reasonablypolymorphic.com/blog/higher-kinded-data/)
+type family H f a where
+ H Identity a = a
+ H f a = f a
+data P f = P
+ { n :: H f String
+ }
+-- See https://github.com/ndmitchell/record-dot-preprocessor/pull/34.
+instance (a ~ H f String) => HasField "n" (P f) a where
+ hasField r = (\x -> case r of P { .. } -> P { n = x, .. }, n r)
+
+main = do
+ let a = Foo { foo = Bar{ bar = Baz { baz = Quux { quux = 42 } } } }
+ let b = Corge{ (&&&) = 12 };
+ let c = Grault {
+ f = Foo { foo = Bar{ bar = Baz { baz = Quux { quux = 1 } } } }
+ , g = Foo { foo = Bar{ bar = Baz { baz = Quux { quux = 1 } } } }
+ }
+
+ -- A "selector" is an expression like '(.a)' or '(.a.b)'.
+ putStrLn "-- selectors:"
+ print $ (.foo) a -- Bar { bar = Baz { baz = Quux { quux = 42 } } }
+ print $ (.foo.bar) a -- Baz { baz = Quux { quux = 42 } }
+ print $ (.foo.bar.baz) a -- Quux { quux = 42 }
+ print $ (.foo.bar.baz.quux) a -- 42
+ print $ ((&&&) b) -- 12
+ -- print $ (b.(&&&)) -- illegal : parse error on input ‘(’
+ print $ getField @"&&&" b -- 12
+
+ -- A "selection" is an expression like 'r.a' or '(f r).a.b'.
+ putStrLn "-- selections:"
+ print $ a.foo.bar.baz.quux -- 42
+ print $ a.foo.bar.baz -- Quux { quux = 42 }
+ print $ a.foo.bar -- Baz { baz = Quux { quux = 42 } }
+ print $ a.foo -- Bar { bar = Baz { baz = Quux { quux = 42 } } }
+ print $ (const "hello") a.foo -- f r.x means f (r.x)
+ -- print $ f a .foo -- f r .x is illegal
+ print $ (const "hello") (id a).foo -- f (g r).x means f ((g r).x)
+ -- print $ f (g a) .foo -- f (g r) .x is illegal
+ print $ a.foo
+ & (.bar.baz.quux) -- 42
+ print $ (a.foo
+ ).bar.baz.quux -- 42
+ print $ (+) a.foo.bar.baz.quux 1 -- 43
+ print $ (+) (id a).foo.bar.baz.quux 1 -- 43
+ print $ (+) ((id a).foo.bar & (.baz.quux)) 1 -- 43
+
+ -- An "update" is an expression like 'r{ a.b = 12 }'.
+ putStrLn "-- updates:"
+ print $ (a.foo.bar.baz) { quux = 2 } -- Quux { quux = 2 }
+ print $ (\b -> b{ bar=Baz{ baz=Quux{ quux=1 } } }) a.foo -- Bar { bar = Baz { baz = Quux { quux = 1 } } }
+ let bar = Bar { bar = Baz { baz = Quux { quux = 44 } } }
+ print $ a{ foo.bar = Baz { baz = Quux { quux = 44 } } } -- Foo { foo = Bar { bar = Baz { baz = Quux { quux = 44 } } } }
+ print $ a{ foo.bar.baz = Quux { quux = 45 } } -- Foo { foo = Bar { bar = Baz { baz = Quux { quux = 45 } } } }
+ print $ a{ foo.bar.baz.quux = 46 } -- Foo { foo = Bar { bar = Baz { baz = Quux { quux = 46 } } } }
+ print $ c{ f.foo.bar.baz.quux = 3, g.foo.bar.baz.quux = 4 } -- Grault { f = Foo { foo = Bar { bar = Baz { baz = Quux { quux = 3 } } } }, g = Foo { foo = Bar { bar = Baz { baz = Quux { quux = 4 } } } } }
+
+ -- A "punned update" is an expression like 'r{ a.b }' (where it is
+ -- understood that 'b' is a variable binding in the environment of
+ -- the field update - enabled only when the extension
+ -- 'NamedFieldPuns' is in effect).
+ putStrLn "-- punned updates:"
+ let quux = 102; baz = Quux { quux }; bar = Baz { baz }; foo = Bar { bar } -- Foo { foo = Bar { bar = Baz { baz = Quux { quux = 102 } } } }
+ print $ a{ foo.bar.baz.quux } -- Foo { foo = Bar { bar = Baz { baz = Quux { quux = 102 } } } }
+ print $ a{ foo.bar.baz } -- Foo { foo = Bar { bar = Baz { baz = Quux { quux = 102 } } } }
+ print $ a{ foo.bar } -- Foo { foo = Bar { bar = Baz { baz = Quux { quux = 102 } } } }
+ print $ a{ foo } -- Foo { foo = Bar { bar = Baz { baz = Quux { quux = 102 } } } }
+ print $ a -- Foo { foo = Bar { bar = Baz { baz = Quux { quux = 42 } } } }
+ print $ c{ f.foo, g.foo.bar.baz.quux = 4 } -- Mix punned and explicit; 102, 4
+ f <- pure a
+ g <- pure a
+ print $ c{ f } -- 42, 1
+ print $ c{ f, g } -- 42, 42
+ print $ c{ f, g.foo.bar.baz.quux = 4 } -- Mix top-level and nested updates; 42, 4
+
+ putStrLn "-- misc:"
+ -- Higher kinded test.
+ let p = P { n = Just "me" } :: P Maybe
+ Just me <- pure p.n
+ putStrLn $ me
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail12.stderr b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail12.stderr
new file mode 100644
index 0000000000..6ef0a51754
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail12.stderr
@@ -0,0 +1,36 @@
+
+RecordDotSyntaxFail12.hs:123:25:
+ Illegal use of punning for field ‘quux’
+ Use NamedFieldPuns to permit this
+
+RecordDotSyntaxFail12.hs:123:46:
+ Illegal use of punning for field ‘baz’
+ Use NamedFieldPuns to permit this
+
+RecordDotSyntaxFail12.hs:123:65:
+ Illegal use of punning for field ‘bar’
+ Use NamedFieldPuns to permit this
+
+RecordDotSyntaxFail12.hs:124:11:
+ For this to work enable NamedFieldPuns.
+
+RecordDotSyntaxFail12.hs:125:11:
+ For this to work enable NamedFieldPuns.
+
+RecordDotSyntaxFail12.hs:126:11:
+ For this to work enable NamedFieldPuns.
+
+RecordDotSyntaxFail12.hs:127:11:
+ For this to work enable NamedFieldPuns.
+
+RecordDotSyntaxFail12.hs:129:11:
+ For this to work enable NamedFieldPuns.
+
+RecordDotSyntaxFail12.hs:132:11:
+ For this to work enable NamedFieldPuns.
+
+RecordDotSyntaxFail12.hs:133:11:
+ For this to work enable NamedFieldPuns.
+
+RecordDotSyntaxFail12.hs:134:11:
+ For this to work enable NamedFieldPuns.
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail13.hs b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail13.hs
new file mode 100644
index 0000000000..7050145b9d
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail13.hs
@@ -0,0 +1,26 @@
+{-# LANGUAGE AllowAmbiguousTypes, FunctionalDependencies, ScopedTypeVariables, PolyKinds, TypeApplications, DataKinds #-}
+{-# LANGUAGE OverloadedRecordDot, OverloadedRecordUpdate #-}
+{-# LANGUAGE RecordWildCards, NamedFieldPuns #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE RebindableSyntax #-}
+import Prelude
+
+class HasField x r a | x r -> a where
+ hasField :: r -> (a -> r, a)
+
+getField :: forall x r a . HasField x r a => r -> a
+getField = snd . hasField @x -- Note: a.x = is getField @"x" a.
+
+setField :: forall x r a . HasField x r a => r -> a -> r
+setField = fst . hasField @x -- Note : a{x = b} is setField @"x" a b.
+
+-- 'Foo' has 'foo' field of type 'Int'
+data Foo = Foo { foo :: Int } deriving (Show, Eq)
+instance HasField "foo" Foo Int where
+ hasField r = (\x -> case r of Foo { .. } -> Foo { foo = x, .. }, foo r)
+main = do
+ let a = Foo {foo = 12};
+ -- let foo = 13;
+ print $ a {foo}
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail13.stderr b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail13.stderr
new file mode 100644
index 0000000000..8b5369731f
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail13.stderr
@@ -0,0 +1,15 @@
+
+RecordDotSyntaxFail13.hs:26:11:
+ Couldn't match type ‘Int’ with ‘Foo -> Int’
+ arising from a functional dependency between:
+ constraint ‘HasField "foo" Foo (Foo -> Int)’
+ << This should not appear in error messages. If you see this
+ in an error message, please report a bug mentioning ‘record update’ at
+ https://gitlab.haskell.org/ghc/ghc/wikis/report-a-bug >>
+ instance ‘HasField "foo" Foo Int’
+ at RecordDotSyntaxFail13.hs:21:10-31
+ In the second argument of ‘($)’, namely ‘a {foo}’
+ In a stmt of a 'do' block: print $ a {foo}
+ In the expression:
+ do let a = ...
+ print $ a {foo}
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail2.hs b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail2.hs
new file mode 100644
index 0000000000..39a3e0256b
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail2.hs
@@ -0,0 +1,11 @@
+{-# LANGUAGE OverloadedRecordDot #-} -- Enable '.'
+{-# LANGUAGE NoOverloadedRecordUpdate #-} -- Definitely not enable overloaded updates.
+
+data Foo = Foo { foo :: Bar }
+data Bar = Bar { bar :: Baz }
+data Baz = Baz { baz :: Quux }
+data Quux = Quux { quux :: Int }
+
+no :: Foo -> Foo
+no foo = foo { bar.baz = Quux { quux = 42 } } } }
+-- For this to work, OverloadedRecordUpdate must be enabled
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail2.stderr b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail2.stderr
new file mode 100644
index 0000000000..5430e37bc9
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail2.stderr
@@ -0,0 +1,2 @@
+RecordDotSyntaxFail2.hs:10:10:
+ OverloadedRecordUpdate needs to be enabled
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail3.hs b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail3.hs
new file mode 100644
index 0000000000..ae1a1fa797
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail3.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE AllowAmbiguousTypes, FunctionalDependencies, ScopedTypeVariables, PolyKinds, TypeApplications, DataKinds #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+
+class HasField x r a | x r -> a where
+ hasField :: r -> (a -> r, a)
+
+getField :: forall x r a . HasField x r a => r -> a
+getField = snd . hasField @x -- Note: a.x = is getField @"x" a.
+
+setField :: forall x r a . HasField x r a => r -> a -> r
+setField = fst . hasField @x -- Note : a{x = b} is setField @"x" a b.
+
+-- 'Corge' has a '&&&' field of type 'Int'
+data Corge = Corge { (&&&) :: Int } deriving (Show, Eq)
+instance HasField "&&&" Corge Int where
+ hasField r = (\x -> case r of Corge { .. } -> Corge { (&&&) = x, .. }, (&&&) r)
+
+main = do
+ let b = Corge { (&&&) = 12 };
+ print $ (b.(&&&))
+ -- Syntax error: Dot notation is not available for fields with
+ -- operator names
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail3.stderr b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail3.stderr
new file mode 100644
index 0000000000..674b0c1e50
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail3.stderr
@@ -0,0 +1 @@
+RecordDotSyntaxFail3.hs:22:14: parse error on input ‘(’
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail4.hs b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail4.hs
new file mode 100644
index 0000000000..b921cbc4b2
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail4.hs
@@ -0,0 +1,8 @@
+{-# LANGUAGE OverloadedRecordDot #-}
+
+data Foo = Foo { foo :: Int }
+
+main = do
+ let a = Foo { foo = 1 }
+ print $ (const "hello") a .foo
+ -- Syntax error: f r .x is illegal.
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail4.stderr b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail4.stderr
new file mode 100644
index 0000000000..4ffc9df51e
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail4.stderr
@@ -0,0 +1,2 @@
+RecordDotSyntaxFail4.hs:7:29: error:
+ parse error on input ‘.’
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail5.hs b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail5.hs
new file mode 100644
index 0000000000..c261a571b7
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail5.hs
@@ -0,0 +1,17 @@
+{-# LANGUAGE OverloadedRecordDot, OverloadedRecordUpdate #-}
+{-# LANGUAGE NoRebindableSyntax #-}
+
+data Foo = Foo { foo :: Bar } deriving (Show, Eq)
+data Bar = Bar { bar :: Baz } deriving (Show, Eq)
+data Baz = Baz { baz :: Quux } deriving (Show, Eq)
+data Quux = Quux { quux :: Int } deriving (Show, Eq)
+
+main = do
+ let a = Foo { foo = Bar{ bar = Baz { baz = Quux { quux = 42 } } } }
+
+ -- An "update" is an expression like 'r{ a.b = 12 }'.
+ --
+ -- We don't support these (in the case Rebindable Syntax is off) yet
+ -- (waiting on HasField support).
+ putStrLn "-- updates:"
+ print $ (a.foo.bar.baz) { quux = 2 } -- Quux { quux = 2 }
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail5.stderr b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail5.stderr
new file mode 100644
index 0000000000..efe360222c
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail5.stderr
@@ -0,0 +1,2 @@
+RecordDotSyntaxFail5.hs:17:11:
+ RebindableSyntax is required if OverloadedRecordUpdate is enabled.
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail6.hs b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail6.hs
new file mode 100644
index 0000000000..8265f56914
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail6.hs
@@ -0,0 +1,11 @@
+{-# LANGUAGE OverloadedRecordDot, OverloadedRecordUpdate, RebindableSyntax #-}
+
+module Main where
+
+import qualified RecordDotSyntaxA as A
+
+main = do
+ let bar = A.Foo { A.foo =1 } -- A defn. Perfectly reasonable.
+ print $ A.foo bar -- Application of a selector. Also reasonable.
+ let baz = bar{A.foo = 2} -- An update with a qualified field; not supported!
+ print $ A.foo baz
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail6.stderr b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail6.stderr
new file mode 100644
index 0000000000..c53990475b
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail6.stderr
@@ -0,0 +1,5 @@
+[1 of 2] Compiling RecordDotSyntaxA ( RecordDotSyntaxA.hs, RecordDotSyntaxA.o )
+ [2 of 2] Compiling Main ( RecordDotSyntaxFail6.hs, RecordDotSyntaxFail6.o )
+
+ RecordDotSyntaxFail6.hs:10:17:
+ Fields cannot be qualified when OverloadedRecordUpdate is enabled
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail7.hs b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail7.hs
new file mode 100644
index 0000000000..0d3d92b431
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail7.hs
@@ -0,0 +1,9 @@
+{-# LANGUAGE OverloadedRecordDot #-}
+
+module Main where
+
+import qualified RecordDotSyntaxA as A
+
+main = do
+ let bar = A.Foo { A.foo =1 } -- A defn. Perfectly reasonable.
+ print $ (bar.A.foo) -- A field selection where the field is qualified; parse error on input ‘A.foo’.
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail7.stderr b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail7.stderr
new file mode 100644
index 0000000000..feee41589f
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail7.stderr
@@ -0,0 +1,4 @@
+[1 of 2] Compiling RecordDotSyntaxA ( RecordDotSyntaxA.hs, RecordDotSyntaxA.o )
+[2 of 2] Compiling Main ( RecordDotSyntaxFail7.hs, RecordDotSyntaxFail7.o )
+
+RecordDotSyntaxFail7.hs:9:16: parse error on input ‘A.foo’
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail8.hs b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail8.hs
new file mode 100644
index 0000000000..3b6fcc97e2
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail8.hs
@@ -0,0 +1,37 @@
+{-# LANGUAGE OverloadedRecordDot, OverloadedRecordUpdate, RebindableSyntax#-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE AllowAmbiguousTypes, FunctionalDependencies, ScopedTypeVariables, PolyKinds, TypeApplications, DataKinds #-}
+
+import Prelude
+
+class HasField x r a | x r -> a where
+ hasField :: r -> (a -> r, a)
+
+getField :: forall x r a . HasField x r a => r -> a
+getField = snd . hasField @x -- Note: a.x = is getField @"x" a.
+
+setField :: forall x r a . HasField x r a => r -> a -> r
+setField = fst . hasField @x -- Note : a{x = b} is setField @"x" a b.
+
+-- 'Foo' has 'foo' field of type 'Bar'
+data Foo = Foo { foo :: Bar } deriving (Show, Eq)
+instance HasField "foo" Foo Bar where
+ hasField r = (\x -> case r of Foo { .. } -> Foo { foo = x, .. }, foo r)
+
+-- 'Bar' has a 'bar' field of type 'Baz'
+data Bar = Bar { bar :: Baz } deriving (Show, Eq)
+instance HasField "bar" Bar Baz where
+ hasField r = (\x -> case r of Bar { .. } -> Bar { bar = x, .. }, bar r)
+
+-- 'Baz' has a 'baz' field of type 'Quux'
+data Baz = Baz { baz :: Quux } deriving (Show, Eq)
+instance HasField "baz" Baz Quux where
+ hasField r = (\x -> case r of Baz { .. } -> Baz { baz = x, .. }, baz r)
+
+-- 'Quux' has a 'quux' field of type 'Int'
+data Quux = Quux { quux :: Int } deriving (Show, Eq)
+-- Forget to write this type's 'HasField' instance
+
+main = do
+ let a = Foo { foo = Bar{ bar = Baz { baz = Quux { quux = 42 } } } }
+ print $ a.foo.bar.baz.quux
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail8.stderr b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail8.stderr
new file mode 100644
index 0000000000..8bf921b79f
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail8.stderr
@@ -0,0 +1,25 @@
+RecordDotSyntaxFail8.hs:37:3:
+ Ambiguous type variable ‘a0’ arising from a use of ‘print’
+ prevents the constraint ‘(Show a0)’ from being solved.
+ Probable fix: use a type annotation to specify what ‘a0’ should be.
+ These potential instances exist:
+ instance Show Ordering -- Defined in ‘GHC.Show’
+ instance Show Bar -- Defined at RecordDotSyntaxFail8.hs:22:41
+ instance Show Baz -- Defined at RecordDotSyntaxFail8.hs:27:42
+ ...plus 27 others
+ ...plus N instances involving out-of-scope types
+ (use -fprint-potential-instances to see them all)
+ In the first argument of ‘($)’, namely ‘print’
+ In a stmt of a 'do' block: print $ ....baz.quux
+ In the expression:
+ do let a = ...
+ print $ ....quux
+
+RecordDotSyntaxFail8.hs:37:11:
+ No instance for (HasField "quux" Quux a0)
+ arising from selecting the field ‘quux’
+ In the second argument of ‘($)’, namely ‘....baz.quux’
+ In a stmt of a 'do' block: print $ ....baz.quux
+ In the expression:
+ do let a = ...
+ print $ ....quux
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail9.hs b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail9.hs
new file mode 100644
index 0000000000..b262215215
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail9.hs
@@ -0,0 +1,8 @@
+{-# LANGUAGE OverloadedRecordDot #-}
+
+data Foo = Foo { foo :: Int } deriving Show
+
+main = do
+ let a = Foo { foo = 42 }
+ let _ = a.foo :: String -- Type error. Does a.foo get underlined?
+ undefined
diff --git a/testsuite/tests/parser/should_fail/RecordDotSyntaxFail9.stderr b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail9.stderr
new file mode 100644
index 0000000000..ee15f9fc6b
--- /dev/null
+++ b/testsuite/tests/parser/should_fail/RecordDotSyntaxFail9.stderr
@@ -0,0 +1,9 @@
+RecordDotSyntaxFail9.hs:7:11:
+ Couldn't match type ‘Int’ with ‘[Char]’
+ arising from selecting the field ‘foo’
+ In the expression: a.foo :: String
+ In a pattern binding: _ = a.foo :: String
+ In the expression:
+ do let a = ...
+ let _ = ...
+ undefined
diff --git a/testsuite/tests/parser/should_fail/all.T b/testsuite/tests/parser/should_fail/all.T
index 88a37ec2ba..21d66337e9 100644
--- a/testsuite/tests/parser/should_fail/all.T
+++ b/testsuite/tests/parser/should_fail/all.T
@@ -175,3 +175,17 @@ test('T18251e', normal, compile_fail, [''])
test('T18251f', normal, compile_fail, [''])
test('T12446', normal, compile_fail, [''])
test('T17045', normal, compile_fail, [''])
+test('RecordDotSyntaxFail0', normal, compile_fail, [''])
+test('RecordDotSyntaxFail1', normal, compile_fail, [''])
+test('RecordDotSyntaxFail2', normal, compile_fail, [''])
+test('RecordDotSyntaxFail3', normal, compile_fail, [''])
+test('RecordDotSyntaxFail4', normal, compile_fail, [''])
+test('RecordDotSyntaxFail5', normal, compile_fail, [''])
+test('RecordDotSyntaxFail6', [extra_files(['RecordDotSyntaxA.hs'])], multimod_compile_fail, ['RecordDotSyntaxFail6', ''])
+test('RecordDotSyntaxFail7', [extra_files(['RecordDotSyntaxA.hs'])], multimod_compile_fail, ['RecordDotSyntaxFail7', ''])
+test('RecordDotSyntaxFail8', normal, compile_fail, [''])
+test('RecordDotSyntaxFail9', normal, compile_fail, [''])
+test('RecordDotSyntaxFail10', normal, compile_fail, [''])
+test('RecordDotSyntaxFail11', normal, compile_fail, [''])
+test('RecordDotSyntaxFail12', normal, compile_fail, [''])
+test('RecordDotSyntaxFail13', normal, compile_fail, [''])
diff --git a/testsuite/tests/parser/should_run/RecordDotSyntax1.hs b/testsuite/tests/parser/should_run/RecordDotSyntax1.hs
new file mode 100644
index 0000000000..2d14218f83
--- /dev/null
+++ b/testsuite/tests/parser/should_run/RecordDotSyntax1.hs
@@ -0,0 +1,141 @@
+{-# LANGUAGE AllowAmbiguousTypes, FunctionalDependencies, ScopedTypeVariables, PolyKinds, TypeApplications, DataKinds #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE OverloadedRecordDot, OverloadedRecordUpdate #-}
+-- For "higher kinded data" test.
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE FlexibleInstances #-}
+
+{-# LANGUAGE RebindableSyntax #-}
+import Prelude
+
+-- Choice (C2a).
+
+import Data.Function -- for &
+import Data.Functor.Identity
+
+class HasField x r a | x r -> a where
+ hasField :: r -> (a -> r, a)
+
+getField :: forall x r a . HasField x r a => r -> a
+getField = snd . hasField @x -- Note: a.x = is getField @"x" a.
+
+setField :: forall x r a . HasField x r a => r -> a -> r
+setField = fst . hasField @x -- Note : a{x = b} is setField @"x" a b.
+
+-- 'Foo' has 'foo' field of type 'Bar'
+data Foo = Foo { foo :: Bar } deriving (Show, Eq)
+instance HasField "foo" Foo Bar where
+ hasField r = (\x -> case r of Foo { .. } -> Foo { foo = x, .. }, foo r)
+
+-- 'Bar' has a 'bar' field of type 'Baz'
+data Bar = Bar { bar :: Baz } deriving (Show, Eq)
+instance HasField "bar" Bar Baz where
+ hasField r = (\x -> case r of Bar { .. } -> Bar { bar = x, .. }, bar r)
+
+-- 'Baz' has a 'baz' field of type 'Quux'
+data Baz = Baz { baz :: Quux } deriving (Show, Eq)
+instance HasField "baz" Baz Quux where
+ hasField r = (\x -> case r of Baz { .. } -> Baz { baz = x, .. }, baz r)
+
+-- 'Quux' has a 'quux' field of type 'Int'
+data Quux = Quux { quux :: Int } deriving (Show, Eq)
+instance HasField "quux" Quux Int where
+ hasField r = (\x -> case r of Quux { .. } -> Quux { quux = x, .. }, quux r)
+
+-- 'Corge' has a '&&&' field of type 'Int'
+data Corge = Corge { (&&&) :: Int } deriving (Show, Eq)
+instance HasField "&&&" Corge Int where
+ hasField r = (\x -> case r of Corge { .. } -> Corge { (&&&) = x, .. }, (&&&) r)
+-- Note : Dot notation is not available for fields with operator
+-- names.
+
+-- 'Grault' has two fields 'f' and 'g' of type 'Foo'.
+data Grault = Grault {f :: Foo, g :: Foo} deriving (Show, Eq)
+instance HasField "f" Grault Foo where
+ hasField r = (\x -> case r of Grault { .. } -> Grault { f = x, .. }, f r)
+instance HasField "g" Grault Foo where
+ hasField r = (\x -> case r of Grault { .. } -> Grault { g = x, .. }, g r)
+
+-- "Higher kinded data"
+-- (see https://reasonablypolymorphic.com/blog/higher-kinded-data/)
+type family H f a where
+ H Identity a = a
+ H f a = f a
+data P f = P
+ { n :: H f String
+ }
+-- See https://github.com/ndmitchell/record-dot-preprocessor/pull/34.
+instance (a ~ H f String) => HasField "n" (P f) a where
+ hasField r = (\x -> case r of P { .. } -> P { n = x, .. }, n r)
+
+main = do
+ let a = Foo { foo = Bar{ bar = Baz { baz = Quux { quux = 42 } } } }
+ let b = Corge{ (&&&) = 12 };
+ let c = Grault {
+ f = Foo { foo = Bar{ bar = Baz { baz = Quux { quux = 1 } } } }
+ , g = Foo { foo = Bar{ bar = Baz { baz = Quux { quux = 1 } } } }
+ }
+
+ -- A "selector" is an expression like '(.a)' or '(.a.b)'.
+ putStrLn "-- selectors:"
+ print $ (.foo) a -- Bar { bar = Baz { baz = Quux { quux = 42 } } }
+ print $ (.foo.bar) a -- Baz { baz = Quux { quux = 42 } }
+ print $ (.foo.bar.baz) a -- Quux { quux = 42 }
+ print $ (.foo.bar.baz.quux) a -- 42
+ print $ ((&&&) b) -- 12
+ -- print $ (b.(&&&)) -- illegal : parse error on input ‘(’
+ print $ getField @"&&&" b -- 12
+
+ -- A "selection" is an expression like 'r.a' or '(f r).a.b'.
+ putStrLn "-- selections:"
+ print $ a.foo.bar.baz.quux -- 42
+ print $ a.foo.bar.baz -- Quux { quux = 42 }
+ print $ a.foo.bar -- Baz { baz = Quux { quux = 42 } }
+ print $ a.foo -- Bar { bar = Baz { baz = Quux { quux = 42 } } }
+ print $ (const "hello") a.foo -- f r.x means f (r.x)
+ -- print $ f a .foo -- f r .x is illegal
+ print $ (const "hello") (id a).foo -- f (g r).x means f ((g r).x)
+ -- print $ f (g a) .foo -- f (g r) .x is illegal
+ print $ a.foo
+ & (.bar.baz.quux) -- 42
+ print $ (a.foo
+ ).bar.baz.quux -- 42
+ print $ (+) a.foo.bar.baz.quux 1 -- 43
+ print $ (+) (id a).foo.bar.baz.quux 1 -- 43
+ print $ (+) ((id a).foo.bar & (.baz.quux)) 1 -- 43
+
+ -- An "update" is an expression like 'r{ a.b = 12 }'.
+ putStrLn "-- updates:"
+ print $ (a.foo.bar.baz) { quux = 2 } -- Quux { quux = 2 }
+ print $ (\b -> b{ bar=Baz{ baz=Quux{ quux=1 } } }) a.foo -- Bar { bar = Baz { baz = Quux { quux = 1 } } }
+ let bar = Bar { bar = Baz { baz = Quux { quux = 44 } } }
+ print $ a{ foo.bar = Baz { baz = Quux { quux = 44 } } } -- Foo { foo = Bar { bar = Baz { baz = Quux { quux = 44 } } } }
+ print $ a{ foo.bar.baz = Quux { quux = 45 } } -- Foo { foo = Bar { bar = Baz { baz = Quux { quux = 45 } } } }
+ print $ a{ foo.bar.baz.quux = 46 } -- Foo { foo = Bar { bar = Baz { baz = Quux { quux = 46 } } } }
+ print $ c{ f.foo.bar.baz.quux = 3, g.foo.bar.baz.quux = 4 } -- Grault { f = Foo { foo = Bar { bar = Baz { baz = Quux { quux = 3 } } } }, g = Foo { foo = Bar { bar = Baz { baz = Quux { quux = 4 } } } } }
+
+ -- A "punned update" is an expression like 'r{ a.b }' (where it is
+ -- understood that 'b' is a variable binding in the environment of
+ -- the field update - enabled only when the extension
+ -- 'NamedFieldPuns' is in effect).
+ putStrLn "-- punned updates:"
+ let quux = 102; baz = Quux { quux }; bar = Baz { baz }; foo = Bar { bar } -- Foo { foo = Bar { bar = Baz { baz = Quux { quux = 102 } } } }
+ print $ a{ foo.bar.baz.quux } -- Foo { foo = Bar { bar = Baz { baz = Quux { quux = 102 } } } }
+ print $ a{ foo.bar.baz } -- Foo { foo = Bar { bar = Baz { baz = Quux { quux = 102 } } } }
+ print $ a{ foo.bar } -- Foo { foo = Bar { bar = Baz { baz = Quux { quux = 102 } } } }
+ print $ a{ foo } -- Foo { foo = Bar { bar = Baz { baz = Quux { quux = 102 } } } }
+ print $ a -- Foo { foo = Bar { bar = Baz { baz = Quux { quux = 42 } } } }
+ print $ c{ f.foo, g.foo.bar.baz.quux = 4 } -- Mix punned and explicit; 102, 4
+ f <- pure a
+ g <- pure a
+ print $ c{ f } -- 42, 1
+ print $ c{ f, g } -- 42, 42
+ print $ c{ f, g.foo.bar.baz.quux = 4 } -- Mix top-level and nested updates; 42, 4
+
+ putStrLn "-- misc:"
+ -- Higher kinded test.
+ let p = P { n = Just "me" } :: P Maybe
+ Just me <- pure p.n
+ putStrLn $ me
diff --git a/testsuite/tests/parser/should_run/RecordDotSyntax1.stdout b/testsuite/tests/parser/should_run/RecordDotSyntax1.stdout
new file mode 100644
index 0000000000..9582e17da9
--- /dev/null
+++ b/testsuite/tests/parser/should_run/RecordDotSyntax1.stdout
@@ -0,0 +1,38 @@
+-- selectors:
+Bar {bar = Baz {baz = Quux {quux = 42}}}
+Baz {baz = Quux {quux = 42}}
+Quux {quux = 42}
+42
+12
+12
+-- selections:
+42
+Quux {quux = 42}
+Baz {baz = Quux {quux = 42}}
+Bar {bar = Baz {baz = Quux {quux = 42}}}
+"hello"
+"hello"
+42
+42
+43
+43
+43
+-- updates:
+Quux {quux = 2}
+Bar {bar = Baz {baz = Quux {quux = 1}}}
+Foo {foo = Bar {bar = Baz {baz = Quux {quux = 44}}}}
+Foo {foo = Bar {bar = Baz {baz = Quux {quux = 45}}}}
+Foo {foo = Bar {bar = Baz {baz = Quux {quux = 46}}}}
+Grault {f = Foo {foo = Bar {bar = Baz {baz = Quux {quux = 3}}}}, g = Foo {foo = Bar {bar = Baz {baz = Quux {quux = 4}}}}}
+-- punned updates:
+Foo {foo = Bar {bar = Baz {baz = Quux {quux = 102}}}}
+Foo {foo = Bar {bar = Baz {baz = Quux {quux = 102}}}}
+Foo {foo = Bar {bar = Baz {baz = Quux {quux = 102}}}}
+Foo {foo = Bar {bar = Baz {baz = Quux {quux = 102}}}}
+Foo {foo = Bar {bar = Baz {baz = Quux {quux = 42}}}}
+Grault {f = Foo {foo = Bar {bar = Baz {baz = Quux {quux = 102}}}}, g = Foo {foo = Bar {bar = Baz {baz = Quux {quux = 4}}}}}
+Grault {f = Foo {foo = Bar {bar = Baz {baz = Quux {quux = 42}}}}, g = Foo {foo = Bar {bar = Baz {baz = Quux {quux = 1}}}}}
+Grault {f = Foo {foo = Bar {bar = Baz {baz = Quux {quux = 42}}}}, g = Foo {foo = Bar {bar = Baz {baz = Quux {quux = 42}}}}}
+Grault {f = Foo {foo = Bar {bar = Baz {baz = Quux {quux = 42}}}}, g = Foo {foo = Bar {bar = Baz {baz = Quux {quux = 4}}}}}
+-- misc:
+me
diff --git a/testsuite/tests/parser/should_run/RecordDotSyntax2.hs b/testsuite/tests/parser/should_run/RecordDotSyntax2.hs
new file mode 100644
index 0000000000..89c520009f
--- /dev/null
+++ b/testsuite/tests/parser/should_run/RecordDotSyntax2.hs
@@ -0,0 +1,33 @@
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE NoRebindableSyntax #-}
+
+data Foo = Foo { foo :: Bar } deriving (Show, Eq)
+data Bar = Bar { bar :: Baz } deriving (Show, Eq)
+data Baz = Baz { baz :: Quux } deriving (Show, Eq)
+data Quux = Quux { quux :: Int } deriving (Show, Eq)
+
+main = do
+ let a = Foo { foo = Bar{ bar = Baz { baz = Quux { quux = 42 } } } }
+
+ -- A "selector" is an expression like '(.a)' or '(.a.b)'.
+ putStrLn "-- selectors:"
+ print $ (.foo) a -- Bar { bar = Baz { baz = Quux { quux = 42 } } }
+ print $ (.foo.bar) a -- Baz { baz = Quux { quux = 42 } }
+ print $ (.foo.bar.baz) a -- Quux { quux = 42 }
+ print $ (.foo.bar.baz.quux) a -- 42
+
+ -- A "selection" is an expression like 'r.a' or '(f r).a.b'.
+ putStrLn "-- selections:"
+ print $ a.foo.bar.baz.quux -- 42
+ print $ a.foo.bar.baz -- Quux { quux = 42 }
+ print $ a.foo.bar -- Baz { baz = Quux { quux = 42 } }
+ print $ a.foo -- Bar { bar = Baz { baz = Quux { quux = 42 } } }
+
+ -- An "update" is an expression like 'r{ a.b = 12 }'.
+ --
+ -- We don't support these (in the case Rebindable Syntax is off) yet
+ -- (waiting on HasField support).
+ --
+ -- Regular updates are fine though!
+ print $ a{foo=(foo a){bar = (bar (foo a)){baz = (baz (bar (foo a))){quux = quux (baz (bar (foo a))) + 1}}}}
+ print $ a{foo=(a.foo){bar = (a.foo.bar){baz = (a.foo.bar.baz){quux = a.foo.bar.baz.quux + 1}}}}
diff --git a/testsuite/tests/parser/should_run/RecordDotSyntax2.stdout b/testsuite/tests/parser/should_run/RecordDotSyntax2.stdout
new file mode 100644
index 0000000000..6755663e6a
--- /dev/null
+++ b/testsuite/tests/parser/should_run/RecordDotSyntax2.stdout
@@ -0,0 +1,12 @@
+-- selectors:
+Bar {bar = Baz {baz = Quux {quux = 42}}}
+Baz {baz = Quux {quux = 42}}
+Quux {quux = 42}
+42
+-- selections:
+42
+Quux {quux = 42}
+Baz {baz = Quux {quux = 42}}
+Bar {bar = Baz {baz = Quux {quux = 42}}}
+Foo {foo = Bar {bar = Baz {baz = Quux {quux = 43}}}}
+Foo {foo = Bar {bar = Baz {baz = Quux {quux = 43}}}}
diff --git a/testsuite/tests/parser/should_run/RecordDotSyntax3.hs b/testsuite/tests/parser/should_run/RecordDotSyntax3.hs
new file mode 100644
index 0000000000..1ee7565573
--- /dev/null
+++ b/testsuite/tests/parser/should_run/RecordDotSyntax3.hs
@@ -0,0 +1,14 @@
+{-# LANGUAGE OverloadedRecordDot #-}
+
+module Main where
+
+import qualified RecordDotSyntaxA as A
+
+
+main = do
+ print $ id A.n -- Foo {foo = 2}; f M.x means f (M.x)
+ print $ id A.n.foo -- 2; f M.n.x means f (M.n.x)
+
+ let bar = A.Foo {A.foo = 1}
+ print $ bar.foo -- Ok; 1
+ -- print $ bar.A.foo -- parse error on input 'A.foo'
diff --git a/testsuite/tests/parser/should_run/RecordDotSyntax3.stdout b/testsuite/tests/parser/should_run/RecordDotSyntax3.stdout
new file mode 100644
index 0000000000..0de59d2464
--- /dev/null
+++ b/testsuite/tests/parser/should_run/RecordDotSyntax3.stdout
@@ -0,0 +1,3 @@
+Foo {foo = 2}
+2
+1
diff --git a/testsuite/tests/parser/should_run/RecordDotSyntax4.hs b/testsuite/tests/parser/should_run/RecordDotSyntax4.hs
new file mode 100644
index 0000000000..924ed03bde
--- /dev/null
+++ b/testsuite/tests/parser/should_run/RecordDotSyntax4.hs
@@ -0,0 +1,9 @@
+{-# LANGUAGE OverloadedRecordDot #-}
+
+module Main where
+
+import qualified RecordDotSyntaxA as A
+
+main = do
+ let bar = A.Foo {A.foo = 1}
+ print $ bar{A.foo = 2} -- Qualified labels ok in regular updates.
diff --git a/testsuite/tests/parser/should_run/RecordDotSyntax4.stdout b/testsuite/tests/parser/should_run/RecordDotSyntax4.stdout
new file mode 100644
index 0000000000..43c812f394
--- /dev/null
+++ b/testsuite/tests/parser/should_run/RecordDotSyntax4.stdout
@@ -0,0 +1 @@
+Foo {foo = 2}
diff --git a/testsuite/tests/parser/should_run/RecordDotSyntaxA.hs b/testsuite/tests/parser/should_run/RecordDotSyntaxA.hs
new file mode 100644
index 0000000000..907d6a23f6
--- /dev/null
+++ b/testsuite/tests/parser/should_run/RecordDotSyntaxA.hs
@@ -0,0 +1,6 @@
+module RecordDotSyntaxA where
+
+data Foo = Foo { foo :: Int } deriving Show
+
+n :: Foo
+n = Foo {foo = 2}
diff --git a/testsuite/tests/parser/should_run/all.T b/testsuite/tests/parser/should_run/all.T
index 064ef8fffd..caf0e2bc65 100644
--- a/testsuite/tests/parser/should_run/all.T
+++ b/testsuite/tests/parser/should_run/all.T
@@ -23,3 +23,7 @@ test('CountParserDeps',
compile_and_run,
['-package ghc'])
test('LexNegLit', normal, compile_and_run, [''])
+test('RecordDotSyntax1', normal, compile_and_run, [''])
+test('RecordDotSyntax2', normal, compile_and_run, [''])
+test('RecordDotSyntax3', [extra_files(['RecordDotSyntaxA.hs'])], multimod_compile_and_run, ['RecordDotSyntax3', ''])
+test('RecordDotSyntax4', [extra_files(['RecordDotSyntaxA.hs'])], multimod_compile_and_run, ['RecordDotSyntax4', ''])