From 4d3acbcf4c78636afd79883ad021ed6f7ea92a75 Mon Sep 17 00:00:00 2001
From: Zejun Wu <watashi@fb.com>
Date: Tue, 2 Apr 2024 19:34:35 -0700
Subject: [PATCH] Make renamer to be more flexible with parens in the LHS of
 the rules

We used to reject LHS like `(f a) b` in RULES and requires it to be written as
`f a b`. It will be handy to allow both as the expression may be more
readable with extra parens in some cases when infix operator is involved.
Espceially when TemplateHaskell is used, extra parens may be added out of
user's control and result in "valid" rules being rejected and there
are not always ways to workaround it.

Fixes #24621
---
 compiler/GHC/Rename/Module.hs                        | 11 +++++++++++
 .../tests/rename/should_compile/T24621_normal.hs     | 12 ++++++++++++
 testsuite/tests/rename/should_compile/T24621_th.hs   | 12 ++++++++++++
 testsuite/tests/rename/should_compile/all.T          |  2 ++
 4 files changed, 37 insertions(+)
 create mode 100644 testsuite/tests/rename/should_compile/T24621_normal.hs
 create mode 100644 testsuite/tests/rename/should_compile/T24621_th.hs

diff --git a/compiler/GHC/Rename/Module.hs b/compiler/GHC/Rename/Module.hs
index b55cef4e5162..d83e03e89a60 100644
--- a/compiler/GHC/Rename/Module.hs
+++ b/compiler/GHC/Rename/Module.hs
@@ -1226,6 +1226,15 @@ with LHSs with a complicated desugaring (and hence unlikely to match);
 But there are legitimate non-trivial args ei, like sections and
 lambdas.  So it seems simpler not to check at all, and that is why
 check_e is commented out.
+
+Note [Parens on the LHS of a RULE]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+You may think that no one would write
+
+   {-# RULES "foo" (f True) = blah #-}
+
+with the LHS wrapped in parens. But Template Haskell does (#24621)!
+So we should accommodate them.
 -}
 
 checkValidRule :: FastString -> [Name] -> LHsExpr GhcRn -> NameSet -> RnM ()
@@ -1253,6 +1262,8 @@ validRuleLhs foralls lhs
     check (HsAppType _ e _)               = checkl e
     check (HsVar _ lv)
       | (unLoc lv) `notElem` foralls      = Nothing
+    -- See Note [Parens on the LHS of a RULE]
+    check (HsPar _ e)                     = checkl e
     check other                           = Just other  -- Failure
 
         -- Check an argument
diff --git a/testsuite/tests/rename/should_compile/T24621_normal.hs b/testsuite/tests/rename/should_compile/T24621_normal.hs
new file mode 100644
index 000000000000..bc96c8ccf183
--- /dev/null
+++ b/testsuite/tests/rename/should_compile/T24621_normal.hs
@@ -0,0 +1,12 @@
+{-# OPTIONS_GHC -Wno-inline-rule-shadowing #-}
+module T24621_normal where
+
+import Data.Function
+
+foo :: a -> a
+foo x = x
+
+{-# RULES "" forall a b c. a * c + b * c = (a + b) * c :: Int #-}
+{-# RULES "." forall f g. (f . g) foo = f (g foo) #-}
+{-# RULES "foo" forall a b. (foo a) b = a b #-}
+{-# RULES "on" forall a b. (flip compare `on` foo) a b = compare b a #-}
diff --git a/testsuite/tests/rename/should_compile/T24621_th.hs b/testsuite/tests/rename/should_compile/T24621_th.hs
new file mode 100644
index 000000000000..80d212754114
--- /dev/null
+++ b/testsuite/tests/rename/should_compile/T24621_th.hs
@@ -0,0 +1,12 @@
+{-# LANGUAGE TemplateHaskell #-}
+{-# OPTIONS_GHC -Wno-inline-rule-shadowing #-}
+module T24621_th where
+
+import Data.Function
+
+foo :: a -> a
+foo x = x
+
+$( [d| {-# RULES "" forall a b c. a * c + b * c = (a + b) * c :: Int #-} |] )
+$( [d| {-# RULES "." forall a b. (.) a b foo = a (b foo) #-} |] )
+$( [d| {-# RULES "foo" forall a b. foo a b = a b #-} |] )
diff --git a/testsuite/tests/rename/should_compile/all.T b/testsuite/tests/rename/should_compile/all.T
index 745335949bb2..86fedd8aedf4 100644
--- a/testsuite/tests/rename/should_compile/all.T
+++ b/testsuite/tests/rename/should_compile/all.T
@@ -223,4 +223,6 @@ test('T22478a', req_th, compile, [''])
 test('RecordWildCardDeprecation', normal, multimod_compile, ['RecordWildCardDeprecation', '-Wno-duplicate-exports'])
 test('T14032b', normal, compile_and_run, [''])
 test('T14032d', normal, compile, [''])
+test('T24621_normal', normal, compile, [''])
+test('T24621_th', req_th, compile, [''])
 test('T24732', normal, compile_and_run, ['-package "base(Prelude, Text.Printf as P\')"'])
-- 
GitLab