[Git][ghc/ghc][wip/aidylns/ttg-remove-hsunboundvar-via-hshole] HsHole constructor for unbound variables, parse errors, holes
Adriaan Leijnse (@aidylns)
gitlab at gitlab.haskell.org
Tue Jan 14 12:12:35 UTC 2025
Adriaan Leijnse pushed to branch wip/aidylns/ttg-remove-hsunboundvar-via-hshole at Glasgow Haskell Compiler / GHC
Commits:
7b0d3835 by Adriaan Leijnse at 2025-01-14T12:12:10+00:00
HsHole constructor for unbound variables, parse errors, holes
- - - - -
25 changed files:
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Hs/Syn/Type.hs
- compiler/GHC/HsToCore/Expr.hs
- compiler/GHC/HsToCore/Quote.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Rename/Expr.hs
- compiler/GHC/Rename/HsType.hs
- compiler/GHC/Rename/Module.hs
- compiler/GHC/Tc/Errors.hs
- compiler/GHC/Tc/Gen/App.hs
- compiler/GHC/Tc/Gen/Expr.hs
- compiler/GHC/Tc/Gen/Head.hs
- compiler/GHC/Tc/Types/Constraint.hs
- compiler/GHC/Tc/Types/Evidence.hs
- compiler/GHC/Tc/Types/Origin.hs
- compiler/GHC/Tc/Zonk/Type.hs
- compiler/Language/Haskell/Syntax/Expr.hs
- compiler/Language/Haskell/Syntax/Extension.hs
- testsuite/tests/perf/compiler/hard_hole_fits.hs
- testsuite/tests/perf/compiler/hard_hole_fits.stderr
- testsuite/tests/plugins/T20803-plugin/FixErrorsPlugin.hs
- utils/check-exact/ExactPrint.hs
Changes:
=====================================
compiler/GHC/Hs/Expr.hs
=====================================
@@ -11,6 +11,7 @@
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilyDependencies #-}
+{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE UndecidableInstances #-} -- Wrinkle in Note [Trees That Grow]
-- in module Language.Haskell.Syntax.Extension
@@ -220,9 +221,9 @@ data EpAnnLam = EpAnnLam
instance NoAnn EpAnnLam where
noAnn = EpAnnLam noAnn noAnn
-data EpAnnUnboundVar = EpAnnUnboundVar
- { hsUnboundBackquotes :: (EpToken "`", EpToken "`")
- , hsUnboundHole :: EpToken "_"
+data EpAnnHole = EpAnnHole
+ { hsHoleBackquotes :: (EpToken "`", EpToken "`")
+ , hsHoleHole :: EpToken "_"
} deriving Data
-- Record selectors at parse time are HsVar; they convert to HsRecSel
@@ -241,14 +242,6 @@ type instance XOverLabel GhcTc = DataConCantHappen
type instance XVar (GhcPass _) = NoExtField
-type instance XUnboundVar GhcPs = Maybe EpAnnUnboundVar
-type instance XUnboundVar GhcRn = NoExtField
-type instance XUnboundVar GhcTc = HoleExprRef
- -- We really don't need the whole HoleExprRef; just the IORef EvTerm
- -- would be enough. But then deriving a Data instance becomes impossible.
- -- Much, much easier just to define HoleExprRef with a Data instance and
- -- store the whole structure.
-
type instance XIPVar GhcPs = NoExtField
type instance XIPVar GhcRn = NoExtField
type instance XIPVar GhcTc = DataConCantHappen
@@ -394,6 +387,63 @@ type instance XEmbTy GhcTc = DataConCantHappen
-- A free-standing HsEmbTy is an error.
-- Valid usages are immediately desugared into Type.
+
+-- | Expression Hole.
+--
+-- Parser: produced on encountering an anonymous
+-- expression hole ("_"), or a parse error.
+--
+-- Renamer: produced from 'HsVar' when encountering out-of-scope variables
+-- (which can also be named expression holes, i.e. "_hole"). The 'ParseError'
+-- case is unhandled in this and the type checking phase.
+--
+-- Type checker: the HoleExprRef is where the erroring expression will be written after
+-- solving. See Note [Holes in expressions] in GHC.Tc.Types.Constraint.
+--
+-- We really don't need the whole HoleExprRef; just the IORef EvTerm would
+-- be enough. But then deriving a Data instance becomes impossible. Much,
+-- much easier just to define HoleExprRef with a Data instance and store
+-- the whole structure.
+type instance XHole (GhcPass p) =
+ (HoleKind (XHoleVar (GhcPass p)) (XHoleParseError (GhcPass p)), XHoleShared (GhcPass p))
+
+data HoleKind unboundVarInfo parseErrorInfo
+ = HoleVar unboundVarInfo
+ | HoleParseError parseErrorInfo
+ deriving Data
+
+-- | HsHole extensions for (named) holes and unbound variables.
+type family XHoleVar x
+type instance XHoleVar GhcPs = Maybe EpAnnHole
+type instance XHoleVar GhcRn = RdrName
+type instance XHoleVar GhcTc = RdrName
+
+-- | HsHole extension for parse errors. Unused for now except to encode that
+-- these cannot occur after the renamer.
+type family XHoleParseError p
+type instance XHoleParseError GhcPs = NoExtField
+type instance XHoleParseError GhcRn = DataConCantHappen
+type instance XHoleParseError GhcTc = DataConCantHappen
+
+-- | HsHole extension shared between all types of holes.
+type family XHoleShared p
+type instance XHoleShared GhcPs = NoExtField
+type instance XHoleShared GhcRn = NoExtField
+type instance XHoleShared GhcTc = HoleExprRef
+
+-- | The RdrName for an unnamed hole ("_").
+unnamedHoleRdrName :: RdrName
+unnamedHoleRdrName = mkUnqual varName (fsLit "_")
+
+-- | The RdrName for an unnamed ("_") hole or named hole/unbound variable
+-- ("_hole").
+holeVarRdrName :: forall p. IsPass p => XHoleVar (GhcPass p) -> RdrName
+holeVarRdrName hv = case (ghcPass @p, hv) of
+ (GhcPs, _) -> unnamedHoleRdrName
+ (GhcRn, r) -> r
+ (GhcTc, r) -> r
+
+
type instance XForAll GhcPs = NoExtField
type instance XForAll GhcRn = NoExtField
type instance XForAll GhcTc = DataConCantHappen
@@ -698,7 +748,11 @@ ppr_lexpr e = ppr_expr (unLoc e)
ppr_expr :: forall p. (OutputableBndrId p)
=> HsExpr (GhcPass p) -> SDoc
ppr_expr (HsVar _ (L _ v)) = pprPrefixOcc v
-ppr_expr (HsUnboundVar _ uv) = pprPrefixOcc uv
+ppr_expr (HsHole (HoleVar v, _)) = pprPrefixOcc (holeVarRdrName @p v)
+ppr_expr (HsHole (HoleParseError x, _)) = case ghcPass @p of
+ GhcPs -> pprPrefixOcc unnamedHoleRdrName
+ GhcRn -> dataConCantHappen x
+ GhcTc -> dataConCantHappen x
ppr_expr (HsIPVar _ v) = ppr v
ppr_expr (HsOverLabel s l) = case ghcPass @p of
GhcPs -> helper s
@@ -954,10 +1008,13 @@ instance Outputable XXExprGhcTc where
ppr exp, text ")"]
ppr (HsRecSelTc f) = pprPrefixOcc f
-
ppr_infix_expr :: forall p. (OutputableBndrId p) => HsExpr (GhcPass p) -> Maybe SDoc
ppr_infix_expr (HsVar _ (L _ v)) = Just (pprInfixOcc v)
-ppr_infix_expr (HsUnboundVar _ occ) = Just (pprInfixOcc occ)
+ppr_infix_expr (HsHole (HoleVar hv, _)) = Just (pprInfixOcc (holeVarRdrName @p hv))
+ppr_infix_expr (HsHole (HoleParseError x, _)) = case ghcPass @p of
+ GhcPs -> Just (pprInfixOcc unnamedHoleRdrName) -- TODO: Why not print the actual source text in case of a parse error?
+ GhcRn -> dataConCantHappen x
+ GhcTc -> dataConCantHappen x
ppr_infix_expr (XExpr x) = case ghcPass @p of
GhcRn -> ppr_infix_expr_rn x
GhcTc -> ppr_infix_expr_tc x
@@ -976,7 +1033,6 @@ ppr_infix_expr_tc (HsTick {}) = Nothing
ppr_infix_expr_tc (HsBinTick {}) = Nothing
ppr_infix_expr_tc (HsRecSelTc f) = Just (pprInfixOcc f)
-
ppr_infix_hs_expansion :: HsThingRn -> Maybe SDoc
ppr_infix_hs_expansion (OrigExpr e) = ppr_infix_expr e
ppr_infix_hs_expansion _ = Nothing
@@ -1023,7 +1079,6 @@ hsExprNeedsParens prec = go
where
go :: HsExpr (GhcPass p) -> Bool
go (HsVar{}) = False
- go (HsUnboundVar{}) = False
go (HsIPVar{}) = False
go (HsOverLabel{}) = False
go (HsLit _ l) = hsLitNeedsParens prec l
@@ -1065,6 +1120,7 @@ hsExprNeedsParens prec = go
go (HsProjection{}) = True
go (HsGetField{}) = False
go (HsEmbTy{}) = prec > topPrec
+ go (HsHole{}) = False
go (HsForAll{}) = prec >= funPrec
go (HsQual{}) = prec >= funPrec
go (HsFunArr{}) = prec >= funPrec
@@ -1120,7 +1176,7 @@ isAtomicHsExpr (HsLit {}) = True
isAtomicHsExpr (HsOverLit {}) = True
isAtomicHsExpr (HsIPVar {}) = True
isAtomicHsExpr (HsOverLabel {}) = True
-isAtomicHsExpr (HsUnboundVar {}) = True
+isAtomicHsExpr (HsHole{}) = True
isAtomicHsExpr (XExpr x)
| GhcTc <- ghcPass @p = go_x_tc x
| GhcRn <- ghcPass @p = go_x_rn x
=====================================
compiler/GHC/Hs/Syn/Type.hs
=====================================
@@ -103,7 +103,6 @@ lhsExprType (L _ e) = hsExprType e
-- | Compute the 'Type' of an @'HsExpr' 'GhcTc'@ in a pure fashion.
hsExprType :: HsExpr GhcTc -> Type
hsExprType (HsVar _ (L _ id)) = idType id
-hsExprType (HsUnboundVar (HER _ ty _) _) = ty
hsExprType (HsOverLabel v _) = dataConCantHappen v
hsExprType (HsIPVar v _) = dataConCantHappen v
hsExprType (HsOverLit _ lit) = overLitType lit
@@ -146,6 +145,8 @@ hsExprType (HsProc _ _ lcmd_top) = lhsCmdTopType lcmd_top
hsExprType (HsStatic (_, ty) _s) = ty
hsExprType (HsPragE _ _ e) = lhsExprType e
hsExprType (HsEmbTy x _) = dataConCantHappen x
+hsExprType (HsHole (HoleVar _, HER _ ty _)) = ty
+hsExprType (HsHole (HoleParseError x, _)) = dataConCantHappen x
hsExprType (HsQual x _ _) = dataConCantHappen x
hsExprType (HsForAll x _ _) = dataConCantHappen x
hsExprType (HsFunArr x _ _ _) = dataConCantHappen x
=====================================
compiler/GHC/HsToCore/Expr.hs
=====================================
@@ -273,8 +273,9 @@ dsExpr e@(HsVar {}) = dsApp e
dsExpr e@(HsApp {}) = dsApp e
dsExpr e@(HsAppType {}) = dsApp e
-dsExpr (HsUnboundVar (HER ref _ _) _) = dsEvTerm =<< readMutVar ref
- -- See Note [Holes] in GHC.Tc.Types.Constraint
+dsExpr (HsHole (HoleVar _, HER ref _ _)) = dsEvTerm =<< readMutVar ref
+ -- See Note [Holes in expressions] in GHC.Tc.Types.Constraint.
+dsExpr (HsHole (HoleParseError x, _)) = dataConCantHappen x
dsExpr (HsPar _ e) = dsLExpr e
dsExpr (ExprWithTySig _ e _) = dsLExpr e
@@ -316,6 +317,7 @@ dsExpr e@(XExpr ext_expr_tc)
}
+
-- Strip ticks due to #21701, need to be invariant about warnings we produce whether
-- this is enabled or not.
dsExpr (NegApp _ (L loc
=====================================
compiler/GHC/HsToCore/Quote.hs
=====================================
@@ -1541,6 +1541,10 @@ repE (HsVar _ (L _ x)) =
Just (DsBound y) -> repVarOrCon x (coreVar y)
Just (DsSplice e) -> do { e' <- lift $ dsExpr e
; return (MkC e') } }
+repE (HsHole (HoleVar uv, NoExtField)) = do
+ name <- repRdrName uv
+ repUnboundVar name
+repE (HsHole (HoleParseError x, NoExtField)) = dataConCantHappen x
repE (HsIPVar _ n) = rep_implicit_param_name n >>= repImplicitParamVar
repE (HsOverLabel _ s) = repOverLabel s
@@ -1679,9 +1683,6 @@ repE (HsTypedSplice n _) = rep_splice n
repE (HsUntypedSplice (HsUntypedSpliceNested n) _) = rep_splice n
repE e@(HsUntypedSplice (HsUntypedSpliceTop _ _) _) = pprPanic "repE: top level splice" (ppr e)
repE (HsStatic _ e) = repLE e >>= rep2 staticEName . (:[]) . unC
-repE (HsUnboundVar _ uv) = do
- name <- repRdrName uv
- repUnboundVar name
repE (HsGetField _ e (L _ (DotFieldOcc _ (L _ (FieldLabelString f))))) = do
e1 <- repLE e
repGetField e1 f
@@ -1716,10 +1717,8 @@ repE e@(XExpr (ExpandedThingRn o x))
else repE e }
| otherwise
= notHandled (ThExpressionForm e)
-
repE (XExpr (PopErrCtxt (L _ e))) = repE e
repE (XExpr (HsRecSelRn (FieldOcc _ (L _ x)))) = repE (HsVar noExtField (noLocA x))
-
repE e@(HsPragE _ (HsPragSCC {}) _) = notHandled (ThCostCentres e)
repE e@(HsTypedBracket{}) = notHandled (ThExpressionForm e)
repE e@(HsUntypedBracket{}) = notHandled (ThExpressionForm e)
=====================================
compiler/GHC/HsToCore/Ticks.hs
=====================================
@@ -470,13 +470,12 @@ addBinTickLHsExpr boxLabel e@(L pos e0)
addTickHsExpr :: HsExpr GhcTc -> TM (HsExpr GhcTc)
addTickHsExpr e@(HsVar _ (L _ id)) = do freeVar id; return e
-addTickHsExpr e@(HsUnboundVar {}) = return e
-
addTickHsExpr e@(HsIPVar {}) = return e
addTickHsExpr e@(HsOverLit {}) = return e
addTickHsExpr e@(HsOverLabel{}) = return e
addTickHsExpr e@(HsLit {}) = return e
addTickHsExpr e@(HsEmbTy {}) = return e
+addTickHsExpr e@(HsHole {}) = return e
addTickHsExpr e@(HsQual {}) = return e
addTickHsExpr e@(HsForAll {}) = return e
addTickHsExpr e@(HsFunArr {}) = return e
=====================================
compiler/GHC/Iface/Ext/Ast.hs
=====================================
@@ -1203,7 +1203,6 @@ instance HiePass p => ToHie (LocatedA (HsExpr (GhcPass p))) where
[ toHie $ C Use (L mspan var)
-- Patch up var location since typechecker removes it
]
- HsUnboundVar _ _ -> [] -- there is an unbound name here, but that causes trouble
HsOverLabel {} -> []
HsIPVar _ _ -> []
HsOverLit _ o ->
@@ -1351,6 +1350,7 @@ instance HiePass p => ToHie (LocatedA (HsExpr (GhcPass p))) where
]
HsGetField {} -> []
HsProjection {} -> []
+ HsHole _ -> [] -- there is a hole here, but that causes trouble
XExpr x -> case hiePass @p of
HieTc -> case x of
WrapExpr w a
=====================================
compiler/GHC/Parser.y
=====================================
@@ -3926,7 +3926,7 @@ qopm :: { forall b. DisambInfixOp b => PV (LocatedN b) } -- used in section
| hole_op { mkHsInfixHolePV $1 }
hole_op :: { LocatedN (HsExpr GhcPs) } -- used in sections
-hole_op : '`' '_' '`' { sLLa $1 $> (hsHoleExpr (Just $ EpAnnUnboundVar (epTok $1, epTok $3) (epTok $2))) }
+hole_op : '`' '_' '`' { sLLa $1 $> (HsHole (HoleVar (Just $ EpAnnHole (epTok $1, epTok $3) (epTok $2)), NoExtField)) }
qvarop :: { LocatedN RdrName }
: qvarsym { $1 }
=====================================
compiler/GHC/Parser/PostProcess.hs
=====================================
@@ -108,7 +108,6 @@ module GHC.Parser.PostProcess (
withArrowParsingMode, withArrowParsingMode',
setTelescopeBndrsNameSpace,
PatBuilder,
- hsHoleExpr,
-- Type/datacon ambiguity resolution
DisambTD(..),
@@ -1897,11 +1896,11 @@ instance DisambECP (HsExpr GhcPs) where
type Body (HsExpr GhcPs) = HsExpr
ecpFromCmd' (L l c) = do
addError $ mkPlainErrorMsgEnvelope (locA l) $ PsErrArrowCmdInExpr c
- return (L l (hsHoleExpr noAnn))
+ return (L l parseError)
ecpFromExp' = return
ecpFromPat' p@(L l _) = do
addError $ mkPlainErrorMsgEnvelope (locA l) $ PsErrOrPatInExpr p
- return (L l (hsHoleExpr noAnn))
+ return (L l parseError)
mkHsProjUpdatePV l fields arg isPun anns = do
!cs <- getCommentsFor l
return $ mkRdrProjUpdate (EpAnn (spanAsAnchor l) noAnn cs) fields arg isPun anns
@@ -1950,7 +1949,7 @@ instance DisambECP (HsExpr GhcPs) where
mkHsOverLitPV (L (EpAnn l an csIn) a) = do
!cs <- getCommentsFor (locA l)
return $ L (EpAnn l an (cs Semi.<> csIn)) (HsOverLit NoExtField a)
- mkHsWildCardPV l = return $ L (noAnnSrcSpan l) (hsHoleExpr noAnn)
+ mkHsWildCardPV l = return $ L (noAnnSrcSpan l) (HsHole (HoleVar noAnn, NoExtField))
mkHsTySigPV l@(EpAnn anc an csIn) a sig anns = do
!cs <- getCommentsFor (locA l)
return $ L (EpAnn anc an (csIn Semi.<> cs)) (ExprWithTySig anns a (hsTypeToHsSigWcType sig))
@@ -1971,11 +1970,11 @@ instance DisambECP (HsExpr GhcPs) where
!cs <- getCommentsFor l
return $ L (EpAnn (spanAsAnchor l) noAnn cs) (SectionR noExtField op e)
mkHsAsPatPV l v _ e = addError (mkPlainErrorMsgEnvelope l $ PsErrTypeAppWithoutSpace (unLoc v) e)
- >> return (L (noAnnSrcSpan l) (hsHoleExpr noAnn))
+ >> return (L (noAnnSrcSpan l) parseError)
mkHsLazyPatPV l e _ = addError (mkPlainErrorMsgEnvelope l $ PsErrLazyPatWithoutSpace e)
- >> return (L (noAnnSrcSpan l) (hsHoleExpr noAnn))
+ >> return (L (noAnnSrcSpan l) parseError)
mkHsBangPatPV l e _ = addError (mkPlainErrorMsgEnvelope l $ PsErrBangPatWithoutSpace e)
- >> return (L (noAnnSrcSpan l) (hsHoleExpr noAnn))
+ >> return (L (noAnnSrcSpan l) parseError)
mkSumOrTuplePV = mkSumOrTupleExpr
mkHsEmbTyPV l toktype ty =
return $ L (noAnnSrcSpan l) $
@@ -2002,9 +2001,6 @@ instance DisambECP (HsExpr GhcPs) where
(PsErrUnallowedPragma prag)
rejectPragmaPV _ = return ()
-hsHoleExpr :: Maybe EpAnnUnboundVar -> HsExpr GhcPs
-hsHoleExpr anns = HsUnboundVar anns (mkRdrUnqual (mkVarOccFS (fsLit "_")))
-
instance DisambECP (PatBuilder GhcPs) where
type Body (PatBuilder GhcPs) = PatBuilder
ecpFromCmd' (L l c) = addFatalError $ mkPlainErrorMsgEnvelope (locA l) $ PsErrArrowCmdInPat c
@@ -3678,3 +3674,6 @@ mkListSyntaxTy1 brkOpen t brkClose =
annsKeyword = (NoEpTok, brkOpen, brkClose)
annParen = AnnParensSquare brkOpen brkClose
+
+parseError :: HsExpr GhcPs
+parseError = HsHole (HoleParseError NoExtField, NoExtField)
=====================================
compiler/GHC/Rename/Expr.hs
=====================================
@@ -318,7 +318,7 @@ rnUnboundVar v = do
deferOutofScopeVariables <- goptM Opt_DeferOutOfScopeVariables
-- See Note [Reporting unbound names] for difference between qualified and unqualified names.
unless (isUnqual v || deferOutofScopeVariables) (reportUnboundName v >> return ())
- return (HsUnboundVar noExtField v, emptyFVs)
+ return (HsHole (HoleVar v, NoExtField), emptyFVs)
rnExpr (HsVar _ (L l v))
= do { dflags <- getDynFlags
@@ -353,8 +353,10 @@ rnExpr (HsVar _ (L l v))
rnExpr (HsIPVar x v)
= return (HsIPVar x v, emptyFVs)
-rnExpr (HsUnboundVar _ v)
- = return (HsUnboundVar noExtField v, emptyFVs)
+rnExpr (HsHole (HoleVar _, NoExtField))
+ = return (HsHole (HoleVar unnamedHoleRdrName, NoExtField), emptyFVs)
+rnExpr (HsHole (HoleParseError NoExtField, NoExtField))
+ = panic "rnExpr tried to rename a HoleParseError"
-- HsOverLabel: see Note [Handling overloaded and rebindable constructs]
rnExpr (HsOverLabel src v)
@@ -859,8 +861,8 @@ See #18151.
Note [Reporting unbound names]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Faced with an out-of-scope `RdrName` there are two courses of action
-A. Report an error immediately (and return a HsUnboundVar). This will halt GHC after the renamer is complete
-B. Return a HsUnboundVar without reporting an error. That will allow the typechecker to run, which in turn
+A. Report an error immediately (and return a `HsHole (HoleVar ...)`). This will halt GHC after the renamer is complete
+B. Return a `HsHole (HoleVar ...)` without reporting an error. That will allow the typechecker to run, which in turn
can give a better error message, notably giving the type of the variable via the "typed holes" mechanism.
When `-fdefer-out-of-scope-variables` is on we follow plan B.
=====================================
compiler/GHC/Rename/HsType.hs
=====================================
@@ -1466,10 +1466,10 @@ data NegationHandling = ReassociateNegation | KeepNegationIntact
----------------------------
get_op :: LHsExpr GhcRn -> OpName
--- An unbound name could be either HsVar or HsUnboundVar
+-- An unbound name could be either HsVar or (HsHole (HoleVar _, _))
-- See GHC.Rename.Expr.rnUnboundVar
get_op (L _ (HsVar _ n)) = NormalOp (unLoc n)
-get_op (L _ (HsUnboundVar _ uv)) = UnboundOp uv
+get_op (L _ (HsHole (HoleVar uv, NoExtField))) = UnboundOp uv
get_op (L _ (XExpr (HsRecSelRn fld))) = RecFldOp fld
get_op other = pprPanic "get_op" (ppr other)
=====================================
compiler/GHC/Rename/Module.hs
=====================================
@@ -1298,7 +1298,7 @@ badRuleLhsErr name lhs bad_e
= TcRnIllegalRuleLhs errReason name lhs bad_e
where
errReason = case bad_e of
- HsUnboundVar _ uv ->
+ HsHole (HoleVar uv, NoExtField) ->
UnboundVariable uv $ notInScopeErr WL_Global uv
_ -> IllegalExpression
=====================================
compiler/GHC/Tc/Errors.hs
=====================================
@@ -1526,7 +1526,7 @@ maybeAddDeferredBindings hole report = do
when (deferringAnyBindings ctxt) $ do
err_tm <- mkErrorTerm (hole_loc hole) ref_ty report
-- NB: ref_ty, not hole_ty. hole_ty might be rewritten.
- -- See Note [Holes] in GHC.Tc.Types.Constraint
+ -- See Note [Holes in expressions] in GHC.Tc.Types.Constraint
writeMutVar ref err_tm
_ -> pure ()
where
=====================================
compiler/GHC/Tc/Gen/App.hs
=====================================
@@ -221,7 +221,7 @@ A "head" has three special cases (for which we can infer a polytype
using tcInferAppHead_maybe); otherwise is just any old expression (for
which we can infer a rho-type (via tcInfer).
-There is no special treatment for HsUnboundVar, HsOverLit etc, because
+There is no special treatment for HsHole (HsVar ...), HsOverLit, etc, because
we can't get a polytype from them.
Left and right sections (e.g. (x +) and (+ x)) are not yet supported.
@@ -680,7 +680,7 @@ tcInstFun do_ql inst_final (tc_fun, fun_ctxt) fun_sigma rn_args
fun_is_out_of_scope -- See Note [VTA for out-of-scope functions]
= case tc_fun of
- HsUnboundVar {} -> True
+ HsHole (HoleVar {}, _) -> True
_ -> False
inst_fun :: [HsExprArg 'TcpRn] -> ForAllTyFlag -> Bool
@@ -1056,7 +1056,7 @@ expr_to_type earg =
= do { t <- go (L l e)
; let splice_result' = HsUntypedSpliceTop finalizers t
; return (L l (HsSpliceTy splice_result' splice)) }
- go (L l (HsUnboundVar _ rdr))
+ go (L l (HsHole (HoleVar rdr, NoExtField)))
| isUnderscore occ = return (L l (HsWildCardTy noExtField))
| startsWithUnderscore occ =
-- See Note [Wildcards in the T2T translation]
@@ -1128,7 +1128,7 @@ This conversion is in the TcM monad because
vfun [x | x <- xs] Can't convert list comprehension to a type
vfun (\x -> x) Can't convert a lambda to a type
* It needs to check for LangExt.NamedWildCards to generate an appropriate
- error message for HsUnboundVar.
+ error message for HsHole (HsVar ...).
vfun _a Not in scope: ‘_a’
(NamedWildCards disabled)
vfun _a Illegal named wildcard in a required type argument: ‘_a’
@@ -1479,7 +1479,7 @@ Note [VTA for out-of-scope functions]
Suppose 'wurble' is not in scope, and we have
(wurble @Int @Bool True 'x')
-Then the renamer will make (HsUnboundVar "wurble") for 'wurble',
+Then the renamer will make (HsHole (HsVar "wurble", NoExtField)) for 'wurble',
and the typechecker will typecheck it with tcUnboundId, giving it
a type 'alpha', and emitting a deferred Hole constraint, to
be reported later.
@@ -1494,7 +1494,7 @@ tcUnboundId. It later reports 'wurble' as out of scope, and tries to
give its type.
Fortunately in tcInstFun we still have access to the function, so we
-can check if it is a HsUnboundVar. We use this info to simply skip
+can check if it is a HsHole. We use this info to simply skip
over any visible type arguments. We'll /already/ have emitted a
Hole constraint; failing preserves that constraint.
=====================================
compiler/GHC/Tc/Gen/Expr.hs
=====================================
@@ -298,13 +298,15 @@ tcExpr (XExpr e) res_ty = tcXExpr e res_ty
-- Typecheck an occurrence of an unbound Id
--
-- Some of these started life as a true expression hole "_".
--- Others might simply be variables that accidentally have no binding site
-tcExpr (HsUnboundVar _ occ) res_ty
+-- Others might simply be variables that accidentally have no binding site.
+tcExpr (HsHole (HoleVar occ, NoExtField)) res_ty
= do { ty <- expTypeToType res_ty -- Allow Int# etc (#12531)
; her <- emitNewExprHole occ ty
; tcEmitBindingUsage bottomUE -- Holes fit any usage environment
-- (#18491)
- ; return (HsUnboundVar her occ) }
+ ; return (HsHole (HoleVar occ, her))
+ }
+tcExpr (HsHole (HoleParseError x, NoExtField)) _ = dataConCantHappen x
tcExpr e@(HsLit x lit) res_ty
= do { let lit_ty = hsLitType lit
@@ -765,6 +767,7 @@ tcXExpr xe@(ExpandedThingRn o e') res_ty
| OrigStmt ls@(L loc _) <- o
= setSrcSpanA loc $
mkExpandedStmtTc ls <$> tcApp (XExpr xe) res_ty
+
tcXExpr xe res_ty = tcApp (XExpr xe) res_ty
{-
=====================================
compiler/GHC/Tc/Gen/Head.hs
=====================================
@@ -1271,10 +1271,9 @@ addStmtCtxt stmt =
addExprCtxt :: HsExpr GhcRn -> TcRn a -> TcRn a
addExprCtxt e thing_inside
= case e of
- HsUnboundVar {} -> thing_inside
+ HsHole (HoleVar {}, NoExtField) -> thing_inside
_ -> addErrCtxt (ExprCtxt e) thing_inside
- -- The HsUnboundVar special case addresses situations like
+ -- The HsHole special case addresses situations like
-- f x = _
-- when we don't want to say "In the expression: _",
-- because it is mentioned in the error message itself
-
=====================================
compiler/GHC/Tc/Types/Constraint.hs
=====================================
@@ -361,7 +361,7 @@ data DelayedError
= DE_Hole Hole
-- ^ A hole (in a type or in a term).
--
- -- See Note [Holes].
+ -- See Note [Holes in expressions].
| DE_NotConcrete NotConcreteError
-- ^ A type could not be ensured to be concrete.
--
@@ -380,7 +380,7 @@ instance Outputable DelayedError where
-- | A hole stores the information needed to report diagnostics
-- about holes in terms (unbound identifiers or underscores) or
-- in types (also called wildcards, as used in partial type
--- signatures). See Note [Holes].
+-- signatures). See Note [Holes in expressions] for holes in terms.
data Hole
= Hole { hole_sort :: HoleSort -- ^ What flavour of hole is this?
, hole_occ :: RdrName -- ^ The name of this hole
@@ -670,43 +670,78 @@ of the rhs. This is necessary because these constraints are used for substitutio
during solving. If the kinds differed, then the substitution would take a well-kinded
type to an ill-kinded one.
-Note [Holes]
-~~~~~~~~~~~~
-This Note explains how GHC tracks *holes*.
-
-A hole represents one of two conditions:
- - A missing bit of an expression. Example: foo x = x + _
- - A missing bit of a type. Example: bar :: Int -> _
-
-What these have in common is that both cause GHC to emit a diagnostic to the
-user describing the bit that is left out.
-
-When a hole is encountered, a new entry of type Hole is added to the ambient
-WantedConstraints. The type (hole_ty) of the hole is then simplified during
-solving (with respect to any Givens in surrounding implications). It is
-reported with all the other errors in GHC.Tc.Errors.
-
-For expression holes, the user has the option of deferring errors until runtime
-with -fdefer-type-errors. In this case, the hole actually has evidence: this
-evidence is an erroring expression that prints an error and crashes at runtime.
-The ExprHole variant of holes stores an IORef EvTerm that will contain this evidence;
-during constraint generation, this IORef was stored in the HsUnboundVar extension
-field by the type checker. The desugarer simply dereferences to get the CoreExpr.
-
-Prior to fixing #17812, we used to invent an Id to hold the erroring
-expression, and then bind it during type-checking. But this does not support
-representation-polymorphic out-of-scope identifiers. See
-typecheck/should_compile/T17812. We thus use the mutable-CoreExpr approach
-described above.
-
-You might think that the type in the HoleExprRef is the same as the type of the
-hole. However, because the hole type (hole_ty) is rewritten with respect to
-givens, this might not be the case. That is, the hole_ty is always (~) to the
-type of the HoleExprRef, but they might not be `eqType`. We need the type of the generated
-evidence to match what is expected in the context of the hole, and so we must
-store these types separately.
-
-Type-level holes have no evidence at all.
+Note [Holes in expressions]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This Note explains how GHC tracks "holes" in expressions. It does not
+deal with holes in types, nor with partial type signatures.
+
+A hole represents a missing bit of an expression. Example:
+ foo x = x && _
+GHC then emits a diagnostic, describing the bit that is left out:
+ Foo.hs:5:14: error: [GHC-88464]
+ • Found hole: _ :: Bool
+ • In the second argument of ‘(&&)’, namely ‘_’
+ In the expression: x && _
+
+GHC uses the same mechanism is used to give diagnostics for out-of-scope
+variables:
+ foo x = x && y
+gives diagnostic
+ Foo.hs:5:14: error: [GHC-88464]
+ Variable not in scope: y :: Bool
+
+Here is how holes are represented in expressions:
+
+* If the user wrote "_":
+ Parser HsHole (HoleVar (EpAnnHole ...), NoExtField)
+ Renamer HsHole (HoleVar "_", NoExtField)
+ Typechecker HsHole (HoleVar "_", ref)
+
+* If the user wrote "x", where `x` is not in scope
+ Parser HsVar "x"
+ Renamer HsHole (HoleVar "x", NoExtField)
+ Typechecker HsHole (HoleVar "x", ref)
+
+In both cases (ref::HoleExprRef) contains
+ - The type of the hole.
+ - A ref-cell that is filled in (by the typechecker) with an
+ error thunk. With -fdefer-type errors we use this as the
+ value of the hole.
+ - A Unique (see Note [Uniques and tags]).
+
+Typechecking holes
+
+* When the typechecker encounters a `HsHole`, it returns one with the
+ HoleExprRef, but also emits a `DelayedError` into the `WantedConstraints`.
+
+* This DelayedError later triggers the error reporting, and the filling-in of
+ the error thunk, in GHC.Tc.Errors.
+
+* The user has the option of deferring errors until runtime with
+ `-fdefer-type-errors`. In this case, the hole carries evidence in its
+ `HoleExprRef`. This evidence is an erroring expression that prints an error
+ and crashes at runtime.
+
+Desugaring holes
+
+* During desugaring, the `(HsHole (HoleVar "x", ref))` is desugared by
+ reading the ref-cell to find the error thunk evidence term, put there by the
+ constraint solver.
+
+Wrinkles:
+
+* Prior to fixing #17812, we used to invent an Id to hold the erroring
+ expression, and then bind it during type-checking. But this does not support
+ representation-polymorphic out-of-scope identifiers. See
+ typecheck/should_compile/T17812. We thus use the mutable-CoreExpr approach
+ described above.
+
+* You might think that the type in the HoleExprRef is the same as the type of the
+ hole. However, because the hole type (hole_ty) is rewritten with respect to
+ givens, this might not be the case. That is, the hole_ty is always (~) to the
+ type of the HoleExprRef, but they might not be `eqType`. We need the type of the generated
+ evidence to match what is expected in the context of the hole, and so we must
+ store these types separately.
-}
mkNonCanonical :: CtEvidence -> Ct
=====================================
compiler/GHC/Tc/Types/Evidence.hs
=====================================
@@ -585,7 +585,7 @@ data EvCallStack
-}
-- | Where to store evidence for expression holes
--- See Note [Holes] in GHC.Tc.Types.Constraint
+-- See Note [Holes in expressions] in GHC.Tc.Types.Constraint
data HoleExprRef = HER (IORef EvTerm) -- ^ where to write the erroring expression
TcType -- ^ expected type of that expression
Unique -- ^ for debug output only
=====================================
compiler/GHC/Tc/Types/Origin.hs
=====================================
@@ -717,7 +717,6 @@ lexprCtOrigin (L _ e) = exprCtOrigin e
exprCtOrigin :: HsExpr GhcRn -> CtOrigin
exprCtOrigin (HsVar _ (L _ name)) = OccurrenceOf name
exprCtOrigin (HsGetField _ _ (L _ f)) = GetFieldOrigin (field_label $ unLoc $ dfoLabel f)
-exprCtOrigin (HsUnboundVar {}) = Shouldn'tHappenOrigin "unbound variable"
exprCtOrigin (HsOverLabel _ l) = OverLabelOrigin l
exprCtOrigin (ExplicitList {}) = ListOrigin
exprCtOrigin (HsIPVar _ ip) = IPOccOrigin ip
@@ -751,6 +750,8 @@ exprCtOrigin (HsUntypedSplice {}) = Shouldn'tHappenOrigin "TH untyped splice"
exprCtOrigin (HsProc {}) = Shouldn'tHappenOrigin "proc"
exprCtOrigin (HsStatic {}) = Shouldn'tHappenOrigin "static expression"
exprCtOrigin (HsEmbTy {}) = Shouldn'tHappenOrigin "type expression"
+exprCtOrigin (HsHole (HoleVar _, _)) = Shouldn'tHappenOrigin "(named) hole expression"
+exprCtOrigin (HsHole (HoleParseError x, _)) = dataConCantHappen x
exprCtOrigin (HsForAll {}) = Shouldn'tHappenOrigin "forall telescope" -- See Note [Types in terms]
exprCtOrigin (HsQual {}) = Shouldn'tHappenOrigin "constraint context" -- See Note [Types in terms]
exprCtOrigin (HsFunArr {}) = Shouldn'tHappenOrigin "function arrow" -- See Note [Types in terms]
=====================================
compiler/GHC/Tc/Zonk/Type.hs
=====================================
@@ -925,15 +925,16 @@ zonkExpr (HsVar x (L l id))
do { id' <- zonkIdOcc id
; return (HsVar x (L l id')) }
-zonkExpr (HsUnboundVar her occ)
+zonkExpr (HsHole (HoleVar occ, her))
= do her' <- zonk_her her
- return (HsUnboundVar her' occ)
+ return (HsHole (HoleVar occ, her'))
where
zonk_her :: HoleExprRef -> ZonkTcM HoleExprRef
zonk_her (HER ref ty u)
= do updTcRefM ref zonkEvTerm
ty' <- zonkTcTypeToTypeX ty
return (HER ref ty' u)
+zonkExpr (HsHole (HoleParseError x, _)) = dataConCantHappen x
zonkExpr (HsIPVar x _) = dataConCantHappen x
=====================================
compiler/Language/Haskell/Syntax/Expr.hs
=====================================
@@ -41,7 +41,6 @@ import Data.Bool
import Data.Eq
import Data.Maybe
import Data.List.NonEmpty ( NonEmpty )
-import GHC.Types.Name.Reader
{- Note [RecordDotSyntax field updates]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -335,19 +334,6 @@ data HsExpr p
(LIdP p) -- ^ Variable
-- See Note [Located RdrNames]
- | HsUnboundVar (XUnboundVar p)
- RdrName -- ^ Unbound variable; also used for "holes"
- -- (_ or _x).
- -- Turned from HsVar to HsUnboundVar by the
- -- renamer, when it finds an out-of-scope
- -- variable or hole.
- -- The (XUnboundVar p) field becomes an HoleExprRef
- -- after typechecking; this is where the
- -- erroring expression will be written after
- -- solving. See Note [Holes] in GHC.Tc.Types.Constraint.
-
-
-
| HsOverLabel (XOverLabel p) FastString
-- ^ Overloaded label (Note [Overloaded labels] in GHC.OverloadedLabels)
@@ -380,7 +366,7 @@ data HsExpr p
-- NB Bracketed ops such as (+) come out as Vars.
-- NB Sadly, we need an expr for the operator in an OpApp/Section since
- -- the renamer may turn a HsVar into HsRecSel or HsUnboundVar
+ -- the renamer may turn a HsVar into HsRecSel or HsHole.
| OpApp (XOpApp p)
(LHsExpr p) -- left operand
@@ -529,6 +515,10 @@ data HsExpr p
| HsEmbTy (XEmbTy p)
(LHsWcType (NoGhcTc p))
+ -- | Holes in expressions, i.e. '_'.
+ -- See Note [Holes in expressions] in GHC.Tc.Types.Constraint.
+ | HsHole (XHole p)
+
-- | Forall-types @forall tvs. t@ and @forall tvs -> t at .
-- Used with @RequiredTypeArguments@, e.g. @fn (forall a. Proxy a)@.
-- See Note [Types in terms]
=====================================
compiler/Language/Haskell/Syntax/Extension.hs
=====================================
@@ -445,6 +445,7 @@ type family XTick x
type family XBinTick x
type family XPragE x
type family XEmbTy x
+type family XHole x
type family XForAll x
type family XQual x
type family XFunArr x
=====================================
testsuite/tests/perf/compiler/hard_hole_fits.hs
=====================================
@@ -12,7 +12,7 @@ import GHC (GhcPs)
testMe :: HsExpr GhcPs -> Int
testMe (HsVar a b) = _
-testMe (HsUnboundVar xuv uv) = _
+testMe (HsHole a) = _
testMe (HsOverLabel xol m_ip) = _
testMe (HsIPVar xv hin) = _
testMe (HsOverLit xole hol) = _
=====================================
testsuite/tests/perf/compiler/hard_hole_fits.stderr
=====================================
@@ -17,14 +17,12 @@ hard_hole_fits.hs:14:22: warning: [GHC-88464] [-Wtyped-holes (in -Wdefault)]
(imported from ‘Prelude’ at hard_hole_fits.hs:8:8-20
(and originally defined in ‘GHC.Internal.Enum’))
-hard_hole_fits.hs:15:32: warning: [GHC-88464] [-Wtyped-holes (in -Wdefault)]
- • Found hole: _ :: Int
- • In an equation for ‘testMe’: testMe (HsUnboundVar xuv uv) = _
- • Relevant bindings include
- uv :: GHC.Types.Name.Reader.RdrName
- (bound at hard_hole_fits.hs:15:26)
- xuv :: Language.Haskell.Syntax.Extension.XUnboundVar GhcPs
- (bound at hard_hole_fits.hs:15:22)
+hard_hole_fits.hs:15:21: warning: [GHC-88464] [-Wtyped-holes (in -Wdefault)]
+ Found hole: _ :: Int
+ In an equation for ‘testMe’: testMe (HsHole a) = _
+ Relevant bindings include
+ a :: Language.Haskell.Syntax.Extension.XHole GhcPs
+ (bound at hard_hole_fits.hs:15:16)
testMe :: HsExpr GhcPs -> Int (bound at hard_hole_fits.hs:14:1)
Valid hole fits include
maxBound :: forall a. Bounded a => a
=====================================
testsuite/tests/plugins/T20803-plugin/FixErrorsPlugin.hs
=====================================
@@ -20,15 +20,15 @@ import Data.Maybe
plugin :: Plugin
plugin = defaultPlugin {parsedResultAction = parsedAction}
--- Replace every hole (and other unbound vars) with the given expression
+-- Replace every hole with the given expression
replaceHoles :: forall a . Data a => HsExpr GhcPs -> a -> a
replaceHoles new = gmapT \case
(d :: d) -> replaceHoles new d `fromMaybe` tryHole
where
tryHole :: Maybe d
tryHole = eqT @d @(HsExpr GhcPs) >>= \case
- Eq.Refl | HsUnboundVar _ _ <- d -> Just new
- _ -> Nothing
+ Eq.Refl | HsHole _ <- d -> Just new
+ _ -> Nothing
parsedAction :: [CommandLineOption] -> ModSummary
-> ParsedResult -> Hsc ParsedResult
=====================================
utils/check-exact/ExactPrint.hs
=====================================
@@ -314,7 +314,7 @@ instance HasTrailing AnnExplicitSum where
trailing _ = []
setTrailing a _ = a
-instance HasTrailing (Maybe EpAnnUnboundVar) where
+instance HasTrailing (Maybe EpAnnHole) where
trailing _ = []
setTrailing a _ = a
@@ -2879,16 +2879,17 @@ instance ExactPrint (HsExpr GhcPs) where
then markAnnotated n
else return n
return (HsVar x n')
- exact (HsUnboundVar an n) = do
+ exact (HsHole (HoleVar an, NoExtField)) = do
case an of
- Just (EpAnnUnboundVar (ob,cb) l) -> do
+ Just (EpAnnHole (ob,cb) l) -> do
ob' <- markEpToken ob
l' <- markEpToken l
cb' <- markEpToken cb
- return (HsUnboundVar (Just (EpAnnUnboundVar (ob',cb') l')) n)
- _ -> do
+ return (HsHole (HoleVar (Just (EpAnnHole (ob',cb') l')), NoExtField))
+ Nothing -> do
printStringAdvanceA "_" >> return ()
- return (HsUnboundVar an n)
+ return (HsHole (HoleVar an, NoExtField))
+ exact (HsHole (HoleParseError NoExtField, NoExtField)) = error "Cannot exact print HoleParseError"
exact x@(HsOverLabel src l) = do
printStringAdvanceA "#" >> return ()
case src of
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/7b0d3835532299357d8b1c29c69a68b1693de4af
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/7b0d3835532299357d8b1c29c69a68b1693de4af
You're receiving this email because of your account on gitlab.haskell.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20250114/95fad745/attachment-0001.html>
More information about the ghc-commits
mailing list