summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Gundry <adam@well-typed.com>2015-10-30 14:14:21 +0000
committerAdam Gundry <adam@well-typed.com>2015-10-30 14:14:28 +0000
commit0a16374109ad16d9337185f5c0a845a3f20141cb (patch)
tree024a9d8a6505c58f7612375597ae7e2e48899452
parent268aa9a2ee98d800594875c930cfcd76cb5e221b (diff)
downloadhaskell-0a16374109ad16d9337185f5c0a845a3f20141cb.tar.gz
Disambiguate record selectors by type signature
This makes DuplicateRecordFields more liberal in when it will accept ambiguous record selectors, making use of type information in a similar way to updates. See Note [Disambiguating record fields] for more details. I've also refactored how record updates are disambiguated. Test Plan: New and amended tests in overloadedrecflds Reviewers: simonpj, goldfire, bgamari, austin Subscribers: thomie Differential Revision: https://phabricator.haskell.org/D1391
-rw-r--r--compiler/deSugar/DsExpr.hs2
-rw-r--r--compiler/deSugar/DsMeta.hs6
-rw-r--r--compiler/hsSyn/HsExpr.hs8
-rw-r--r--compiler/hsSyn/HsPat.hs2
-rw-r--r--compiler/hsSyn/HsTypes.hs5
-rw-r--r--compiler/rename/RnEnv.hs1
-rw-r--r--compiler/rename/RnExpr.hs11
-rw-r--r--compiler/rename/RnPat.hs2
-rw-r--r--compiler/typecheck/TcExpr.hs314
-rw-r--r--testsuite/tests/overloadedrecflds/should_fail/all.T2
-rw-r--r--testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail01.stderr14
-rw-r--r--testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail04.hs2
-rw-r--r--testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail09.stderr2
-rw-r--r--testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail12.hs3
-rw-r--r--testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail12.stderr4
-rw-r--r--testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail13.hs20
-rw-r--r--testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail13.stderr22
-rw-r--r--testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail14.hs14
-rw-r--r--testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail14.stderr5
-rw-r--r--testsuite/tests/overloadedrecflds/should_run/all.T1
-rw-r--r--testsuite/tests/overloadedrecflds/should_run/overloadedrecfldsrun06.hs21
-rw-r--r--testsuite/tests/overloadedrecflds/should_run/overloadedrecfldsrun06.stdout2
22 files changed, 336 insertions, 127 deletions
diff --git a/compiler/deSugar/DsExpr.hs b/compiler/deSugar/DsExpr.hs
index f47843aa06..2e278fd2ca 100644
--- a/compiler/deSugar/DsExpr.hs
+++ b/compiler/deSugar/DsExpr.hs
@@ -711,7 +711,7 @@ dsExpr (EViewPat {}) = panic "dsExpr:EViewPat"
dsExpr (ELazyPat {}) = panic "dsExpr:ELazyPat"
dsExpr (HsType {}) = panic "dsExpr:HsType"
dsExpr (HsDo {}) = panic "dsExpr:HsDo"
-dsExpr (HsSingleRecFld{}) = panic "dsExpr: HsSingleRecFld"
+dsExpr (HsRecFld {}) = panic "dsExpr:HsRecFld"
findField :: [LHsRecField Id arg] -> Name -> [arg]
diff --git a/compiler/deSugar/DsMeta.hs b/compiler/deSugar/DsMeta.hs
index ad1d5016cc..90dcea427e 100644
--- a/compiler/deSugar/DsMeta.hs
+++ b/compiler/deSugar/DsMeta.hs
@@ -1073,6 +1073,10 @@ repE (HsVar x) =
; return (MkC e') } }
repE e@(HsIPVar _) = notHandled "Implicit parameters" (ppr e)
+repE e@(HsRecFld f) = case f of
+ Unambiguous _ x -> repE (HsVar x)
+ Ambiguous{} -> notHandled "Ambiguous record selectors" (ppr e)
+
-- Remember, we're desugaring renamer output here, so
-- HsOverlit can definitely occur
repE (HsOverLit l) = do { a <- repOverloadedLiteral l; repLit a }
@@ -1241,7 +1245,7 @@ repUpdFields = repList fieldExpQTyConName rep_fld
Unambiguous _ sel_name -> do { fn <- lookupLOcc (L l sel_name)
; e <- repLE (hsRecFieldArg fld)
; repFieldExp fn e }
- _ -> notHandled "ambiguous record updates" (ppr fld)
+ _ -> notHandled "Ambiguous record updates" (ppr fld)
diff --git a/compiler/hsSyn/HsExpr.hs b/compiler/hsSyn/HsExpr.hs
index 0b62d1f2c8..84264b448f 100644
--- a/compiler/hsSyn/HsExpr.hs
+++ b/compiler/hsSyn/HsExpr.hs
@@ -135,7 +135,7 @@ data HsExpr id
-- Turned into HsVar by type checker, to support deferred
-- type errors. (The HsUnboundVar only has an OccName.)
- | HsSingleRecFld (FieldOcc id) -- ^ Variable that corresponds to a record selector
+ | HsRecFld (AmbiguousFieldOcc id) -- ^ Variable pointing to record selector
| HsIPVar HsIPName -- ^ Implicit parameter
| HsOverLit (HsOverLit id) -- ^ Overloaded literals
@@ -801,7 +801,7 @@ ppr_expr (HsArrForm (L _ (HsVar v)) (Just _) [arg1, arg2])
ppr_expr (HsArrForm op _ args)
= hang (ptext (sLit "(|") <+> ppr_lexpr op)
4 (sep (map (pprCmdArg.unLoc) args) <+> ptext (sLit "|)"))
-ppr_expr (HsSingleRecFld f) = ppr f
+ppr_expr (HsRecFld f) = ppr f
pprExternalSrcLoc :: (StringLiteral,(Int,Int),(Int,Int)) -> SDoc
pprExternalSrcLoc (StringLiteral _ src,(n1,n2),(n3,n4))
@@ -853,7 +853,7 @@ hsExprNeedsParens (HsRnBracketOut {}) = False
hsExprNeedsParens (HsTcBracketOut {}) = False
hsExprNeedsParens (HsDo sc _ _)
| isListCompExpr sc = False
-hsExprNeedsParens (HsSingleRecFld{}) = False
+hsExprNeedsParens (HsRecFld{}) = False
hsExprNeedsParens _ = True
@@ -866,7 +866,7 @@ isAtomicHsExpr (HsIPVar {}) = True
isAtomicHsExpr (HsUnboundVar {}) = True
isAtomicHsExpr (HsWrap _ e) = isAtomicHsExpr e
isAtomicHsExpr (HsPar e) = isAtomicHsExpr (unLoc e)
-isAtomicHsExpr (HsSingleRecFld{}) = True
+isAtomicHsExpr (HsRecFld{}) = True
isAtomicHsExpr _ = False
{-
diff --git a/compiler/hsSyn/HsPat.hs b/compiler/hsSyn/HsPat.hs
index b37d836403..3fd6f73202 100644
--- a/compiler/hsSyn/HsPat.hs
+++ b/compiler/hsSyn/HsPat.hs
@@ -324,6 +324,8 @@ data HsRecField' id arg = HsRecField {
-- The typechecker will determine the particular selector:
--
-- hsRecFieldLbl = Unambiguous "x" $sel:x:MkS :: AmbiguousFieldOcc Id
+--
+-- See also Note [Disambiguating record fields] in TcExpr.
hsRecFields :: HsRecFields id arg -> [PostRn id id]
hsRecFields rbinds = map (unLoc . hsRecFieldSel . unLoc) (rec_flds rbinds)
diff --git a/compiler/hsSyn/HsTypes.hs b/compiler/hsSyn/HsTypes.hs
index 17e1050691..73f961c84b 100644
--- a/compiler/hsSyn/HsTypes.hs
+++ b/compiler/hsSyn/HsTypes.hs
@@ -587,7 +587,8 @@ mkFieldOcc rdr = FieldOcc rdr PlaceHolder
-- (for unambiguous occurrences) or the typechecker (for ambiguous
-- occurrences).
--
--- See Note [HsRecField and HsRecUpdField] in HsPat
+-- See Note [HsRecField and HsRecUpdField] in HsPat and
+-- Note [Disambiguating record fields] in TcExpr.
data AmbiguousFieldOcc name
= Unambiguous RdrName (PostRn name name)
| Ambiguous RdrName (PostTc name name)
@@ -615,7 +616,7 @@ unambiguousFieldOcc :: AmbiguousFieldOcc Id -> FieldOcc Id
unambiguousFieldOcc (Unambiguous rdr sel) = FieldOcc rdr sel
unambiguousFieldOcc (Ambiguous rdr sel) = FieldOcc rdr sel
-ambiguousFieldOcc :: FieldOcc Id -> AmbiguousFieldOcc Id
+ambiguousFieldOcc :: FieldOcc name -> AmbiguousFieldOcc name
ambiguousFieldOcc (FieldOcc rdr sel) = Unambiguous rdr sel
{-
diff --git a/compiler/rename/RnEnv.hs b/compiler/rename/RnEnv.hs
index 1ed55ba64b..0404013f0f 100644
--- a/compiler/rename/RnEnv.hs
+++ b/compiler/rename/RnEnv.hs
@@ -16,6 +16,7 @@ module RnEnv (
lookupGlobalOccRn, lookupGlobalOccRn_maybe,
lookupOccRn_overloaded, lookupGlobalOccRn_overloaded,
reportUnboundName, unknownNameSuggestions,
+ addNameClashErrRn,
HsSigCtxt(..), lookupLocalTcNames, lookupSigOccRn,
lookupSigCtxtOccRn,
diff --git a/compiler/rename/RnExpr.hs b/compiler/rename/RnExpr.hs
index b4c63f3d93..e633f523c8 100644
--- a/compiler/rename/RnExpr.hs
+++ b/compiler/rename/RnExpr.hs
@@ -94,7 +94,8 @@ rnUnboundVar v
; return (HsVar n, emptyFVs) } }
rnExpr (HsVar v)
- = do { mb_name <- lookupOccRn_overloaded False v
+ = do { opt_DuplicateRecordFields <- xoptM Opt_DuplicateRecordFields
+ ; mb_name <- lookupOccRn_overloaded opt_DuplicateRecordFields v
; case mb_name of {
Nothing -> rnUnboundVar v ;
Just (Left name)
@@ -104,9 +105,11 @@ rnExpr (HsVar v)
| otherwise
-> finishHsVar name ;
- Just (Right (f:fs)) -> ASSERT( null fs )
- return (HsSingleRecFld f, unitFV (selectorFieldOcc f)) ;
- Just (Right []) -> error "runExpr/HsVar" } }
+ Just (Right [f]) -> return (HsRecFld (ambiguousFieldOcc f)
+ , unitFV (selectorFieldOcc f)) ;
+ Just (Right fs@(_:_:_)) -> return (HsRecFld (Ambiguous v PlaceHolder)
+ , mkFVs (map selectorFieldOcc fs));
+ Just (Right []) -> error "runExpr/HsVar" } }
rnExpr (HsIPVar v)
= return (HsIPVar v, emptyFVs)
diff --git a/compiler/rename/RnPat.hs b/compiler/rename/RnPat.hs
index 6637156d2b..f5005740df 100644
--- a/compiler/rename/RnPat.hs
+++ b/compiler/rename/RnPat.hs
@@ -672,7 +672,7 @@ rnHsRecUpdFields flds
= do { let lbl = rdrNameAmbiguousFieldOcc f
; sel <- setSrcSpan loc $
-- Defer renaming of overloaded fields to the typechecker
- -- See Note [Disambiguating record updates] in TcExpr
+ -- See Note [Disambiguating record fields] in TcExpr
if overload_ok
then do { mb <- lookupGlobalOccRn_overloaded overload_ok lbl
; case mb of
diff --git a/compiler/typecheck/TcExpr.hs b/compiler/typecheck/TcExpr.hs
index fe9e0cb5bd..5295ed967f 100644
--- a/compiler/typecheck/TcExpr.hs
+++ b/compiler/typecheck/TcExpr.hs
@@ -28,7 +28,9 @@ import BasicTypes
import Inst
import TcBinds
import FamInst ( tcGetFamInstEnvs, tcLookupDataFamInst )
-import RnEnv ( addUsedGRE )
+import FamInstEnv ( FamInstEnvs )
+import RnEnv ( addUsedGRE, addNameClashErrRn
+ , unknownSubordinateErr )
import TcEnv
import TcArrows
import TcMatches
@@ -693,7 +695,7 @@ following.
tcExpr (RecordUpd record_expr rbnds _ _ _ _ ) res_ty
= ASSERT( notNull rbnds )
do {
- -- STEP -1 See Note [Disambiguating record updates]
+ -- STEP -1 See Note [Disambiguating record fields]
-- After this we know that rbinds is unambiguous
rbinds <- disambiguateRecordBinds record_expr rbnds res_ty
; let upd_flds = map (unLoc . hsRecFieldLbl . unLoc) rbinds
@@ -826,7 +828,7 @@ tcExpr (RecordUpd record_expr rbnds _ _ _ _ ) res_ty
RecordUpd (mkLHsWrap scrut_co record_expr') rbinds'
relevant_cons scrut_inst_tys result_inst_tys req_wrap }
-tcExpr (HsSingleRecFld f) res_ty
+tcExpr (HsRecFld f) res_ty
= tcCheckRecSelId f res_ty
{-
@@ -973,6 +975,14 @@ tcApp (L loc (HsVar fun)) args res_ty
, [arg1,arg2] <- args
= tcSeq loc fun arg1 arg2 res_ty
+-- Look for applications of ambiguous record selectors to arguments
+-- with type signatures, see Note [Disambiguating record fields]
+tcApp (L loc (HsRecFld (Ambiguous lbl _))) args@(L _ arg:_) res_ty
+ | Just sig_ty <- obviousSig arg
+ = do { sig_tc_ty <- tcHsSigType ExprSigCtxt sig_ty
+ ; sel_name <- disambiguateSelector lbl sig_tc_ty
+ ; tcApp (L loc (HsRecFld (Unambiguous lbl sel_name))) args res_ty }
+
tcApp fun args res_ty
= do { -- Type-check the function
; (fun1, fun_tau) <- tcInferFun fun
@@ -1011,7 +1021,7 @@ tcInferFun (L loc (HsVar name))
-- Don't wrap a context around a plain Id
; return (L loc fun, ty) }
-tcInferFun (L loc (HsSingleRecFld f))
+tcInferFun (L loc (HsRecFld f))
= do { (fun, ty) <- setSrcSpan loc (tcInferRecSelId f)
-- Don't wrap a context around a plain Id
; return (L loc fun, ty) }
@@ -1108,19 +1118,27 @@ tcCheckId name res_ty
; addErrCtxtM (funResCtxt False (HsVar name) actual_res_ty res_ty) $
tcWrapResult expr actual_res_ty res_ty }
-tcCheckRecSelId :: FieldOcc Name -> TcRhoType -> TcM (HsExpr TcId)
-tcCheckRecSelId f res_ty
+tcCheckRecSelId :: AmbiguousFieldOcc Name -> TcRhoType -> TcM (HsExpr TcId)
+tcCheckRecSelId f@(Unambiguous _ _) res_ty
= do { (expr, actual_res_ty) <- tcInferRecSelId f
- ; addErrCtxtM (funResCtxt False (HsSingleRecFld f) actual_res_ty res_ty) $
+ ; addErrCtxtM (funResCtxt False (HsRecFld f) actual_res_ty res_ty) $
tcWrapResult expr actual_res_ty res_ty }
+tcCheckRecSelId (Ambiguous lbl _) res_ty
+ = case tcSplitFunTy_maybe res_ty of
+ Nothing -> ambiguousSelector lbl
+ Just (arg, _) -> do { sel_name <- disambiguateSelector lbl arg
+ ; tcCheckRecSelId (Unambiguous lbl sel_name) res_ty }
------------------------
tcInferId :: Name -> TcM (HsExpr TcId, TcRhoType)
-- Infer type, and deeply instantiate
tcInferId n = tcInferIdWithOrig (OccurrenceOf n) (nameRdrName n) n
-tcInferRecSelId :: FieldOcc Name -> TcM (HsExpr TcId, TcRhoType)
-tcInferRecSelId (FieldOcc lbl sel) = tcInferIdWithOrig (OccurrenceOfRecSel lbl) lbl sel
+tcInferRecSelId :: AmbiguousFieldOcc Name -> TcM (HsExpr TcId, TcRhoType)
+tcInferRecSelId (Unambiguous lbl sel)
+ = tcInferIdWithOrig (OccurrenceOfRecSel lbl) lbl sel
+tcInferRecSelId (Ambiguous lbl _)
+ = ambiguousSelector lbl
------------------------
tcInferIdWithOrig :: CtOrigin -> RdrName -> Name ->
@@ -1407,15 +1425,15 @@ getFixedTyVars upd_fld_occs univ_tvs cons
, tv `elemVarSet` fixed_tvs ]
{-
-Note [Disambiguating record updates]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Note [Disambiguating record fields]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When the -XDuplicateRecordFields extension is used, and the renamer
-encounters a record update that it cannot immediately disambiguate
-(because it involves fields that belong to multiple datatypes), it
-will defer resolution of the ambiguity to the typechecker. In this
-case, the `hsRecUpdFieldSel` field of the `HsRecUpdField` stores a
-list of candidate selectors.
+encounters a record selector or update that it cannot immediately
+disambiguate (because it involves fields that belong to multiple
+datatypes), it will defer resolution of the ambiguity to the
+typechecker. In this case, the `Ambiguous` constructor of
+`AmbiguousFieldOcc` is used.
Consider the following definitions:
@@ -1423,9 +1441,31 @@ Consider the following definitions:
data T = MkT { foo :: Int, bar :: Int }
data U = MkU { bar :: Int, baz :: Int }
-When the renamer sees an update of `foo`, it will not know which
-parent datatype is in use. The `disambiguateRecordBinds` function
-tries to determine the parent in three ways:
+When the renamer sees `foo` as a selector or an update, it will not
+know which parent datatype is in use.
+
+For selectors, there are two possible ways to disambiguate:
+
+1. Check if the pushed-in type is a function whose domain is a
+ datatype, for example:
+
+ f s = (foo :: S -> Int) s
+
+ g :: T -> Int
+ g = foo
+
+ This is checked by `tcCheckRecSelId` when checking `HsRecFld foo`.
+
+2. Check if the selector is applied to an argument that has a type
+ signature, for example:
+
+ h = foo (s :: S)
+
+ This is checked by `tcApp`.
+
+
+Updates are slightly more complex. The `disambiguateRecordBinds`
+function tries to determine the parent datatype in three ways:
1. Check for types that have all the fields being updated. For example:
@@ -1450,10 +1490,13 @@ tries to determine the parent in three ways:
h x = (x :: T) { foo = 3 }
+
Note that we do not look up the types of variables being updated, and
no constraint-solving is performed, so for example the following will
be rejected as ambiguous:
+ let bad (s :: S) = foo s
+
let r :: T
r = blah
in r { foo = 3 }
@@ -1462,107 +1505,162 @@ be rejected as ambiguous:
We could add further tests, of a more heuristic nature. For example,
rather than looking for an explicit signature, we could try to infer
-the type of the record expression, in case we are lucky enough to get
-a TyConApp straight away. However, it might be hard for programmers to
-predict whether a particular update is sufficiently obvious for the
-signature to be omitted.
+the type of the argument to a selector or the record expression being
+updated, in case we are lucky enough to get a TyConApp straight
+away. However, it might be hard for programmers to predict whether a
+particular update is sufficiently obvious for the signature to be
+omitted. Moreover, this might change the behaviour of typechecker in
+non-obvious ways.
+
+See also Note [HsRecField and HsRecUpdField] in HsPat.
-}
+-- Given a RdrName that refers to multiple record fields, and the type
+-- of its argument, try to determine the name of the selector that is
+-- meant.
+disambiguateSelector :: RdrName -> Type -> RnM Name
+disambiguateSelector rdr parent_type
+ = do { fam_inst_envs <- tcGetFamInstEnvs
+ ; case tyConOf fam_inst_envs parent_type of
+ Nothing -> ambiguousSelector rdr
+ Just p ->
+ do { xs <- lookupParents rdr
+ ; let parent = RecSelData p
+ ; case lookup parent xs of
+ Just gre -> do { addUsedGRE True gre
+ ; return (gre_name gre) }
+ Nothing -> failWithTc (fieldNotInType parent rdr) } }
+
+-- This field name really is ambiguous, so add a suitable "ambiguous
+-- occurrence" error, then give up.
+ambiguousSelector :: RdrName -> RnM a
+ambiguousSelector rdr
+ = do { env <- getGlobalRdrEnv
+ ; let gres = lookupGRE_RdrName rdr env
+ ; setErrCtxt [] $ addNameClashErrRn rdr gres
+ ; failM }
+
+-- Disambiguate the fields in a record update.
+-- See Note [Disambiguating record fields]
disambiguateRecordBinds :: LHsExpr Name -> [LHsRecUpdField Name] -> Type
-> TcM [LHsRecField' (AmbiguousFieldOcc Id) (LHsExpr Name)]
disambiguateRecordBinds record_expr rbnds res_ty
+ -- Are all the fields unambiguous?
= case mapM isUnambiguous rbnds of
+ -- If so, just skip to looking up the Ids
-- Always the case if DuplicateRecordFields is off
- Just rbnds' -> lookupSelectors rbnds'
- Nothing -> do
- { fam_inst_envs <- tcGetFamInstEnvs
- ; (rbnds_with_parents) <- fmap (zip rbnds) $ mapM getParents rbnds
- ; (p :: RecSelParent) <- case possibleParents (map snd rbnds_with_parents) of
- [] -> failWithTc (noPossibleParents rbnds)
- [p] -> return p
- _ | Just p <- tyConOf fam_inst_envs res_ty -> return (RecSelData p)
- _ | Just sig_ty <- obviousSig (unLoc record_expr) ->
- do { sig_tc_ty <- tcHsSigType ExprSigCtxt sig_ty
- ; case tyConOf fam_inst_envs sig_tc_ty of
- Just p -> return (RecSelData p)
- Nothing -> failWithTc badOverloadedUpdate }
- _ -> failWithTc badOverloadedUpdate
- ; assignParent p rbnds_with_parents }
+ Just rbnds' -> mapM lookupSelector rbnds'
+ Nothing -> -- If not, try to identify a single parent
+ do { fam_inst_envs <- tcGetFamInstEnvs
+ -- Look up the possible parents for each field
+ ; rbnds_with_parents <- getUpdFieldsParents
+ ; let possible_parents = map (map fst . snd) rbnds_with_parents
+ -- Identify a single parent
+ ; p <- identifyParent fam_inst_envs possible_parents
+ -- Pick the right selector with that parent for each field
+ ; checkNoErrs $ mapM (pickParent p) rbnds_with_parents }
where
+ -- Extract the selector name of a field update if it is unambiguous
isUnambiguous :: LHsRecUpdField Name -> Maybe (LHsRecUpdField Name, Name)
isUnambiguous x = case unLoc (hsRecFieldLbl (unLoc x)) of
Unambiguous _ sel_name -> Just (x, sel_name)
Ambiguous{} -> Nothing
- lookupSelectors :: [(LHsRecUpdField Name, Name)] -> TcM [LHsRecField' (AmbiguousFieldOcc Id) (LHsExpr Name)]
- lookupSelectors = mapM look
- where
- look :: (LHsRecUpdField Name, Name) -> TcM (LHsRecField' (AmbiguousFieldOcc Id) (LHsExpr Name))
- look (L l x, n) = do i <- tcLookupId n
- let L loc af = hsRecFieldLbl x
- lbl = rdrNameAmbiguousFieldOcc af
- return $ L l x { hsRecFieldLbl = L loc (Unambiguous lbl i) }
-
- -- Extract the outermost TyCon of a type, if there is one; for
- -- data families this is the representation tycon (because that's
- -- where the fields live).
- tyConOf fam_inst_envs ty = case tcSplitTyConApp_maybe ty of
- Just (tc, tys) -> Just (fstOf3 (tcLookupDataFamInst fam_inst_envs tc tys))
- Nothing -> Nothing
-
- -- Calculate the list of possible parent tycons, by taking the
- -- intersection of the possibilities for each field.
- possibleParents :: [[(RecSelParent, a)]] -> [RecSelParent]
- possibleParents = foldr1 intersect . map (map fst)
-
- -- Look up the parent tycon for each candidate record selector.
- getParents :: LHsRecUpdField Name -> RnM [(RecSelParent, GlobalRdrElt)]
- getParents (L _ fld) = do
- { env <- getGlobalRdrEnv
- ; let gres = lookupGRE_RdrName (unLoc (hsRecUpdFieldRdr fld)) env
- ; mapM lookupParent gres }
-
+ -- Look up the possible parents and selector GREs for each field
+ getUpdFieldsParents :: TcM [(LHsRecUpdField Name
+ , [(RecSelParent, GlobalRdrElt)])]
+ getUpdFieldsParents
+ = fmap (zip rbnds) $ mapM
+ (lookupParents . unLoc . hsRecUpdFieldRdr . unLoc)
+ rbnds
+
+ -- Given a the lists of possible parents for each field,
+ -- identify a single parent
+ identifyParent :: FamInstEnvs -> [[RecSelParent]] -> TcM RecSelParent
+ identifyParent fam_inst_envs possible_parents
+ = case foldr1 intersect possible_parents of
+ -- No parents for all fields: record update is ill-typed
+ [] -> failWithTc (noPossibleParents rbnds)
+ -- Exactly one datatype with all the fields: use that
+ [p] -> return p
+ -- Multiple possible parents: try harder to disambiguate
+ -- Can we get a parent TyCon from the pushed-in type?
+ _:_ | Just p <- tyConOf fam_inst_envs res_ty -> return (RecSelData p)
+ -- Does the expression being updated have a type signature?
+ -- If so, try to extract a parent TyCon from it
+ | Just sig_ty <- obviousSig (unLoc record_expr)
+ -> do { sig_tc_ty <- tcHsSigType ExprSigCtxt sig_ty
+ ; case tyConOf fam_inst_envs sig_tc_ty of
+ Just p -> return (RecSelData p)
+ Nothing -> failWithTc badOverloadedUpdate }
+ -- Nothing else we can try...
+ _ -> failWithTc badOverloadedUpdate
+
+ -- Make a field unambiguous by choosing the given parent.
+ -- Emits an error if the field cannot have that parent,
+ -- e.g. if the user writes
+ -- r { x = e } :: T
+ -- where T does not have field x.
+ pickParent :: RecSelParent
+ -> (LHsRecUpdField Name, [(RecSelParent, GlobalRdrElt)])
+ -> TcM (LHsRecField' (AmbiguousFieldOcc Id) (LHsExpr Name))
+ pickParent p (upd, xs)
+ = case lookup p xs of
+ -- Phew! The parent is valid for this field.
+ -- Previously ambiguous fields must be marked as
+ -- used now that we know which one is meant, but
+ -- unambiguous ones shouldn't be recorded again
+ -- (giving duplicate deprecation warnings).
+ Just gre -> do { unless (null (tail xs)) $ do
+ let L loc _ = hsRecFieldLbl (unLoc upd)
+ setSrcSpan loc $ addUsedGRE True gre
+ ; lookupSelector (upd, gre_name gre) }
+ -- The field doesn't belong to this parent, so report
+ -- an error but keep going through all the fields
+ Nothing -> do { addErrTc (fieldNotInType p
+ (unLoc (hsRecUpdFieldRdr (unLoc upd))))
+ ; lookupSelector (upd, gre_name (snd (head xs))) }
+
+ -- Given a (field update, selector name) pair, look up the
+ -- selector to give a field update with an unambiguous Id
+ lookupSelector :: (LHsRecUpdField Name, Name)
+ -> TcM (LHsRecField' (AmbiguousFieldOcc Id) (LHsExpr Name))
+ lookupSelector (L l upd, n)
+ = do { i <- tcLookupId n
+ ; let L loc af = hsRecFieldLbl upd
+ lbl = rdrNameAmbiguousFieldOcc af
+ ; return $ L l upd { hsRecFieldLbl = L loc (Unambiguous lbl i) } }
+
+
+-- Extract the outermost TyCon of a type, if there is one; for
+-- data families this is the representation tycon (because that's
+-- where the fields live).
+tyConOf :: FamInstEnvs -> Type -> Maybe TyCon
+tyConOf fam_inst_envs ty = case tcSplitTyConApp_maybe ty of
+ Just (tc, tys) -> Just (fstOf3 (tcLookupDataFamInst fam_inst_envs tc tys))
+ Nothing -> Nothing
+
+-- For an ambiguous record field, find all the candidate record
+-- selectors (as GlobalRdrElts) and their parents.
+lookupParents :: RdrName -> RnM [(RecSelParent, GlobalRdrElt)]
+lookupParents rdr
+ = do { env <- getGlobalRdrEnv
+ ; let gres = lookupGRE_RdrName rdr env
+ ; mapM lookupParent gres }
+ where
lookupParent :: GlobalRdrElt -> RnM (RecSelParent, GlobalRdrElt)
lookupParent gre = do { id <- tcLookupId (gre_name gre)
- ; ASSERT(isRecordSelector id)
- return (recordSelectorTyCon id, gre) }
+ ; if isRecordSelector id
+ then return (recordSelectorTyCon id, gre)
+ else failWithTc (notSelector (gre_name gre)) }
- -- Make all the fields unambiguous by choosing the given parent.
- -- Fails with an error if any of the ambiguous fields cannot have
- -- that parent, e.g. if the user writes
- -- r { x = e } :: T
- -- where T does not have field x.
- assignParent :: RecSelParent -> [(LHsRecUpdField Name, [(RecSelParent, GlobalRdrElt)])]
- -> RnM [LHsRecField' (AmbiguousFieldOcc Id) (LHsExpr Name)]
- assignParent p rbnds
- | null orphans = do rbnds'' <- mapM f rbnds'
- lookupSelectors rbnds''
- | otherwise = failWithTc (orphanFields p orphans)
- where
- (orphans, rbnds') = partitionWith pickParent rbnds
-
- -- Previously ambiguous fields must be marked as used now that
- -- we know which one is meant, but unambiguous ones shouldn't
- -- be recorded again (giving duplicate deprecation warnings).
- f (fld, gre, was_unambiguous)
- = do { unless was_unambiguous $ do
- setSrcSpan (getLoc fld) $ addUsedGRE True gre
- ; return (fld, gre_name gre) }
-
- -- Returns Right if fld can have parent p, or Left lbl if not.
- pickParent :: (LHsRecUpdField Name, [(RecSelParent, GlobalRdrElt)])
- -> Either (Located RdrName) (LHsRecUpdField Name, GlobalRdrElt, Bool)
- pickParent (fld, xs)
- = case lookup p xs of
- Just gre -> Right (fld, gre, null (tail xs))
- Nothing -> Left (hsRecUpdFieldRdr (unLoc fld))
-
- -- A type signature on the record expression must be "obvious",
- -- i.e. the outermost constructor ignoring parentheses.
- obviousSig :: HsExpr Name -> Maybe (LHsType Name)
- obviousSig (ExprWithTySig _ ty _) = Just ty
- obviousSig (HsPar p) = obviousSig (unLoc p)
- obviousSig _ = Nothing
+-- A type signature on the argument of an ambiguous record selector or
+-- the record expression in an update must be "obvious", i.e. the
+-- outermost constructor ignoring parentheses.
+obviousSig :: HsExpr Name -> Maybe (LHsType Name)
+obviousSig (ExprWithTySig _ ty _) = Just ty
+obviousSig (HsPar p) = obviousSig (unLoc p)
+obviousSig _ = Nothing
{-
@@ -1886,8 +1984,6 @@ noPossibleParents rbinds
badOverloadedUpdate :: SDoc
badOverloadedUpdate = ptext (sLit "Record update is ambiguous, and requires a type signature")
-orphanFields :: RecSelParent -> [Located RdrName] -> SDoc
-orphanFields p flds
- = hang (ptext (sLit "Type") <+> ppr p <+>
- ptext (sLit "does not have field") <> plural flds <> colon)
- 2 (pprQuotedList flds)
+fieldNotInType :: RecSelParent -> RdrName -> SDoc
+fieldNotInType p rdr
+ = unknownSubordinateErr (ptext (sLit "field of type") <+> quotes (ppr p)) rdr
diff --git a/testsuite/tests/overloadedrecflds/should_fail/all.T b/testsuite/tests/overloadedrecflds/should_fail/all.T
index fe7a85af70..5ff61e2735 100644
--- a/testsuite/tests/overloadedrecflds/should_fail/all.T
+++ b/testsuite/tests/overloadedrecflds/should_fail/all.T
@@ -20,3 +20,5 @@ test('overloadedrecfldsfail11', normal, compile_fail, [''])
test('overloadedrecfldsfail12',
extra_clean(['OverloadedRecFldsFail12_A.hi', 'OverloadedRecFldsFail12_A.o']),
multimod_compile_fail, ['overloadedrecfldsfail12', ''])
+test('overloadedrecfldsfail13', normal, compile_fail, [''])
+test('overloadedrecfldsfail14', normal, compile_fail, [''])
diff --git a/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail01.stderr b/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail01.stderr
index fbf8a61176..4f51a6f6a9 100644
--- a/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail01.stderr
+++ b/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail01.stderr
@@ -1,16 +1,22 @@
-overloadedrecfldsfail01.hs:11:10:
+overloadedrecfldsfail01.hs:11:10: error:
Record update is ambiguous, and requires a type signature
In the expression: r {x = 3}
In an equation for ‘upd1’: upd1 r = r {x = 3}
-overloadedrecfldsfail01.hs:14:10:
+overloadedrecfldsfail01.hs:14:10: error:
No type has all these fields: ‘x’, ‘y’, ‘z’
In the expression: r {x = 3, y = True, z = False}
In an equation for ‘upd2’: upd2 r = r {x = 3, y = True, z = False}
-overloadedrecfldsfail01.hs:17:10:
- Type U does not have fields: ‘w’, ‘x’
+overloadedrecfldsfail01.hs:17:10: error:
+ ‘w’ is not a (visible) field of type ‘U’
+ In the expression: r {w = True, x = 3, y = True} :: U
+ In an equation for ‘upd3’:
+ upd3 r = r {w = True, x = 3, y = True} :: U
+
+overloadedrecfldsfail01.hs:17:10: error:
+ ‘x’ is not a (visible) field of type ‘U’
In the expression: r {w = True, x = 3, y = True} :: U
In an equation for ‘upd3’:
upd3 r = r {w = True, x = 3, y = True} :: U
diff --git a/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail04.hs b/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail04.hs
index 9d35bbe5dd..47f16da56d 100644
--- a/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail04.hs
+++ b/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail04.hs
@@ -10,3 +10,5 @@ x' = I.x
-- But this is okay
f e = e { I.x = True, I.y = False }
+
+main = return ()
diff --git a/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail09.stderr b/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail09.stderr
index 8d892e380a..415099d79b 100644
--- a/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail09.stderr
+++ b/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail09.stderr
@@ -1,4 +1,4 @@
overloadedrecfldsfail09.hs:9:11: error:
- ambiguous record updates not (yet) handled by Template Haskell
+ Ambiguous record updates not (yet) handled by Template Haskell
x = 3
diff --git a/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail12.hs b/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail12.hs
index 0516e43d63..56092b6ce0 100644
--- a/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail12.hs
+++ b/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail12.hs
@@ -9,4 +9,7 @@ data S = MkS { foo :: Bool }
f :: T -> T
f e = e { foo = 3, bar = 3 }
+s :: T -> Int
+s = foo
+
main = return ()
diff --git a/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail12.stderr b/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail12.stderr
index 65733ed6e8..f4a2f7bcfc 100644
--- a/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail12.stderr
+++ b/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail12.stderr
@@ -9,5 +9,9 @@ overloadedrecfldsfail12.hs:10:20: warning:
In the use of ‘bar’ (imported from OverloadedRecFldsFail12_A):
"Deprecated bar"
+overloadedrecfldsfail12.hs:13:5: warning:
+ In the use of ‘foo’ (imported from OverloadedRecFldsFail12_A):
+ "Deprecated foo"
+
<no location info>: error:
Failing due to -Werror.
diff --git a/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail13.hs b/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail13.hs
new file mode 100644
index 0000000000..773bd60c8c
--- /dev/null
+++ b/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail13.hs
@@ -0,0 +1,20 @@
+-- Test that giving a stupid type annotation to an ambiguous field
+-- yields a sensible error message
+
+{-# LANGUAGE DuplicateRecordFields #-}
+
+data S = MkS { x :: Int }
+data T = MkT { x :: Bool }
+data U = MkU
+
+a = x (MkU :: U)
+
+b = x (MkU :: a)
+
+c :: U -> Int
+c = x
+
+d :: a -> Int
+d = x
+
+main = return ()
diff --git a/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail13.stderr b/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail13.stderr
new file mode 100644
index 0000000000..7c61ab769e
--- /dev/null
+++ b/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail13.stderr
@@ -0,0 +1,22 @@
+
+overloadedrecfldsfail13.hs:10:5: error:
+ ‘x’ is not a (visible) field of type ‘U’
+ In the expression: x (MkU :: U)
+ In an equation for ‘a’: a = x (MkU :: U)
+
+overloadedrecfldsfail13.hs:12:5: error:
+ Ambiguous occurrence ‘x’
+ It could refer to either the field ‘x’,
+ defined at overloadedrecfldsfail13.hs:7:16
+ or the field ‘x’, defined at overloadedrecfldsfail13.hs:6:16
+
+overloadedrecfldsfail13.hs:15:5: error:
+ ‘x’ is not a (visible) field of type ‘U’
+ In the expression: x
+ In an equation for ‘c’: c = x
+
+overloadedrecfldsfail13.hs:18:5: error:
+ Ambiguous occurrence ‘x’
+ It could refer to either the field ‘x’,
+ defined at overloadedrecfldsfail13.hs:7:16
+ or the field ‘x’, defined at overloadedrecfldsfail13.hs:6:16
diff --git a/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail14.hs b/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail14.hs
new file mode 100644
index 0000000000..7785bb2403
--- /dev/null
+++ b/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail14.hs
@@ -0,0 +1,14 @@
+{-# LANGUAGE DuplicateRecordFields #-}
+
+-- Test that we deal gracefully with non-fields in updates
+
+data S = MkS { x :: Int }
+data T = MkT { x :: Int }
+
+y :: Bool
+y = True
+
+-- y isn't a field
+f r = r { x = 3, y = False }
+
+main = return ()
diff --git a/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail14.stderr b/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail14.stderr
new file mode 100644
index 0000000000..908996f39e
--- /dev/null
+++ b/testsuite/tests/overloadedrecflds/should_fail/overloadedrecfldsfail14.stderr
@@ -0,0 +1,5 @@
+
+overloadedrecfldsfail14.hs:12:7: error:
+ ‘y’ is not a record selector
+ In the expression: r {x = 3, y = False}
+ In an equation for ‘f’: f r = r {x = 3, y = False}
diff --git a/testsuite/tests/overloadedrecflds/should_run/all.T b/testsuite/tests/overloadedrecflds/should_run/all.T
index 012916ab6a..3d7cef2c54 100644
--- a/testsuite/tests/overloadedrecflds/should_run/all.T
+++ b/testsuite/tests/overloadedrecflds/should_run/all.T
@@ -7,3 +7,4 @@ test('overloadedrecfldsrun02',
test('overloadedrecfldsrun03', normal, compile_and_run, [''])
test('overloadedrecfldsrun04', normal, compile_and_run, [''])
test('overloadedrecfldsrun05', normal, compile_and_run, [''])
+test('overloadedrecfldsrun06', normal, compile_and_run, [''])
diff --git a/testsuite/tests/overloadedrecflds/should_run/overloadedrecfldsrun06.hs b/testsuite/tests/overloadedrecflds/should_run/overloadedrecfldsrun06.hs
new file mode 100644
index 0000000000..92f870833d
--- /dev/null
+++ b/testsuite/tests/overloadedrecflds/should_run/overloadedrecfldsrun06.hs
@@ -0,0 +1,21 @@
+-- Test that ambiguous selectors can be disambiguated by providing
+-- type signatures in various places
+
+{-# LANGUAGE DuplicateRecordFields #-}
+
+data S = MkS { x :: Int }
+data T = MkT { x :: Bool }
+data U a = MkU { x :: a }
+
+x_for_s :: S -> Int
+x_for_s = x
+
+x_for_t = x :: T -> Bool
+
+x_for_u u = x (u :: U Int)
+
+k :: (T -> Bool) -> Bool
+k f = f (MkT True)
+
+main = do print (x_for_s (MkS 42))
+ print (k x)
diff --git a/testsuite/tests/overloadedrecflds/should_run/overloadedrecfldsrun06.stdout b/testsuite/tests/overloadedrecflds/should_run/overloadedrecfldsrun06.stdout
new file mode 100644
index 0000000000..abc4e3b957
--- /dev/null
+++ b/testsuite/tests/overloadedrecflds/should_run/overloadedrecfldsrun06.stdout
@@ -0,0 +1,2 @@
+42
+True