diff --git a/CHANGES.md b/CHANGES.md index b3235af..f9d81d5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,12 +3,24 @@ Version 1.14 [????.??.??] ------------------------- +* Support GHC 9.4. * Drop support for GHC 7.8 and 7.10. As a consequence of this, the `strictToBang` was removed as it no longer serves a useful purpose. * Desugared lambda expressions and guards that bind multiple patterns can now have patterns with unlifted types. The desugared code uses `UnboxedTuples` to make this possible, so if you load the desugared code into GHCi on prior to GHC 9.2, you will need to enable `-fobject-code`. +* `th-desugar` now desugars `PromotedInfixT` and `PromotedUInfixT`, which were + added in GHC 9.4. Mirroring the existing treatment of other `Promoted*` + `Type`s, `PromotedInfixT` is desugared to an application of a `DConT` applied + to two arguments, just like `InfixT` is desugared. Similarly, attempting to + desugar a `PromotedUInfixT` results in an error, just like attempting to + desugar a `UInfixT` would be. +* `th-desugar` now supports `DefaultD` (i.e., `default` declarations) and + `OpaqueP` (i.e., `OPAQUE` pragmas), which were added in GHC 9.4. +* `th-desugar` now desugars `LamCasesE` (i.e., `\cases` expressions), which was + added in GHC 9.4. A `\cases` expression is desugared to an ordinary lambda + expression, much like `\case` is currently desugared. * Fix an inconsistency which caused non-exhaustive `case` expressions to be desugared into uses of `EmptyCase`. Non-exhaustive `case` expressions are now desugared into code that throws a "`Non-exhaustive patterns in...`" error at diff --git a/Language/Haskell/TH/Desugar/AST.hs b/Language/Haskell/TH/Desugar/AST.hs index f826ae9..b425390 100644 --- a/Language/Haskell/TH/Desugar/AST.hs +++ b/Language/Haskell/TH/Desugar/AST.hs @@ -135,6 +135,7 @@ data DDec = DLetDec DLetDec | DKiSigD Name DKind -- DKiSigD is part of DDec, not DLetDec, because standalone kind -- signatures can only appear on the top level. + | DDefaultD [DType] deriving (Eq, Show, Data, Generic) -- | Corresponds to TH's 'PatSynDir' type @@ -254,6 +255,7 @@ data DPragma = DInlineP Name Inline RuleMatch Phases | DAnnP AnnTarget DExp | DLineP Int String | DCompleteP [Name] (Maybe Name) + | DOpaqueP Name deriving (Eq, Show, Data, Generic) -- | Corresponds to TH's @RuleBndr@ type. diff --git a/Language/Haskell/TH/Desugar/Core.hs b/Language/Haskell/TH/Desugar/Core.hs index 5fb5c6a..864e001 100644 --- a/Language/Haskell/TH/Desugar/Core.hs +++ b/Language/Haskell/TH/Desugar/Core.hs @@ -247,6 +247,35 @@ dsExp (ProjectionE fields) = comp :: DExp -> String -> DExp comp acc f = DVarE '(.) `DAppE` mkGetFieldProj f `DAppE` acc #endif +#if __GLASGOW_HASKELL__ >= 903 +dsExp (LamCasesE clauses) = do + clauses' <- dsClauses CaseAlt clauses + numArgs <- + case clauses' of + (DClause pats _:_) -> return $ length pats + [] -> fail "\\cases expression must have at least one alternative" + args <- replicateM numArgs (newUniqueName "x") + return $ DLamE args $ DCaseE (mkUnboxedTupleDExp (map DVarE args)) + (map dClauseToUnboxedTupleMatch clauses') +#endif + +-- | Convert a 'DClause' to a 'DMatch' by bundling all of the clause's patterns +-- into a match on a single unboxed tuple pattern. That is, convert this: +-- +-- @ +-- f x y z = rhs +-- @ +-- +-- To this: +-- +-- @ +-- f (# x, y, z #) = rhs +-- @ +-- +-- This is used to desugar @\\cases@ expressions into lambda expressions. +dClauseToUnboxedTupleMatch :: DClause -> DMatch +dClauseToUnboxedTupleMatch (DClause pats rhs) = + DMatch (mkUnboxedTupleDPat pats) rhs #if __GLASGOW_HASKELL__ >= 809 dsTup :: DsMonad q => (Int -> Name) -> [Maybe Exp] -> q DExp @@ -737,6 +766,9 @@ dsDec (ImplicitParamBindD {}) = impossible "Non-`let`-bound implicit param bindi #if __GLASGOW_HASKELL__ >= 809 dsDec (KiSigD n ki) = (:[]) <$> (DKiSigD n <$> dsType ki) #endif +#if __GLASGOW_HASKELL__ >= 903 +dsDec (DefaultD tys) = (:[]) <$> (DDefaultD <$> mapM dsType tys) +#endif -- | Desugar a 'DataD' or 'NewtypeD'. dsDataDec :: DsMonad q @@ -841,7 +873,7 @@ dsLetDecs decs = do -- 'DLetDec's to support parallel assignment of implicit params. dsLetDec :: DsMonad q => Dec -> q ([DLetDec], DExp -> DExp) dsLetDec (FunD name clauses) = do - clauses' <- dsClauses name clauses + clauses' <- dsClauses (FunRhs name) clauses return ([DFunD name clauses'], id) dsLetDec (ValD pat body where_decs) = do (pat', vars) <- dsPatX pat @@ -1011,6 +1043,9 @@ dsPragma (LineP n str) = return $ DLineP n str #if __GLASGOW_HASKELL__ >= 801 dsPragma (CompleteP cls mty) = return $ DCompleteP cls mty #endif +#if __GLASGOW_HASKELL__ >= 903 +dsPragma (OpaqueP n) = return $ DOpaqueP n +#endif -- | Desugar a @RuleBndr@. dsRuleBndr :: DsMonad q => RuleBndr -> q DRuleBndr @@ -1036,19 +1071,19 @@ dsTySynEqn n (TySynEqn lhss rhs) = do -- | Desugar clauses to a function definition dsClauses :: DsMonad q - => Name -- ^ Name of the function + => MatchContext -- ^ The context in which the clauses arise -> [Clause] -- ^ Clauses to desugar -> q [DClause] dsClauses _ [] = return [] -dsClauses n (Clause pats (NormalB exp) where_decs : rest) = do +dsClauses mc (Clause pats (NormalB exp) where_decs : rest) = do -- this case is necessary to maintain the roundtrip property. - rest' <- dsClauses n rest + rest' <- dsClauses mc rest exp' <- dsExp exp (where_decs', ip_binder) <- dsLetDecs where_decs let exp_with_wheres = maybeDLetE where_decs' (ip_binder exp') (pats', exp'') <- dsPatsOverExp pats exp_with_wheres return $ DClause pats' exp'' : rest' -dsClauses n clauses@(Clause outer_pats _ _ : _) = do +dsClauses mc clauses@(Clause outer_pats _ _ : _) = do arg_names <- replicateM (length outer_pats) (newUniqueName "arg") let scrutinee = mkUnboxedTupleDExp (map DVarE arg_names) clause <- DClause (map DVarP arg_names) <$> @@ -1057,8 +1092,7 @@ dsClauses n clauses@(Clause outer_pats _ _ : _) = do where clause_to_dmatch :: DsMonad q => DExp -> Clause -> [DMatch] -> q [DMatch] clause_to_dmatch scrutinee (Clause pats body where_decs) failure_matches = do - let failure_exp = maybeDCaseE (FunRhs n) - scrutinee failure_matches + let failure_exp = maybeDCaseE mc scrutinee failure_matches exp <- dsBody body where_decs failure_exp (pats', exp') <- dsPatsOverExp pats exp uni_pats <- fmap getAll $ concatMapM (fmap All . isUniversalPattern) pats' @@ -1112,10 +1146,8 @@ dsType (AppT t1 t2) = DAppT <$> dsType t1 <*> dsType t2 dsType (SigT ty ki) = DSigT <$> dsType ty <*> dsType ki dsType (VarT name) = return $ DVarT name dsType (ConT name) = return $ DConT name - -- the only difference between ConT and PromotedT is the name lookup. Here, we assume - -- that the TH quote mechanism figured out the right name. Note that lookupDataName name - -- does not necessarily work, because `name` has its original module attached, which - -- may not be in scope. +-- The PromotedT case is identical to the ConT case above. +-- See Note [Desugaring promoted types]. dsType (PromotedT name) = return $ DConT name dsType (TupleT n) = return $ DConT (tupleTypeName n) dsType (UnboxedTupleT n) = return $ DConT (unboxedTupleTypeName n) @@ -1128,8 +1160,8 @@ dsType StarT = return $ DConT typeKindName dsType ConstraintT = return $ DConT ''Constraint dsType (LitT lit) = return $ DLitT lit dsType EqualityT = return $ DConT ''(~) -dsType (InfixT t1 n t2) = DAppT <$> (DAppT (DConT n) <$> dsType t1) <*> dsType t2 -dsType (UInfixT _ _ _) = fail "Cannot desugar unresolved infix operators." +dsType (InfixT t1 n t2) = dsInfixT t1 n t2 +dsType (UInfixT{}) = dsUInfixT dsType (ParensT t) = dsType t dsType WildCardT = return DWildCardT #if __GLASGOW_HASKELL__ >= 801 @@ -1145,6 +1177,12 @@ dsType (ImplicitParamT n t) = do dsType (ForallVisT tvbs ty) = DForallT <$> (DForallVis <$> mapM dsTvbUnit tvbs) <*> dsType ty #endif +#if __GLASGOW_HASKELL__ >= 903 +-- The PromotedInfixT case is identical to the InfixT case above. +-- See Note [Desugaring promoted types]. +dsType (PromotedInfixT t1 n t2) = dsInfixT t1 n t2 +dsType PromotedUInfixT{} = dsUInfixT +#endif #if __GLASGOW_HASKELL__ >= 900 -- | Desugar a 'TyVarBndr'. @@ -1190,8 +1228,45 @@ We adopt a similar stance in L.H.TH.Desugar.Reify when locally reifying the types of data constructors: since th-desugar doesn't currently support linear types, we pretend as if MulArrowT does not exist. As a result, the type of `Just` would be locally reified as `a -> Maybe a`, not `a #-> Maybe a`. + +Note [Desugaring promoted types] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +ConT and PromotedT both contain Names as a payload, the only difference being +that PromotedT is intended to refer exclusively to promoted data constructor +Names, while ConT can refer to both type and data constructor Names alike. + +When desugaring a PromotedT, we make the assumption that the TH quoting +mechanism produced the correct Name and wrap the name in a DConT. In other +words, we desugar ConT and PromotedT identically. This assumption about +PromotedT may not always be correct, however. Consider this example: + + data a :+: b = Inl a | Inr b + data Exp a = ... | Exp :+: Exp + +How should `PromotedT (mkName ":+:")` be desugared? Morally, it ought to be +desugared to a DConT that contains (:+:) the data constructor, not (:+:) the +type constructor. Deciding between the two is not always straightforward, +however. We could use the `lookupDataName` function to try and distinguish +between the two Names, but this may not necessarily work. This is because the +Name passed to `lookupDataName` could have its original module attached, which +may not be in scope. + +Long story short: we make things simple (albeit slightly wrong) by desugaring +ConT and PromotedT identically. We'll wait for someone to complain about the +wrongness of this approach before researching a more accurate solution. + +Note that the same considerations also apply to InfixT and PromotedInfixT, +which are also desugared identically. -} +-- | Desugar an infix 'Type'. +dsInfixT :: DsMonad q => Type -> Name -> Type -> q DType +dsInfixT t1 n t2 = DAppT <$> (DAppT (DConT n) <$> dsType t1) <*> dsType t2 + +-- | We cannot desugar unresolved infix operators, so fail if we encounter one. +dsUInfixT :: Fail.MonadFail m => m a +dsUInfixT = fail "Cannot desugar unresolved infix operators." + -- | Desugar a 'TyVarBndrSpec'. dsTvbSpec :: DsMonad q => TyVarBndrSpec -> q DTyVarBndrSpec #if __GLASGOW_HASKELL__ >= 900 @@ -1245,7 +1320,7 @@ dsDerivStrategy (ViaStrategy ty) = DViaStrategy <$> dsType ty dsPatSynDir :: DsMonad q => Name -> PatSynDir -> q DPatSynDir dsPatSynDir _ Unidir = pure DUnidir dsPatSynDir _ ImplBidir = pure DImplBidir -dsPatSynDir n (ExplBidir clauses) = DExplBidir <$> dsClauses n clauses +dsPatSynDir n (ExplBidir clauses) = DExplBidir <$> dsClauses (FunRhs n) clauses #endif -- | Desugar a @Pred@, flattening any internal tuples @@ -1283,8 +1358,8 @@ dsPred ConstraintT = dsPred t@(LitT _) = impossible $ "Type literal seen as head of constraint: " ++ show t dsPred EqualityT = return [DConT ''(~)] -dsPred (InfixT t1 n t2) = (:[]) <$> (DAppT <$> (DAppT (DConT n) <$> dsType t1) <*> dsType t2) -dsPred (UInfixT _ _ _) = fail "Cannot desugar unresolved infix operators." +dsPred (InfixT t1 n t2) = (:[]) <$> dsInfixT t1 n t2 +dsPred (UInfixT{}) = dsUInfixT dsPred (ParensT t) = dsPred t dsPred WildCardT = return [DWildCardT] #if __GLASGOW_HASKELL__ >= 801 @@ -1306,6 +1381,11 @@ dsPred t@(ForallVisT {}) = #if __GLASGOW_HASKELL__ >= 900 dsPred MulArrowT = impossible "Linear arrow seen as head of constraint." #endif +#if __GLASGOW_HASKELL__ >= 903 +dsPred t@PromotedInfixT{} = + impossible $ "Promoted infix type seen as head of constraint: " ++ show t +dsPred PromotedUInfixT{} = dsUInfixT +#endif -- | Desugar a quantified constraint. dsForallPred :: DsMonad q => [TyVarBndrSpec] -> Cxt -> Pred -> q DCxt diff --git a/Language/Haskell/TH/Desugar/Sweeten.hs b/Language/Haskell/TH/Desugar/Sweeten.hs index 0a1a618..d4a77e9 100644 --- a/Language/Haskell/TH/Desugar/Sweeten.hs +++ b/Language/Haskell/TH/Desugar/Sweeten.hs @@ -140,6 +140,12 @@ decToTH (DKiSigD n ki) = KiSigD n (typeToTH ki) decToTH (DKiSigD {}) = error "Standalone kind signatures supported only in GHC 8.10+" #endif +#if __GLASGOW_HASKELL__ >= 903 +decToTH (DDefaultD tys) = DefaultD (map typeToTH tys) +#else +decToTH (DDefaultD{}) = + error "Default declarations supported only in GHC 9.4+" +#endif #if __GLASGOW_HASKELL__ < 801 patSynErr :: a @@ -244,6 +250,11 @@ pragmaToTH (DCompleteP {}) = error "COMPLETE pragmas only supported in GHC 8.2+" #else pragmaToTH (DCompleteP cls mty) = CompleteP cls mty #endif +#if __GLASGOW_HASKELL__ >= 903 +pragmaToTH (DOpaqueP n) = OpaqueP n +#else +pragmaToTH (DOpaqueP {}) = error "OPAQUE pragmas only supported in GHC 9.4+" +#endif ruleBndrToTH :: DRuleBndr -> RuleBndr ruleBndrToTH (DRuleVar n) = RuleVar n diff --git a/Test/Run.hs b/Test/Run.hs index f72f948..92fefdf 100644 --- a/Test/Run.hs +++ b/Test/Run.hs @@ -146,6 +146,10 @@ tests = test [ "sections" ~: $test1_sections @=? $(dsSplice test1_sections) #endif #if __GLASGOW_HASKELL__ >= 902 , "overloaded_record_dot" ~: $test54_overloaded_record_dot @=? $(dsSplice test54_overloaded_record_dot) +#endif +#if __GLASGOW_HASKELL__ >= 903 + , "opaque_pragma" ~: $test55_opaque_pragma @=? $(dsSplice test55_opaque_pragma) + , "lambda_cases" ~: $test56_lambda_cases @=? $(dsSplice test56_lambda_cases) #endif ] diff --git a/Test/Splices.hs b/Test/Splices.hs index bb81453..d2eaee8 100644 --- a/Test/Splices.hs +++ b/Test/Splices.hs @@ -339,6 +339,18 @@ test54_overloaded_record_dot = in (ord2.unORD2.unORD1, (.unORD2.unORD1) ord2) |] #endif +#if __GLASGOW_HASKELL__ >= 903 +test55_opaque_pragma = + [| let f :: String -> String + f x = x + {-# OPAQUE f #-} + in f "Hello, World!" |] + +test56_lambda_cases = + [| (\cases (Just x) (Just y) -> x ++ y + _ _ -> "") (Just "Hello") (Just "World") |] +#endif + type family TFExpand x type instance TFExpand Int = Bool type instance TFExpand (Maybe a) = [a] @@ -758,5 +770,9 @@ test_exprs = [ test1_sections #endif #if __GLASGOW_HASKELL__ >= 902 , test54_overloaded_record_dot +#endif +#if __GLASGOW_HASKELL__ >= 903 + , test55_opaque_pragma + , test56_lambda_cases #endif ]