[commit: ghc] wip/hasfield: Comment matchHasField, move addUsedGRE to right place (97ebccc)

git at git.haskell.org git at git.haskell.org
Mon May 16 08:07:25 UTC 2016


Repository : ssh://git@git.haskell.org/ghc

On branch  : wip/hasfield
Link       : http://ghc.haskell.org/trac/ghc/changeset/97ebccc0f0f256eb216dca81545ea51c6df8417f/ghc

>---------------------------------------------------------------

commit 97ebccc0f0f256eb216dca81545ea51c6df8417f
Author: Adam Gundry <adam at well-typed.com>
Date:   Tue Dec 22 17:37:14 2015 +0000

    Comment matchHasField, move addUsedGRE to right place


>---------------------------------------------------------------

97ebccc0f0f256eb216dca81545ea51c6df8417f
 compiler/typecheck/TcInteract.hs | 69 +++++++++++++++++++++++++++++++++++++---
 1 file changed, 64 insertions(+), 5 deletions(-)

diff --git a/compiler/typecheck/TcInteract.hs b/compiler/typecheck/TcInteract.hs
index 66cacea..096af23 100644
--- a/compiler/typecheck/TcInteract.hs
+++ b/compiler/typecheck/TcInteract.hs
@@ -2131,6 +2131,50 @@ matchLiftedCoercible args = pprPanic "matchLiftedCoercible" (ppr args)
 *                                                                     *
 ***********************************************************************-}
 
+{-
+Note [HasField instances]
+~~~~~~~~~~~~~~~~~~~~~~~~~
+Suppose we have
+
+    data T y = MkT { foo :: [y] }
+
+and `foo` is in scope.  Then GHC will automatically solve a constraint like
+
+    HasField "foo" (T Int) b
+
+by emitting a new wanted ([Int] ~# b) and building a HasField dictionary
+out of the selector function `foo`.  The HasField class is defined (in
+GHC.Records) thus:
+
+    class HasField (x :: Symbol) r a | x r -> a where
+      getField :: Proxy# x -> r -> a
+
+Since this is a one-method class, it is represented as a newtype.
+Hence we can solve `HasField "foo" (T Int) b` by taking an expression
+of type `Proxy# "foo" -> T Int -> b` and coercing it appropriately.
+Note that
+
+    foo :: forall y . T y -> [y]
+
+so the expression we construct is
+
+    \ (_ :: Proxy# "foo") -> foo @Int |> co
+
+where
+
+    co :: (T Int -> [Int]) ~# (T Int -> b)
+
+is built from the new wanted ([Int] ~# b).
+
+If `foo` is not in scope, higher-rank or existentially quantified then
+the constraint is not solved automatically, but may be solved by a
+user-supplied HasField instance.  Similarly, if we encounter a
+HasField constraint where the field is not a literal string, or does
+not belong to the type, then we fall back on the normal constraint
+solver behaviour.
+-}
+
+-- See Note [HasField instances]
 matchHasField :: DynFlags -> Class -> [Type] -> CtLoc -> TcS LookupInstResult
 matchHasField dflags clas tys@[x_ty, r_ty, a_ty] loc
   | Just x <- isStrLitTy x_ty
@@ -2138,32 +2182,44 @@ matchHasField dflags clas tys@[x_ty, r_ty, a_ty] loc
   , Just fl <- lookupFsEnv (tyConFieldLabelEnv tycon) x
   , Just (_, ax) <- tcInstNewTyCon_maybe (classTyCon clas) tys
   = do { env <- getGlobalRdrEnvTcS
-       ; let gres = lookupGRE_Field_Name env (flSelector fl) (flLabel fl)
-       ; case gres of
+         -- Check that the field selector is in scope
+       ; case lookupGRE_Field_Name env (flSelector fl) (flLabel fl) of
            []      -> matchInstEnv dflags clas tys loc
            (gre:_) -> do {
-       ; addUsedGRE True gre
        ; sel_id <- tcLookupId (flSelector fl)
 
+         -- We've already looked up the field label in this tycon, so
+         -- there must be at least one data con with the field: find
+         -- it and hence the field's type.
        ; let data_cons_with_field = [ (dc, ty)
                                     | dc <- tyConDataCons tycon
                                     , Just ty <- [dataConFieldType_maybe dc x]
                                     ]
-             (data_con, field_ty) = ASSERT( not (null data_cons_with_field) ) head data_cons_with_field
+             (data_con, field_ty) = ASSERT( not (null data_cons_with_field) )
+                                    head data_cons_with_field
+
+         -- Calculate the new wanted constraint by equating the actual
+         -- type of the field (instantiated appropriately) with the
+         -- type in the third parameter of the HasField constraint.
              tenv = mkTopTCvSubst (dataConUnivTyVars data_con `zip` r_args)
              inst_field_ty = substTy tenv field_ty
              theta = mkTyConApp eqPrimTyCon [liftedTypeKind, liftedTypeKind, inst_field_ty, a_ty ]
 
+         -- Give up if the selector is "naughty" (i.e. this is an
+         -- existentially quantified type) or has a higher-rank type.
        ; if isNaughtyRecordSelector sel_id || not (isTauTy inst_field_ty)
          then matchInstEnv dflags clas tys loc
          else do {
 
+         -- Record usage of the selector, as we need it to build an instance
+       ; addUsedGRE True gre
+
+         -- Build evidence term as described in Note [HasField instances]
        ; let mk_ev [ev] = EvCast (EvExpr (mkHsLamConst proxy_ty (mkFunTy r_ty a_ty) body)) (mkTcSymCo ax)
                where
                 proxy_ty = mkProxyPrimTy typeSymbolKind x_ty
                 co = mkTcFunCo Nominal (mkTcReflCo Nominal r_ty) (evTermCoercion ev)
                 body = mkHsWrap (mkWpCastN co <.> mkWpTyApps r_args) (HsVar (noLoc sel_id))
-
              mk_ev _ = panic "matchHasField.mk_ev"
 
        ; return (GenInst { lir_new_theta = [ theta ]
@@ -2173,6 +2229,9 @@ matchHasField dflags clas tys@[x_ty, r_ty, a_ty] loc
        } } }
 matchHasField dflags clas tys loc = matchInstEnv dflags clas tys loc
 
+-- | Make a constant lambda-expression
+--
+-- > \ (_ :: arg_ty) -> (body :: res_ty)
 mkHsLamConst :: Type -> Type -> HsExpr Id -> HsExpr Id
 mkHsLamConst arg_ty res_ty body = HsLam mg
   where



More information about the ghc-commits mailing list