summaryrefslogtreecommitdiff
path: root/compiler/iface/TcIface.hs
diff options
context:
space:
mode:
authorEdward Z. Yang <ezyang@cs.stanford.edu>2017-02-28 23:55:00 -0800
committerEdward Z. Yang <ezyang@cs.stanford.edu>2017-03-02 15:59:02 -0800
commitdf919fb21c951c1892bd96d9e6306ce1bec3daa9 (patch)
tree39dd2ffdc9d61f1de04a114481832876f68d4adf /compiler/iface/TcIface.hs
parentfb5cd9d6d6185afe6d4ef2f3df3f895b6d0abf4c (diff)
downloadhaskell-df919fb21c951c1892bd96d9e6306ce1bec3daa9.tar.gz
Fix roles merging to apply only to non-rep-injective types.
Test Plan: validate Reviewers: simonpj Subscribers:
Diffstat (limited to 'compiler/iface/TcIface.hs')
-rw-r--r--compiler/iface/TcIface.hs49
1 files changed, 47 insertions, 2 deletions
diff --git a/compiler/iface/TcIface.hs b/compiler/iface/TcIface.hs
index 0363c9e581..2a56392910 100644
--- a/compiler/iface/TcIface.hs
+++ b/compiler/iface/TcIface.hs
@@ -235,20 +235,65 @@ mergeIfaceDecl d1 d2
ifSigs = ops,
ifMinDef = BF.mkOr [noLoc bf1, noLoc bf2]
}
- }
+ } `withRolesFrom` d2
-- It doesn't matter; we'll check for consistency later when
-- we merge, see 'mergeSignatures'
- | otherwise = d1
+ | otherwise = d1 `withRolesFrom` d2
+
+-- Note [Role merging]
+-- ~~~~~~~~~~~~~~~~~~~
+-- First, why might it be necessary to do a non-trivial role
+-- merge? It may rescue a merge that might otherwise fail:
+--
+-- signature A where
+-- type role T nominal representational
+-- data T a b
+--
+-- signature A where
+-- type role T representational nominal
+-- data T a b
+--
+-- A module that defines T as representational in both arguments
+-- would successfully fill both signatures, so it would be better
+-- if if we merged the roles of these types in some nontrivial
+-- way.
+--
+-- However, we have to be very careful about how we go about
+-- doing this, because role subtyping is *conditional* on
+-- the supertype being NOT representationally injective, e.g.,
+-- if we have instead:
+--
+-- signature A where
+-- type role T nominal representational
+-- data T a b = T a b
+--
+-- signature A where
+-- type role T representational nominal
+-- data T a b = T a b
+--
+-- Should we merge the definitions of T so that the roles are R/R (or N/N)?
+-- Absolutely not: neither resulting type is a subtype of the original
+-- types (see Note [Role subtyping]), because data is not representationally
+-- injective.
+--
+-- Thus, merging only occurs when BOTH TyCons in question are
+-- representationally injective. If they're not, no merge.
withRolesFrom :: IfaceDecl -> IfaceDecl -> IfaceDecl
d1 `withRolesFrom` d2
| Just roles1 <- ifMaybeRoles d1
, Just roles2 <- ifMaybeRoles d2
+ , not (isRepInjectiveIfaceDecl d1 || isRepInjectiveIfaceDecl d2)
= d1 { ifRoles = mergeRoles roles1 roles2 }
| otherwise = d1
where
mergeRoles roles1 roles2 = zipWith max roles1 roles2
+isRepInjectiveIfaceDecl :: IfaceDecl -> Bool
+isRepInjectiveIfaceDecl IfaceData{ ifCons = IfDataTyCon _ } = True
+isRepInjectiveIfaceDecl IfaceFamily{ ifFamFlav = IfaceDataFamilyTyCon } = True
+isRepInjectiveIfaceDecl _ = False
+
mergeIfaceClassOp :: IfaceClassOp -> IfaceClassOp -> IfaceClassOp
mergeIfaceClassOp op1@(IfaceClassOp _ _ (Just _)) _ = op1
mergeIfaceClassOp _ op2 = op2