Opened 9 years ago

Closed 9 years ago

#5037 closed bug (fixed)

TH mkName bug

Reported by: igloo Owned by:
Priority: normal Milestone: 7.4.1
Component: Template Haskell Version: 7.0.2
Keywords: Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Test Case: th/T5037
Blocked By: Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:


This is accepted:

{-# LANGUAGE TemplateHaskell #-}

import Language.Haskell.TH

f :: Maybe Int -> Int
f Nothing = 3
f (Just x) = $(varE (mkName "x"))

but this fails:

{-# LANGUAGE TemplateHaskell #-}

import Language.Haskell.TH

$( do ds <- [d|
                f :: Maybe Int -> Int
                f Nothing = 3
                f (Just x) = $(varE (mkName "x"))
      runIO $ putStrLn $ pprint ds
      return ds )


f :: Data.Maybe.Maybe GHC.Types.Int -> GHC.Types.Int
f (Data.Maybe.Nothing) = 3
f (Data.Maybe.Just x_0) = x

q.hs:5:4: Not in scope: `x'

I expect it to be accepted.

Change History (2)

comment:1 Changed 9 years ago by igloo

Milestone: 7.4.1

comment:2 Changed 9 years ago by simonpj

Resolution: fixed
Status: newclosed
Test Case: th/T5037

Fixed by

commit e3dcc0d5a9f805518f004a9ef42b3405b013a083
Author: Simon Peyton Jones <>
Date:   Thu Jun 16 14:23:08 2011 +0100

    Re-do (again) the handling of binders in Template Haskell
    See the long Note [Binders in Template Haskell] in Convert.lhs
    which explains it all.  This patch fixes Trac #5037.
    The key change is that NameU binders (ones made up by newName in
    Template Haskell, and by TH quotations) now make Exact RdrNames again,
    rather than making RdrNames with heavily encoded OccNames like x[03cv].
    (This encoding is what was making #5037 fail.)

 compiler/hsSyn/Convert.lhs  |  100 +++++++++++++++++++++++++++++++------------
 compiler/iface/IfaceEnv.lhs |    2 +-
 compiler/rename/RnBinds.lhs |   10 +++-
 compiler/rename/RnEnv.lhs   |   35 +++++++++------
 compiler/rename/RnPat.lhs   |   15 ++++---
 5 files changed, 113 insertions(+), 49 deletions(-)

The relevant comment from Convert is

Note [Binders in Template Haskell]
Consider this TH term construction:
  do { x1 <- TH.newName "x"   -- newName :: String -> Q TH.Name
     ; x2 <- TH.newName "x"   -- Builds a NameU
     ; x3 <- TH.newName "x"

     ; let x = mkName "x"     -- mkName :: String -> TH.Name
       	       	      	      -- Builds a NameL

     ; return (LamE (..pattern [x1,x2]..) $
               LamE (VarPat x3) $
               ..tuple (x1,x2,x3,x)) }

It represents the term   \[x1,x2]. \x3. (x1,x2,x3,x)

a) We don't want to complain about "x" being bound twice in 
   the pattern [x1,x2]
b) We don't want x3 to shadow the x1,x2
c) We *do* want 'x' (dynamically bound with mkName) to bind 
   to the innermost binding of "x", namely x3.. (In this
d) When pretty printing, we want to print a unique with x1,x2 
   etc, else they'll all print as "x" which isn't very helpful

When we convert all this to HsSyn, the TH.Names are converted with
thRdrName.  To achieve (b) we want the binders to be Exact RdrNames.
Achieving (a) is a bit awkward, because
   - We must check for duplicate and shadowed names on Names, 
     not RdrNames, *after* renaming.   
     See Note [Collect binders only after renaming] in HsUtils

   - But to achieve (a) we must distinguish between the Exact
     RdrNames arising from TH and the Unqual RdrNames that would
     come from a user writing \[x,x] -> blah

So in Convert (here) we translate
   TH Name		            RdrName
   NameU (arising from newName) --> Exact (Name{ System })
   NameS (arising from mkName)  --> Unqual

Notice that the NameUs generate *System* Names.  Then, when
figuring out shadowing and duplicates, we can filter out
System Names.

This use of System Names fits with other uses of System Names, eg for
temporary variables "a". Since there are lots of things called "a" we
usually want to print the name with the unique, and that is indeed
the way System Names are printed.

There's a small complication of course.  For data types and
classes we'll now have system Names in the binding positions
for constructors, TyCons etc.  For example
    [d| data T = MkT Int |]
when we splice in and Convert to HsSyn RdrName, we'll get
    data (Exact (system Name "T")) = (Exact (system Name "MkT")) ...
So RnEnv.newGlobalBinder we spot Exact RdrNames that wrap a
non-External Name, and make an External name for.  (Remember, 
constructors and the like need External Names.)  Oddly, the 
*occurrences* will continue to be that (non-External) System Name, 
but that will come out in the wash.

Test added.

Note: See TracTickets for help on using tickets.