diff options
author | Edward Z. Yang <ezyang@cs.stanford.edu> | 2017-02-10 00:38:34 -0800 |
---|---|---|
committer | Edward Z. Yang <ezyang@cs.stanford.edu> | 2017-02-26 16:03:13 -0800 |
commit | 923d7ca2d90c1cb9816d14768abdd2e46adcd5dd (patch) | |
tree | 943e3c0dfff8e9674bbd6252b98101e713d47e3e /compiler/iface/TcIface.hs | |
parent | 9603de6ac7a75ea7c620ce05e3c5f500bcaf5dd6 (diff) | |
download | haskell-923d7ca2d90c1cb9816d14768abdd2e46adcd5dd.tar.gz |
Subtyping for roles in signatures.
Summary:
This commit implements the plan in #13140:
* Today, roles in signature files default to representational. Let's change the
default to nominal, as this is the most flexible implementation side. If a
client of the signature needs to coerce with a type, the signature can be
adjusted to have more stringent requirements.
* If a parameter is declared as nominal in a signature, it can be implemented
by a data type which is actually representational.
* When merging abstract data declarations, we take the smallest role for every
parameter. The roles are considered fix once we specify the structure of an
ADT.
* Critically, abstract types are NOT injective, so we aren't allowed to
make inferences like "if T a ~R T b, then a ~N b" based on the nominal
role of a parameter in an abstract type (this would be unsound if the
parameter ended up being phantom.) This restriction is similar to the
restriction we have on newtypes.
Signed-off-by: Edward Z. Yang <ezyang@cs.stanford.edu>
Test Plan: validate
Reviewers: simonpj, bgamari, austin, goldfire
Subscribers: goldfire, thomie
Differential Revision: https://phabricator.haskell.org/D3123
Diffstat (limited to 'compiler/iface/TcIface.hs')
-rw-r--r-- | compiler/iface/TcIface.hs | 26 |
1 files changed, 21 insertions, 5 deletions
diff --git a/compiler/iface/TcIface.hs b/compiler/iface/TcIface.hs index ffe29c6ce7..d8461f364f 100644 --- a/compiler/iface/TcIface.hs +++ b/compiler/iface/TcIface.hs @@ -212,25 +212,41 @@ isAbstractIfaceDecl IfaceClass{ ifCtxt = [], ifSigs = [], ifATs = [] } = True isAbstractIfaceDecl IfaceFamily{ ifFamFlav = IfaceAbstractClosedSynFamilyTyCon } = True isAbstractIfaceDecl _ = False +ifMaybeRoles :: IfaceDecl -> Maybe [Role] +ifMaybeRoles IfaceData { ifRoles = rs } = Just rs +ifMaybeRoles IfaceSynonym { ifRoles = rs } = Just rs +ifMaybeRoles IfaceClass { ifRoles = rs } = Just rs +ifMaybeRoles _ = Nothing + -- | Merge two 'IfaceDecl's together, preferring a non-abstract one. If -- both are non-abstract we pick one arbitrarily (and check for consistency -- later.) mergeIfaceDecl :: IfaceDecl -> IfaceDecl -> IfaceDecl mergeIfaceDecl d1 d2 - | isAbstractIfaceDecl d1 = d2 - | isAbstractIfaceDecl d2 = d1 + -- TODO: need to merge roles + | isAbstractIfaceDecl d1 = d2 `withRolesFrom` d1 + | isAbstractIfaceDecl d2 = d1 `withRolesFrom` d2 | IfaceClass{ ifSigs = ops1, ifMinDef = bf1 } <- d1 , IfaceClass{ ifSigs = ops2, ifMinDef = bf2 } <- d2 = let ops = nameEnvElts $ plusNameEnv_C mergeIfaceClassOp (mkNameEnv [ (n, op) | op@(IfaceClassOp n _ _) <- ops1 ]) (mkNameEnv [ (n, op) | op@(IfaceClassOp n _ _) <- ops2 ]) - in d1 { ifSigs = ops + in d1 { 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 + +withRolesFrom :: IfaceDecl -> IfaceDecl -> IfaceDecl +d1 `withRolesFrom` d2 + | Just roles1 <- ifMaybeRoles d1 + , Just roles2 <- ifMaybeRoles d2 + = d1 { ifRoles = mergeRoles roles1 roles2 } + | otherwise = d1 + where + mergeRoles roles1 roles2 = zipWith max roles1 roles2 mergeIfaceClassOp :: IfaceClassOp -> IfaceClassOp -> IfaceClassOp mergeIfaceClassOp op1@(IfaceClassOp _ _ (Just _)) _ = op1 |