Skip to content

Commit 895388c

Browse files
committed
Support qualified module renamings
Qualified module renamings let you bring all modules from a library into scope, but qualified under some module prefix. If you write 'pkg qualified Prefix', then if pkg exposes A and B, you will be able to access them as Prefix.A and Prefix.B. This functionality doesn't require any GHC changes; Cabal takes care of desugaring the qualified syntax into an explicit list of renamings. Partially address #7290 Signed-off-by: Edward Z. Yang <[email protected]>
1 parent 89df281 commit 895388c

File tree

10 files changed

+80
-1
lines changed

10 files changed

+80
-1
lines changed

Cabal/src/Distribution/Backpack/UnifyM.hs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,13 @@ convertInclude ci@(ComponentInclude {
487487
req_rename_list <-
488488
case req_rns of
489489
DefaultRenaming -> return []
490+
QualifiedRenaming _ -> do
491+
-- TODO: support this for requires (req_rename_list
492+
-- is a bad representation for this, alas, need
493+
-- a bigger refactor here)
494+
addErr $ text "Unsupported syntax" <+>
495+
quotes (text "requires qualified (...)")
496+
return []
490497
HidingRenaming _ -> do
491498
-- Not valid here for requires!
492499
addErr $ text "Unsupported syntax" <+>
@@ -561,6 +568,12 @@ convertInclude ci@(ComponentInclude {
561568
(pre_prov_scope, prov_rns') <-
562569
case prov_rns of
563570
DefaultRenaming -> return (Map.toList provs, prov_rns)
571+
QualifiedRenaming prefix ->
572+
let prov_scope0 = [ (prefix `joinModuleName` k,v) | (k,v) <- Map.toList provs ]
573+
prov_rns0 = [ (k, prefix `joinModuleName` k) | (k,_) <- Map.toList provs ]
574+
-- GHC doesn't understand qualification, so return
575+
-- the expanded version
576+
in return (prov_scope0, ModuleRenaming prov_rns0)
564577
HidingRenaming hides ->
565578
let hides_set = Set.fromList hides
566579
in let r = [ (k,v)

Cabal/src/Distribution/ModuleName.hs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module Distribution.ModuleName (
1818
fromString,
1919
fromComponents,
2020
components,
21+
joinModuleName,
2122
toFilePath,
2223
main,
2324
-- * Internal
@@ -44,6 +45,12 @@ newtype ModuleName = ModuleName ShortText
4445
unModuleName :: ModuleName -> String
4546
unModuleName (ModuleName s) = fromShortText s
4647

48+
-- Guaranteed to be valid by invariants on the initial module
49+
-- names (in particular, empty module name is not valid)
50+
joinModuleName :: ModuleName -> ModuleName -> ModuleName
51+
joinModuleName a b =
52+
ModuleName (toShortText (unModuleName a ++ "." ++ unModuleName b))
53+
4754
instance Binary ModuleName
4855
instance Structured ModuleName
4956

Cabal/src/Distribution/Types/ModuleRenaming.hs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ data ModuleRenaming
4141
-- | Hiding renaming, e.g., @hiding (A, B)@, bringing all
4242
-- exported modules into scope except the hidden ones.
4343
| HidingRenaming [ModuleName]
44+
-- | Qualified renaming, @(qualified P)@, brining all
45+
-- exported modules into scope with P prefix. So if M
46+
-- was provided by the package, it is now in scope as
47+
-- P.M
48+
| QualifiedRenaming ModuleName
4449
deriving (Show, Read, Eq, Ord, Typeable, Data, Generic)
4550

4651
-- | Interpret a 'ModuleRenaming' as a partial map from 'ModuleName'
@@ -54,6 +59,8 @@ interpModuleRenaming (ModuleRenaming rns) =
5459
interpModuleRenaming (HidingRenaming hs) =
5560
let s = Set.fromList hs
5661
in \k -> if k `Set.member` s then Nothing else Just k
62+
interpModuleRenaming (QualifiedRenaming prefix) =
63+
\k -> Just (prefix `joinModuleName` k)
5764

5865
-- | The default renaming, if something is specified in @build-depends@
5966
-- only.
@@ -79,6 +86,8 @@ instance Pretty ModuleRenaming where
7986
pretty DefaultRenaming = mempty
8087
pretty (HidingRenaming hides)
8188
= text "hiding" <+> parens (hsep (punctuate comma (map pretty hides)))
89+
pretty (QualifiedRenaming prefix)
90+
= text "qualified" <+> pretty prefix
8291
pretty (ModuleRenaming rns)
8392
= parens . hsep $ punctuate comma (map dispEntry rns)
8493
where dispEntry (orig, new)
@@ -109,7 +118,7 @@ moduleRenamingParsec
109118
-> m ModuleRenaming
110119
moduleRenamingParsec bp mn =
111120
-- NB: try not necessary as the first token is obvious
112-
P.choice [ parseRename, parseHiding, return DefaultRenaming ]
121+
P.choice [ parseRename, parseHiding, parseQualified, return DefaultRenaming ]
113122
where
114123
cma = P.char ',' >> P.spaces
115124
parseRename = do
@@ -121,6 +130,11 @@ moduleRenamingParsec bp mn =
121130
P.spaces -- space isn't strictly required as next is an open paren
122131
hides <- bp (P.sepBy mn cma)
123132
return (HidingRenaming hides)
133+
parseQualified = do
134+
_ <- P.string "qualified"
135+
P.spaces
136+
prefix <- mn
137+
return (QualifiedRenaming prefix)
124138
parseList =
125139
P.sepBy parseEntry cma
126140
parseEntry = do
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module A where
2+
import Prefix.Quxbaz
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: QualifiedIncludes
2+
version: 0.1.0.0
3+
license: BSD3
4+
author: Edward Z. Yang
5+
maintainer: [email protected]
6+
build-type: Simple
7+
cabal-version: 2.0
8+
9+
library impl
10+
build-depends: base
11+
exposed-modules: Foobar, Quxbaz
12+
hs-source-dirs: impl
13+
default-language: Haskell2010
14+
15+
library good
16+
build-depends: base, impl
17+
mixins: impl qualified Prefix
18+
exposed-modules: A
19+
default-language: Haskell2010
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module Foobar where
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module Quxbaz where
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Setup configure
2+
Configuring Includes5-0.1.0.0...
3+
# Setup build
4+
Preprocessing library 'impl' for Includes5-0.1.0.0..
5+
Building library 'impl' for Includes5-0.1.0.0..
6+
Preprocessing library 'good' for Includes5-0.1.0.0..
7+
Building library 'good' for Includes5-0.1.0.0..
8+
Preprocessing library 'bad' for Includes5-0.1.0.0..
9+
Building library 'bad' for Includes5-0.1.0.0..
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Setup configure
2+
Configuring QualifiedIncludes-0.1.0.0...
3+
# Setup build
4+
Preprocessing library 'impl' for QualifiedIncludes-0.1.0.0..
5+
Building library 'impl' for QualifiedIncludes-0.1.0.0..
6+
Preprocessing library 'good' for QualifiedIncludes-0.1.0.0..
7+
Building library 'good' for QualifiedIncludes-0.1.0.0..
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import Test.Cabal.Prelude
2+
main = setupAndCabalTest $ do
3+
skipUnlessGhcVersion ">= 8.1"
4+
setup "configure" []
5+
setup "build" []
6+
return ()

0 commit comments

Comments
 (0)