Simon Peyton Jones pushed to branch wip/spj-apporv-Oct24 at Glasgow Haskell Compiler / GHC
Commits:
-
55c4f2b4
by Simon Peyton Jones at 2026-01-27T16:40:20+00:00
3 changed files:
Changes:
| ... | ... | @@ -762,47 +762,73 @@ tcExpr (SectionR {}) ty = pprPanic "tcExpr:SectionR" (ppr ty) |
| 762 | 762 | ************************************************************************
|
| 763 | 763 | -}
|
| 764 | 764 | |
| 765 | -{- Note [Overview of Typechecking an XExpr]
|
|
| 766 | -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 767 | -Certain constructs undergo expansion right before type checking.
|
|
| 765 | +{- Note [Typechecking by expansion: overview]
|
|
| 766 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 767 | +For many constructs, rather than typechecking the user-written code
|
|
| 768 | +directly, it's much easier to
|
|
| 769 | + * Expand (or desugar) the code to something simpler
|
|
| 770 | + * Typecheck that simpler expression
|
|
| 768 | 771 | |
| 769 | - tcExpr ue@(RecordUpd{}) rho = do { ee <- expand e; tcExpr ee rho }
|
|
| 772 | +Example: record updates. The typechecker looks like this:
|
|
| 770 | 773 | |
| 771 | -See Note [Handling overloaded and rebindable constructs] and
|
|
| 772 | -Note [Doing XXExprGhcRn in the Renamer vs Typechecker]
|
|
| 773 | -for details about which constructs are expanded.
|
|
| 774 | + tcExpr e@(RecordUpd{}) rho = do { ee <- expandExpr e
|
|
| 775 | + ; tcExpr ee rho }
|
|
| 774 | 776 | |
| 775 | -The expansion process typically takes a user written thing
|
|
| 776 | - L lspan ue
|
|
| 777 | -and returns
|
|
| 778 | - L lspan (XExpr (ExpandedThingRn { xrn_orig = ue
|
|
| 779 | - , xrn_expanded = ee } ))
|
|
| 777 | +The `expandExpr` replaces the record update (e { x = rhs })
|
|
| 778 | +with something like
|
|
| 779 | + case e of { MkT a b _ d -> MkT a b rhs d }
|
|
| 780 | +and we then typecheck the latter.
|
|
| 780 | 781 | |
| 781 | -where `ee` is the expansion of the user written thing `ue`
|
|
| 782 | +See also Note [Handling overloaded and rebindable constructs]
|
|
| 783 | + and Note [Doing XXExprGhcRn in the Renamer vs Typechecker]
|
|
| 782 | 784 | |
| 783 | -Now, when a `tcMonoLHsExpr :: LHsExpr GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)`
|
|
| 784 | -gets a located expression, It does 2 things:
|
|
| 785 | -1. calls `addLExprCtxt` to perform error context management, and;
|
|
| 786 | -2. calls tcExpr to typecheck the expression.
|
|
| 785 | +The Big Question is how to ensure that error messages mention
|
|
| 786 | +only user-written source code, and never talk about the expanded code.
|
|
| 787 | +The rest of this Note explains how that is done.
|
|
| 787 | 788 | |
| 788 | -The type checker context has 2 key fields:
|
|
| 789 | +* The expansion process typically takes a user written thing
|
|
| 790 | + L lspan ue
|
|
| 791 | + and returns
|
|
| 792 | + L lspan (XExpr (ExpandedThingRn { xrn_orig = ue
|
|
| 793 | + , xrn_expanded = ee } ))
|
|
| 794 | + where `ee` is the expansion of the user written thing `ue`
|
|
| 789 | 795 | |
| 790 | - TcLclCtxt { tcl_loc :: RealSrcSpan
|
|
| 791 | - , tcl_err_ctxt :: [ErrCtxt]
|
|
| 796 | +* The type checker context has 2 key fields that describe the context:
|
|
| 797 | + TcLclCtxt { tcl_loc :: RealSrcSpan
|
|
| 798 | + , tcl_err_ctxt :: [ErrCtxt]
|
|
| 792 | 799 | , ... }
|
| 793 | - |
|
| 794 | -When called on an XExpr, `addLExprCtxt` updates the location of `tcl_loc` with
|
|
| 795 | -the `lspan` above and adds an ErrCtxt on top of the `tcl_err_ctxt`. If the
|
|
| 796 | -`lspan` is generated, then `addLExprCtxt` is a no-op.
|
|
| 797 | - |
|
| 798 | -The type checker error stack element `GHC.Tc.Types.ErrCtxt.ErrCtxt` has two fields
|
|
| 799 | - |
|
| 800 | - ErrCtxt = EC CodeSrcFlag ErrCtxtMsgM
|
|
| 801 | - |
|
| 802 | -`CodeSrcFlag` says whether we are typechecking an expanded thing, and what that expanded thing is
|
|
| 803 | -`ErrCtxtMsgM` stores the pre-text error message itself. When called on an `XExpr`, `addLExprCtxt`,
|
|
| 804 | -adds the user written thing `ue`, and the error message provided by the caller on the `ErrCtxtStack`
|
|
| 805 | -See Note [ErrCtxtStack Manipulation] for more details.
|
|
| 800 | + Note `tcl_loc` always points to a real place in the source code,
|
|
| 801 | + hence `RealSrcSpan`.
|
|
| 802 | + |
|
| 803 | + The `tcl_err_ctxt` is a stack of contexts, each saying something
|
|
| 804 | + like "In the expression: x+y" or "In the record update: r { x=2 }"
|
|
| 805 | + |
|
| 806 | +* Now, when
|
|
| 807 | + tcMonoLHsExpr :: LHsExpr GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)
|
|
| 808 | + gets a located expression, it does 2 things:
|
|
| 809 | + * Calls `addLExprCtxt` to perform error context management
|
|
| 810 | + * Calls `tcExpr` to typecheck the expression.
|
|
| 811 | + |
|
| 812 | +* `addLExprCtxt span expr`
|
|
| 813 | + (1) updates the location of `tcl_loc` with the `span` above,
|
|
| 814 | + (2) adds an `ErrCtxt` on top of the `tcl_err_ctxt`.
|
|
| 815 | + |
|
| 816 | +* However, if the `span` is generated (see `isGeneratedSrcSpan`), then
|
|
| 817 | + `addLExprCtxt` is a no-op. Crucially, when we generate code in `expandExpr`,
|
|
| 818 | + all the generated AST notes are tagged with a `GeneratedSrcSpan`. This
|
|
| 819 | + is how we avoid populating the TcLclCtxt with generated code.
|
|
| 820 | + |
|
| 821 | +* The type checker error-stack element `GHC.Tc.Types.ErrCtxt.ErrCtxt`
|
|
| 822 | + has two fields
|
|
| 823 | + data ErrCtxt = EC ErrCtxt
|
|
| 824 | + |
|
| 825 | + * `CodeSrcFlag` says whether we are typechecking an expanded thing,
|
|
| 826 | + and what that expanded thing is
|
|
| 827 | + * `ErrCtxtMsgM` stores the pre-text error message itself.
|
|
| 828 | + |
|
| 829 | + When called on an `XExpr`, `addLExprCtxt`, adds the user written thing
|
|
| 830 | + `ue`, and the error message provided by the caller on the `ErrCtxtStack` See
|
|
| 831 | + Note [ErrCtxtStack Manipulation] for more details.
|
|
| 806 | 832 | |
| 807 | 833 | -}
|
| 808 | 834 |
| ... | ... | @@ -19,8 +19,7 @@ module GHC.Tc.Gen.Head |
| 19 | 19 | , tyConOf, tyConOfET
|
| 20 | 20 | , nonBidirectionalErr
|
| 21 | 21 | |
| 22 | - , pprArgInst
|
|
| 23 | - , addLExprCtxt, addFunResCtxt ) where
|
|
| 22 | + , pprArgInst, addFunResCtxt ) where
|
|
| 24 | 23 | |
| 25 | 24 | import {-# SOURCE #-} GHC.Tc.Gen.Expr( tcExpr, tcCheckPolyExprNC, tcPolyLExprSig )
|
| 26 | 25 | import {-# SOURCE #-} GHC.Tc.Gen.Splice( getUntypedSpliceBody )
|
| ... | ... | @@ -1076,39 +1075,3 @@ Notice that tcSplitNestedSigmaTys looks through function arrows too, regardless |
| 1076 | 1075 | of simple/deep subsumption. Here we are concerned only whether there is a
|
| 1077 | 1076 | mis-match in the number of value arguments.
|
| 1078 | 1077 | -} |
| 1079 | - |
|
| 1080 | - |
|
| 1081 | -{- *********************************************************************
|
|
| 1082 | -* *
|
|
| 1083 | - Misc utility functions
|
|
| 1084 | -* *
|
|
| 1085 | -********************************************************************* -}
|
|
| 1086 | - |
|
| 1087 | --- | !Caution!: Users should not call add_expr_ctxt, they ought to use addLExprCtxt
|
|
| 1088 | -add_expr_ctxt :: HsExpr GhcRn -> TcRn a -> TcRn a
|
|
| 1089 | -add_expr_ctxt e thing_inside
|
|
| 1090 | - = case e of
|
|
| 1091 | - HsHole{} -> thing_inside
|
|
| 1092 | - -- The HsHole special case addresses situations like
|
|
| 1093 | - -- f x = _
|
|
| 1094 | - -- when we don't want to say "In the expression: _",
|
|
| 1095 | - -- because it is mentioned in the error message itself
|
|
| 1096 | - |
|
| 1097 | - ExprWithTySig _ (L _ e') _
|
|
| 1098 | - | XExpr (ExpandedThingRn o _) <- e' -> addExpansionErrCtxt o (ExprCtxt e) thing_inside
|
|
| 1099 | - -- There is a special case for expressions with signatures to avoid having too verbose
|
|
| 1100 | - -- error context. So here we flip the ErrCtxt state to expanded if the expression is expanded.
|
|
| 1101 | - -- c.f. RecordDotSyntaxFail9
|
|
| 1102 | - |
|
| 1103 | - XExpr (ExpandedThingRn o _) -> addExpansionErrCtxt o (srcCodeOriginErrCtxMsg o) thing_inside
|
|
| 1104 | - -- Flip error ctxt into expansion mode
|
|
| 1105 | - |
|
| 1106 | - _ -> addErrCtxt (ExprCtxt e) thing_inside
|
|
| 1107 | - |
|
| 1108 | - |
|
| 1109 | -addLExprCtxt :: SrcSpan -> HsExpr GhcRn -> TcRn a -> TcRn a
|
|
| 1110 | -addLExprCtxt lspan e thing_inside
|
|
| 1111 | - | not (isGeneratedSrcSpan lspan)
|
|
| 1112 | - = setSrcSpan lspan $ add_expr_ctxt e thing_inside
|
|
| 1113 | - | otherwise -- no op in generated code
|
|
| 1114 | - = thing_inside |
| ... | ... | @@ -88,7 +88,7 @@ module GHC.Tc.Utils.Monad( |
| 88 | 88 | |
| 89 | 89 | -- * Context management for the type checker
|
| 90 | 90 | getErrCtxt, setErrCtxt, addErrCtxt, addErrCtxtM, addLandmarkErrCtxt,
|
| 91 | - addExpansionErrCtxt, addExpansionErrCtxtM,
|
|
| 91 | + addLExprCtxt, addExpansionErrCtxt, addExpansionErrCtxtM,
|
|
| 92 | 92 | addLandmarkErrCtxtM, popErrCtxt, getCtLocM, setCtLocM, mkCtLocEnv,
|
| 93 | 93 | |
| 94 | 94 | -- * Diagnostic message generation (type checker)
|
| ... | ... | @@ -1324,6 +1324,35 @@ relation with pattern-match checks |
| 1324 | 1324 | - See Note [ErrCtxtStack Manipulation] in `GHC.Tc.Types.LclEnv` for info about `ErrCtxtStack`
|
| 1325 | 1325 | -}
|
| 1326 | 1326 | |
| 1327 | +addLExprCtxt :: SrcSpan -> HsExpr GhcRn -> TcRn a -> TcRn a
|
|
| 1328 | +addLExprCtxt lspan e thing_inside
|
|
| 1329 | + | not (isGeneratedSrcSpan lspan)
|
|
| 1330 | + = setSrcSpan lspan $ add_expr_ctxt e thing_inside
|
|
| 1331 | + | otherwise -- no op in generated code
|
|
| 1332 | + = thing_inside
|
|
| 1333 | + |
|
| 1334 | +-- | !Caution!: Users should not call add_expr_ctxt, they ought to use addLExprCtxt
|
|
| 1335 | +add_expr_ctxt :: HsExpr GhcRn -> TcRn a -> TcRn a
|
|
| 1336 | +add_expr_ctxt e thing_inside
|
|
| 1337 | + = case e of
|
|
| 1338 | + HsHole{} -> thing_inside
|
|
| 1339 | + -- The HsHole special case addresses situations like
|
|
| 1340 | + -- f x = _
|
|
| 1341 | + -- when we don't want to say "In the expression: _",
|
|
| 1342 | + -- because it is mentioned in the error message itself
|
|
| 1343 | + |
|
| 1344 | + ExprWithTySig _ (L _ e') _
|
|
| 1345 | + | XExpr (ExpandedThingRn o _) <- e' -> addExpansionErrCtxt o (ExprCtxt e) thing_inside
|
|
| 1346 | + -- There is a special case for expressions with signatures to avoid having too verbose
|
|
| 1347 | + -- error context. So here we flip the ErrCtxt state to expanded if the expression is expanded.
|
|
| 1348 | + -- c.f. RecordDotSyntaxFail9
|
|
| 1349 | + |
|
| 1350 | + XExpr (ExpandedThingRn o _) -> addExpansionErrCtxt o (srcCodeOriginErrCtxMsg o) thing_inside
|
|
| 1351 | + -- Flip error ctxt into expansion mode
|
|
| 1352 | + |
|
| 1353 | + _ -> addErrCtxt (ExprCtxt e) thing_inside
|
|
| 1354 | + |
|
| 1355 | + |
|
| 1327 | 1356 | getErrCtxt :: TcM [ErrCtxt]
|
| 1328 | 1357 | getErrCtxt = do { env <- getLclEnv; return (getLclEnvErrCtxt env) }
|
| 1329 | 1358 | |
| ... | ... | @@ -1335,7 +1364,7 @@ setErrCtxt ctxt = updLclEnv (setLclEnvErrCtxt ctxt) |
| 1335 | 1364 | -- do any tidying.
|
| 1336 | 1365 | -- See Note [Rebindable syntax and XXExprGhcRn] in GHC.Hs.Expr
|
| 1337 | 1366 | addErrCtxt :: ErrCtxtMsg -> TcM a -> TcM a
|
| 1338 | -{-# INLINE addErrCtxt #-} -- Note [Inlining addErrCtxt]
|
|
| 1367 | +o{-# INLINE addErrCtxt #-} -- Note [Inlining addErrCtxt]
|
|
| 1339 | 1368 | addErrCtxt msg = addErrCtxtM (\env -> return (env, msg))
|
| 1340 | 1369 | |
| 1341 | 1370 | -- See Note [ErrCtxtStack Manipulation]
|