[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