[Git][ghc/ghc][wip/spj-apporv-Oct24] add Note to explain the tcExprSigma, QuickLook and Deepsubsumption flag impl
Apoorv Ingle pushed to branch wip/spj-apporv-Oct24 at Glasgow Haskell Compiler / GHC Commits: abc37a34 by Apoorv Ingle at 2026-03-30T21:29:37-05:00 add Note to explain the tcExprSigma, QuickLook and Deepsubsumption flag impl - - - - - 6 changed files: - compiler/GHC/Tc/Gen/App.hs - compiler/GHC/Tc/Gen/App.hs-boot - compiler/GHC/Tc/Gen/Do.hs - compiler/GHC/Tc/Gen/Head.hs - compiler/GHC/Tc/Types/LclEnv.hs - compiler/GHC/Tc/Utils/Unify.hs Changes: ===================================== compiler/GHC/Tc/Gen/App.hs ===================================== @@ -171,14 +171,90 @@ Note [Instantiation variables are short lived] * * ********************************************************************* -} +{- Note [splitHsApps, XExpr and tcExprSigma] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This implementation is WIP and is subject to change once MR!15811 is figured out + +To simplify the implementation of `splitHsApps`, we do not look through +XExpr's, however, we still need to deal with cases such as: + + (XExpr (ExpandedThingRn f1 (f `HsApp` e1))) `HsApp` e2 `HsApp` e3 + +Otherwise stuff like overloaded labels (#19154) won't work. + +How do we do it? + +`splitHsApps` peals off the arguments until it hits an XExpr +From above example, + + splitHsApps ((XExpr (ExpandedThingRn f1 (f `HsApp` e1))) `HsApp` e2 `HsApp` e3) + = { head = XExpr (ExpandedThingRn f1 (f `HsApp` e1)) + , args = [e3, e2] -- NB: arguments are in the reverse order + } + +Now, we infer the type of head using tcInferAppHead/tcInferAppHead_maybe. +Which calls `tcExprSigma` on the XExpr. It performs a mini-`tcApp` +where it uses the CtOrigin obtained from f1, splits the application chain: f `HsApp` e1. +and finally returns an uninstantiated sigma type and a typechecked expression + +It is crucial for tcExprSigma to return an uninstantiated type so that visible type +applications with rebindable syntax works fine. Eg. T19167 + + + fromListN :: Int -> [elt] -> (forall list. (IsList list, elt ~ Item list) => list) + fromListN n l = Predule.fromListN n l + + shouldBeANonEmpty = ['x', 'y', 'z'] @(NonEmpty Char) + +here the RHS of `shouldBeNonEmpty` is expanded to + (XExpr (ExpandedThingRn (['x', 'y', 'z']) (fromListN 3 ['x', 'y', 'z']) `HsTypeApp` (NonEmpty Char) + +Now, if we were to instantiate the head of the expression, we will +fail to typecheck the expression as the type `NonEmpty Char`. + + +Wrinkle [DeepSubsumption Flag and Multiplicity] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Consider the expression: + + (1 :) $ [2, 3] + +Here, the head of the expression is ($) and it is applied to two arguments + Arg1 = (1 :) + Arg2 = [2, 3] + +Arg1 is an expanded expression as it is a left section wrapped with parenthesis + + Arg1 = HsPar (XExpr (ExpandedThingRn (HSE (1 :) ((:) 1)))) + +As `($)` is the head of the application chain, we perform QuickLook on the arguments +in `tcInstFun`. See Note [Quick Look for particular Ids] and Note [tcApp: typechecking applications]. + +Thus, Arg1 will be transformed into + + EValArgQL { eaql_tc_head = XExpr (ExpandedThingRn (HSE { hs_ctxt = 1 : , hs_expr = ((:) 1)))) + , eaql_arg_ty = [alpha] %1 -> [beta] + , eqql_args = [EPar] } + +Now, in `tcValArg` Arg1 is expected to have type [Int] -> [Int], +and according to Note [Typechecking data constructors], we ought to be able to +coerce the type [alpha] %1 -> [beta] into type [Int] -> [Int] in `checkResultTy` +because the "actual" head of Arg1 is (:), thus we need to traverse eaql_tc_head. + +The hope is that future refactoring simplifies this delicate and complicated process. + +-} -- Very similar to tcApp, but returns a sigma (uninstantiated) type -- CAUTION: Any changes to tcApp should be reflected here -- cf. T19167. the head is an expanded expression applied to a type -- Caution: Currently we assume that the expression is compiler generated/expanded -- Because that is what T19167 test case expects. -- This function should go away after MR!15778 lands -tcExprSigma :: Bool -> CtOrigin -> HsExpr GhcRn -> TcM (HsExpr GhcTc, TcSigmaType) -tcExprSigma inst fun_orig rn_expr +-- See Note [splitHsApps, XExpr and tcExprSigma] +tcExprSigma :: CtOrigin -> HsExpr GhcRn -> TcM (HsExpr GhcTc, TcSigmaType) +tcExprSigma fun_orig rn_expr = do { (fun@(rn_fun,fun_lspan), rn_args) <- splitHsApps rn_expr ; do_ql <- wantQuickLook rn_fun ; (tc_fun, fun_sigma) <- tcInferAppHead fun @@ -186,7 +262,7 @@ tcExprSigma inst fun_orig rn_expr ; traceTc "tcExprSigma" (vcat [ text "rn_expr:" <+> ppr rn_expr , text "tc_fun" <+> ppr tc_fun , text "inGeneratedCode:" <+> ppr inGenCode]) - ; (inst_args, app_res_sigma) <- tcInstFun do_ql inst (fun_orig, rn_fun, fun_lspan) + ; (inst_args, app_res_sigma) <- tcInstFun do_ql False (fun_orig, rn_fun, fun_lspan) tc_fun fun_sigma rn_args ; tc_args <- tcValArgs do_ql (rn_fun, fun_lspan) inst_args ; let tc_expr = rebuildHsApps (tc_fun, fun_lspan) tc_args ===================================== compiler/GHC/Tc/Gen/App.hs-boot ===================================== @@ -7,6 +7,4 @@ import GHC.Tc.Utils.TcType ( TcSigmaType ) import GHC.Hs.Extension ( GhcRn, GhcTc ) -import GHC.Prelude (Bool) - -tcExprSigma :: Bool -> CtOrigin -> HsExpr GhcRn -> TcM (HsExpr GhcTc, TcSigmaType) +tcExprSigma :: CtOrigin -> HsExpr GhcRn -> TcM (HsExpr GhcTc, TcSigmaType) ===================================== compiler/GHC/Tc/Gen/Do.hs ===================================== @@ -383,7 +383,7 @@ The `fail`-block wrapping is done by `GHC.Tc.Gen.Do.mk_failable_expr`. * _Wrinkle 2_: The call to `fail` will give rise to a `MonadFail` constraint. What `CtOrigin` do we attach to that constraint? When the `MonadFail` constraint can't be solved, it'll show up in error messages and it needs to be a good location. Ideally, it should identify the - pattern `p`. Hence, we wrap the `fail` alternative expression with a `ExpandedPat` + pattern `p`. Hence, we wrap the `fail` alternative expression with a `ExpandedPatRn` that tags the fail expression with the failable pattern. (See testcase MonadFailErrors.hs) Part 2. Generate warnings for discarded body statement results @@ -412,7 +412,7 @@ Part 3. Blaming Offending Source Code and Generating Appropriate Error Messages To ensure we correctly track source of the offending user written source code, in this case the `do`-statement, we need to keep track of which source statement's expansion the typechecker is currently typechecking. -For this purpose we use the `XXExprGhcRn.ExpansionRn`. +For this purpose we use the `XXExprGhcRn.ExpandedThingRn`. It stores the original statement (with location) and the expanded expression A. Expanding Body Statements @@ -453,7 +453,7 @@ It stores the original statement (with location) and the expanded expression This popping is implicitly done when we push the error context message for the next statment. See `LclEnv.setLclCtxtHsCtxt` - Sans the popping business for error context stack, + Sans the implicit overwriting business for error context stack, if there were to be a type error in `e2`, we would get a spurious and confusing error message which mentions "In the stmt of a do block e1" along with the message "In the stmt of a do block e2". ===================================== compiler/GHC/Tc/Gen/Head.hs ===================================== @@ -144,7 +144,7 @@ takes apart either an HsApp, or an infix OpApp, returning innermost un-expanded head as the "error head". * A list of HsExprArg, the arguments -* We do not look through expanded expressions (except PopErrCtxt.) +* We do not look through expanded expressions (`XExpr`s) -} data TcPass = TcpRn -- Arguments decomposed @@ -349,29 +349,6 @@ That makes it possible to typecheck something like where f :: forall a. t1 -> forall b. t2 -> t3 -Note [Looking through ExpandedThingRn] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When creating an application chain in splitHsApps, we must deal with - ExpandedThingRn f1 (f `HsApp` e1) `HsApp` e2 `HsApp` e3 - -as a single application chain `f e1 e2 e3`. Otherwise stuff like overloaded -labels (#19154) won't work. - -It's easy to achieve this: `splitHsApps` unwraps `ExpandedThingRn`. - -In order to be able to more accurately reconstruct the original `SrcSpan`s -from the renamer in `rebuildHsApps`, we also have to track the `SrcSpan` -of the current application in `VAExpansion` when unwrapping `ExpandedThingRn` -in `splitHsApps`, just as we track it in a non-expanded expression. - -Previously, `rebuildHsApps` substituted the location of the original -expression as given by `splitHsApps` for this. As a result, the application -head in expanded expressions, e.g. the call to `fromListN`, would either -have `noSrcSpan` set as its location post-typecheck, or get the location -of the original expression, depending on whether the `XExpr` given to -`splitHsApps` is in the outermost layer. The span it got in the renamer -would always be discarded, causing #23120. - Note [Looking through Template Haskell splices in splitHsApps] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When typechecking an application, we must look through untyped TH splices in @@ -467,7 +444,7 @@ tcInferAppHead_maybe fun = case fun of -> Just <$> tcInferRecSelId f XExpr (ExpandedThingRn (HSE o (L loc e))) -> setSrcSpan (locA loc) $ Just <$> - do { (e', ty) <- tcExprSigma False (hsCtxtCtOrigin o) e + do { (e', ty) <- tcExprSigma (hsCtxtCtOrigin o) e ; return (mkExpandedTc o (L loc e'), ty) } -- We do not want to instantiate the type of the head as there may be -- visible type applications in the argument. ===================================== compiler/GHC/Tc/Types/LclEnv.hs ===================================== @@ -173,6 +173,7 @@ setLclEnvHsCtxt ec = modifyLclCtxt (setLclCtxtHsCtxt ec) setLclCtxtHsCtxt :: HsCtxt -> TcLclCtxt -> TcLclCtxt setLclCtxtHsCtxt ec lclCtxt -- Never stack 2 do statement error messages on top of each other + -- See Part 3 A of Note [Expanding HsDo with XXExprGhcRn] in `GHC.Tc.Gen.Do` | StmtErrCtxt{} : ecs <- tcl_err_ctxt lclCtxt , StmtErrCtxt{} <- ec = lclCtxt { tcl_err_ctxt = ec : ecs } ===================================== compiler/GHC/Tc/Utils/Unify.hs ===================================== @@ -2078,6 +2078,9 @@ getDeepSubsumptionFlag_DataConHead app_head = -> go app_head } where + -- Why do we look through ExpandedThingTc and HsApps? + -- See Wrinkle [DeepSubsumption Flag and Multiplicity] in + -- Note [splitHsApps, XExpr and tcExprSigma] go :: HsExpr GhcTc -> DeepSubsumptionFlag go (XExpr (ConLikeTc (RealDataCon {}))) = Deep TopSub go (XExpr (ExpandedThingTc (HSE _ (L _ f)))) = go f View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/abc37a34a51014ff68b06c3986a67114... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/abc37a34a51014ff68b06c3986a67114... You're receiving this email because of your account on gitlab.haskell.org.
participants (1)
-
Apoorv Ingle (@ani)