Expand declaration QQs first (#10047)
authorMichael Smith <michael@diglumi.com>
Wed, 2 Sep 2015 11:57:44 +0000 (13:57 +0200)
committerBen Gamari <ben@smart-cactus.org>
Wed, 2 Sep 2015 11:58:59 +0000 (13:58 +0200)
Declaration QuasiQuoters do not cause a group split like $(...)
splices, and are run and expanded before other declarations in
the group.

Resolves the lingering issue with #10047, and fixes broken tests
qq007 and qq008.

Test Plan: validate

Reviewers: goldfire, austin, bgamari

Reviewed By: bgamari

Subscribers: goldfire, simonpj, thomie, spinda

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

GHC Trac Issues: #10047

compiler/rename/RnSource.hs
compiler/rename/RnSplice.hs-boot
docs/users_guide/7.12.1-notes.xml
docs/users_guide/glasgow_exts.xml
testsuite/tests/quasiquotation/qq007/test.T
testsuite/tests/quasiquotation/qq008/test.T
testsuite/tests/quasiquotation/qq009/Makefile [new file with mode: 0644]
testsuite/tests/quasiquotation/qq009/QQ.hs [new file with mode: 0644]
testsuite/tests/quasiquotation/qq009/Test.hs [new file with mode: 0644]
testsuite/tests/quasiquotation/qq009/test.T [new file with mode: 0644]

index f6a3007..1a6fa17 100644 (file)
@@ -13,7 +13,7 @@ module RnSource (
 #include "HsVersions.h"
 
 import {-# SOURCE #-} RnExpr( rnLExpr )
-import {-# SOURCE #-} RnSplice ( rnSpliceDecl )
+import {-# SOURCE #-} RnSplice ( rnSpliceDecl, rnTopSpliceDecls )
 
 import HsSyn
 import RdrName
@@ -1510,6 +1510,13 @@ addl gp (L l d : ds) = add gp l d ds
 add :: HsGroup RdrName -> SrcSpan -> HsDecl RdrName -> [LHsDecl RdrName]
     -> RnM (HsGroup RdrName, Maybe (SpliceDecl RdrName, [LHsDecl RdrName]))
 
+-- #10047: Declaration QuasiQuoters are expanded immediately, without
+--         causing a group split
+add gp _ (SpliceD (SpliceDecl (L _ qq@HsQuasiQuote{}) _)) ds
+  = do { (ds', _) <- rnTopSpliceDecls qq
+       ; addl gp (ds' ++ ds)
+       }
+
 add gp loc (SpliceD splice@(SpliceDecl _ flag)) ds
   = do { -- We've found a top-level splice.  If it is an *implicit* one
          -- (i.e. a naked top level expression)
index ece78f8..b079b30 100644 (file)
@@ -13,3 +13,5 @@ rnSpliceType :: HsSplice RdrName   -> PostTc Name Kind
 rnSplicePat  :: HsSplice RdrName   -> RnM ( Either (Pat RdrName) (Pat Name)
                                           , FreeVars )
 rnSpliceDecl :: SpliceDecl RdrName -> RnM (SpliceDecl Name, FreeVars)
+
+rnTopSpliceDecls :: HsSplice RdrName -> RnM ([LHsDecl RdrName], FreeVars)
index 3916e71..fa3ea01 100644 (file)
                      pragmas.
                 </para>
             </listitem>
+             <listitem>
+                 <para>
+                     Internally, the implementation of quasi-quotes has been
+                     unified with that of normal Template Haskell splices. Under
+                     the previous implementation, top-level declaration
+                     quasi-quotes did not cause a break in the declaration
+                     groups, unlike splices of the form
+                     <literal>$(...)</literal>. This behavior has been
+                     preserved under the new implementation, and is now
+                     recognized and documented in <xref linked="th-syntax"/>.
+                </para>
+            </listitem>
        </itemizedlist>
     </sect3>
 
            </listitem>
             <listitem>
                 <para>
-                    The functions <literal>error</literal>,
-                    <literal>undefined</literal>, and
-                    <literal>assertError</literal> now take an implicit
-                    <literal>CallStack</literal> parameter, and will
-                    output a formatted call stack alongside the error
-                    message.
+                    A new module <literal>GHC.SrcLoc</literal> was added,
+                    exporting a new type <literal>SrcLoc</literal>. A
+                    <literal>SrcLoc</literal> contains package, module,
+                    and file names, as well as start and end positions.
                </para>
+           </listitem>
+            <listitem>
                 <para>
-                    See <xref linkend="implicit-parameters-special"/> for a description of the
-                    implicit call stack feature.
+                    A new type <literal>CallStack</literal> was added for use
+                    with the new implicit callstack parameters. A
+                    <literal>CallStack</literal> is a
+                    <literal>[(String, SrcLoc)]</literal>, sorted by most-recent
+                    call.
                </para>
            </listitem>
             <listitem>
                     Version number XXXXX (was 0.3.1.0)
                </para>
            </listitem>
-            <listitem>
-                <para>
-                    A new type <literal>SrcLoc</literal> was added. A
-                    <literal>SrcLoc</literal> contains package, module,
-                    and file names, as well as start and end positions.
-               </para>
-           </listitem>
-            <listitem>
-                <para>
-                    A new type <literal>CallStack</literal> was added for use
-                    with the new implicit callstack parameters. A
-                    <literal>CallStack</literal> is a
-                    <literal>[(String, SrcLoc)]</literal>, sorted by most-recent
-                    call.
-               </para>
-           </listitem>
        </itemizedlist>
     </sect3>
 
index f8fa9c3..1a4fbdb 100644 (file)
@@ -9721,8 +9721,8 @@ Wiki page</ulink>.
              </listitem>
 
              <listitem><para>
-                 A quasi-quotation can appear in either a pattern context or an
-                 expression context and is also written in Oxford brackets:
+                 A quasi-quotation can appear in a pattern, type, expression, or
+                 declaration context and is also written in Oxford brackets:
                  <itemizedlist>
                    <listitem><para> <literal>[<replaceable>varid</replaceable>| ... |]</literal>,
                         where the "..." is an arbitrary string; a full description of the
@@ -9868,6 +9868,17 @@ f n = \ [haskell|y|] -> y+n
                declaration group, but no more.
              </para>
 
+        <para>
+    Unlike normal declaration splices, declaration quasiquoters
+    do not cause a break. These quasiquoters are expanded before
+    the rest of the declaration group is processed, and the
+    declarations they generate are merged into the surrounding
+    declaration group. Consequently, the type environment seen
+    by <literal>reify</literal> from a declaration quasiquoter
+    will not include anything from the quasiquoter's declaration
+    group.
+        </para>
+
              <para>
                Concretely, consider the following code
 <programlisting>
@@ -9876,6 +9887,8 @@ module M where
    f x = x
    $(th1 4)
    h y = k y y $(blah1)
+   [qq|blah|]
+   k x y = x + y
    $(th2 10)
    w z = $(blah2)
 </programlisting>
@@ -9918,6 +9931,23 @@ module M where
                    as the splice <literal>$(th2...)</literal>.
                  </para>
                </listitem>
+               <listitem>
+      <para>
+        The body of <literal>h</literal> <emphasis>is</emphasis>
+        able to refer to the function <literal>k</literal>
+        appearing on the other side of the declaration
+        quasiquoter, as quasiquoters never cause a declaration
+        group to be broken up.
+      </para>
+      <para>
+        A <literal>reify</literal> inside the
+        <literal>qq</literal> quasiquoter would be able to see
+        the definition of <literal>f</literal> from the
+        preceding declaration group, but not the definitions of
+        <literal>h</literal> or <literal>k</literal>, or any
+        definitions from subsequent declaration groups.
+      </para>
+               </listitem>
               </orderedlist>
              </para>
            </listitem>
@@ -10220,6 +10250,11 @@ A quasi-quote is expanded by applying the appropriate parser to the string
 enclosed by the Oxford brackets.  The context of the quasi-quote (expression, pattern,
 type, declaration) determines which of the parsers is called.
 </para></listitem>
+<listitem><para>
+Unlike normal declaration splices of the form <literal>$(...)</literal>,
+declaration quasi-quotes do not cause a declaration group break. See
+<xref linkend="th-syntax"/> for more information.
+</para></listitem>
 </itemizedlist>
 </para>
 <para>
index d9a2df3..0b4448c 100644 (file)
@@ -2,7 +2,6 @@ test('qq007',
      [when(fast(), skip),
       extra_clean(['QQ.hi', 'QQ.o', 'Test.hi', 'Test.o']),
       pre_cmd('$MAKE -s --no-print-directory TH_QQ'),
-      expect_broken(10047),
       ],
      multimod_compile,
      ['Test', '-v0'])
index 5bdd2a9..8cac1a9 100644 (file)
@@ -2,7 +2,6 @@ test('qq008',
      [when(fast(), skip),
       extra_clean(['QQ.hi', 'QQ.o', 'Test.hi', 'Test.o']),
       pre_cmd('$MAKE -s --no-print-directory TH_QQ'),
-      expect_broken(10047),
       ],
      multimod_compile,
      ['Test', '-v0'])
diff --git a/testsuite/tests/quasiquotation/qq009/Makefile b/testsuite/tests/quasiquotation/qq009/Makefile
new file mode 100644 (file)
index 0000000..0fa91db
--- /dev/null
@@ -0,0 +1,11 @@
+TOP=../../..
+include $(TOP)/mk/boilerplate.mk
+include $(TOP)/mk/test.mk
+
+.PHONY: TH_QQ
+TH_QQ:
+ifeq "$(GhcDynamic)" "YES"
+       '$(TEST_HC)' $(TEST_HC_OPTS) -c QQ.hs -dynamic -osuf dyn_o -hisuf dyn_hi
+else
+       '$(TEST_HC)' $(TEST_HC_OPTS) -c QQ.hs
+endif
diff --git a/testsuite/tests/quasiquotation/qq009/QQ.hs b/testsuite/tests/quasiquotation/qq009/QQ.hs
new file mode 100644 (file)
index 0000000..89350ba
--- /dev/null
@@ -0,0 +1,14 @@
+{-# LANGUAGE TemplateHaskell #-}
+module QQ where
+
+import Language.Haskell.TH.Quote
+import Language.Haskell.TH.Syntax
+import Language.Haskell.TH
+
+pq = QuasiQuoter { quoteDec = \_ -> return [sig],
+                   quoteType = \_ -> undefined,
+                   quoteExp = \_ -> undefined,
+                   quotePat = \_ -> undefined }
+
+sig = SigD (mkName "f") (ArrowT `AppT` int `AppT` int)
+int = ConT (mkName "Int")
diff --git a/testsuite/tests/quasiquotation/qq009/Test.hs b/testsuite/tests/quasiquotation/qq009/Test.hs
new file mode 100644 (file)
index 0000000..1b43fb2
--- /dev/null
@@ -0,0 +1,9 @@
+{-# LANGUAGE QuasiQuotes #-}
+module Test where
+
+import QQ
+
+f' = f . (+ 1)
+
+[pq| foo |]         -- Expands to f :: Int -> Int
+f x = x + 1
diff --git a/testsuite/tests/quasiquotation/qq009/test.T b/testsuite/tests/quasiquotation/qq009/test.T
new file mode 100644 (file)
index 0000000..10b939a
--- /dev/null
@@ -0,0 +1,7 @@
+test('qq009',
+     [when(fast(), skip),
+      extra_clean(['QQ.hi', 'QQ.o', 'Test.hi', 'Test.o']),
+      pre_cmd('$MAKE -s --no-print-directory TH_QQ'),
+      ],
+     multimod_compile,
+     ['Test', '-v0'])