Skip to content

DuplicateRecordFields trigger false import warnings

Summary

This is a bit of a tricky bug, but there's a bad interaction between DuplicateRecordFields and redundant import warnings.

Suppose you have a module:

module Lib where

data X = X { name :: String }

data Y = Y { age :: Int }

You might want to consume that module like this:

module Main where

import qualified Lib
import qualified Lib as X (X(..))
import qualified Lib as Y (Y(..))

main :: IO ()
main = do
    let x = Lib.X { X.name = "hello" }
    print x
    print $ Lib.Y { Y.age = 3 }

This compiles just fine. But, suppose you add the {-# LANGUAGE DuplicateRecordFields #-} pragma to the top of the file. It will now give you a warning:

/home/matt/Projects/dupfield/app/Main.hs:6:1: warning: [-Wunused-imports]
    The qualified import of ‘Lib’ is redundant
      except perhaps to import instances from ‘Lib’
    To import instances alone, use: import Lib()
  |
6 | import qualified Lib as X (X(..))
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

/home/matt/Projects/dupfield/app/Main.hs:7:1: warning: [-Wunused-imports]
    The qualified import of ‘Lib’ is redundant
      except perhaps to import instances from ‘Lib’
    To import instances alone, use: import Lib()
  |
7 | import qualified Lib as Y (Y(..))
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If I delete the import, then I'll get compiler errors:

/home/matt/Projects/dupfield/app/Main.hs:11:21: error:
    Not in scope: ‘X.name’
    No module named ‘X’ is imported.
   |
11 |     let x = Lib.X { X.name = "hello" }
   |                     ^^^^^^

/home/matt/Projects/dupfield/app/Main.hs:13:21: error:
    Not in scope: ‘Y.age’
    No module named ‘Y’ is imported.
   |
13 |     print $ Lib.Y { Y.age = 3 }
   |                     ^^^^^

It appears that using the field label as a constructor does not trigger as a "use" if DuplicateRecordFields is present in the module. Using it as an accessor counts, as well as using it as an update. It seems to only be creation that does not count.

Now, in the real Lib in our codebase, we have many uses of DuplicateRecordFields, but that's actually not necessary to trigger the bug, and so it's trimmed out. We use DuplicateRecordFields enough that it's a default-extension in our cabal file.

Steps to reproduce

I made a reproduction here. To reproduce, stack build --fast --file-watch. I made some comments in the app/Main.hs module, feel free to play with commenting out various parts of the file to see behavior change.

Expected behavior

I expect that using a field label in record construction syntax would not warn that the identifier isn't used.

Environment

  • GHC version used: 8.8.2

Optional:

  • Operating System: Ubuntu 18.04
  • Stackage LTS-15.0
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information