From 393531ff00d0d487f0721c0680a67dbc1db2fa3d Mon Sep 17 00:00:00 2001 From: Simon Peyton Jones <simon.peytonjones@gmail.com> Date: Mon, 5 Feb 2024 23:25:46 +0000 Subject: [PATCH] Specialising expressions -- at last This MR addresses #24359, which implements the GHC proposal 493 on SPECIALISE pragmas. * The old code path (using SpecSig and SpecPrag) still exists. * The new code path (using SpecSigE and SpecPragE) runs alongside it. * All SPECIALISE pragmas are routed through the new code path, except if you give multiple type sigs, when the old code path is still used. * Main documentation: Note [Handling new-form SPECIALISE pragmas] in GHC.Tc.Gen.Sig` Thanks to @sheaf for helping with this MR. The Big Thing is to introduce {-# SPECIALISE forall x. f @Int x True #-} where you can give type arguments and value argument to specialise; and you can quantify them with forall, just as in Rules. I thought it was going to be pretty simple, but it was a Long, Long Saga. Highlights * Overview Note [Handling new-form SPECIALISE pragmas] in GHC.Tc.Gen.Sig - New data constructor `SpecSigE` in data type `L.H.S.Binds.Sig` - New data construtor `SpecPragE` in data type `GHC.Hs.Binds.TcSpecPrag` - Renamer: uses `checkSpecESigShape` to decide which function to assocate the SPECIALISE pragma with - Some of the action is in `GHC.Tc.Gen.Sig.tcSpecPrag` - The rest is in `GHC.HsToCore.Binds.dsSpec` * We use a new TcS mode, TcSFullySolve, when simplifying the Wanteds that arise from the specialise expression. The mechanism is explained in Note [TcSFullySolve] in GHC.Tc.Solver.Monad. The reason why we need to do this is explained in Note [Fully solving constraints for specialisation] in GHC.Tc.Gen.Sig. * All of GHC.Tc.Gen.Rule is moved into GHC.Tc.Gen.Sig, because the code is very closely related. * The forall'd binders for SPECIALISE are the same as those for a RULE, so I refactored, introducing data type `L.H.S.Binds.RuleBndrs`, with functions to rename, zonk, typecheck it. I refactored this data type a bit; nicer now. * On the LHS of RULES, or SPECIALISE, we want to disable the tricky mechanims described in Note [Desugaring non-canonical evidence] in GHC.HsToCore.Expr. Previously it wasn't fully disabled (just set to the empty set), and that didn't quite work in the new regime. * There are knock-on changes to Template Haskell. * For the LHS of a RULE and a SPECIALISE expression, I wanted to simplify it /without/ inlining the let-bindings for evidence variables. I added a flag `so_inline` to the SimpleOpt optimiser to support this. The entry point is `GHC.Core.SimpleOpt.simpleOptExprNoInline` * Since forever we have had a hack for type variables on the LHS of RULES. I took the opportunity to tidy this up. The main action is in the zonker. See GHC.Tc.Zonk.Type Note [Free tyvars on rule LHS], and especially data construtor `SkolemiseFlexi` in data type `GHC.Tc.Zonk.Env.ZonkFlexi` * Move `scopedSort` from GHC.Core.TyCo.FVs to GHC.Core.Predicate Reason: it now works for Ids as well, and I wanted to use isEvVar, which is defined in GHC.Core.Predicate Avoiding module loops meant that instead of exporting GHC.Core.TyCo.Tidy from GHC.Core.Type, modules now import the former directly. I also took the opportunity to remove unused exports from GHC.Core.Type.hs-boot * Flag stuff: - Add flag `-Wdeprecated-pragmas` and use it to control the warning when using old-style SPECIALISE pragmas with multiple type ascriptions, - Add flag `-Wuseless-specialisations` and use it to control the warning emitted when GHC determines that a SPECIALISE pragma would have no effect. Don't want if the SPECIALISE is SPECIALISE INLINE (#4444) In response to #25389, we continue to generate these seemingly code for these seemingly useless SPECIALISE pragmas - Adds deprecations to Template Haskell `pragSpecD` and `pracSpecInlD`, * Split up old-style SPECIALISE pragmas in GHC.Internal.Float, GHC.Internal.Numeric, GHC.Internal.Real * Remove useless SPECIALISE pragmas in Data.Array (updating the array submodule) Smaller things: - Update the Users Guide - Add mention of the changes to the 9.14 release notes as well as the Template Haskell changelog, --- compiler/GHC/Builtin/Names/TH.hs | 14 +- compiler/GHC/Builtin/PrimOps/Ids.hs | 1 + compiler/GHC/Core/FamInstEnv.hs | 1 + compiler/GHC/Core/InstEnv.hs | 7 +- compiler/GHC/Core/Make.hs | 2 +- compiler/GHC/Core/Opt/CSE.hs | 3 + compiler/GHC/Core/Opt/Simplify/Iteration.hs | 20 +- compiler/GHC/Core/Opt/Simplify/Utils.hs | 10 +- compiler/GHC/Core/Opt/SpecConstr.hs | 2 +- compiler/GHC/Core/Predicate.hs | 126 ++- compiler/GHC/Core/SimpleOpt.hs | 30 +- compiler/GHC/Core/Tidy.hs | 5 +- compiler/GHC/Core/TyCo/FVs.hs | 106 --- compiler/GHC/Core/TyCo/Ppr.hs | 5 +- compiler/GHC/Core/TyCo/Tidy.hs | 1 + compiler/GHC/Core/Type.hs | 26 - compiler/GHC/Core/Type.hs-boot | 35 +- compiler/GHC/Core/Unify.hs | 1 + compiler/GHC/CoreToIface.hs | 2 +- compiler/GHC/Driver/Config.hs | 1 + compiler/GHC/Driver/Flags.hs | 8 +- compiler/GHC/Driver/Session.hs | 2 + compiler/GHC/Hs/Binds.hs | 128 ++- compiler/GHC/Hs/Decls.hs | 27 +- compiler/GHC/Hs/Instances.hs | 7 + compiler/GHC/Hs/Stats.hs | 1 + compiler/GHC/HsToCore.hs | 17 +- compiler/GHC/HsToCore/Binds.hs | 655 ++++++++++---- compiler/GHC/HsToCore/Errors/Ppr.hs | 54 +- compiler/GHC/HsToCore/Errors/Types.hs | 33 +- compiler/GHC/HsToCore/Expr.hs | 22 +- compiler/GHC/HsToCore/Foreign/Call.hs | 27 +- compiler/GHC/HsToCore/Monad.hs | 18 +- compiler/GHC/HsToCore/Quote.hs | 113 ++- compiler/GHC/HsToCore/Types.hs | 17 +- compiler/GHC/Iface/Decl.hs | 1 + compiler/GHC/Iface/Ext/Ast.hs | 25 +- compiler/GHC/Iface/Tidy.hs | 1 + compiler/GHC/Parser.y | 62 +- compiler/GHC/Parser/Errors/Ppr.hs | 12 + compiler/GHC/Parser/Errors/Types.hs | 16 + compiler/GHC/Parser/PostProcess.hs | 99 ++- compiler/GHC/Rename/Bind.hs | 102 ++- compiler/GHC/Rename/HsType.hs | 1 + compiler/GHC/Rename/Module.hs | 102 +-- compiler/GHC/Rename/Names.hs | 2 +- compiler/GHC/Runtime/Debugger.hs | 1 + compiler/GHC/Runtime/Eval.hs | 9 +- compiler/GHC/Runtime/Heap/Inspect.hs | 1 + compiler/GHC/StgToByteCode.hs | 1 + compiler/GHC/Tc/Deriv.hs | 34 +- compiler/GHC/Tc/Errors.hs | 2 +- compiler/GHC/Tc/Errors/Ppr.hs | 10 + compiler/GHC/Tc/Errors/Types.hs | 10 + compiler/GHC/Tc/Gen/Bind.hs | 32 +- compiler/GHC/Tc/Gen/HsType.hs | 45 +- compiler/GHC/Tc/Gen/Rule.hs | 525 ------------ compiler/GHC/Tc/Gen/Sig.hs | 811 +++++++++++++++++- compiler/GHC/Tc/Module.hs | 17 +- compiler/GHC/Tc/Solver.hs | 52 +- compiler/GHC/Tc/Solver/Default.hs | 1 + compiler/GHC/Tc/Solver/Dict.hs | 21 - compiler/GHC/Tc/Solver/InertSet.hs | 72 +- compiler/GHC/Tc/Solver/Irred.hs | 4 - compiler/GHC/Tc/Solver/Monad.hs | 147 +++- compiler/GHC/Tc/Solver/Solve.hs | 159 +++- compiler/GHC/Tc/TyCl.hs | 2 +- compiler/GHC/Tc/TyCl/Instance.hs | 14 +- compiler/GHC/Tc/TyCl/PatSyn.hs | 3 +- compiler/GHC/Tc/Types/Constraint.hs | 1 + compiler/GHC/Tc/Types/Evidence.hs | 14 +- compiler/GHC/Tc/Types/Origin.hs | 17 +- compiler/GHC/Tc/Utils/Instantiate.hs | 1 + compiler/GHC/Tc/Utils/Monad.hs | 15 +- compiler/GHC/Tc/Utils/TcMType.hs | 7 +- compiler/GHC/Tc/Validity.hs | 15 +- compiler/GHC/Tc/Zonk/Env.hs | 10 +- compiler/GHC/Tc/Zonk/TcType.hs | 1 + compiler/GHC/Tc/Zonk/Type.hs | 196 +++-- compiler/GHC/ThToHs.hs | 47 +- compiler/GHC/Types/Basic.hs | 7 +- compiler/GHC/Types/Error/Codes.hs | 26 +- compiler/GHC/Types/Hint.hs | 5 + compiler/GHC/Types/Hint/Ppr.hs | 2 + compiler/GHC/Types/Var.hs | 2 +- compiler/Language/Haskell/Syntax/Binds.hs | 55 +- compiler/Language/Haskell/Syntax/Decls.hs | 24 +- compiler/Language/Haskell/Syntax/Extension.hs | 6 + compiler/ghc.cabal.in | 1 - docs/users_guide/9.14.1-notes.rst | 29 + docs/users_guide/exts/pragmas.rst | 44 +- docs/users_guide/using-warnings.rst | 35 + libraries/array | 2 +- libraries/ghc-boot-th/GHC/Boot/TH/Ppr.hs | 28 +- .../ghc-internal/src/GHC/Internal/Float.hs | 8 +- .../ghc-internal/src/GHC/Internal/Numeric.hs | 9 +- .../ghc-internal/src/GHC/Internal/Real.hs | 14 +- .../ghc-internal/src/GHC/Internal/TH/Lib.hs | 27 + .../src/GHC/Internal/TH/Syntax.hs | 6 +- .../Language/Haskell/TH/Lib.hs | 4 +- libraries/template-haskell/changelog.md | 7 + .../deSugar/should_compile/T10251.stderr | 5 + testsuite/tests/diagnostic-codes/codes.stdout | 5 +- testsuite/tests/ghc-api/T18522-dbg-ppr.hs | 4 +- .../template-haskell-exports.stdout | 10 +- .../should_compile/OpaqueParseWarn1.stderr | 4 +- .../tests/parser/should_fail/T7848.stderr | 14 +- .../simplCore/should_compile/DsSpecPragmas.hs | 147 ++++ .../should_compile/DsSpecPragmas.stderr | 75 ++ .../simplCore/should_compile/T12603.stdout | 2 +- .../simplCore/should_compile/T15445.stderr | 1 + .../tests/simplCore/should_compile/T24359a.hs | 18 + .../simplCore/should_compile/T24359a.stderr | 7 + .../tests/simplCore/should_compile/T25389.hs | 19 + .../simplCore/should_compile/T25389.stderr | 103 +++ .../simplCore/should_compile/T4398.stderr | 4 +- .../tests/simplCore/should_compile/T5821.hs | 3 + .../simplCore/should_compile/T8537.stderr | 6 +- .../tests/simplCore/should_compile/T9578b.hs | 84 ++ .../tests/simplCore/should_compile/all.T | 3 + .../simplCore/should_compile/simpl016.stderr | 10 - .../tests/simplCore/should_fail/T25117a.hs | 6 + .../simplCore/should_fail/T25117a.stderr | 2 + .../tests/simplCore/should_fail/T25117b.hs | 7 + .../simplCore/should_fail/T25117b.stderr | 5 + testsuite/tests/simplCore/should_fail/all.T | 2 + .../tests/simplCore/should_run/T24359b.hs | 24 + .../tests/simplCore/should_run/T24359b.stdout | 1 + testsuite/tests/simplCore/should_run/all.T | 1 + testsuite/tests/th/T13123.stderr | 5 + testsuite/tests/th/T19363.stdout | 6 +- testsuite/tests/th/T7064.stdout | 12 +- testsuite/tests/th/TH_pragma.hs | 4 + testsuite/tests/th/TH_pragma.stderr | 8 + .../typecheck/should_compile/T10504.stderr | 6 +- .../typecheck/should_compile/T2494.stderr | 14 +- .../typecheck/should_compile/TcSpecPragmas.hs | 56 ++ .../should_compile/TcSpecPragmas.stderr | 15 + .../tests/typecheck/should_compile/all.T | 1 + .../tests/typecheck/should_compile/tc186.hs | 2 +- .../tests/typecheck/should_compile/tc212.hs | 1 + .../typecheck/should_fail/SpecPragmasFail.hs | 14 + .../should_fail/SpecPragmasFail.stderr | 6 + .../tests/typecheck/should_fail/T5853.stderr | 6 +- .../should_compile/SpecMultipleTys.hs | 12 + .../should_compile/SpecMultipleTys.stderr | 5 + .../warnings/should_compile/T19296.stderr | 19 +- testsuite/tests/warnings/should_compile/all.T | 1 + .../warnings/should_fail/SpecEMultipleTys.hs | 10 + .../should_fail/SpecEMultipleTys.stderr | 5 + testsuite/tests/warnings/should_fail/all.T | 1 + utils/check-exact/ExactPrint.hs | 111 ++- 152 files changed, 3836 insertions(+), 1644 deletions(-) delete mode 100644 compiler/GHC/Tc/Gen/Rule.hs create mode 100644 testsuite/tests/deSugar/should_compile/T10251.stderr create mode 100644 testsuite/tests/simplCore/should_compile/DsSpecPragmas.hs create mode 100644 testsuite/tests/simplCore/should_compile/DsSpecPragmas.stderr create mode 100644 testsuite/tests/simplCore/should_compile/T24359a.hs create mode 100644 testsuite/tests/simplCore/should_compile/T24359a.stderr create mode 100644 testsuite/tests/simplCore/should_compile/T25389.hs create mode 100644 testsuite/tests/simplCore/should_compile/T25389.stderr create mode 100644 testsuite/tests/simplCore/should_compile/T9578b.hs delete mode 100644 testsuite/tests/simplCore/should_compile/simpl016.stderr create mode 100644 testsuite/tests/simplCore/should_fail/T25117a.hs create mode 100644 testsuite/tests/simplCore/should_fail/T25117a.stderr create mode 100644 testsuite/tests/simplCore/should_fail/T25117b.hs create mode 100644 testsuite/tests/simplCore/should_fail/T25117b.stderr create mode 100644 testsuite/tests/simplCore/should_run/T24359b.hs create mode 100644 testsuite/tests/simplCore/should_run/T24359b.stdout create mode 100644 testsuite/tests/th/T13123.stderr create mode 100644 testsuite/tests/typecheck/should_compile/TcSpecPragmas.hs create mode 100644 testsuite/tests/typecheck/should_compile/TcSpecPragmas.stderr create mode 100644 testsuite/tests/typecheck/should_fail/SpecPragmasFail.hs create mode 100644 testsuite/tests/typecheck/should_fail/SpecPragmasFail.stderr create mode 100644 testsuite/tests/warnings/should_compile/SpecMultipleTys.hs create mode 100644 testsuite/tests/warnings/should_compile/SpecMultipleTys.stderr create mode 100644 testsuite/tests/warnings/should_fail/SpecEMultipleTys.hs create mode 100644 testsuite/tests/warnings/should_fail/SpecEMultipleTys.stderr diff --git a/compiler/GHC/Builtin/Names/TH.hs b/compiler/GHC/Builtin/Names/TH.hs index a888294a687..7fd5d3c6bda 100644 --- a/compiler/GHC/Builtin/Names/TH.hs +++ b/compiler/GHC/Builtin/Names/TH.hs @@ -75,7 +75,9 @@ templateHaskellNames = [ funDName, valDName, dataDName, newtypeDName, typeDataDName, tySynDName, classDName, instanceWithOverlapDName, standaloneDerivWithStrategyDName, sigDName, kiSigDName, forImpDName, - pragInlDName, pragOpaqueDName, pragSpecDName, pragSpecInlDName, pragSpecInstDName, + pragInlDName, pragOpaqueDName, + pragSpecDName, pragSpecInlDName, pragSpecEDName, pragSpecInlEDName, + pragSpecInstDName, pragRuleDName, pragCompleteDName, pragAnnDName, pragSCCFunDName, pragSCCFunNamedDName, defaultSigDName, defaultDName, dataFamilyDName, openTypeFamilyDName, closedTypeFamilyDName, @@ -386,7 +388,8 @@ recSName = libFun (fsLit "recS") recSIdKey -- data Dec = ... funDName, valDName, dataDName, newtypeDName, typeDataDName, tySynDName, classDName, instanceWithOverlapDName, sigDName, kiSigDName, forImpDName, pragInlDName, - pragSpecDName, pragSpecInlDName, pragSpecInstDName, pragRuleDName, + pragSpecDName, pragSpecInlDName, pragSpecEDName, pragSpecInlEDName, + pragSpecInstDName, pragRuleDName, pragAnnDName, pragSCCFunDName, pragSCCFunNamedDName, standaloneDerivWithStrategyDName, defaultSigDName, defaultDName, dataInstDName, newtypeInstDName, tySynInstDName, dataFamilyDName, @@ -411,6 +414,8 @@ pragInlDName = libFun (fsLit "pragInlD") pragOpaqueDName = libFun (fsLit "pragOpaqueD") pragOpaqueDIdKey pragSpecDName = libFun (fsLit "pragSpecD") pragSpecDIdKey pragSpecInlDName = libFun (fsLit "pragSpecInlD") pragSpecInlDIdKey +pragSpecEDName = libFun (fsLit "pragSpecED") pragSpecEDIdKey +pragSpecInlEDName = libFun (fsLit "pragSpecInlED") pragSpecInlEDIdKey pragSpecInstDName = libFun (fsLit "pragSpecInstD") pragSpecInstDIdKey pragRuleDName = libFun (fsLit "pragRuleD") pragRuleDIdKey pragCompleteDName = libFun (fsLit "pragCompleteD") pragCompleteDIdKey @@ -962,7 +967,8 @@ funDIdKey, valDIdKey, dataDIdKey, newtypeDIdKey, tySynDIdKey, classDIdKey, infixLWithSpecDIdKey, infixRWithSpecDIdKey, infixNWithSpecDIdKey, roleAnnotDIdKey, patSynDIdKey, patSynSigDIdKey, pragCompleteDIdKey, implicitParamBindDIdKey, kiSigDIdKey, defaultDIdKey, pragOpaqueDIdKey, - typeDataDIdKey, pragSCCFunDKey, pragSCCFunNamedDKey :: Unique + typeDataDIdKey, pragSCCFunDKey, pragSCCFunNamedDKey, + pragSpecEDIdKey, pragSpecInlEDIdKey :: Unique funDIdKey = mkPreludeMiscIdUnique 320 valDIdKey = mkPreludeMiscIdUnique 321 dataDIdKey = mkPreludeMiscIdUnique 322 @@ -1001,6 +1007,8 @@ pragOpaqueDIdKey = mkPreludeMiscIdUnique 354 typeDataDIdKey = mkPreludeMiscIdUnique 355 pragSCCFunDKey = mkPreludeMiscIdUnique 356 pragSCCFunNamedDKey = mkPreludeMiscIdUnique 357 +pragSpecEDIdKey = mkPreludeMiscIdUnique 358 +pragSpecInlEDIdKey = mkPreludeMiscIdUnique 359 -- type Cxt = ... cxtIdKey :: Unique diff --git a/compiler/GHC/Builtin/PrimOps/Ids.hs b/compiler/GHC/Builtin/PrimOps/Ids.hs index cec163b9b15..488064ad909 100644 --- a/compiler/GHC/Builtin/PrimOps/Ids.hs +++ b/compiler/GHC/Builtin/PrimOps/Ids.hs @@ -16,6 +16,7 @@ import GHC.Prelude import {-# SOURCE #-} GHC.Core.Opt.ConstantFold (primOpRules) import GHC.Core.TyCo.Rep ( scaledThing ) import GHC.Core.Type +import GHC.Core.Predicate( tyCoVarsOfTypeWellScoped ) import GHC.Core.FVs (mkRuleInfo) import GHC.Builtin.PrimOps diff --git a/compiler/GHC/Core/FamInstEnv.hs b/compiler/GHC/Core/FamInstEnv.hs index 283f88a1c59..a44169329e1 100644 --- a/compiler/GHC/Core/FamInstEnv.hs +++ b/compiler/GHC/Core/FamInstEnv.hs @@ -42,6 +42,7 @@ import GHC.Core( IsOrphan, chooseOrphanAnchor ) import GHC.Core.Unify import GHC.Core.Type as Type import GHC.Core.TyCo.Rep +import GHC.Core.TyCo.Tidy import GHC.Core.TyCo.Compare( eqType, eqTypes ) import GHC.Core.TyCon import GHC.Core.Coercion diff --git a/compiler/GHC/Core/InstEnv.hs b/compiler/GHC/Core/InstEnv.hs index 2dffc859a11..ee44c7e6afd 100644 --- a/compiler/GHC/Core/InstEnv.hs +++ b/compiler/GHC/Core/InstEnv.hs @@ -927,11 +927,8 @@ Here are the moving parts: * That info is recorded in the `cir_is_coherent` field of `OneInst`, and thence transferred to the `ep_is_coherent` field of the `EvBind` for the dictionary. -* `GHC.HsToCore.Binds.dsHsWrapper` desugars the evidence application (f d) into - (nospec f d) if `d` is incoherent. It has to do a dependency analysis to - determine transitive dependencies, but we need to do that anyway. - See Note [Desugaring non-canonical evidence] in GHC.HsToCore.Binds. - +* In the desugarer we exploit this info: + see Note [Desugaring non-canonical evidence] in GHC.HsToCore.Expr. See also Note [nospecId magic] in GHC.Types.Id.Make. -} diff --git a/compiler/GHC/Core/Make.hs b/compiler/GHC/Core/Make.hs index ad67f65832d..bd17b163cb9 100644 --- a/compiler/GHC/Core/Make.hs +++ b/compiler/GHC/Core/Make.hs @@ -65,7 +65,7 @@ import GHC.Types.Unique.Supply import GHC.Core import GHC.Core.Utils ( exprType, mkSingleAltCase, bindNonRec ) import GHC.Core.Type -import GHC.Core.Predicate ( isCoVarType ) +import GHC.Core.Predicate ( scopedSort, isCoVarType ) import GHC.Core.TyCo.Compare ( eqType ) import GHC.Core.Coercion ( isCoVar ) import GHC.Core.DataCon ( DataCon, dataConWorkId, dataConWrapId ) diff --git a/compiler/GHC/Core/Opt/CSE.hs b/compiler/GHC/Core/Opt/CSE.hs index 74b6f3dfa58..2c004199beb 100644 --- a/compiler/GHC/Core/Opt/CSE.hs +++ b/compiler/GHC/Core/Opt/CSE.hs @@ -638,6 +638,9 @@ Notes: doing this if there are no RULES; and other things being equal it delays optimisation to delay inlining (#17409) +* There can be a subtle order-dependency, as described in #25526; + it may matter whether we end up with f=g or g=f. + ---- Historical note --- diff --git a/compiler/GHC/Core/Opt/Simplify/Iteration.hs b/compiler/GHC/Core/Opt/Simplify/Iteration.hs index 6d828e40586..a13bfc091c5 100644 --- a/compiler/GHC/Core/Opt/Simplify/Iteration.hs +++ b/compiler/GHC/Core/Opt/Simplify/Iteration.hs @@ -1159,15 +1159,15 @@ simplExprF :: SimplEnv -> SimplM (SimplFloats, OutExpr) simplExprF !env e !cont -- See Note [Bangs in the Simplifier] - = {- pprTrace "simplExprF" (vcat - [ ppr e - , text "cont =" <+> ppr cont - , text "inscope =" <+> ppr (seInScope env) - , text "tvsubst =" <+> ppr (seTvSubst env) - , text "idsubst =" <+> ppr (seIdSubst env) - , text "cvsubst =" <+> ppr (seCvSubst env) - ]) $ -} - simplExprF1 env e cont +-- = pprTrace "simplExprF" (vcat +-- [ ppr e +-- , text "cont =" <+> ppr cont +-- , text "inscope =" <+> ppr (seInScope env) +-- , text "tvsubst =" <+> ppr (seTvSubst env) +-- , text "idsubst =" <+> ppr (seIdSubst env) +-- , text "cvsubst =" <+> ppr (seCvSubst env) +-- ]) $ + = simplExprF1 env e cont simplExprF1 :: HasDebugCallStack => SimplEnv -> InExpr -> SimplCont @@ -2514,7 +2514,7 @@ field of the ArgInfo record is the state of a little state-machine: If we inline `f` before simplifying `BIG` well use preInlineUnconditionally, and we'll simplify BIG once, at x's occurrence, rather than twice. -* GHC.Core.Opt.Simplify.Utils. mkRewriteCall: if there are no rules, and no +* GHC.Core.Opt.Simplify.Utils.mkRewriteCall: if there are no rules, and no unfolding, we can skip both TryRules and TryInlining, which saves work. Note [Avoid redundant simplification] diff --git a/compiler/GHC/Core/Opt/Simplify/Utils.hs b/compiler/GHC/Core/Opt/Simplify/Utils.hs index b217588fcd0..b637fa754da 100644 --- a/compiler/GHC/Core/Opt/Simplify/Utils.hs +++ b/compiler/GHC/Core/Opt/Simplify/Utils.hs @@ -361,11 +361,17 @@ data ArgSpec -- Coercion is optimised instance Outputable ArgInfo where - ppr (ArgInfo { ai_fun = fun, ai_args = args, ai_dmds = dmds }) + ppr (ArgInfo { ai_fun = fun, ai_args = args, ai_dmds = dmds, ai_rewrite = rewrite }) = text "ArgInfo" <+> braces (sep [ text "fun =" <+> ppr fun , text "dmds(first 10) =" <+> ppr (take 10 dmds) - , text "args =" <+> ppr args ]) + , text "args =" <+> ppr args + , text "rewrite =" <+> ppr rewrite ]) + +instance Outputable RewriteCall where + ppr (TryRules ac _rules) = text "TryRules" <+> ppr ac + ppr TryInlining = text "TryInlining" + ppr TryNothing = text "TryNothing" instance Outputable ArgSpec where ppr (ValArg { as_arg = arg }) = text "ValArg" <+> ppr arg diff --git a/compiler/GHC/Core/Opt/SpecConstr.hs b/compiler/GHC/Core/Opt/SpecConstr.hs index bbc2f3c599f..d8073ee6ae5 100644 --- a/compiler/GHC/Core/Opt/SpecConstr.hs +++ b/compiler/GHC/Core/Opt/SpecConstr.hs @@ -35,7 +35,7 @@ import GHC.Core.DataCon import GHC.Core.Class( classTyVars ) import GHC.Core.Coercion hiding( substCo ) import GHC.Core.Rules -import GHC.Core.Predicate ( typeDeterminesValue ) +import GHC.Core.Predicate ( scopedSort, typeDeterminesValue ) import GHC.Core.Type hiding ( substTy ) import GHC.Core.TyCon (TyCon, tyConName ) import GHC.Core.Multiplicity diff --git a/compiler/GHC/Core/Predicate.hs b/compiler/GHC/Core/Predicate.hs index 4e2b3f76f36..b354260d23f 100644 --- a/compiler/GHC/Core/Predicate.hs +++ b/compiler/GHC/Core/Predicate.hs @@ -30,7 +30,11 @@ module GHC.Core.Predicate ( isIPPred_maybe, -- Evidence variables - DictId, isEvVar, isDictId + DictId, isEvVar, isDictId, + + -- * Well-scoped free variables + scopedSort, tyCoVarsOfTypeWellScoped, + tyCoVarsOfTypesWellScoped ) where @@ -39,9 +43,11 @@ import GHC.Prelude import GHC.Core.Type import GHC.Core.Class import GHC.Core.TyCo.Compare( eqType ) +import GHC.Core.TyCo.FVs( tyCoVarsOfTypeList, tyCoVarsOfTypesList ) import GHC.Core.TyCon import GHC.Core.TyCon.RecWalk import GHC.Types.Var +import GHC.Types.Var.Set import GHC.Core.Multiplicity ( scaledThing ) import GHC.Builtin.Names @@ -248,6 +254,14 @@ see Note [Equality superclasses in quantified constraints] in GHC.Tc.Solver.Dict. -} +isPredTy :: HasDebugCallStack => Type -> Bool +-- Precondition: expects a type that classifies values +-- See Note [Types for coercions, predicates, and evidence] in GHC.Core.TyCo.Rep +-- Returns True for types of kind (CONSTRAINT _), False for ones of kind (TYPE _) +isPredTy ty = case typeTypeOrConstraint ty of + TypeLike -> False + ConstraintLike -> True + -- | Does this type classify a core (unlifted) Coercion? -- At either role nominal or representational -- (t1 ~# t2) or (t1 ~R# t2) @@ -492,3 +506,113 @@ isEvVar var = isEvVarType (varType var) isDictId :: Id -> Bool isDictId id = isDictTy (varType id) + + +{- ********************************************************************* +* * + scopedSort + + This function lives here becuase it uses isEvVar +* * +********************************************************************* -} + +{- Note [ScopedSort] +~~~~~~~~~~~~~~~~~~~~ +Consider + + foo :: Proxy a -> Proxy (b :: k) -> Proxy (a :: k2) -> () + +This function type is implicitly generalised over [a, b, k, k2]. These +variables will be Specified; that is, they will be available for visible +type application. This is because they are written in the type signature +by the user. + +However, we must ask: what order will they appear in? In cases without +dependency, this is easy: we just use the lexical left-to-right ordering +of first occurrence. With dependency, we cannot get off the hook so +easily. + +We thus state: + + * These variables appear in the order as given by ScopedSort, where + the input to ScopedSort is the left-to-right order of first occurrence. + +Note that this applies only to *implicit* quantification, without a +`forall`. If the user writes a `forall`, then we just use the order given. + +ScopedSort is defined thusly (as proposed in #15743): + * Work left-to-right through the input list, with a cursor. + * If variable v at the cursor is depended on by any earlier variable w, + move v immediately before the leftmost such w. + +INVARIANT: The prefix of variables before the cursor form a valid telescope. + +Note that ScopedSort makes sense only after type inference is done and all +types/kinds are fully settled and zonked. + +-} + +-- | Do a topological sort on a list of tyvars, +-- so that binders occur before occurrences +-- E.g. given @[ a::k, k::Type, b::k ]@ +-- it'll return a well-scoped list @[ k::Type, a::k, b::k ]@. +-- +-- This is a deterministic sorting operation +-- (that is, doesn't depend on Uniques). +-- +-- It is also meant to be stable: that is, variables should not +-- be reordered unnecessarily. This is specified in Note [ScopedSort] +-- See also Note [Ordering of implicit variables] in "GHC.Rename.HsType" + +scopedSort :: [Var] -> [Var] +scopedSort = go [] [] + where + go :: [Var] -- already sorted, in reverse order + -> [TyCoVarSet] -- each set contains all the variables which must be placed + -- before the tv corresponding to the set; they are accumulations + -- of the fvs in the sorted Var's types + + -- This list is in 1-to-1 correspondence with the sorted Vars + -- INVARIANT: + -- all (\tl -> all (`subVarSet` head tl) (tail tl)) (tails fv_list) + -- That is, each set in the list is a superset of all later sets. + + -> [Var] -- yet to be sorted + -> [Var] + go acc _fv_list [] = reverse acc + go acc fv_list (tv:tvs) + = go acc' fv_list' tvs + where + (acc', fv_list') = insert tv acc fv_list + + insert :: Var -- var to insert + -> [Var] -- sorted list, in reverse order + -> [TyCoVarSet] -- list of fvs, as above + -> ([Var], [TyCoVarSet]) -- augmented lists + -- Generally we put the new Var at the front of the accumulating list + -- (leading to a stable sort) unless there is are reason to put it later. + insert v [] [] = ([v], [tyCoVarsOfType (varType v)]) + insert v (a:as) (fvs:fvss) + | (isTyVar v && isId a) || -- TyVars precede Ids + (isEvVar v && isId a && not (isEvVar a)) || -- DictIds precede non-DictIds + (v `elemVarSet` fvs) + -- (a) put Ids after TyVars, and (b) respect dependencies + , (as', fvss') <- insert v as fvss + = (a:as', fvs `unionVarSet` fv_v : fvss') + + | otherwise -- Put `v` at the front + = (v:a:as, fvs `unionVarSet` fv_v : fvs : fvss) + where + fv_v = tyCoVarsOfType (varType v) + + -- lists not in correspondence + insert _ _ _ = panic "scopedSort" + +-- | Get the free vars of a type in scoped order +tyCoVarsOfTypeWellScoped :: Type -> [TyVar] +tyCoVarsOfTypeWellScoped = scopedSort . tyCoVarsOfTypeList + +-- | Get the free vars of types in scoped order +tyCoVarsOfTypesWellScoped :: [Type] -> [TyVar] +tyCoVarsOfTypesWellScoped = scopedSort . tyCoVarsOfTypesList + diff --git a/compiler/GHC/Core/SimpleOpt.hs b/compiler/GHC/Core/SimpleOpt.hs index 9efc3484658..93e45ce0362 100644 --- a/compiler/GHC/Core/SimpleOpt.hs +++ b/compiler/GHC/Core/SimpleOpt.hs @@ -8,7 +8,7 @@ module GHC.Core.SimpleOpt ( SimpleOpts (..), defaultSimpleOpts, -- ** Simple expression optimiser - simpleOptPgm, simpleOptExpr, simpleOptExprWith, + simpleOptPgm, simpleOptExpr, simpleOptExprNoInline, simpleOptExprWith, -- ** Join points joinPointBinding_maybe, joinPointBindings_maybe, @@ -114,6 +114,8 @@ data SimpleOpts = SimpleOpts { so_uf_opts :: !UnfoldingOpts -- ^ Unfolding options , so_co_opts :: !OptCoercionOpts -- ^ Coercion optimiser options , so_eta_red :: !Bool -- ^ Eta reduction on? + , so_inline :: !Bool -- ^ False <=> do no inlining whatsoever, + -- even for trivial or used-once things } -- | Default options for the Simple optimiser. @@ -122,6 +124,7 @@ defaultSimpleOpts = SimpleOpts { so_uf_opts = defaultUnfoldingOpts , so_co_opts = OptCoercionOpts { optCoercionEnabled = False } , so_eta_red = False + , so_inline = True } simpleOptExpr :: HasDebugCallStack => SimpleOpts -> CoreExpr -> CoreExpr @@ -159,6 +162,17 @@ simpleOptExpr opts expr -- It's a bit painful to call exprFreeVars, because it makes -- three passes instead of two (occ-anal, and go) +simpleOptExprNoInline :: HasDebugCallStack => SimpleOpts -> CoreExpr -> CoreExpr +-- A variant of simpleOptExpr, but without +-- occurrence analysis or inlining of any kind. +-- Result: we don't inline evidence bindings, which is useful for the specialiser +simpleOptExprNoInline opts expr + = simple_opt_expr init_env expr + where + init_opts = opts { so_inline = False } + init_env = (emptyEnv init_opts) { soe_subst = init_subst } + init_subst = mkEmptySubst (mkInScopeSet (exprFreeVars expr)) + simpleOptExprWith :: HasDebugCallStack => SimpleOpts -> Subst -> InExpr -> OutExpr -- See Note [The simple optimiser] simpleOptExprWith opts subst expr @@ -468,7 +482,7 @@ simple_bind_pair :: SimpleOptEnv -- (simple_bind_pair subst in_var out_rhs) -- either extends subst with (in_var -> out_rhs) -- or returns Nothing -simple_bind_pair env@(SOE { soe_inl = inl_env, soe_subst = subst }) +simple_bind_pair env@(SOE { soe_inl = inl_env, soe_subst = subst, soe_opts = opts }) in_bndr mb_out_bndr clo@(rhs_env, in_rhs) top_level | Type ty <- in_rhs -- let a::* = TYPE ty in <body> @@ -510,6 +524,7 @@ simple_bind_pair env@(SOE { soe_inl = inl_env, soe_subst = subst }) pre_inline_unconditionally :: Bool pre_inline_unconditionally + | not (so_inline opts) = False -- Not if so_inline is False | isExportedId in_bndr = False | stable_unf = False | not active = False -- Note [Inline prag in simplOpt] @@ -561,13 +576,14 @@ simple_out_bind_pair :: SimpleOptEnv -> InId -> Maybe OutId -> OutExpr -> OccInfo -> Bool -> Bool -> TopLevelFlag -> (SimpleOptEnv, Maybe (OutVar, OutExpr)) -simple_out_bind_pair env in_bndr mb_out_bndr out_rhs +simple_out_bind_pair env@(SOE { soe_subst = subst, soe_opts = opts }) + in_bndr mb_out_bndr out_rhs occ_info active stable_unf top_level | assertPpr (isNonCoVarId in_bndr) (ppr in_bndr) -- Type and coercion bindings are caught earlier -- See Note [Core type and coercion invariant] post_inline_unconditionally - = ( env' { soe_subst = extendIdSubst (soe_subst env) in_bndr out_rhs } + = ( env' { soe_subst = extendIdSubst subst in_bndr out_rhs } , Nothing) | otherwise @@ -580,6 +596,7 @@ simple_out_bind_pair env in_bndr mb_out_bndr out_rhs post_inline_unconditionally :: Bool post_inline_unconditionally + | not (so_inline opts) = False -- Not if so_inline is False | isExportedId in_bndr = False -- Note [Exported Ids and trivial RHSs] | stable_unf = False -- Note [Stable unfoldings and postInlineUnconditionally] | not active = False -- in GHC.Core.Opt.Simplify.Utils @@ -852,7 +869,7 @@ too. Achieving all this is surprisingly tricky: (MC1) We must compulsorily unfold MkAge to a cast. See Note [Compulsory newtype unfolding] in GHC.Types.Id.Make -(MC2) We must compulsorily unfolding coerce on the rule LHS, yielding +(MC2) We must compulsorily unfold coerce on the rule LHS, yielding forall a b (dict :: Coercible * a b). map @a @b (\(x :: a) -> case dict of MkCoercible (co :: a ~R# b) -> x |> co) = ... @@ -869,7 +886,6 @@ too. Achieving all this is surprisingly tricky: Unfortunately, this still abstracts over a Coercible dictionary. We really want it to abstract over the ~R# evidence. So, we have Desugar.unfold_coerce, which transforms the above to - Desugar) forall a b (co :: a ~R# b). let dict = MkCoercible @* @a @b co in @@ -894,7 +910,7 @@ too. Achieving all this is surprisingly tricky: (MC4) The map/coerce rule is the only compelling reason for having a RULE that quantifies over a coercion variable, something that is otherwise Very Deeply - Suspicous. See Note [Casts in the template] in GHC.Core.Rules. Ugh! + Suspicious. See Note [Casts in the template] in GHC.Core.Rules. Ugh! This is all a fair amount of special-purpose hackery, but it's for a good cause. And it won't hurt other RULES and such that it comes across. diff --git a/compiler/GHC/Core/Tidy.hs b/compiler/GHC/Core/Tidy.hs index de4fcbdc265..1e19467c0ea 100644 --- a/compiler/GHC/Core/Tidy.hs +++ b/compiler/GHC/Core/Tidy.hs @@ -16,12 +16,12 @@ import GHC.Prelude import GHC.Core import GHC.Core.Type - +import GHC.Core.TyCo.Tidy import GHC.Core.Seq ( seqUnfolding ) + import GHC.Types.Id import GHC.Types.Id.Info import GHC.Types.Demand ( zapDmdEnvSig, isStrUsedDmd ) -import GHC.Core.Coercion ( tidyCo ) import GHC.Types.Var import GHC.Types.Var.Env import GHC.Types.Unique (getUnique) @@ -30,6 +30,7 @@ import GHC.Types.Name hiding (tidyNameOcc) import GHC.Types.Name.Set import GHC.Types.SrcLoc import GHC.Types.Tickish + import GHC.Data.Maybe import GHC.Utils.Misc import Data.List (mapAccumL) diff --git a/compiler/GHC/Core/TyCo/FVs.hs b/compiler/GHC/Core/TyCo/FVs.hs index 58a58fb454a..375fd712067 100644 --- a/compiler/GHC/Core/TyCo/FVs.hs +++ b/compiler/GHC/Core/TyCo/FVs.hs @@ -40,10 +40,6 @@ module GHC.Core.TyCo.FVs -- * Occurrence-check expansion occCheckExpand, - -- * Well-scoped free variables - scopedSort, tyCoVarsOfTypeWellScoped, - tyCoVarsOfTypesWellScoped, - -- * Closing over kinds closeOverKindsDSet, closeOverKindsList, closeOverKinds, @@ -72,7 +68,6 @@ import GHC.Types.Unique.Set import GHC.Types.Var.Set import GHC.Types.Var.Env import GHC.Utils.Misc -import GHC.Utils.Panic import GHC.Data.Pair import Data.Semigroup @@ -989,107 +984,6 @@ noFreeVarsOfCo co = not $ DM.getAny (f co) where (_, _, f, _) = foldTyCo (afvFolder (const True)) emptyVarSet -{- ********************************************************************* -* * - scopedSort -* * -********************************************************************* -} - -{- Note [ScopedSort] -~~~~~~~~~~~~~~~~~~~~ -Consider - - foo :: Proxy a -> Proxy (b :: k) -> Proxy (a :: k2) -> () - -This function type is implicitly generalised over [a, b, k, k2]. These -variables will be Specified; that is, they will be available for visible -type application. This is because they are written in the type signature -by the user. - -However, we must ask: what order will they appear in? In cases without -dependency, this is easy: we just use the lexical left-to-right ordering -of first occurrence. With dependency, we cannot get off the hook so -easily. - -We thus state: - - * These variables appear in the order as given by ScopedSort, where - the input to ScopedSort is the left-to-right order of first occurrence. - -Note that this applies only to *implicit* quantification, without a -`forall`. If the user writes a `forall`, then we just use the order given. - -ScopedSort is defined thusly (as proposed in #15743): - * Work left-to-right through the input list, with a cursor. - * If variable v at the cursor is depended on by any earlier variable w, - move v immediately before the leftmost such w. - -INVARIANT: The prefix of variables before the cursor form a valid telescope. - -Note that ScopedSort makes sense only after type inference is done and all -types/kinds are fully settled and zonked. - --} - --- | Do a topological sort on a list of tyvars, --- so that binders occur before occurrences --- E.g. given [ a::k, k::*, b::k ] --- it'll return a well-scoped list [ k::*, a::k, b::k ] --- --- This is a deterministic sorting operation --- (that is, doesn't depend on Uniques). --- --- It is also meant to be stable: that is, variables should not --- be reordered unnecessarily. This is specified in Note [ScopedSort] --- See also Note [Ordering of implicit variables] in "GHC.Rename.HsType" - -scopedSort :: [TyCoVar] -> [TyCoVar] -scopedSort = go [] [] - where - go :: [TyCoVar] -- already sorted, in reverse order - -> [TyCoVarSet] -- each set contains all the variables which must be placed - -- before the tv corresponding to the set; they are accumulations - -- of the fvs in the sorted tvs' kinds - - -- This list is in 1-to-1 correspondence with the sorted tyvars - -- INVARIANT: - -- all (\tl -> all (`subVarSet` head tl) (tail tl)) (tails fv_list) - -- That is, each set in the list is a superset of all later sets. - - -> [TyCoVar] -- yet to be sorted - -> [TyCoVar] - go acc _fv_list [] = reverse acc - go acc fv_list (tv:tvs) - = go acc' fv_list' tvs - where - (acc', fv_list') = insert tv acc fv_list - - insert :: TyCoVar -- var to insert - -> [TyCoVar] -- sorted list, in reverse order - -> [TyCoVarSet] -- list of fvs, as above - -> ([TyCoVar], [TyCoVarSet]) -- augmented lists - insert tv [] [] = ([tv], [tyCoVarsOfType (tyVarKind tv)]) - insert tv (a:as) (fvs:fvss) - | tv `elemVarSet` fvs - , (as', fvss') <- insert tv as fvss - = (a:as', fvs `unionVarSet` fv_tv : fvss') - - | otherwise - = (tv:a:as, fvs `unionVarSet` fv_tv : fvs : fvss) - where - fv_tv = tyCoVarsOfType (tyVarKind tv) - - -- lists not in correspondence - insert _ _ _ = panic "scopedSort" - --- | Get the free vars of a type in scoped order -tyCoVarsOfTypeWellScoped :: Type -> [TyVar] -tyCoVarsOfTypeWellScoped = scopedSort . tyCoVarsOfTypeList - --- | Get the free vars of types in scoped order -tyCoVarsOfTypesWellScoped :: [Type] -> [TyVar] -tyCoVarsOfTypesWellScoped = scopedSort . tyCoVarsOfTypesList - {- ************************************************************************ * * diff --git a/compiler/GHC/Core/TyCo/Ppr.hs b/compiler/GHC/Core/TyCo/Ppr.hs index 137f7406163..b5fda91c203 100644 --- a/compiler/GHC/Core/TyCo/Ppr.hs +++ b/compiler/GHC/Core/TyCo/Ppr.hs @@ -41,8 +41,11 @@ import GHC.Core.TyCo.Rep import GHC.Core.TyCo.Tidy import GHC.Core.TyCo.FVs import GHC.Core.Class -import GHC.Types.Var +import GHC.Core.Predicate( scopedSort ) import GHC.Core.Multiplicity( pprArrowWithMultiplicity ) + +import GHC.Types.Var + import GHC.Iface.Type import GHC.Types.Var.Set diff --git a/compiler/GHC/Core/TyCo/Tidy.hs b/compiler/GHC/Core/TyCo/Tidy.hs index f5eb845a7ed..69c460965b9 100644 --- a/compiler/GHC/Core/TyCo/Tidy.hs +++ b/compiler/GHC/Core/TyCo/Tidy.hs @@ -19,6 +19,7 @@ module GHC.Core.TyCo.Tidy import GHC.Prelude import GHC.Data.FastString +import GHC.Core.Predicate( scopedSort ) import GHC.Core.TyCo.Rep import GHC.Core.TyCo.FVs import GHC.Types.Name hiding (varName) diff --git a/compiler/GHC/Core/Type.hs b/compiler/GHC/Core/Type.hs index dd6b2a09d71..a2be9f39629 100644 --- a/compiler/GHC/Core/Type.hs +++ b/compiler/GHC/Core/Type.hs @@ -69,8 +69,6 @@ module GHC.Core.Type ( mkCharLitTy, isCharLitTy, isLitTy, - isPredTy, - getRuntimeRep, splitRuntimeRep_maybe, kindRep_maybe, kindRep, getLevity, levityType_maybe, @@ -177,10 +175,6 @@ module GHC.Core.Type ( closeOverKindsDSet, closeOverKindsList, closeOverKinds, - -- * Well-scoped lists of variables - scopedSort, tyCoVarsOfTypeWellScoped, - tyCoVarsOfTypesWellScoped, - -- * Forcing evaluation of types seqType, seqTypes, @@ -222,17 +216,6 @@ module GHC.Core.Type ( substTyCoBndr, substTyVarToTyVar, cloneTyVarBndr, cloneTyVarBndrs, lookupTyVar, - -- * Tidying type related things up for printing - tidyType, tidyTypes, - tidyOpenType, tidyOpenTypes, - tidyOpenTypeX, tidyOpenTypesX, - tidyVarBndr, tidyVarBndrs, - tidyFreeTyCoVars, - tidyFreeTyCoVarX, tidyFreeTyCoVarsX, - tidyTyCoVarOcc, - tidyTopType, - tidyForAllTyBinder, tidyForAllTyBinders, - -- * Kinds isTYPEorCONSTRAINT, isConcreteType, @@ -248,7 +231,6 @@ import GHC.Types.Basic import GHC.Core.TyCo.Rep import GHC.Core.TyCo.Subst -import GHC.Core.TyCo.Tidy import GHC.Core.TyCo.FVs -- friends: @@ -2760,14 +2742,6 @@ typeTypeOrConstraint ty | otherwise -> pprPanic "typeOrConstraint" (ppr ty <+> dcolon <+> ppr (typeKind ty)) -isPredTy :: HasDebugCallStack => Type -> Bool --- Precondition: expects a type that classifies values --- See Note [Types for coercions, predicates, and evidence] in GHC.Core.TyCo.Rep --- Returns True for types of kind (CONSTRAINT _), False for ones of kind (TYPE _) -isPredTy ty = case typeTypeOrConstraint ty of - TypeLike -> False - ConstraintLike -> True - -- | Does this classify a type allowed to have values? Responds True to things -- like *, TYPE Lifted, TYPE IntRep, TYPE v, Constraint. isTYPEorCONSTRAINT :: Kind -> Bool diff --git a/compiler/GHC/Core/Type.hs-boot b/compiler/GHC/Core/Type.hs-boot index b4cf5d8a222..fef4308ac4c 100644 --- a/compiler/GHC/Core/Type.hs-boot +++ b/compiler/GHC/Core/Type.hs-boot @@ -9,31 +9,18 @@ import GHC.Utils.Misc import GHC.Types.Var( FunTyFlag, TyVar ) import GHC.Types.Basic( TypeOrConstraint ) -isPredTy :: HasDebugCallStack => Type -> Bool -isCoercionTy :: Type -> Bool -mkAppTy :: Type -> Type -> Type -mkCastTy :: Type -> Coercion -> Type -mkTyConApp :: TyCon -> [Type] -> Type -mkCoercionTy :: Coercion -> Type -piResultTy :: HasDebugCallStack => Type -> Type -> Type - -typeKind :: HasDebugCallStack => Type -> Type -typeTypeOrConstraint :: HasDebugCallStack => Type -> TypeOrConstraint - -coreView :: Type -> Maybe Type -rewriterView :: Type -> Maybe Type -isRuntimeRepTy :: Type -> Bool -isLevityTy :: Type -> Bool -isMultiplicityTy :: Type -> Bool +coreView :: Type -> Maybe Type +rewriterView :: Type -> Maybe Type +chooseFunTyFlag :: HasDebugCallStack => Type -> Type -> FunTyFlag +typeKind :: HasDebugCallStack => Type -> Type +isCoercionTy :: Type -> Bool +mkAppTy :: Type -> Type -> Type +mkCastTy :: Type -> Coercion -> Type +mkTyConApp :: TyCon -> [Type] -> Type +getLevity :: HasDebugCallStack => Type -> Type +getTyVar_maybe :: Type -> Maybe TyVar isLiftedTypeKind :: Type -> Bool -splitTyConApp_maybe :: HasDebugCallStack => Type -> Maybe (TyCon, [Type]) -tyConAppTyCon_maybe :: Type -> Maybe TyCon -getTyVar_maybe :: Type -> Maybe TyVar - -getLevity :: HasDebugCallStack => Type -> Type - partitionInvisibleTypes :: TyCon -> [Type] -> ([Type], [Type]) - -chooseFunTyFlag :: HasDebugCallStack => Type -> Type -> FunTyFlag +typeTypeOrConstraint :: HasDebugCallStack => Type -> TypeOrConstraint diff --git a/compiler/GHC/Core/Unify.hs b/compiler/GHC/Core/Unify.hs index 5b2c3ec2e3f..58664a5ebfd 100644 --- a/compiler/GHC/Core/Unify.hs +++ b/compiler/GHC/Core/Unify.hs @@ -34,6 +34,7 @@ import GHC.Types.Name( Name, mkSysTvName, mkSystemVarName ) import GHC.Builtin.Names( tYPETyConKey, cONSTRAINTTyConKey ) import GHC.Core.Type hiding ( getTvSubstEnv ) import GHC.Core.Coercion hiding ( getCvSubstEnv ) +import GHC.Core.Predicate( scopedSort ) import GHC.Core.TyCon import GHC.Core.TyCo.Rep import GHC.Core.TyCo.Compare ( eqType, tcEqType ) diff --git a/compiler/GHC/CoreToIface.hs b/compiler/GHC/CoreToIface.hs index 32a8af16c88..f6d0bad23d8 100644 --- a/compiler/GHC/CoreToIface.hs +++ b/compiler/GHC/CoreToIface.hs @@ -63,7 +63,7 @@ import GHC.Core.Multiplicity import GHC.Core.PatSyn import GHC.Core.TyCo.Rep import GHC.Core.TyCo.Compare( eqType ) -import GHC.Core.TyCo.Tidy ( tidyCo ) +import GHC.Core.TyCo.Tidy import GHC.Builtin.Types.Prim ( eqPrimTyCon, eqReprPrimTyCon ) import GHC.Builtin.Types ( heqTyCon ) diff --git a/compiler/GHC/Driver/Config.hs b/compiler/GHC/Driver/Config.hs index 642cc01d5cb..4537e4c2d2d 100644 --- a/compiler/GHC/Driver/Config.hs +++ b/compiler/GHC/Driver/Config.hs @@ -25,6 +25,7 @@ initSimpleOpts dflags = SimpleOpts { so_uf_opts = unfoldingOpts dflags , so_co_opts = initOptCoercionOpts dflags , so_eta_red = gopt Opt_DoEtaReduction dflags + , so_inline = True } -- | Extract GHCi options from DynFlags and step diff --git a/compiler/GHC/Driver/Flags.hs b/compiler/GHC/Driver/Flags.hs index c776e7058ad..5de4e975cfd 100644 --- a/compiler/GHC/Driver/Flags.hs +++ b/compiler/GHC/Driver/Flags.hs @@ -1081,6 +1081,8 @@ data WarningFlag = | Opt_WarnDataKindsTC -- Since 9.10 | Opt_WarnDefaultedExceptionContext -- Since 9.10 | Opt_WarnViewPatternSignatures -- Since 9.12 + | Opt_WarnUselessSpecialisations -- Since 9.14 + | Opt_WarnDeprecatedPragmas -- Since 9.14 deriving (Eq, Ord, Show, Enum, Bounded) -- | Return the names of a WarningFlag @@ -1197,6 +1199,8 @@ warnFlagNames wflag = case wflag of Opt_WarnDataKindsTC -> "data-kinds-tc" :| [] Opt_WarnDefaultedExceptionContext -> "defaulted-exception-context" :| [] Opt_WarnViewPatternSignatures -> "view-pattern-signatures" :| [] + Opt_WarnUselessSpecialisations -> "useless-specialisations" :| ["useless-specializations"] + Opt_WarnDeprecatedPragmas -> "deprecated-pragmas" :| [] -- ----------------------------------------------------------------------------- -- Standard sets of warning options @@ -1338,7 +1342,9 @@ standardWarnings -- see Note [Documenting warning flags] Opt_WarnInconsistentFlags, Opt_WarnDataKindsTC, Opt_WarnTypeEqualityOutOfScope, - Opt_WarnViewPatternSignatures + Opt_WarnViewPatternSignatures, + Opt_WarnUselessSpecialisations, + Opt_WarnDeprecatedPragmas ] -- | Things you get with -W diff --git a/compiler/GHC/Driver/Session.hs b/compiler/GHC/Driver/Session.hs index 86b8286edbf..2944c166d69 100644 --- a/compiler/GHC/Driver/Session.hs +++ b/compiler/GHC/Driver/Session.hs @@ -2358,6 +2358,8 @@ wWarningFlagsDeps = [minBound..maxBound] >>= \x -> case x of Opt_WarnDataKindsTC -> warnSpec x Opt_WarnDefaultedExceptionContext -> warnSpec x Opt_WarnViewPatternSignatures -> warnSpec x + Opt_WarnUselessSpecialisations -> warnSpec x + Opt_WarnDeprecatedPragmas -> warnSpec x warningGroupsDeps :: [(Deprecation, FlagSpec WarningGroup)] warningGroupsDeps = map mk warningGroups diff --git a/compiler/GHC/Hs/Binds.hs b/compiler/GHC/Hs/Binds.hs index f130b970c72..71f22be4d1d 100644 --- a/compiler/GHC/Hs/Binds.hs +++ b/compiler/GHC/Hs/Binds.hs @@ -26,14 +26,16 @@ Datatype for: @BindGroup@, @Bind@, @Sig@, @Bind@. module GHC.Hs.Binds ( module Language.Haskell.Syntax.Binds , module GHC.Hs.Binds + , HsRuleBndrsAnn(..) ) where import GHC.Prelude import Language.Haskell.Syntax.Extension import Language.Haskell.Syntax.Binds +import Language.Haskell.Syntax.Expr( LHsExpr ) -import {-# SOURCE #-} GHC.Hs.Expr ( pprExpr, pprFunBind, pprPatBind ) +import {-# SOURCE #-} GHC.Hs.Expr ( pprExpr, pprLExpr, pprFunBind, pprPatBind ) import {-# SOURCE #-} GHC.Hs.Pat (pprLPat ) import GHC.Data.BooleanFormula ( LBooleanFormula, pprBooleanFormulaNormal ) @@ -724,6 +726,11 @@ type instance XSpecInstSig (GhcPass p) = ((EpaLocation, EpToken "instance", type instance XMinimalSig (GhcPass p) = ((EpaLocation, EpToken "#-}"), SourceText) type instance XSCCFunSig (GhcPass p) = ((EpaLocation, EpToken "#-}"), SourceText) type instance XCompleteMatchSig (GhcPass p) = ((EpaLocation, Maybe TokDcolon, EpToken "#-}"), SourceText) + +type instance XSpecSigE GhcPs = AnnSpecSig +type instance XSpecSigE GhcRn = Name +type instance XSpecSigE GhcTc = NoExtField + -- SourceText: See Note [Pragma source text] in "GHC.Types.SourceText" type instance XXSig GhcPs = DataConCantHappen type instance XXSig GhcRn = IdSig @@ -738,7 +745,7 @@ data AnnSpecSig = AnnSpecSig { ass_open :: EpaLocation, ass_close :: EpToken "#-}", - ass_dcolon :: TokDcolon, + ass_dcolon :: Maybe TokDcolon, ass_act :: ActivationAnn } deriving Data @@ -817,25 +824,47 @@ instance NoAnn AnnSig where -- | Type checker Specialisation Pragmas -- --- 'TcSpecPrags' conveys @SPECIALISE@ pragmas from the type checker to the desugarer +-- 'TcSpecPrags' conveys @SPECIALISE@ pragmas from the type checker +-- to the desugarer data TcSpecPrags = IsDefaultMethod -- ^ Super-specialised: a default method should -- be macro-expanded at every call site | SpecPrags [LTcSpecPrag] - deriving Data --- | Located Type checker Specification Pragmas +-- | Located Type checker Specialisation Pragmas type LTcSpecPrag = Located TcSpecPrag --- | Type checker Specification Pragma +-- | Type checker Specialisation Pragma +-- +-- This data type is used to communicate between the typechecker and +-- the desugarer. data TcSpecPrag + -- | Old-form specialise pragma = SpecPrag - Id - HsWrapper - InlinePragma - -- ^ The Id to be specialised, a wrapper that specialises the - -- polymorphic function, and inlining spec for the specialised function - deriving Data + Id + -- ^ 'Id' to be specialised + HsWrapper + -- ^ wrapper that specialises the polymorphic function + InlinePragma + -- ^ inlining spec for the specialised function + -- | New-form specialise pragma + | SpecPragE + { spe_fn_nm :: Name + -- ^ 'Name' of the 'Id' being specialised + , spe_fn_id :: Id + -- ^ 'Id' being specialised + -- + -- Note that 'spe_fn_nm' may differ from @'idName' 'spe_fn_id'@ + -- in the case of instance methods, where the 'Name' is the + -- class-op selector but the 'spe_fn_id' is that for the local method + , spe_inl :: InlinePragma + -- ^ (optional) INLINE annotation and activation phase annotation + + , spe_bndrs :: [Var] + -- ^ TyVars, EvVars, and Ids + , spe_call :: LHsExpr GhcTc + -- ^ The type-checked specialise expression + } noSpecPrags :: TcSpecPrags noSpecPrags = SpecPrags [] @@ -858,19 +887,34 @@ ppr_sig (ClassOpSig _ is_deflt vars ty) | is_deflt = text "default" <+> pprVarSig (map unLoc vars) (ppr ty) | otherwise = pprVarSig (map unLoc vars) (ppr ty) ppr_sig (FixSig _ fix_sig) = ppr fix_sig -ppr_sig (SpecSig _ var ty inl@(InlinePragma { inl_inline = spec })) - = pragSrcBrackets (inlinePragmaSource inl) pragmaSrc (pprSpec (unLoc var) - (interpp'SP ty) inl) + +ppr_sig (SpecSig _ var ty inl@(InlinePragma { inl_src = src, inl_inline = spec })) + = pragSrcBrackets (inlinePragmaSource inl) pragmaSrc $ + pprSpec (unLoc var) (interpp'SP ty) inl where pragmaSrc = case spec of - NoUserInlinePrag -> "{-# " ++ extractSpecPragName (inl_src inl) - _ -> "{-# " ++ extractSpecPragName (inl_src inl) ++ "_INLINE" + NoUserInlinePrag -> "{-# " ++ extractSpecPragName src + _ -> "{-# " ++ extractSpecPragName src ++ "_INLINE" + +ppr_sig (SpecSigE _ bndrs spec_e inl@(InlinePragma { inl_src = src, inl_inline = spec })) + = pragSrcBrackets (inlinePragmaSource inl) pragmaSrc $ + pp_inl <+> hang (ppr bndrs) 2 (pprLExpr spec_e) + where + -- SPECIALISE or SPECIALISE_INLINE + pragmaSrc = case spec of + NoUserInlinePrag -> "{-# " ++ extractSpecPragName src + _ -> "{-# " ++ extractSpecPragName src ++ "_INLINE" + + pp_inl | isDefaultInlinePragma inl = empty + | otherwise = pprInline inl + ppr_sig (InlineSig _ var inl) = ppr_pfx <+> pprInline inl <+> pprPrefixOcc (unLoc var) <+> text "#-}" where ppr_pfx = case inlinePragmaSource inl of SourceText src -> ftext src NoSourceText -> text "{-#" <+> inlinePragmaName (inl_inline inl) + ppr_sig (SpecInstSig (_, src) ty) = pragSrcBrackets src "{-# pragma" (text "instance" <+> ppr ty) ppr_sig (MinimalSig (_, src) bf) @@ -905,6 +949,7 @@ hsSigDoc (ClassOpSig _ is_deflt _ _) | is_deflt = text "default type signature" | otherwise = text "class method signature" hsSigDoc (SpecSig _ _ _ inl) = (inlinePragmaName . inl_inline $ inl) <+> text "pragma" +hsSigDoc (SpecSigE _ _ _ inl) = (inlinePragmaName . inl_inline $ inl) <+> text "pragma" hsSigDoc (InlineSig _ _ prag) = (inlinePragmaName . inl_inline $ prag) <+> text "pragma" -- Using the 'inlinePragmaName' function ensures that the pragma name for any -- one of the INLINE/INLINABLE/NOINLINE pragmas are printed after being extracted @@ -967,10 +1012,58 @@ pprTcSpecPrags (SpecPrags ps) = vcat (map (ppr . unLoc) ps) instance Outputable TcSpecPrag where ppr (SpecPrag var _ inl) = text (extractSpecPragName $ inl_src inl) <+> pprSpec var (text "<type>") inl + ppr (SpecPragE { spe_bndrs = bndrs, spe_call = spec_e, spe_inl = inl }) + = text (extractSpecPragName $ inl_src inl) + <+> hang (ppr bndrs) 2 (pprLExpr spec_e) pprMinimalSig :: OutputableBndrId p => LBooleanFormula (GhcPass p) -> SDoc pprMinimalSig (L _ bf) = pprBooleanFormulaNormal bf + +{- ********************************************************************* +* * + RuleBndrs +* * +********************************************************************* -} + +data HsRuleBndrsAnn + = HsRuleBndrsAnn + { rb_tyanns :: Maybe (TokForall, EpToken ".") + -- ^ The locations of 'forall' and '.' for forall'd type vars + -- Using AddEpAnn to capture possible unicode variants + , rb_tmanns :: Maybe (TokForall, EpToken ".") + -- ^ The locations of 'forall' and '.' for forall'd term vars + -- Using AddEpAnn to capture possible unicode variants + } deriving (Data, Eq) + +instance NoAnn HsRuleBndrsAnn where + noAnn = HsRuleBndrsAnn Nothing Nothing + + + +type instance XXRuleBndrs (GhcPass _) = DataConCantHappen +type instance XCRuleBndrs GhcPs = HsRuleBndrsAnn +type instance XCRuleBndrs GhcRn = NoExtField +type instance XCRuleBndrs GhcTc = [Var] -- Binders of the rule, not + -- necessarily in dependency order + +type instance XRuleBndrSig (GhcPass _) = AnnTyVarBndr +type instance XCRuleBndr (GhcPass _) = AnnTyVarBndr +type instance XXRuleBndr (GhcPass _) = DataConCantHappen + +instance (OutputableBndrId p) => Outputable (RuleBndrs (GhcPass p)) where + ppr (RuleBndrs { rb_tyvs = tyvs, rb_tmvs = tmvs }) + = pp_forall_ty tyvs <+> pp_forall_tm tyvs + where + pp_forall_ty Nothing = empty + pp_forall_ty (Just qtvs) = forAllLit <+> fsep (map ppr qtvs) <> dot + pp_forall_tm Nothing | null tmvs = empty + pp_forall_tm _ = forAllLit <+> fsep (map ppr tmvs) <> dot + +instance (OutputableBndrId p) => Outputable (RuleBndr (GhcPass p)) where + ppr (RuleBndr _ name) = ppr name + ppr (RuleBndrSig _ name ty) = parens (ppr name <> dcolon <> ppr ty) + {- ************************************************************************ * * @@ -982,6 +1075,7 @@ pprMinimalSig (L _ bf) = pprBooleanFormulaNormal bf type instance Anno (HsBindLR (GhcPass idL) (GhcPass idR)) = SrcSpanAnnA type instance Anno (IPBind (GhcPass p)) = SrcSpanAnnA type instance Anno (Sig (GhcPass p)) = SrcSpanAnnA +type instance Anno (RuleBndr (GhcPass p)) = EpAnnCO type instance Anno (FixitySig (GhcPass p)) = SrcSpanAnnA diff --git a/compiler/GHC/Hs/Decls.hs b/compiler/GHC/Hs/Decls.hs index b5576851722..76dcc941642 100644 --- a/compiler/GHC/Hs/Decls.hs +++ b/compiler/GHC/Hs/Decls.hs @@ -67,7 +67,7 @@ module GHC.Hs.Decls ( XViaStrategyPs(..), -- ** @RULE@ declarations LRuleDecls,RuleDecls(..),RuleDecl(..),LRuleDecl,HsRuleRn(..), - HsRuleAnn(..), ActivationAnn(..), + HsRuleAnn(..), RuleBndr(..),LRuleBndr, collectRuleBndrSigTys, flattenRuleDecls, pprFullRuleName, @@ -1317,15 +1317,13 @@ type instance XCRuleDecls GhcTc = SourceText type instance XXRuleDecls (GhcPass _) = DataConCantHappen -type instance XHsRule GhcPs = (HsRuleAnn, SourceText) +type instance XHsRule GhcPs = ((ActivationAnn, EpToken "="), SourceText) type instance XHsRule GhcRn = (HsRuleRn, SourceText) type instance XHsRule GhcTc = (HsRuleRn, SourceText) data HsRuleRn = HsRuleRn NameSet NameSet -- Free-vars from the LHS and RHS deriving Data -type instance XXRuleDecl (GhcPass _) = DataConCantHappen - data HsRuleAnn = HsRuleAnn { ra_tyanns :: Maybe (TokForall, EpToken ".") @@ -1337,13 +1335,11 @@ data HsRuleAnn instance NoAnn HsRuleAnn where noAnn = HsRuleAnn Nothing Nothing noAnn noAnn +type instance XXRuleDecl (GhcPass _) = DataConCantHappen + flattenRuleDecls :: [LRuleDecls (GhcPass p)] -> [LRuleDecl (GhcPass p)] flattenRuleDecls decls = concatMap (rds_rules . unLoc) decls -type instance XCRuleBndr (GhcPass _) = AnnTyVarBndr -type instance XRuleBndrSig (GhcPass _) = AnnTyVarBndr -type instance XXRuleBndr (GhcPass _) = DataConCantHappen - instance (OutputableBndrId p) => Outputable (RuleDecls (GhcPass p)) where ppr (HsRules { rds_ext = ext , rds_rules = rules }) @@ -1358,28 +1354,18 @@ instance (OutputableBndrId p) => Outputable (RuleDecl (GhcPass p)) where ppr (HsRule { rd_ext = ext , rd_name = name , rd_act = act - , rd_tyvs = tys - , rd_tmvs = tms + , rd_bndrs = bndrs , rd_lhs = lhs , rd_rhs = rhs }) = sep [pprFullRuleName st name <+> ppr act, - nest 4 (pp_forall_ty tys <+> pp_forall_tm tys - <+> pprExpr (unLoc lhs)), + nest 4 (ppr bndrs <+> pprExpr (unLoc lhs)), nest 6 (equals <+> pprExpr (unLoc rhs)) ] where - pp_forall_ty Nothing = empty - pp_forall_ty (Just qtvs) = forAllLit <+> fsep (map ppr qtvs) <> dot - pp_forall_tm Nothing | null tms = empty - pp_forall_tm _ = forAllLit <+> fsep (map ppr tms) <> dot st = case ghcPass @p of GhcPs | (_, st) <- ext -> st GhcRn | (_, st) <- ext -> st GhcTc | (_, st) <- ext -> st -instance (OutputableBndrId p) => Outputable (RuleBndr (GhcPass p)) where - ppr (RuleBndr _ name) = ppr name - ppr (RuleBndrSig _ name ty) = parens (ppr name <> dcolon <> ppr ty) - pprFullRuleName :: SourceText -> GenLocated a (RuleName) -> SDoc pprFullRuleName st (L _ n) = pprWithSourceText st (doubleQuotes $ ftext n) @@ -1509,7 +1495,6 @@ type instance Anno (ForeignDecl (GhcPass p)) = SrcSpanAnnA type instance Anno (RuleDecls (GhcPass p)) = SrcSpanAnnA type instance Anno (RuleDecl (GhcPass p)) = SrcSpanAnnA type instance Anno (SourceText, RuleName) = EpAnnCO -type instance Anno (RuleBndr (GhcPass p)) = EpAnnCO type instance Anno (WarnDecls (GhcPass p)) = SrcSpanAnnA type instance Anno (WarnDecl (GhcPass p)) = SrcSpanAnnA type instance Anno (AnnDecl (GhcPass p)) = SrcSpanAnnA diff --git a/compiler/GHC/Hs/Instances.hs b/compiler/GHC/Hs/Instances.hs index 6801211edd7..a77cc738740 100644 --- a/compiler/GHC/Hs/Instances.hs +++ b/compiler/GHC/Hs/Instances.hs @@ -261,6 +261,13 @@ deriving instance Data (RuleBndr GhcPs) deriving instance Data (RuleBndr GhcRn) deriving instance Data (RuleBndr GhcTc) +deriving instance Data (RuleBndrs GhcPs) +deriving instance Data (RuleBndrs GhcRn) +deriving instance Data (RuleBndrs GhcTc) + +deriving instance Data TcSpecPrags +deriving instance Data TcSpecPrag + -- deriving instance (DataId p) => Data (WarnDecls p) deriving instance Data (WarnDecls GhcPs) deriving instance Data (WarnDecls GhcRn) diff --git a/compiler/GHC/Hs/Stats.hs b/compiler/GHC/Hs/Stats.hs index ead9fa6e04b..9d0d3ea5ba0 100644 --- a/compiler/GHC/Hs/Stats.hs +++ b/compiler/GHC/Hs/Stats.hs @@ -115,6 +115,7 @@ ppSourceStats short (L _ (HsModule{ hsmodExports = exports, hsmodImports = impor sig_info (FixSig {}) = (1,0,0,0,0) sig_info (TypeSig {}) = (0,1,0,0,0) sig_info (SpecSig {}) = (0,0,1,0,0) + sig_info (SpecSigE {}) = (0,0,1,0,0) sig_info (InlineSig {}) = (0,0,0,1,0) sig_info (ClassOpSig {}) = (0,0,0,0,1) sig_info _ = (0,0,0,0,0) diff --git a/compiler/GHC/HsToCore.hs b/compiler/GHC/HsToCore.hs index 4b0cfea3981..74ad71b764b 100644 --- a/compiler/GHC/HsToCore.hs +++ b/compiler/GHC/HsToCore.hs @@ -52,7 +52,7 @@ import GHC.Core.SimpleOpt ( simpleOptPgm, simpleOptExpr ) import GHC.Core.Utils import GHC.Core.Unfold.Make import GHC.Core.Coercion -import GHC.Core.Predicate( mkNomEqPred ) +import GHC.Core.Predicate( scopedSort, mkNomEqPred ) import GHC.Core.DataCon ( dataConWrapId ) import GHC.Core.Make import GHC.Core.Rules @@ -69,7 +69,7 @@ import GHC.Data.SizedSeq ( sizeSS ) import GHC.Utils.Error import GHC.Utils.Outputable -import GHC.Utils.Panic.Plain +import GHC.Utils.Panic import GHC.Utils.Misc import GHC.Utils.Monad import GHC.Utils.Logger @@ -277,12 +277,6 @@ deSugar hsc_env ; return (msgs, Just mod_guts) }}}} -dsImpSpecs :: [LTcSpecPrag] -> DsM (OrdList (Id,CoreExpr), [CoreRule]) -dsImpSpecs imp_specs - = do { spec_prs <- mapMaybeM (dsSpec Nothing) imp_specs - ; let (spec_binds, spec_rules) = unzip spec_prs - ; return (concatOL spec_binds, spec_rules) } - combineEvBinds :: [CoreBind] -> [(Id,CoreExpr)] -> [CoreBind] -- Top-level bindings can include coercion bindings, but not via superclasses -- See Note [Top-level evidence] @@ -427,11 +421,14 @@ Reason dsRule :: LRuleDecl GhcTc -> DsM (Maybe CoreRule) dsRule (L loc (HsRule { rd_name = name , rd_act = rule_act - , rd_tmvs = vars + , rd_bndrs = RuleBndrs { rb_ext = bndrs } , rd_lhs = lhs , rd_rhs = rhs })) = putSrcSpanDs (locA loc) $ - do { let bndrs' = [var | L _ (RuleBndr _ (L _ var)) <- vars] + do { let bndrs' = scopedSort bndrs + -- The scopedSort is because the binders may not + -- be in dependency order; see wrinkle (FTV1) in + -- Note [Free tyvars on rule LHS] in GHC.Tc.Zonk.Type ; lhs' <- unsetGOptM Opt_EnableRewriteRules $ unsetWOptM Opt_WarnIdentities $ diff --git a/compiler/GHC/HsToCore/Binds.hs b/compiler/GHC/HsToCore/Binds.hs index 3c1c40878b9..2e025ddf96e 100644 --- a/compiler/GHC/HsToCore/Binds.hs +++ b/compiler/GHC/HsToCore/Binds.hs @@ -1,5 +1,6 @@ {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE TypeFamilies #-} {- @@ -15,7 +16,8 @@ lower levels it is preserved with @let@/@letrec@s). -} module GHC.HsToCore.Binds - ( dsTopLHsBinds, dsLHsBinds, decomposeRuleLhs, dsSpec + ( dsTopLHsBinds, dsLHsBinds + , dsImpSpecs, decomposeRuleLhs , dsHsWrapper, dsHsWrappers , dsEvTerm, dsTcEvBinds, dsTcEvBinds_s, dsEvBinds , dsWarnOrphanRule @@ -42,7 +44,7 @@ import GHC.HsToCore.Pmc ( addTyCs, pmcGRHSs ) import GHC.Hs -- lots of things import GHC.Core -- lots of things -import GHC.Core.SimpleOpt ( simpleOptExpr ) +import GHC.Core.SimpleOpt import GHC.Core.Opt.OccurAnal ( occurAnalyseExpr ) import GHC.Core.InstEnv ( CanonicalEvidence(..) ) import GHC.Core.Make @@ -63,6 +65,7 @@ import GHC.Builtin.Types ( naturalTy, typeSymbolKind, charTy ) import GHC.Tc.Types.Evidence import GHC.Types.Id +import GHC.Types.Id.Info (IdDetails(..)) import GHC.Types.Name import GHC.Types.Var.Set import GHC.Types.Var.Env @@ -75,7 +78,6 @@ import GHC.Data.Maybe import GHC.Data.OrdList import GHC.Data.Graph.Directed import GHC.Data.Bag -import qualified Data.Set as S import GHC.Utils.Constants (debugIsOn) import GHC.Utils.Misc @@ -85,6 +87,7 @@ import GHC.Utils.Panic import Control.Monad + {-********************************************************************** * * Desugaring a MonoBinds @@ -786,6 +789,214 @@ The restrictions are: 4. Unlifted binds may not be recursive. Checked in second clause of ds_val_bind. +Note [Desugaring new-form SPECIALISE pragmas] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"New-form" SPECIALISE pragmas generate a SpecPragE record in the typechecker, +which is desugared in this module by `dsSpec`. For the context see +Note [Handling new-form SPECIALISE pragmas] in GHC.Tc.Gen.Sig + +Suppose we have f :: forall p q. (Ord p, Eq q) => p -> q -> q, and a pragma + + {-# SPECIALISE forall x. f @[a] @[Int] x [3,4] #-} + +In `dsSpec` the `SpecPragE` will look something like this: + + SpecPragE { spe_fn_id = f + , spe_bndrs = @a (d:Ord a) (x:[a]) + , spe_call = let d2:Ord [a] = $dfOrdList d + d3:Eq [Int] = $dfEqList $dfEqInt + in f @[a] @[Int] d2 d3 x [3,4] } +We want to get + + RULE forall a (d2:Ord a) (d3:Eq [Int]) (x:[a]). + f @[a] @[Int] d2 d3 x [3,4] = $sf d2 x + + $sf :: forall a. Ord [a] => a -> Int + $sf = /\a. \d2 x. + let d3 = $dfEqList $dfEqInt + in <f-rhs> @[a] @[Int] d2 d3 x [3,4] + +Notice that + +(SP1) If the expression in the SPECIALISE pragma had a type signature, such as + SPECIALISE f :: Eq b => Int -> b -> b + then the desugared expression may have type abstractions and applications + "in the way", like this: + (/\b. (\d:Eq b). let d1 = $dfOrdInt in f @Int @b d1 d) @b (d2:Eq b) + The lambdas come from the type signature, which is then re-instantiated, + hence the applications of those lambdas. + + We use the simple optimiser to simplify this to + let { d = d2; d1 = $dfOrdInt } in f @Int @b (d2:Eq b) + + Important: do no inlining in this "simple optimiser" phase: + use `simpleOptExprNoInline`. E.g. we don't want to turn + let { d1=d; d2=d } in f d1 d2 --> f d d + because the latter is harder to match. + +(SP2) $sf does not simply quantify over (d:Ord a). Instead, to figure out what + it should quantify over, and to include the 'd3' binding in the body of $sf, + we use the function `prepareSpecLHS`. It takes the simplified LHS `core_call`, + and uses the dictionary bindings to figure out the RULE LHS and RHS. + + This is described in Note [prepareSpecLHS]. + +Note [prepareSpecLHS] +~~~~~~~~~~~~~~~~~~~~~ +To compute the appropriate RULE LHS and RHS for a new-form specialise pragma, +as described in Note [Desugaring new-form SPECIALISE pragmas], we use a function +called prepareSpecLHS. +It takes as input a list of (type and evidence) binders, and a Core expression. +For example, suppose its input is of the following form: + + spe_bndrs = @a (d:Ord a) + spe_call = + let + -- call these bindings the call_binds + d1 :: Num Int + d1 = $dfNumInt + d2 :: Ord [a] + d2 = $dfOrdList d + d3 :: Eq a + d3 = $p1Ord d3 + d4 :: Ord (F a) + d4 = d |> co1 + d5 :: Ord (G a) + d5 = d4 |> co2 + in + f @[a] @Int d1 d2 d3 d5 + +The goal of prepareSpecLHS is then to generate the following two things: + + - A specialisation, of the form: + + $sf <spec_args> = + let <spec_binds> + in <f-rhs> @[a] @Int d1 d2 d3 d5 + + - A rule, of the form: + + RULE forall a d1 d2 d3 d5. f @[a] @Int d1 d2 d3 d5 = + let <rule_binds> + in $sf <spec_args> + +That is, we must compute 'spec_args', 'rule_binds' and 'spec_binds'. A first +approach might be: + + - take spec_args = spe_bndrs, + - spec_binds = call_binds. + +If we did so, the RULE would look like: + + RULE forall a d1 d2 d3 d5. f @[a] @Int d1 d2 d3 d5 = + let d = <???> + in $sf @a d + +The problem is: how do we recover 'd' from 'd1', 'd2', 'd3', 'd5'? Essentially, +we need to run call_binds in reverse. In this example, we had: + + d1 :: Num Int + d1 = $dfNumInt + d2 :: Ord [a] + d2 = $dfOrdList d + d3 :: Eq a + d3 = $p1Ord d3 + d4 :: Ord (F a) + d4 = d |> co1 + d5 :: Ord (G a) + d5 = d4 |> co2 + +Let's try to recover (d: Ord a) from 'd1', 'd2', 'd4', 'd5': + + - d1 is a constant binding, so it doesn't help us. + - d2 uses a top-level instance, which we can't run in reverse; we can't + obtain Ord a from Ord [a]. + - d3 uses a superclass selector which prevents us from making progress. + - d5 is defined using d4, and both involve a cast. + In theory we could define d = d5 |> sym (co1 ; co2), but this gets + pretty complicated. + +This demonstrates the following: + + 1. The bindings can't necessarily be "run in reverse". + 2. Even if the bindings theoretically can be "run in reverse", it is not + straightforward to do so. + +Now, we could strive to make the let-bindings reversible. We already do this +to some extent for quantified constraints, as explained in +Note [Fully solving constraints for specialisation] in GHC.Tc.Gen.Sig, +using the TcSSpecPrag solver mode described in Note [TcSSpecPrag] in GHC.Tc.Solver.Monad. +However, given (2), we don't bother ensuring that e.g. we don't use top-level +class instances like in d2 above. Instead, we handle these bindings in +prepareSpecLHS as follows: + + (a) Go through the bindings in order. + + (1) Bindings like + d1 = $dfNumInt + depend only on constants and move to the specialised function body. + That is crucial -- it makes those specialised methods available in the + specialised body. These are the `spec_const_binds`. + + Note that these binds /can/ depend on locally-quantified /type/ variables. + For example, if we have + instance Monad (ST s) where ... + then the dictionary for (Monad (ST s)) is effectively a constant dictionary. + This is important to get specialisation for such types. Example in test T8331. + + (2) Other bindings, like + d2:Ord [a] = $dfOrdList d + d3 = d + depend on a locally-quantifed evidence variable `d`. + Surprisingly, /we want to drop these bindings entirely!/ + This is because, as explained above, it is too difficult to run these + in reverse. Instead, we drop them, and re-compute which dictionaries + we will quantify over. + + (3) Finally, inside those dictionary bindings we should find the call of the + function itself + f @[a] @[Int] d2 d3 x [3,4] + 'prepareSpecLHS' takes the call apart and returns its arguments. + + (b) Now, (a)(2) means that the RULE does not quantify over 'd' any more; it + quantifies over 'd1' 'd2' 'd3' 'd5'. So we recompute the `rule_bndrs` + from scratch. + + Moreover, the specialised function also no longer quantifies over 'd', + it quantifies over 'd2' 'd3' 'd5'. This set of binders is computed by + taking the RULE binders and subtracting off the binders from + the `spec_const_binds`. + +[Shortcoming] This whole approach does have one downside, compared to running +the let-bindings in reverse: it doesn't allow us to common-up dictionaries. +Consider for example: + + g :: forall a b. ( Eq a, Ord b ) => a -> b -> Bool + {-# SPECIALISE g :: forall c. Ord c => c -> c -> Bool #-} + +The input to prepareSpecLHS will be (more or less): + + spe_bndrs: @c (d:Ord c) + spe_call = + let + d1 :: Eq c + d1 = $p1Ord d + d2 :: Ord c + d2 = d + in + g @c @c d1 d2 + +The approach described in (2) will thus lead us to generate: + + RULE g @c @c d1 d2 = $sg @c d1 d2 + $sg @c d1 d2 = <g-rhs> @c @c d1 d2 + +when we would rather avoid passing both dictionaries, and instead generate: + + RULE g @c @c d1 d2 = let { d = d2 } in $sg @c d + $sg @c d = let { d1 = $p1Ord d; d2 = d } in <g-rhs> @c @c d1 d2 + +For now, we accept this infelicity. -} ------------------------ @@ -793,117 +1004,268 @@ dsSpecs :: CoreExpr -- Its rhs -> TcSpecPrags -> DsM ( OrdList (Id,CoreExpr) -- Binding for specialised Ids , [CoreRule] ) -- Rules for the Global Ids --- See Note [Handling SPECIALISE pragmas] in GHC.Tc.Gen.Bind +-- See Note [Overview of SPECIALISE pragmas] in GHC.Tc.Gen.Sig dsSpecs _ IsDefaultMethod = return (nilOL, []) dsSpecs poly_rhs (SpecPrags sps) - = do { pairs <- mapMaybeM (dsSpec (Just poly_rhs)) sps + = do { pairs <- mapMaybeM (dsLSpec poly_rhs) sps ; let (spec_binds_s, rules) = unzip pairs ; return (concatOL spec_binds_s, rules) } -dsSpec :: Maybe CoreExpr -- Just rhs => RULE is for a local binding - -- Nothing => RULE is for an imported Id - -- rhs is in the Id's unfolding - -> Located TcSpecPrag +dsImpSpecs :: [LTcSpecPrag] -> DsM (OrdList (Id,CoreExpr), [CoreRule]) +dsImpSpecs imp_specs + = do { spec_prs <- mapMaybeM spec_one imp_specs + ; let (spec_binds, spec_rules) = unzip spec_prs + ; return (concatOL spec_binds, spec_rules) } + where + spec_one (L loc prag) = putSrcSpanDs loc $ + dsSpec (get_rhs prag) prag + + get_rhs (SpecPrag poly_id _ _) = get_rhs1 poly_id + get_rhs (SpecPragE { spe_fn_id = poly_id }) = get_rhs1 poly_id + + get_rhs1 poly_id + | Just unfolding <- maybeUnfoldingTemplate (realIdUnfolding poly_id) + = unfolding -- Imported Id; this is its unfolding + -- Use realIdUnfolding so we get the unfolding + -- even when it is a loop breaker. + -- We want to specialise recursive functions! + | otherwise = pprPanic "dsImpSpecs" (ppr poly_id) + -- The type checker has checked that it *has* an unfolding + +dsLSpec :: CoreExpr -> Located TcSpecPrag + -> DsM (Maybe (OrdList (Id,CoreExpr), CoreRule)) +dsLSpec poly_rhs (L loc prag) + = putSrcSpanDs loc $ dsSpec poly_rhs prag + +dsSpec :: CoreExpr -- RHS to be specialised + -> TcSpecPrag -> DsM (Maybe (OrdList (Id,CoreExpr), CoreRule)) -dsSpec mb_poly_rhs (L loc (SpecPrag poly_id spec_co spec_inl)) - | isJust (isClassOpId_maybe poly_id) - = putSrcSpanDs loc $ - do { diagnosticDs (DsUselessSpecialiseForClassMethodSelector poly_id) - ; return Nothing } -- There is no point in trying to specialise a class op - -- Moreover, classops don't (currently) have an inl_sat arity set - -- (it would be Just 0) and that in turn makes makeCorePair bleat - - | no_act_spec && isNeverActive rule_act - = putSrcSpanDs loc $ - do { diagnosticDs (DsUselessSpecialiseForNoInlineFunction poly_id) - ; return Nothing } -- Function is NOINLINE, and the specialisation inherits that - -- See Note [Activation pragmas for SPECIALISE] - - | otherwise - = putSrcSpanDs loc $ - do { uniq <- newUnique - ; let poly_name = idName poly_id - spec_occ = mkSpecOcc (getOccName poly_name) - spec_name = mkInternalName uniq spec_occ (getSrcSpan poly_name) - (spec_bndrs, spec_app) = collectHsWrapBinders spec_co +dsSpec poly_rhs (SpecPrag poly_id spec_co spec_inl) + -- SpecPrag case: See Note [Handling old-form SPECIALISE pragmas] in GHC.Tc.Gen.Sig + | (spec_bndrs, spec_app) <- collectHsWrapBinders spec_co -- spec_co looks like -- \spec_bndrs. [] spec_args -- perhaps with the body of the lambda wrapped in some WpLets -- E.g. /\a \(d:Eq a). let d2 = $df d in [] (Maybe a) d2 - - ; dsHsWrapper spec_app $ \core_app -> do - - { let ds_lhs = core_app (Var poly_id) - spec_ty = mkLamTypes spec_bndrs (exprType ds_lhs) - ; -- pprTrace "dsRule" (vcat [ text "Id:" <+> ppr poly_id - -- , text "spec_co:" <+> ppr spec_co - -- , text "ds_rhs:" <+> ppr ds_lhs ]) $ - dflags <- getDynFlags - ; case decomposeRuleLhs dflags spec_bndrs ds_lhs (mkVarSet spec_bndrs) of { + = dsHsWrapper spec_app $ \core_app -> + do { dflags <- getDynFlags + ; case decomposeRuleLhs dflags spec_bndrs (core_app (Var poly_id)) + (mkVarSet spec_bndrs) of { Left msg -> do { diagnosticDs msg; return Nothing } ; - Right (rule_bndrs, _fn, rule_lhs_args) -> do - - { this_mod <- getModule - ; let fn_unf = realIdUnfolding poly_id - simpl_opts = initSimpleOpts dflags - spec_unf = specUnfolding simpl_opts spec_bndrs core_app rule_lhs_args fn_unf - spec_id = mkLocalId spec_name ManyTy spec_ty -- Specialised binding is toplevel, hence Many. - `setInlinePragma` inl_prag - `setIdUnfolding` spec_unf - - rule = mkSpecRule dflags this_mod False rule_act (text "USPEC") - poly_id rule_bndrs rule_lhs_args - (mkVarApps (Var spec_id) spec_bndrs) - spec_rhs = mkLams spec_bndrs (core_app poly_rhs) - - ; dsWarnOrphanRule rule - - ; tracePm "dsSpec" (vcat - [ text "fun:" <+> ppr poly_id - , text "spec_co:" <+> ppr spec_co - , text "spec_bndrs:" <+> ppr spec_bndrs - , text "ds_lhs:" <+> ppr ds_lhs - , text "args:" <+> ppr rule_lhs_args ]) - ; return (Just (unitOL (spec_id, spec_rhs), rule)) - -- NB: do *not* use makeCorePair on (spec_id,spec_rhs), because - -- makeCorePair overwrites the unfolding, which we have - -- just created using specUnfolding - } } } } + Right (rule_bndrs, poly_id, rule_lhs_args) -> + finishSpecPrag (idName poly_id) poly_rhs + rule_bndrs poly_id rule_lhs_args + spec_bndrs core_app spec_inl } } + +dsSpec poly_rhs ( + SpecPragE + { spe_fn_nm = poly_nm + , spe_fn_id = poly_id + , spe_inl = inl + , spe_bndrs = bndrs + , spe_call = the_call + }) + -- SpecPragE case: See Note [Handling new-form SPECIALISE pragmas] in GHC.Tc.Gen.Sig + = do { ds_call <- unsetGOptM Opt_EnableRewriteRules $ -- Note [Desugaring RULE left hand sides] + unsetWOptM Opt_WarnIdentities $ + zapUnspecables $ + dsLExpr the_call + + -- Simplify the (desugared) call; see wrinkle (SP1) + -- in Note [Desugaring new-form SPECIALISE pragmas] + ; dflags <- getDynFlags + ; let simpl_opts = initSimpleOpts dflags + core_call = simpleOptExprNoInline simpl_opts ds_call + + ; case prepareSpecLHS poly_id bndrs core_call of { + Nothing -> do { diagnosticDs (DsRuleLhsTooComplicated ds_call core_call) + ; return Nothing } ; + + Just (bndr_set, spec_const_binds, lhs_args) -> + + do { let const_bndrs = mkVarSet (bindersOfBinds spec_const_binds) + all_bndrs = bndr_set `unionVarSet` const_bndrs + -- all_bndrs: all binders in core_call that should be quantified + + -- rule_bndrs; see (SP3) in Note [Desugaring new-form SPECIALISE pragmas] + rule_bndrs = scopedSort (exprsSomeFreeVarsList (`elemVarSet` all_bndrs) lhs_args) + spec_bndrs = filterOut (`elemVarSet` const_bndrs) rule_bndrs + + mk_spec_body fn_body = mkLets spec_const_binds $ + mkCoreApps fn_body lhs_args + + ; tracePm "dsSpec" (vcat [ text "poly_id" <+> ppr poly_id + , text "bndrs" <+> ppr bndrs + , text "lhs_args" <+> ppr lhs_args + , text "bndr_set" <+> ppr bndr_set + , text "all_bndrs" <+> ppr all_bndrs + , text "rule_bndrs" <+> ppr rule_bndrs + , text "const_bndrs" <+> ppr const_bndrs + , text "spec_bndrs" <+> ppr spec_bndrs + , text "core_call fvs" <+> ppr (exprFreeVars core_call) + , text "spec_const_binds" <+> ppr spec_const_binds + , text "ds_call" <+> ppr ds_call + , text "core_call" <+> ppr core_call ]) + + ; finishSpecPrag poly_nm poly_rhs + rule_bndrs poly_id lhs_args + spec_bndrs mk_spec_body inl } } } + +prepareSpecLHS :: Id -> [EvVar] -> CoreExpr + -> Maybe (VarSet, [CoreBind], [CoreExpr]) +-- See Note [prepareSpecLHS] +prepareSpecLHS poly_id evs the_call + = go (mkVarSet evs) [] the_call where - is_local_id = isJust mb_poly_rhs - poly_rhs | Just rhs <- mb_poly_rhs - = rhs -- Local Id; this is its rhs - | Just unfolding <- maybeUnfoldingTemplate (realIdUnfolding poly_id) - = unfolding -- Imported Id; this is its unfolding - -- Use realIdUnfolding so we get the unfolding - -- even when it is a loop breaker. - -- We want to specialise recursive functions! - | otherwise = pprPanic "dsImpSpecs" (ppr poly_id) - -- The type checker has checked that it *has* an unfolding - - id_inl = idInlinePragma poly_id + go :: VarSet -- Quantified variables, or dependencies thereof + -> [CoreBind] -- Reversed list of constant evidence bindings + -> CoreExpr + -> Maybe (IdSet, [CoreBind], [CoreExpr]) + go qevs acc (Cast e _) + = go qevs acc e + go qevs acc (Let bind e) + | not (all isDictId bndrs) -- A normal 'let' is too complicated + = Nothing + + -- (a) (1) in Note [prepareSpecLHS] + | all (transfer_to_spec_rhs qevs) $ + rhssOfBind bind -- One of the `const_binds` + = go qevs (bind:acc) e + + -- (a) (2) in Note [prepareSpecLHS] + | otherwise + = go (qevs `extendVarSetList` bndrs) acc e + where + bndrs = bindersOf bind + + go qevs acc e + | (Var fun, args) <- collectArgs e + -- (a) (3) in Note [prepareSpecLHS] + = assertPpr (fun == poly_id) (ppr fun $$ ppr poly_id) $ + Just (qevs, reverse acc, args) + | otherwise + = Nothing + + transfer_to_spec_rhs qevs rhs + = isEmptyVarSet $ exprSomeFreeVars is_quant_id rhs + where + is_quant_id v = isId v && v `elemVarSet` qevs + -- See (a) (2) in Note [prepareSpecLHS] + +finishSpecPrag :: Name -> CoreExpr -- RHS to specialise + -> [Var] -> Id -> [CoreExpr] -- RULE LHS pattern + -> [Var] -> (CoreExpr -> CoreExpr) -> InlinePragma -- Specialised form + -> DsM (Maybe (OrdList (Id,CoreExpr), CoreRule)) +finishSpecPrag poly_nm poly_rhs rule_bndrs poly_id rule_args + spec_bndrs mk_spec_body spec_inl + | Just reason <- mb_useless + = do { diagnosticDs $ DsUselessSpecialisePragma poly_nm is_dfun reason + ; if uselessSpecialisePragmaKeepAnyway reason + then Just <$> finish_prag + else return Nothing } - -- See Note [Activation pragmas for SPECIALISE] - inl_prag | not (isDefaultInlinePragma spec_inl) = spec_inl - | not is_local_id -- See Note [Specialising imported functions] - -- in OccurAnal - , isStrongLoopBreaker (idOccInfo poly_id) = neverInlinePragma - | otherwise = id_inl - -- Get the INLINE pragma from SPECIALISE declaration, or, - -- failing that, from the original Id + | otherwise + = Just <$> finish_prag - spec_prag_act = inlinePragmaActivation spec_inl + where + -- The RULE looks like + -- RULE "USPEC" forall rule_bndrs. f rule_args = $sf spec_bndrs + -- The specialised function looks like + -- $sf spec_bndrs = mk_spec_body <f's original rhs> + -- We also use mk_spec_body to specialise the methods in f's stable unfolding + -- NB: spec_bindrs is a subset of rule_bndrs + finish_prag + = do { this_mod <- getModule + ; uniq <- newUnique + ; dflags <- getDynFlags + ; let poly_name = idName poly_id + spec_occ = mkSpecOcc (getOccName poly_name) + spec_name = mkInternalName uniq spec_occ (getSrcSpan poly_name) + + simpl_opts = initSimpleOpts dflags + fn_unf = realIdUnfolding poly_id + spec_unf = specUnfolding simpl_opts spec_bndrs mk_spec_body rule_args fn_unf + spec_id = mkLocalId spec_name ManyTy spec_ty + -- Specialised binding is toplevel, hence Many. + `setInlinePragma` specFunInlinePrag poly_id id_inl spec_inl + `setIdUnfolding` spec_unf + + rule = mkSpecRule dflags this_mod False rule_act (text "USPEC") + poly_id rule_bndrs rule_args + (mkVarApps (Var spec_id) spec_bndrs) + + rule_ty = exprType (mkApps (Var poly_id) rule_args) + spec_ty = mkLamTypes spec_bndrs rule_ty + spec_rhs = mkLams spec_bndrs (mk_spec_body poly_rhs) + + ; dsWarnOrphanRule rule + + ; tracePm "dsSpec" (vcat + [ text "fun:" <+> ppr poly_id + , text "spec_bndrs:" <+> ppr spec_bndrs + , text "args:" <+> ppr rule_args ]) + ; return (unitOL (spec_id, spec_rhs), rule) } + -- NB: do *not* use makeCorePair on (spec_id,spec_rhs), because + -- makeCorePair overwrites the unfolding, which we have + -- just created using specUnfolding + + -- Is this SPECIALISE pragma useless? + mb_useless :: Maybe UselessSpecialisePragmaReason + mb_useless + | isJust (isClassOpId_maybe poly_id) + -- There is no point in trying to specialise a class op + -- Moreover, classops don't (currently) have an inl_sat arity set + -- (it would be Just 0) and that in turn makes makeCorePair bleat + = Just UselessSpecialiseForClassMethodSelector + + | no_act_spec, isNeverActive rule_act + -- Function is NOINLINE, and the specialisation inherits that + -- See Note [Activation pragmas for SPECIALISE] + = Just UselessSpecialiseForNoInlineFunction + + | all is_nop_arg rule_args, not (isInlinePragma spec_inl) + -- The specialisation does nothing. + -- But don't complain if it is SPECIALISE INLINE (#4444) + = Just UselessSpecialiseNoSpecialisation + + | otherwise + = Nothing -- See Note [Activation pragmas for SPECIALISE] -- no_act_spec is True if the user didn't write an explicit -- phase specification in the SPECIALISE pragma + id_inl = idInlinePragma poly_id + inl_prag_act = inlinePragmaActivation id_inl + spec_prag_act = inlinePragmaActivation spec_inl no_act_spec = case inlinePragmaSpec spec_inl of NoInline _ -> isNeverActive spec_prag_act Opaque _ -> isNeverActive spec_prag_act _ -> isAlwaysActive spec_prag_act - rule_act | no_act_spec = inlinePragmaActivation id_inl -- Inherit - | otherwise = spec_prag_act -- Specified by user - + rule_act | no_act_spec = inl_prag_act -- Inherit + | otherwise = spec_prag_act -- Specified by user + + is_nop_arg (Type {}) = True + is_nop_arg (Coercion {}) = True + is_nop_arg (Cast e _) = is_nop_arg e + is_nop_arg (Tick _ e) = is_nop_arg e + is_nop_arg (Var x) = x `elem` spec_bndrs + is_nop_arg _ = False + + is_dfun = case idDetails poly_id of + DFunId {} -> True + _ -> False + +specFunInlinePrag :: Id -> InlinePragma + -> InlinePragma -> InlinePragma +-- See Note [Activation pragmas for SPECIALISE] +specFunInlinePrag poly_id id_inl spec_inl + | not (isDefaultInlinePragma spec_inl) = spec_inl + | isGlobalId poly_id -- See Note [Specialising imported functions] + -- in OccurAnal + , isStrongLoopBreaker (idOccInfo poly_id) = neverInlinePragma + | otherwise = id_inl + -- Get the INLINE pragma from SPECIALISE declaration, or, + -- failing that, from the original Id dsWarnOrphanRule :: CoreRule -> DsM () dsWarnOrphanRule rule @@ -996,6 +1358,7 @@ decomposeRuleLhs dflags orig_bndrs orig_lhs rhs_fvs -- , text "args2:" <+> ppr args2 -- ]) $ Left (DsRuleLhsTooComplicated orig_lhs lhs2) + Just (fn_id, args) | not (null unbound) -> -- Check for things unbound on LHS @@ -1011,38 +1374,35 @@ decomposeRuleLhs dflags orig_bndrs orig_lhs rhs_fvs -- pprTrace "decomposeRuleLhs 2" (vcat [ text "orig_bndrs:" <+> ppr orig_bndrs -- , text "orig_lhs:" <+> ppr orig_lhs -- , text "lhs1:" <+> ppr lhs1 - -- , text "extra_bndrs:" <+> ppr extra_bndrs + -- , text "trimmed_bndrs:" <+> ppr trimmed_bndrs + -- , text "extra_dicts:" <+> ppr extra_dicts -- , text "fn_id:" <+> ppr fn_id -- , text "args:" <+> ppr args -- , text "args fvs:" <+> ppr (exprsFreeVarsList args) -- ]) $ - Right (trimmed_bndrs ++ extra_bndrs, fn_id, args) + Right (trimmed_bndrs ++ extra_dicts, fn_id, args) where -- See Note [Variables unbound on the LHS] - lhs_fvs = exprsFreeVars args + lhs_fvs = exprsFreeVars args all_fvs = lhs_fvs `unionVarSet` rhs_fvs trimmed_bndrs = filter (`elemVarSet` all_fvs) orig_bndrs unbound = filterOut (`elemVarSet` lhs_fvs) trimmed_bndrs -- Needed on RHS but not bound on LHS - -- Add extra tyvar binders: Note [Free tyvars on rule LHS] - -- and extra dict binders: Note [Free dictionaries on rule LHS] - extra_bndrs = scopedSort extra_tvs ++ extra_dicts - where - extra_tvs = [ v | v <- extra_vars, isTyVar v ] - - -- isEvVar: this includes coercions, matching what - -- happens in `split_lets` (isDictId, isCoVar) - extra_dicts = - [ mkLocalIdOrCoVar (localiseName (idName d)) ManyTy (idType d) - | d <- extra_vars, isEvVar d ] - extra_vars = - [ v - | v <- exprsFreeVarsList args - , not (v `elemVarSet` orig_bndr_set) - , not (v == fn_id) ] - -- fn_id: do not quantify over the function itself, which may - -- itself be a dictionary (in pathological cases, #10251) + -- extra_dicts: see Note [Free dictionaries on rule LHS] +-- ToDo: extra_dicts is needed. E.g. the SPECIALISE rules for `ap` in GHC.Base + extra_dicts + = [ mkLocalIdOrCoVar (localiseName (idName d)) ManyTy (idType d) + | d <- exprsSomeFreeVarsList is_extra args ] + + is_extra v + = isEvVar v + -- isEvVar: includes coercions, matching what + -- happens in `split_lets` (isDictId, isCoVar) + && not (v `elemVarSet` orig_bndr_set) + && not (v == fn_id) + -- fn_id: do not quantify over the function itself, which may + -- itself be a dictionary (in pathological cases, #10251) where simpl_opts = initSimpleOpts dflags @@ -1107,7 +1467,7 @@ Consider #22471 We get two dicts on the LHS, one from `1` and one from `+`. For reasons described in Note [The SimplifyRule Plan] in -GHC.Tc.Gen.Rule, we quantify separately over those dictionaries: +GHC.Tc.Gen.Sig, we quantify separately over those dictionaries: forall f (d1::Num Int) (d2 :: Num Int). foo (\xs. (+) d1 (fromInteger d2 1) xs) = ... @@ -1122,32 +1482,6 @@ There are several things going on here. * simpleOptExpr: see Note [Simplify rule LHS] * extra_dict_bndrs: see Note [Free dictionaries on rule LHS] -Note [Free tyvars on rule LHS] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Consider - data T a = C - - foo :: T a -> Int - foo C = 1 - - {-# RULES "myrule" foo C = 1 #-} - -After type checking the LHS becomes (foo alpha (C alpha)), where alpha -is an unbound meta-tyvar. The zonker in GHC.Tc.Zonk.Type is careful not to -turn the free alpha into Any (as it usually does). Instead it turns it -into a TyVar 'a'. See Note [Zonking the LHS of a RULE] in "GHC.Tc.Zonk.Type". - -Now we must quantify over that 'a'. It's /really/ inconvenient to do that -in the zonker, because the HsExpr data type is very large. But it's /easy/ -to do it here in the desugarer. - -Moreover, we have to do something rather similar for dictionaries; -see Note [Free dictionaries on rule LHS]. So that's why we look for -type variables free on the LHS, and quantify over them. - -This relies on there not being any in-scope tyvars, which is true for -user-defined RULEs, which are always top-level. - Note [Free dictionaries on rule LHS] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When the LHS of a specialisation rule, (/\as\ds. f es) has a free dict, @@ -1182,9 +1516,9 @@ drop_dicts drops dictionary bindings on the LHS where possible. quantify over it. That makes 'd' free in the LHS, but that is later picked up by extra_dict_bndrs (see Note [Unused spec binders]). - NB 1: We can only drop the binding if the RHS doesn't bind - one of the orig_bndrs, which we assume occur on RHS. - Example + NB 1: We can only drop the binding if the RHS of the binding doesn't + mention one of the orig_bndrs, which we assume occur on RHS of + the rule. Example f :: (Eq a) => b -> a -> a {-# SPECIALISE f :: Eq a => b -> [a] -> [a] #-} Here we want to end up with @@ -1192,7 +1526,7 @@ drop_dicts drops dictionary bindings on the LHS where possible. Of course, the ($dfEqlist d) in the pattern makes it less likely to match, but there is no other way to get d:Eq a - NB 2: We do drop_dicts *before* simplOptEpxr, so that we expect all + NB 2: We do drop_dicts *before* simplOptExpr, so that we expect all the evidence bindings to be wrapped around the outside of the LHS. (After simplOptExpr they'll usually have been inlined.) dsHsWrapper does dependency analysis, so that civilised ones @@ -1376,29 +1710,34 @@ dsEvBinds ev_binds thing_inside thing_inside (core_bind:core_binds) } go [] thing_inside = thing_inside [] - ds_component unspecables (AcyclicSCC node) = (NonRec v rhs, new_unspecables) + ds_component mb_unspecables (AcyclicSCC node) = (NonRec v rhs, new_unspecables) where ((v, rhs), (this_canonical, deps)) = unpack_node node - transitively_unspecable = is_unspecable this_canonical || any is_unspecable_dep deps - is_unspecable_dep dep = dep `S.member` unspecables - new_unspecables - | transitively_unspecable = S.singleton v - | otherwise = mempty - ds_component unspecables (CyclicSCC nodes) = (Rec pairs, new_unspecables) + new_unspecables = case mb_unspecables of + Nothing -> [] + Just unspecs | transitively_unspecable -> [v] + | otherwise -> [] + where + transitively_unspecable = is_unspecable this_canonical + || any (`elemVarSet` unspecs) deps + + ds_component mb_unspecables (CyclicSCC nodes) = (Rec pairs, new_unspecables) where (pairs, direct_canonicity) = unzip $ map unpack_node nodes - is_unspecable_remote dep = dep `S.member` unspecables - transitively_unspecable = or [ is_unspecable this_canonical || any is_unspecable_remote deps - | (this_canonical, deps) <- direct_canonicity ] + new_unspecables = case mb_unspecables of + Nothing -> [] + Just unspecs | transitively_unspecable -> map fst pairs + | otherwise -> [] + where + transitively_unspecable + = or [ is_unspecable this_canonical + || any (`elemVarSet` unspecs) deps + | (this_canonical, deps) <- direct_canonicity ] -- Bindings from a given SCC are transitively specialisable if -- all are specialisable and all their remote dependencies are -- also specialisable; see Note [Desugaring non-canonical evidence] - new_unspecables - | transitively_unspecable = S.fromList [ v | (v, _) <- pairs] - | otherwise = mempty - unpack_node DigraphNode { node_key = v, node_payload = (canonical, rhs), node_dependencies = deps } = ((v, rhs), (canonical, deps)) diff --git a/compiler/GHC/HsToCore/Errors/Ppr.hs b/compiler/GHC/HsToCore/Errors/Ppr.hs index 976c87eaa87..66954d15058 100644 --- a/compiler/GHC/HsToCore/Errors/Ppr.hs +++ b/compiler/GHC/HsToCore/Errors/Ppr.hs @@ -83,12 +83,46 @@ instance Diagnostic DsMessage where StrictBinds -> "strict bindings" in mkSimpleDecorated $ hang (text "Top-level" <+> text desc <+> text "aren't allowed:") 2 (ppr bind) - DsUselessSpecialiseForClassMethodSelector poly_id - -> mkSimpleDecorated $ - text "Ignoring useless SPECIALISE pragma for class selector:" <+> quotes (ppr poly_id) - DsUselessSpecialiseForNoInlineFunction poly_id - -> mkSimpleDecorated $ - text "Ignoring useless SPECIALISE pragma for NOINLINE function:" <+> quotes (ppr poly_id) + DsUselessSpecialisePragma poly_nm is_dfun rea -> + mkSimpleDecorated $ + vcat [ what <+> pragma <+> text "pragma" <> why + , additional ] + where + quoted_nm = quotes (ppr poly_nm) + what + | uselessSpecialisePragmaKeepAnyway rea + = text "Dubious" + | otherwise + = text "Ignoring useless" + pragma = if is_dfun + then text "SPECIALISE instance" + else text "SPECIALISE" + why = case rea of + UselessSpecialiseForClassMethodSelector -> + text " for class selector:" <+> quoted_nm + UselessSpecialiseForNoInlineFunction -> + text " for NOINLINE function:" <+> quoted_nm + UselessSpecialiseNoSpecialisation -> + -- Omit the Name for a DFunId, as it will be internal and not + -- very illuminating to users who don't know what a DFunId is. + (if is_dfun then empty else text " for" <+> quoted_nm) <> dot + + additional + | uselessSpecialisePragmaKeepAnyway rea + = -- No specialisation happening, but the pragma may still be useful. + -- For example (#25389): + -- + -- data G a where { G1 :: G Int, G2 :: G Bool } + -- f :: G a -> a + -- f G1 = <branch1>; f G2 = <branch2> + -- {-# SPECIALISE f :: G Int -> Int #-} + -- -- In $sf, we get rid of dead code in <branch2> + vcat + [ text "The pragma does not specialise away any class dictionaries," + , text "and neither is there any value specialisation." + ] + | otherwise + = empty DsOrphanRule rule -> mkSimpleDecorated $ text "Orphan rule:" <+> ppr rule DsRuleLhsTooComplicated orig_lhs lhs2 @@ -109,7 +143,7 @@ instance Diagnostic DsMessage where , text "is not bound in RULE lhs"]) 2 (vcat [ text "Orig bndrs:" <+> ppr orig_bndrs , text "Orig lhs:" <+> ppr orig_lhs - , text "optimised lhs:" <+> ppr lhs2 ]) + , text "Optimised lhs:" <+> ppr lhs2 ]) pp_bndr b | isTyVar b = text "type variable" <+> quotes (ppr b) @@ -224,8 +258,7 @@ instance Diagnostic DsMessage where DsNonExhaustivePatterns _ (ExhaustivityCheckType mb_flag) _ _ _ -> maybe WarningWithoutFlag WarningWithFlag mb_flag DsTopLevelBindsNotAllowed{} -> ErrorWithoutFlag - DsUselessSpecialiseForClassMethodSelector{} -> WarningWithoutFlag - DsUselessSpecialiseForNoInlineFunction{} -> WarningWithoutFlag + DsUselessSpecialisePragma{} -> WarningWithFlag Opt_WarnUselessSpecialisations DsOrphanRule{} -> WarningWithFlag Opt_WarnOrphans DsRuleLhsTooComplicated{} -> WarningWithoutFlag DsRuleIgnoredDueToConstructor{} -> WarningWithoutFlag @@ -260,8 +293,7 @@ instance Diagnostic DsMessage where DsMaxPmCheckModelsReached{} -> [SuggestIncreaseMaxPmCheckModels] DsNonExhaustivePatterns{} -> noHints DsTopLevelBindsNotAllowed{} -> noHints - DsUselessSpecialiseForClassMethodSelector{} -> noHints - DsUselessSpecialiseForNoInlineFunction{} -> noHints + DsUselessSpecialisePragma{} -> noHints DsOrphanRule{} -> noHints DsRuleLhsTooComplicated{} -> noHints DsRuleIgnoredDueToConstructor{} -> noHints diff --git a/compiler/GHC/HsToCore/Errors/Types.hs b/compiler/GHC/HsToCore/Errors/Types.hs index aecaeea1060..170754a0a06 100644 --- a/compiler/GHC/HsToCore/Errors/Types.hs +++ b/compiler/GHC/HsToCore/Errors/Types.hs @@ -1,5 +1,6 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE TypeFamilies #-} module GHC.HsToCore.Errors.Types where @@ -105,9 +106,18 @@ data DsMessage | DsTopLevelBindsNotAllowed !BindsType !(HsBindLR GhcTc GhcTc) - | DsUselessSpecialiseForClassMethodSelector !Id + {-| DsUselessSpecialisePragma is a warning (controlled by the -Wuseless-specialisations flag) + that is emitted for SPECIALISE pragmas that (most likely) don't do anything. - | DsUselessSpecialiseForNoInlineFunction !Id + Examples: + + foo :: forall a. a -> a + {-# SPECIALISE foo :: Int -> Int #-} + -} + | DsUselessSpecialisePragma + !Name + !Bool -- ^ is this a @SPECIALISE instance@ pragma? + !UselessSpecialisePragmaReason | DsOrphanRule !CoreRule @@ -195,6 +205,25 @@ data ThRejectionReason | ThSplicesWithinDeclBrackets | ThNonLinearDataCon +-- | Why is a @SPECIALISE@ pragmas useless? +data UselessSpecialisePragmaReason + -- | Useless @SPECIALISE@ pragma for a class method + = UselessSpecialiseForClassMethodSelector + -- | Useless @SPECIALISE@ pragma for a function with NOINLINE + | UselessSpecialiseForNoInlineFunction + -- | Useless @SPECIALISE@ pragma which generates a specialised function + -- which is identical to the original function at runtime. + | UselessSpecialiseNoSpecialisation + deriving Generic + +uselessSpecialisePragmaKeepAnyway :: UselessSpecialisePragmaReason -> Bool +uselessSpecialisePragmaKeepAnyway = \case + UselessSpecialiseForClassMethodSelector -> False + UselessSpecialiseForNoInlineFunction -> False + UselessSpecialiseNoSpecialisation -> True + -- See #25389/T25389 for why we might want to keep this specialisation + -- around even if it seemingly does nothing. + data NegLiteralExtEnabled = YesUsingNegLiterals | NotUsingNegLiterals diff --git a/compiler/GHC/HsToCore/Expr.hs b/compiler/GHC/HsToCore/Expr.hs index caaba2969d1..6200e3bdbe5 100644 --- a/compiler/GHC/HsToCore/Expr.hs +++ b/compiler/GHC/HsToCore/Expr.hs @@ -62,6 +62,7 @@ import GHC.Types.Id import GHC.Types.Id.Info import GHC.Types.Id.Make import GHC.Types.Var( isInvisibleAnonPiTyBinder ) +import GHC.Types.Var.Set( isEmptyVarSet, elemVarSet ) import GHC.Types.Basic import GHC.Types.SrcLoc import GHC.Types.Tickish @@ -76,7 +77,6 @@ import GHC.Utils.Misc import GHC.Utils.Outputable as Outputable import GHC.Utils.Panic import Control.Monad -import qualified Data.Set as S {- ************************************************************************ @@ -692,8 +692,7 @@ ds_app (XExpr (HsRecSelTc (FieldOcc { foLabel = L _ sel_id }))) _hs_args core_ar = ds_app_rec_sel sel_id sel_id core_args ds_app (HsVar _ lfun) hs_args core_args - = do { tracePm "ds_app" (ppr lfun <+> ppr core_args) - ; ds_app_var lfun hs_args core_args } + = ds_app_var lfun hs_args core_args ds_app e _hs_args core_args = do { core_e <- dsExpr e @@ -789,15 +788,15 @@ ds_app_finish :: Id -> [CoreExpr] -> DsM CoreExpr -- See Note [nospecId magic] in GHC.Types.Id.Make for what `nospec` does. -- See Note [Desugaring non-canonical evidence] ds_app_finish fun_id core_args - = do { unspecables <- getUnspecables + = do { mb_unspecables <- getUnspecables ; let fun_ty = idType fun_id free_dicts = exprsFreeVarsList [ e | (e,pi_bndr) <- core_args `zip` fst (splitPiTys fun_ty) , isInvisibleAnonPiTyBinder pi_bndr ] - is_unspecable_var v = v `S.member` unspecables - fun | not (S.null unspecables) -- Fast path - , any (is_unspecable_var) free_dicts + fun | Just unspecables <- mb_unspecables + , not (isEmptyVarSet unspecables) -- Fast path + , any (`elemVarSet` unspecables) free_dicts = Var nospecId `App` Type fun_ty `App` Var fun_id | otherwise = Var fun_id @@ -916,8 +915,8 @@ Note [Desugaring non-canonical evidence] When constructing an application f @ty1 ty2 .. dict1 dict2 .. arg1 arg2 .. if the evidence `dict_i` is canonical, we simply build that application. -But if any of the `dict_i` are /non-canonical/, we wrap the appication in `nospec`, -thus +But if any of the `dict_i` are /non-canonical/, we wrap the application +in `nospec`, thus nospec @fty f @ty1 @ty2 .. dict1 dict2 .. arg1 arg2 .. where nospec :: forall a. a -> a ensures that the typeclass specialiser doesn't attempt to common up this evidence term with other evidence terms @@ -942,7 +941,7 @@ How do we decide if the arguments are non-canonical dictionaries? Wrinkle: -(NC1) We don't do this in the LHS of a RULE. In paritcular, if we have +(NC1) We don't do this in the LHS of a RULE. In particular, if we have f :: (Num a, HasCallStack) => a -> a {-# SPECIALISE f :: Int -> Int #-} then making a rule like @@ -954,7 +953,8 @@ Wrinkle: We definitely can't desugar that LHS into this! nospec (f @Int d1) d2 - This is done by zapping the unspecables in `dsRule`. + This is done by zapping the unspecables in `dsRule` to Nothing. That `Nothing` + says not to collect unspecables at all. Note [Desugaring explicit lists] diff --git a/compiler/GHC/HsToCore/Foreign/Call.hs b/compiler/GHC/HsToCore/Foreign/Call.hs index b135978e156..fbbf633814e 100644 --- a/compiler/GHC/HsToCore/Foreign/Call.hs +++ b/compiler/GHC/HsToCore/Foreign/Call.hs @@ -22,32 +22,37 @@ where import GHC.Prelude import GHC.Core - -import GHC.HsToCore.Monad import GHC.Core.Utils import GHC.Core.Make -import GHC.Types.SourceText -import GHC.Types.Id.Make -import GHC.Types.ForeignCall import GHC.Core.DataCon -import GHC.HsToCore.Utils - -import GHC.Tc.Utils.TcType import GHC.Core.Type import GHC.Core.Multiplicity import GHC.Core.Coercion -import GHC.Builtin.Types.Prim import GHC.Core.TyCon -import GHC.Builtin.Types +import GHC.Core.Predicate( tyCoVarsOfTypeWellScoped ) + +import GHC.HsToCore.Monad +import GHC.HsToCore.Utils + +import GHC.Types.SourceText +import GHC.Types.Id.Make +import GHC.Types.ForeignCall import GHC.Types.Basic import GHC.Types.Literal +import GHC.Types.RepType (typePrimRep1) + +import GHC.Tc.Utils.TcType + +import GHC.Builtin.Types.Prim +import GHC.Builtin.Types import GHC.Builtin.Names + import GHC.Driver.DynFlags + import GHC.Utils.Outputable import GHC.Utils.Panic import Data.Maybe -import GHC.Types.RepType (typePrimRep1) {- Desugaring of @ccall@s consists of adding some state manipulation, diff --git a/compiler/GHC/HsToCore/Monad.hs b/compiler/GHC/HsToCore/Monad.hs index e70d59492b6..0c905d62b70 100644 --- a/compiler/GHC/HsToCore/Monad.hs +++ b/compiler/GHC/HsToCore/Monad.hs @@ -94,7 +94,8 @@ import GHC.Unit.Module.ModGuts import GHC.Types.Name.Reader import GHC.Types.SourceFile import GHC.Types.Id -import GHC.Types.Var (EvId) +import GHC.Types.Var (EvVar) +import GHC.Types.Var.Set( VarSet, emptyVarSet, extendVarSetList ) import GHC.Types.SrcLoc import GHC.Types.TypeEnv import GHC.Types.Unique.Supply @@ -117,7 +118,6 @@ import qualified GHC.Data.Strict as Strict import Data.IORef import GHC.Driver.Env.KnotVars -import qualified Data.Set as S import GHC.IO.Unsafe (unsafeInterleaveIO) {- @@ -406,7 +406,7 @@ mkDsEnvs unit_env mod rdr_env type_env fam_inst_env ptc msg_var cc_st_var lcl_env = DsLclEnv { dsl_meta = emptyNameEnv , dsl_loc = real_span , dsl_nablas = initNablas - , dsl_unspecables = mempty + , dsl_unspecables = Just emptyVarSet } in (gbl_env, lcl_env) @@ -469,13 +469,17 @@ getPmNablas = do { env <- getLclEnv; return (dsl_nablas env) } updPmNablas :: Nablas -> DsM a -> DsM a updPmNablas nablas = updLclEnv (\env -> env { dsl_nablas = nablas }) -addUnspecables :: S.Set EvId -> DsM a -> DsM a -addUnspecables unspecables = updLclEnv (\env -> env{ dsl_unspecables = unspecables `mappend` dsl_unspecables env }) +addUnspecables :: [EvVar] -> DsM a -> DsM a +addUnspecables new_unspecables + = updLclEnv (\env -> case dsl_unspecables env of + Nothing -> env + Just us -> env { dsl_unspecables + = Just (us `extendVarSetList` new_unspecables) }) zapUnspecables :: DsM a -> DsM a -zapUnspecables = updLclEnv (\env -> env{ dsl_unspecables = mempty }) +zapUnspecables = updLclEnv (\env -> env{ dsl_unspecables = Nothing }) -getUnspecables :: DsM (S.Set EvId) +getUnspecables :: DsM (Maybe VarSet) getUnspecables = dsl_unspecables <$> getLclEnv getSrcSpanDs :: DsM SrcSpan diff --git a/compiler/GHC/HsToCore/Quote.hs b/compiler/GHC/HsToCore/Quote.hs index 4420262bd9e..b0ad0a67811 100644 --- a/compiler/GHC/HsToCore/Quote.hs +++ b/compiler/GHC/HsToCore/Quote.hs @@ -799,29 +799,37 @@ repDefD (L loc (DefaultDecl _ _ tys)) = do { tys1 <- repLTys tys repRuleD :: LRuleDecl GhcRn -> MetaM (SrcSpan, Core (M TH.Dec)) repRuleD (L loc (HsRule { rd_name = n , rd_act = act - , rd_tyvs = m_ty_bndrs - , rd_tmvs = tm_bndrs + , rd_bndrs = bndrs , rd_lhs = lhs , rd_rhs = rhs })) + = fmap (locA loc, ) <$> + repRuleBinders bndrs $ \ ty_bndrs' tm_bndrs' -> + do { n' <- coreStringLit $ unLoc n + ; act' <- repPhases act + ; lhs' <- repLE lhs + ; rhs' <- repLE rhs + ; repPragRule n' ty_bndrs' tm_bndrs' lhs' rhs' act' } + +repRuleBinders :: RuleBndrs GhcRn + -> (Core (Maybe [M (TH.TyVarBndr ())]) -> Core [M TH.RuleBndr] -> MetaM (Core (M a))) + -> MetaM (Core (M a)) +repRuleBinders (RuleBndrs { rb_tyvs = m_ty_bndrs, rb_tmvs = tm_bndrs }) thing_inside = do { let ty_bndrs = fromMaybe [] m_ty_bndrs - ; rule <- addHsTyVarBinds FreshNamesOnly ty_bndrs $ \ ex_bndrs -> - do { let tm_bndr_names = concatMap ruleBndrNames tm_bndrs - ; ss <- mkGenSyms tm_bndr_names - ; rule <- addBinds ss $ - do { elt_ty <- wrapName tyVarBndrUnitTyConName - ; ty_bndrs' <- return $ case m_ty_bndrs of - Nothing -> coreNothing' (mkListTy elt_ty) - Just _ -> coreJust' (mkListTy elt_ty) ex_bndrs - ; tm_bndrs' <- repListM ruleBndrTyConName - repRuleBndr - tm_bndrs - ; n' <- coreStringLit $ unLoc n - ; act' <- repPhases act - ; lhs' <- repLE lhs - ; rhs' <- repLE rhs - ; repPragRule n' ty_bndrs' tm_bndrs' lhs' rhs' act' } - ; wrapGenSyms ss rule } - ; return (locA loc, rule) } + ; addHsTyVarBinds FreshNamesOnly ty_bndrs $ \ ex_bndrs -> + do { let tm_bndr_names = concatMap ruleBndrNames tm_bndrs + ; ss <- mkGenSyms tm_bndr_names + ; x <- addBinds ss $ + do { elt_ty <- wrapName tyVarBndrUnitTyConName + ; ty_bndrs' <- return $ case m_ty_bndrs of + Nothing -> coreNothing' (mkListTy elt_ty) + Just _ -> coreJust' (mkListTy elt_ty) ex_bndrs + ; tm_bndrs' <- repListM ruleBndrTyConName + repRuleBndr + tm_bndrs + ; thing_inside ty_bndrs' tm_bndrs' + } + ; wrapGenSyms ss x } + } ruleBndrNames :: LRuleBndr GhcRn -> [Name] ruleBndrNames (L _ (RuleBndr _ n)) = [unLoc n] @@ -992,8 +1000,11 @@ rep_sig (L loc (FixSig _ fix_sig)) = rep_fix_d (locA loc) fix_sig rep_sig (L loc (InlineSig _ nm ispec))= rep_inline nm ispec (locA loc) rep_sig (L loc (SpecSig _ nm tys ispec)) = concatMapM (\t -> rep_specialise nm t ispec (locA loc)) tys -rep_sig (L loc (SpecInstSig _ ty)) = rep_specialiseInst ty (locA loc) -rep_sig (L _ (MinimalSig {})) = notHandled ThMinimalPragmas +rep_sig (L loc (SpecSigE _nm bndrs expr ispec)) + = fmap (\ d -> [(locA loc, d)]) $ + rep_specialiseE bndrs expr ispec +rep_sig (L loc (SpecInstSig _ ty)) = rep_specialiseInst ty (locA loc) +rep_sig (L _ (MinimalSig {})) = notHandled ThMinimalPragmas rep_sig (L loc (SCCFunSig _ nm str)) = rep_sccFun nm str (locA loc) rep_sig (L loc (CompleteMatchSig _ cls mty)) = rep_complete_sig cls mty (locA loc) @@ -1094,23 +1105,38 @@ rep_inline nm ispec loc ; return [(loc, pragma)] } +rep_inline_phases :: InlinePragma -> MetaM (Maybe (Core TH.Inline), Core TH.Phases) +rep_inline_phases (InlinePragma { inl_act = act, inl_inline = inl }) + = do { phases <- repPhases act + ; inl <- if noUserInlineSpec inl + -- SPECIALISE + then return Nothing + -- SPECIALISE INLINE + else Just <$> repInline inl + ; return (inl, phases) } + rep_specialise :: LocatedN Name -> LHsSigType GhcRn -> InlinePragma -> SrcSpan -> MetaM [(SrcSpan, Core (M TH.Dec))] rep_specialise nm ty ispec loc + -- Old form SPECIALISE pragmas = do { nm1 <- lookupLOcc nm ; ty1 <- repHsSigType ty - ; phases <- repPhases $ inl_act ispec - ; let inline = inl_inline ispec - ; pragma <- if noUserInlineSpec inline - then -- SPECIALISE - repPragSpec nm1 ty1 phases - else -- SPECIALISE INLINE - do { inline1 <- repInline inline - ; repPragSpecInl nm1 ty1 inline1 phases } + ; (inl, phases) <- rep_inline_phases ispec + ; pragma <- repPragSpec nm1 ty1 inl phases ; return [(loc, pragma)] } +rep_specialiseE :: RuleBndrs GhcRn -> LHsExpr GhcRn -> InlinePragma + -> MetaM (Core (M TH.Dec)) +rep_specialiseE bndrs e ispec + -- New form SPECIALISE pragmas + = repRuleBinders bndrs $ \ ty_bndrs tm_bndrs -> + do { (inl, phases) <- rep_inline_phases ispec + ; exp <- repLE e + ; repPragSpecE ty_bndrs tm_bndrs exp inl phases + } + rep_specialiseInst :: LHsSigType GhcRn -> SrcSpan -> MetaM [(SrcSpan, Core (M TH.Dec))] rep_specialiseInst ty loc @@ -2747,15 +2773,26 @@ repPragInl (MkC nm) (MkC inline) (MkC rm) (MkC phases) repPragOpaque :: Core TH.Name -> MetaM (Core (M TH.Dec)) repPragOpaque (MkC nm) = rep2 pragOpaqueDName [nm] -repPragSpec :: Core TH.Name -> Core (M TH.Type) -> Core TH.Phases +repPragSpec :: Core TH.Name -> Core (M TH.Type) -> Maybe (Core (TH.Inline)) + -> Core TH.Phases -> MetaM (Core (M TH.Dec)) -repPragSpec (MkC nm) (MkC ty) (MkC phases) - = rep2 pragSpecDName [nm, ty, phases] - -repPragSpecInl :: Core TH.Name -> Core (M TH.Type) -> Core TH.Inline - -> Core TH.Phases -> MetaM (Core (M TH.Dec)) -repPragSpecInl (MkC nm) (MkC ty) (MkC inline) (MkC phases) - = rep2 pragSpecInlDName [nm, ty, inline, phases] +repPragSpec (MkC nm) (MkC ty) mb_inl (MkC phases) + = case mb_inl of + Nothing -> + rep2 pragSpecDName [nm, ty, phases] + Just (MkC inl) -> + rep2 pragSpecInlDName [nm, ty, inl, phases] + +repPragSpecE :: Core (Maybe [M (TH.TyVarBndr ())]) -> Core [(M TH.RuleBndr)] + -> Core (M TH.Exp) + -> Maybe (Core TH.Inline) -> Core TH.Phases + -> MetaM (Core (M TH.Dec)) +repPragSpecE (MkC ty_bndrs) (MkC tm_bndrs) (MkC expr) mb_inl (MkC phases) + = case mb_inl of + Nothing -> + rep2 pragSpecEDName [ty_bndrs, tm_bndrs, expr, phases] + Just (MkC inl) -> + rep2 pragSpecInlEDName [ty_bndrs, tm_bndrs, expr, inl, phases] repPragSpecInst :: Core (M TH.Type) -> MetaM (Core (M TH.Dec)) repPragSpecInst (MkC ty) = rep2 pragSpecInstDName [ty] diff --git a/compiler/GHC/HsToCore/Types.hs b/compiler/GHC/HsToCore/Types.hs index d843db43a1d..f1433706c1d 100644 --- a/compiler/GHC/HsToCore/Types.hs +++ b/compiler/GHC/HsToCore/Types.hs @@ -14,27 +14,34 @@ module GHC.HsToCore.Types ( import GHC.Prelude (Int) import Data.IORef -import qualified Data.Set as S import GHC.Types.CostCentre.State import GHC.Types.Error import GHC.Types.Name.Env import GHC.Types.SrcLoc import GHC.Types.Var +import GHC.Types.Var.Set import GHC.Types.Name.Reader (GlobalRdrEnv) + import GHC.Hs (LForeignDecl, HsExpr, GhcTc) + import GHC.Tc.Types (TcRnIf, IfGblEnv, IfLclEnv) + import GHC.HsToCore.Pmc.Types (Nablas) import GHC.HsToCore.Errors.Types + import GHC.Core (CoreExpr) import GHC.Core.FamInstEnv import GHC.Utils.Outputable as Outputable import GHC.Unit.Module import GHC.Driver.Hooks (DsForeignsHook) import GHC.Data.OrdList (OrdList) + import GHC.Types.ForeignStubs (ForeignStubs) import GHC.Types.CompleteMatch +import Data.Maybe( Maybe ) + {- ************************************************************************ * * @@ -80,9 +87,11 @@ data DsLclEnv -- ^ See Note [Long-distance information] in "GHC.HsToCore.Pmc". -- The set of reaching values Nablas is augmented as we walk inwards, refined -- through each pattern match in turn - , dsl_unspecables :: S.Set EvVar - -- ^ See Note [Desugaring non-canonical evidence]: this field collects - -- all un-specialisable evidence variables in scope. + + , dsl_unspecables :: Maybe VarSet + -- ^ See Note [Desugaring non-canonical evidence] + -- This field collects all un-specialisable evidence variables in scope. + -- Nothing <=> don't collect this info (used for the LHS of Rules) } -- Inside [| |] brackets, the desugarer looks diff --git a/compiler/GHC/Iface/Decl.hs b/compiler/GHC/Iface/Decl.hs index 44d04720208..09f3211483e 100644 --- a/compiler/GHC/Iface/Decl.hs +++ b/compiler/GHC/Iface/Decl.hs @@ -31,6 +31,7 @@ import GHC.Core.ConLike import GHC.Core.DataCon import GHC.Core.Type import GHC.Core.Multiplicity +import GHC.Core.TyCo.Tidy import GHC.Types.Id import GHC.Types.Var.Env diff --git a/compiler/GHC/Iface/Ext/Ast.hs b/compiler/GHC/Iface/Ext/Ast.hs index 5896a30d811..2a5ff9790f4 100644 --- a/compiler/GHC/Iface/Ext/Ast.hs +++ b/compiler/GHC/Iface/Ext/Ast.hs @@ -1832,6 +1832,10 @@ instance HiePass p => ToHie (SigContext (LocatedA (Sig (GhcPass p)))) where [ toHie $ (C Use) name , toHie $ map (TS (ResolvedScopes [])) typs ] + SpecSigE _ bndrs spec_e _ -> + [ toHieRuleBndrs (locA sp) (mkScope spec_e) bndrs + , toHie spec_e + ] SpecInstSig _ typ -> [ toHie $ TS (ResolvedScopes []) typ ] @@ -2175,18 +2179,25 @@ instance ToHie (LocatedA (RuleDecls GhcRn)) where ] instance ToHie (LocatedA (RuleDecl GhcRn)) where - toHie (L span r@(HsRule _ rname _ tybndrs bndrs exprA exprB)) = concatM + toHie (L span r@(HsRule { rd_name = rname, rd_bndrs = bndrs + , rd_lhs = exprA, rd_rhs = exprB })) + = concatM [ makeNodeA r span , locOnly $ getLocA rname - , toHie $ fmap (tvScopes (ResolvedScopes []) scope) tybndrs - , toHie $ map (RS $ mkScope (locA span)) bndrs + , toHieRuleBndrs (locA span) scope bndrs , toHie exprA , toHie exprB ] - where scope = bndrs_sc `combineScopes` exprA_sc `combineScopes` exprB_sc - bndrs_sc = maybe NoScope mkScope (listToMaybe bndrs) - exprA_sc = mkScope exprA - exprB_sc = mkScope exprB + where + scope = mkScope exprA `combineScopes` mkScope exprB + +toHieRuleBndrs :: SrcSpan -> Scope -> RuleBndrs GhcRn -> HieM [HieAST Type] +toHieRuleBndrs span body_sc (RuleBndrs { rb_tyvs = tybndrs, rb_tmvs = bndrs }) + = concatM [ toHie $ fmap (tvScopes (ResolvedScopes []) full_sc) tybndrs + , toHie $ map (RS $ mkScope (locA span)) bndrs ] + where + full_sc = bndrs_sc `combineScopes` body_sc + bndrs_sc = maybe NoScope mkScope (listToMaybe bndrs) instance ToHie (RScoped (LocatedAn NoEpAnns (RuleBndr GhcRn))) where toHie (RS sc (L span bndr)) = concatM $ makeNodeA bndr span : case bndr of diff --git a/compiler/GHC/Iface/Tidy.hs b/compiler/GHC/Iface/Tidy.hs index 42404f0772f..deb8112a697 100644 --- a/compiler/GHC/Iface/Tidy.hs +++ b/compiler/GHC/Iface/Tidy.hs @@ -58,6 +58,7 @@ import GHC.Core.InstEnv import GHC.Core.Type import GHC.Core.DataCon import GHC.Core.TyCon +import GHC.Core.TyCo.Tidy import GHC.Core.Class import GHC.Core.Opt.OccurAnal ( occurAnalyseExpr ) diff --git a/compiler/GHC/Parser.y b/compiler/GHC/Parser.y index 712c9210089..3d649bd249e 100644 --- a/compiler/GHC/Parser.y +++ b/compiler/GHC/Parser.y @@ -1927,10 +1927,10 @@ rule :: { LRuleDecl GhcPs } {%runPV (unECP $4) >>= \ $4 -> runPV (unECP $6) >>= \ $6 -> amsA' (sLL $1 $> $ HsRule - { rd_ext =(((fstOf3 $3) (epTok $5) (fst $2)), getSTRINGs $1) + { rd_ext =((fst $2, epTok $5), getSTRINGs $1) , rd_name = L (noAnnSrcSpan $ gl $1) (getSTRING $1) - , rd_act = (snd $2) `orElse` AlwaysActive - , rd_tyvs = sndOf3 $3, rd_tmvs = thdOf3 $3 + , rd_act = snd $2 `orElse` AlwaysActive + , rd_bndrs = ruleBndrsOrDef $3 , rd_lhs = $4, rd_rhs = $6 }) } -- Rules can be specified to be NeverActive, unlike inline/specialize pragmas @@ -1966,19 +1966,22 @@ rule_explicit_activation :: { ( ActivationAnn { ( ActivationAnn (epTok $1) (epTok $3) $2 Nothing , NeverActive) } -rule_foralls :: { (EpToken "=" -> ActivationAnn -> HsRuleAnn, Maybe [LHsTyVarBndr () GhcPs], [LRuleBndr GhcPs]) } - : 'forall' rule_vars '.' 'forall' rule_vars '.' {% let tyvs = mkRuleTyVarBndrs $2 - in hintExplicitForall $1 - >> checkRuleTyVarBndrNames (mkRuleTyVarBndrs $2) - >> return (\an_eq an_act -> HsRuleAnn - (Just (epUniTok $1,epTok $3)) - (Just (epUniTok $4,epTok $6)) - an_eq an_act, - Just (mkRuleTyVarBndrs $2), mkRuleBndrs $5) } - | 'forall' rule_vars '.' { (\an_eq an_act -> HsRuleAnn Nothing (Just (epUniTok $1,epTok $3)) an_eq an_act, - Nothing, mkRuleBndrs $2) } +rule_foralls :: { Maybe (RuleBndrs GhcPs) } + : 'forall' rule_vars '.' 'forall' rule_vars '.' + {% hintExplicitForall $1 + >> checkRuleTyVarBndrNames $2 + >> let ann = HsRuleBndrsAnn + (Just (epUniTok $1,epTok $3)) + (Just (epUniTok $4,epTok $6)) + in return (Just (mkRuleBndrs ann (Just $2) $5)) } + + | 'forall' rule_vars '.' + { Just (mkRuleBndrs (HsRuleBndrsAnn Nothing (Just (epUniTok $1,epTok $3))) + Nothing $2) } + -- See Note [%shift: rule_foralls -> {- empty -}] - | {- empty -} %shift { (\an_eq an_act -> HsRuleAnn Nothing Nothing an_eq an_act, Nothing, []) } + | {- empty -} %shift + { Nothing } rule_vars :: { [LRuleTyTmVar] } : rule_var rule_vars { $1 : $2 } @@ -2766,16 +2769,21 @@ sigdecl :: { LHsDecl GhcPs } ; let str_lit = StringLiteral (getSTRINGs $3) scc Nothing ; amsA' (sLL $1 $> (SigD noExtField (SCCFunSig ((glR $1, epTok $4), (getSCC_PRAGs $1)) $2 (Just ( sL1a $3 str_lit))))) }} - | '{-# SPECIALISE' activation qvar '::' sigtypes1 '#-}' - {% amsA' ( - let inl_prag = mkInlinePragma (getSPEC_PRAGs $1) - (NoUserInlinePrag, FunLike) (snd $2) - in sLL $1 $> $ SigD noExtField (SpecSig (AnnSpecSig (glR $1) (epTok $6) (epUniTok $4) (fst $2)) $3 (fromOL $5) inl_prag)) } - - | '{-# SPECIALISE_INLINE' activation qvar '::' sigtypes1 '#-}' - {% amsA' (sLL $1 $> $ SigD noExtField (SpecSig (AnnSpecSig (glR $1) (epTok $6) (epUniTok $4) (fst $2)) $3 (fromOL $5) - (mkInlinePragma (getSPEC_INLINE_PRAGs $1) - (getSPEC_INLINE $1) (snd $2)))) } + | '{-# SPECIALISE' activation rule_foralls infixexp sigtypes_maybe '#-}' + {% runPV (unECP $4) >>= \ $4 -> do + let inl_prag = mkInlinePragma (getSPEC_PRAGs $1) + (NoUserInlinePrag, FunLike) + (snd $2) + spec <- mkSpecSig inl_prag (AnnSpecSig (glR $1) (epTok $6) (fmap fst $5) (fst $2)) $3 $4 $5 + amsA' $ sLL $1 $> $ SigD noExtField spec } + + | '{-# SPECIALISE_INLINE' activation rule_foralls infixexp sigtypes_maybe '#-}' + {% runPV (unECP $4) >>= \ $4 -> do + let inl_prag = mkInlinePragma (getSPEC_INLINE_PRAGs $1) + (getSPEC_INLINE $1) + (snd $2) + spec <- mkSpecSig inl_prag (AnnSpecSig (glR $1) (epTok $6) (fmap fst $5) (fst $2)) $3 $4 $5 + amsA' $ sLL $1 $> $ SigD noExtField spec } | '{-# SPECIALISE' 'instance' inst_type '#-}' {% amsA' (sLL $1 $> $ SigD noExtField (SpecInstSig ((glR $1,epTok $2,epTok $4), (getSPEC_PRAGs $1)) $3)) } @@ -2784,6 +2792,10 @@ sigdecl :: { LHsDecl GhcPs } | '{-# MINIMAL' name_boolformula_opt '#-}' {% amsA' (sLL $1 $> $ SigD noExtField (MinimalSig ((glR $1,epTok $3), (getMINIMAL_PRAGs $1)) $2)) } +sigtypes_maybe :: { Maybe (TokDcolon, OrdList (LHsSigType GhcPs)) } + : '::' sigtypes1 { Just (epUniTok $1, $2) } + | {- empty -} { Nothing } + activation :: { (ActivationAnn,Maybe Activation) } -- See Note [%shift: activation -> {- empty -}] : {- empty -} %shift { (noAnn ,Nothing) } diff --git a/compiler/GHC/Parser/Errors/Ppr.hs b/compiler/GHC/Parser/Errors/Ppr.hs index 9b3a8d04794..47650862a3e 100644 --- a/compiler/GHC/Parser/Errors/Ppr.hs +++ b/compiler/GHC/Parser/Errors/Ppr.hs @@ -559,6 +559,14 @@ instance Diagnostic PsMessage where PsErrIllegalOrPat pat -> mkSimpleDecorated $ vcat [text "Illegal or-pattern:" <+> ppr (unLoc pat)] + PsErrSpecExprMultipleTypeAscription + -> mkSimpleDecorated $ + text "SPECIALISE expression doesn't support multiple type ascriptions" + + PsWarnSpecMultipleTypeAscription + -> mkSimpleDecorated $ + text "SPECIALISE pragmas with multiple type ascriptions are deprecated, and will be removed in GHC 9.18" + diagnosticReason = \case PsUnknownMessage m -> diagnosticReason m PsHeaderMessage m -> psHeaderMessageReason m @@ -677,6 +685,8 @@ instance Diagnostic PsMessage where PsErrInvalidPun {} -> ErrorWithoutFlag PsErrIllegalOrPat{} -> ErrorWithoutFlag PsErrTypeSyntaxInPat{} -> ErrorWithoutFlag + PsErrSpecExprMultipleTypeAscription{} -> ErrorWithoutFlag + PsWarnSpecMultipleTypeAscription{} -> WarningWithFlag Opt_WarnDeprecatedPragmas diagnosticHints = \case PsUnknownMessage m -> diagnosticHints m @@ -846,6 +856,8 @@ instance Diagnostic PsMessage where PsErrInvalidPun {} -> [suggestExtension LangExt.ListTuplePuns] PsErrIllegalOrPat{} -> [suggestExtension LangExt.OrPatterns] PsErrTypeSyntaxInPat{} -> noHints + PsErrSpecExprMultipleTypeAscription {} -> [SuggestSplittingIntoSeveralSpecialisePragmas] + PsWarnSpecMultipleTypeAscription{} -> [SuggestSplittingIntoSeveralSpecialisePragmas] diagnosticCode = constructorCode @GHC diff --git a/compiler/GHC/Parser/Errors/Types.hs b/compiler/GHC/Parser/Errors/Types.hs index 2ae551b3168..9f814ebdecd 100644 --- a/compiler/GHC/Parser/Errors/Types.hs +++ b/compiler/GHC/Parser/Errors/Types.hs @@ -491,6 +491,22 @@ data PsMessage -- T24159_pat_parse_error_6 | PsErrTypeSyntaxInPat !PsErrTypeSyntaxDetails + -- | 'PsErrSpecExprMultipleTypeAscription' is an error that occurs when + -- a user attempts to use the new form SPECIALISE pragma syntax with + -- multiple type signatures, e.g. + -- + -- @{-# SPECIALISE foo 3 :: Float -> Float; Double -> Double #-} + | PsErrSpecExprMultipleTypeAscription + + -- | 'PsWarnSpecMultipleTypeAscription' is a warning that occurs when + -- a user uses the old-form SPECIALISE pragma syntax with + -- multiple type signatures, e.g. + -- + -- @{-# SPECIALISE bar :: Float -> Float; Double -> Double #-} + -- + -- This constructor is deprecated and will be removed in GHC 9.18. + | PsWarnSpecMultipleTypeAscription + deriving Generic -- | Extra details about a parse error, which helps diff --git a/compiler/GHC/Parser/PostProcess.hs b/compiler/GHC/Parser/PostProcess.hs index 27b29076120..cc2f7ddce3f 100644 --- a/compiler/GHC/Parser/PostProcess.hs +++ b/compiler/GHC/Parser/PostProcess.hs @@ -62,8 +62,10 @@ module GHC.Parser.PostProcess ( checkValDef, -- (SrcLoc, HsExp, HsRhs, [HsDecl]) -> P HsDecl checkValSigLhs, LRuleTyTmVar, RuleTyTmVar(..), - mkRuleBndrs, mkRuleTyVarBndrs, + mkRuleBndrs, + ruleBndrsOrDef, checkRuleTyVarBndrNames, + mkSpecSig, checkRecordSyntax, checkEmptyGADTs, addFatalError, hintBangPat, @@ -1006,29 +1008,84 @@ type LRuleTyTmVar = LocatedAn NoEpAnns RuleTyTmVar data RuleTyTmVar = RuleTyTmVar AnnTyVarBndr (LocatedN RdrName) (Maybe (LHsType GhcPs)) -- ^ Essentially a wrapper for a @RuleBndr GhcPs@ --- turns RuleTyTmVars into RuleBnrs - this is straightforward -mkRuleBndrs :: [LRuleTyTmVar] -> [LRuleBndr GhcPs] -mkRuleBndrs = fmap (fmap cvt_one) - where cvt_one (RuleTyTmVar ann v Nothing) = RuleBndr ann v - cvt_one (RuleTyTmVar ann v (Just sig)) = - RuleBndrSig ann v (mkHsPatSigType noAnn sig) - --- turns RuleTyTmVars into HsTyVarBndrs - this is more interesting -mkRuleTyVarBndrs :: [LRuleTyTmVar] -> [LHsTyVarBndr () GhcPs] -mkRuleTyVarBndrs = fmap (setLHsTyVarBndrNameSpace tvName . cvt_one) - where cvt_one (L l (RuleTyTmVar ann v msig)) +ruleBndrsOrDef :: Maybe (RuleBndrs GhcPs) -> RuleBndrs GhcPs +ruleBndrsOrDef (Just bndrs) = bndrs +ruleBndrsOrDef Nothing = mkRuleBndrs noAnn Nothing [] + +mkRuleBndrs :: HsRuleBndrsAnn -> Maybe [LRuleTyTmVar] -> [LRuleTyTmVar] -> RuleBndrs GhcPs +mkRuleBndrs ann tvbs tmbs + = RuleBndrs { rb_ext = ann + , rb_tyvs = fmap (fmap (setLHsTyVarBndrNameSpace tvName . cvt_tv)) tvbs + , rb_tmvs = fmap (fmap cvt_tm) tmbs } + where + -- cvt_tm turns RuleTyTmVars into RuleBnrs - this is straightforward + cvt_tm (RuleTyTmVar ann v Nothing) = RuleBndr ann v + cvt_tm (RuleTyTmVar ann v (Just sig)) = RuleBndrSig ann v (mkHsPatSigType noAnn sig) + + -- cvt_tv turns RuleTyTmVars into HsTyVarBndrs - this is more interesting + cvt_tv (L l (RuleTyTmVar ann v msig)) = L (l2l l) (HsTvb ann () (HsBndrVar noExtField v) (cvt_sig msig)) - cvt_sig Nothing = HsBndrNoKind noExtField - cvt_sig (Just sig) = HsBndrKind noExtField sig + cvt_sig Nothing = HsBndrNoKind noExtField + cvt_sig (Just sig) = HsBndrKind noExtField sig +checkRuleTyVarBndrNames :: [LRuleTyTmVar] -> P () -- See Note [Parsing explicit foralls in Rules] in Parser.y -checkRuleTyVarBndrNames :: [LHsTyVarBndr flag GhcPs] -> P () -checkRuleTyVarBndrNames = mapM_ check . mapMaybe (hsTyVarLName . unLoc) - where check (L loc (Unqual occ)) = - when (occNameFS occ `elem` [fsLit "family",fsLit "role"]) - (addFatalError $ mkPlainErrorMsgEnvelope (locA loc) $ - (PsErrParseErrorOnInput occ)) - check _ = panic "checkRuleTyVarBndrNames" +checkRuleTyVarBndrNames bndrs + = sequence_ [ check lname | L _ (RuleTyTmVar _ lname _) <- bndrs ] + where + check (L loc (Unqual occ)) = + when (occNameFS occ `elem` [fsLit "family",fsLit "role"]) $ + addFatalError $ mkPlainErrorMsgEnvelope (locA loc) $ + PsErrParseErrorOnInput occ + check _ = panic "checkRuleTyVarBndrNames" + +-- | Deal with both old-form and new-form specialise pragmas, using the new +-- 'SpecSigE' form unless there are multiple comma-separated type signatures, +-- in which case we use the old-form. +-- +-- See Note [Overview of SPECIALISE pragmas] in GHC.Tc.Gen.Sig. +mkSpecSig :: InlinePragma + -> AnnSpecSig + -> Maybe (RuleBndrs GhcPs) + -> LHsExpr GhcPs + -> Maybe (TokDcolon, OrdList (LHsSigType GhcPs)) + -> P (Sig GhcPs) +mkSpecSig inl_prag activation_anns m_rule_binds expr m_sigtypes_ascr + = case m_sigtypes_ascr of + Nothing + -- New form, no trailing type signature, e.g {-# SPECIALISE f @Int #-} + -> pure $ + SpecSigE activation_anns + (ruleBndrsOrDef m_rule_binds) expr inl_prag + + Just (colon_ann, sigtype_ol) + + -- Singleton, e.g. {-# SPECIALISE f :: ty #-} + -- Use the SpecSigE route + | [sigtype] <- sigtype_list + -> pure $ + SpecSigE activation_anns + (ruleBndrsOrDef m_rule_binds) + (L (getLoc expr) -- ToDo: not really the right location for (e::ty) + (ExprWithTySig colon_ann expr (mkHsWildCardBndrs sigtype))) + inl_prag + + -- So we must have the old form {# SPECIALISE f :: ty1, ty2, ty3 #-} + -- Use the old SpecSig route + | Nothing <- m_rule_binds + , L _ (HsVar _ var) <- expr + -> do addPsMessage sigs_loc PsWarnSpecMultipleTypeAscription + pure $ + SpecSig activation_anns var sigtype_list inl_prag + + | otherwise -> + addFatalError $ + mkPlainErrorMsgEnvelope sigs_loc PsErrSpecExprMultipleTypeAscription + + where + sigtype_list = fromOL sigtype_ol + sigs_loc = + getHasLoc colon_ann `combineSrcSpans` getHasLoc (last sigtype_list) checkRecordSyntax :: (MonadP m, Outputable a) => LocatedA a -> m (LocatedA a) checkRecordSyntax lr@(L loc r) diff --git a/compiler/GHC/Rename/Bind.hs b/compiler/GHC/Rename/Bind.hs index 5ab6acf5c80..5876e7f27f4 100644 --- a/compiler/GHC/Rename/Bind.hs +++ b/compiler/GHC/Rename/Bind.hs @@ -20,7 +20,7 @@ module GHC.Rename.Bind ( rnLocalBindsAndThen, rnLocalValBindsLHS, rnLocalValBindsRHS, -- Other bindings - rnMethodBinds, renameSigs, + rnMethodBinds, renameSigs, bindRuleBndrs, rnMatchGroup, rnGRHSs, rnGRHS, rnSrcFixityDecl, makeMiniFixityEnv, MiniFixityEnv, emptyMiniFixityEnv, HsSigCtxt(..), @@ -44,14 +44,10 @@ import GHC.Rename.Pat import GHC.Rename.Names import GHC.Rename.Env import GHC.Rename.Fixity -import GHC.Rename.Utils ( mapFvRn - , checkDupRdrNames - , warnUnusedLocalBinds - , checkUnusedRecordWildcard - , checkDupAndShadowedNames, bindLocalNamesFV - , addNoNestedForallsContextsErr, checkInferredVars ) +import GHC.Rename.Utils import GHC.Driver.DynFlags import GHC.Unit.Module + import GHC.Types.Error import GHC.Types.FieldLabel import GHC.Types.Name @@ -60,16 +56,22 @@ import GHC.Types.Name.Set import GHC.Types.Name.Reader ( RdrName, rdrNameOcc ) import GHC.Types.SourceFile import GHC.Types.SrcLoc as SrcLoc -import GHC.Data.List.SetOps ( findDupsEq ) import GHC.Types.Basic ( RecFlag(..), TypeOrKind(..) ) import GHC.Data.Graph.Directed ( SCC(..) ) + +import GHC.Types.Unique.Set + import GHC.Utils.Misc import GHC.Utils.Outputable import GHC.Utils.Panic + import GHC.Types.CompleteMatch -import GHC.Types.Unique.Set + import GHC.Data.Maybe ( orElse, mapMaybe ) +import GHC.Data.List.SetOps ( findDupsEq ) + import GHC.Data.OrdList + import qualified GHC.LanguageExtensions as LangExt import Language.Haskell.Syntax.Basic (FieldLabelString(..)) @@ -1119,6 +1121,13 @@ renameSig ctxt sig@(SpecSig _ v tys inl) = do { (new_ty, fvs_ty) <- rnHsSigType ty_ctxt TypeLevel ty ; return ( new_ty:tys, fvs_ty `plusFV` fvs) } +renameSig _ctxt (SpecSigE _ bndrs spec_e inl) + = do { fn_rdr <- checkSpecESigShape spec_e + ; fn_name <- lookupOccRn fn_rdr -- Checks that the head isn't forall-bound + ; bindRuleBndrs (SpecECtx fn_rdr) bndrs $ \_ bndrs' -> + do { (spec_e', fvs) <- rnLExpr spec_e + ; return (SpecSigE fn_name bndrs' spec_e' inl, fvs) } } + renameSig ctxt sig@(InlineSig _ v s) = do { new_v <- lookupSigOccRn ctxt sig v ; return (InlineSig noAnn new_v s, emptyFVs) } @@ -1158,6 +1167,21 @@ renameSig _ctxt (CompleteMatchSig (_, s) bf mty) return (rn_sig, emptyFVs) +checkSpecESigShape :: LHsExpr GhcPs -> RnM RdrName +-- Checks the shape of a SPECIALISE +-- That it looks like (f a1 .. an [ :: ty ]) +checkSpecESigShape spec_e = go_l spec_e + where + go_l (L _ e) = go e + + go :: HsExpr GhcPs -> RnM RdrName + go (ExprWithTySig _ fn _) = go_l fn + go (HsApp _ fn _) = go_l fn + go (HsAppType _ fn _) = go_l fn + go (HsVar _ (L _ fn)) = return fn + go (HsPar _ e) = go_l e + go _ = failWithTc (TcRnSpecSigShape spec_e) + {- Note [Orphan COMPLETE pragmas] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1203,10 +1227,8 @@ okHsSig ctxt (L _ sig) (InlineSig {}, HsBootCtxt {}) -> False (InlineSig {}, _) -> True - (SpecSig {}, TopSigCtxt {}) -> True - (SpecSig {}, LocalBindCtxt {}) -> True - (SpecSig {}, InstDeclCtxt {}) -> True - (SpecSig {}, _) -> False + (SpecSig {}, ctxt) -> ok_spec_ctxt ctxt + (SpecSigE {}, ctxt) -> ok_spec_ctxt ctxt (SpecInstSig {}, InstDeclCtxt {}) -> True (SpecInstSig {}, _) -> False @@ -1224,6 +1246,12 @@ okHsSig ctxt (L _ sig) (XSig {}, InstDeclCtxt {}) -> True (XSig {}, _) -> False +ok_spec_ctxt ::HsSigCtxt -> Bool +-- Contexts where SPECIALISE can occur +ok_spec_ctxt (TopSigCtxt {}) = True +ok_spec_ctxt (LocalBindCtxt {}) = True +ok_spec_ctxt (InstDeclCtxt {}) = True +ok_spec_ctxt _ = False ------------------- findDupSigs :: [LSig GhcPs] -> [NonEmpty (LocatedN RdrName, Sig GhcPs)] @@ -1275,6 +1303,54 @@ localCompletePragmas sigs = mapMaybe (getCompleteSig . unLoc) $ reverse sigs -- backwards wrt. declaration order. So we reverse them here, because it makes -- a difference for incomplete match suggestions. +bindRuleBndrs :: HsDocContext -> RuleBndrs GhcPs + -> ([Name] -> RuleBndrs GhcRn -> RnM (a,FreeVars)) + -> RnM (a,FreeVars) +bindRuleBndrs doc (RuleBndrs { rb_tyvs = tyvs, rb_tmvs = tmvs }) thing_inside + = do { let rdr_names_w_loc = map (get_var . unLoc) tmvs + ; checkDupRdrNames rdr_names_w_loc + ; checkShadowedRdrNames rdr_names_w_loc + ; names <- newLocalBndrsRn rdr_names_w_loc + ; bindRuleTyVars doc tyvs $ \ tyvs' -> + bindRuleTmVars doc tyvs' tmvs names $ \ tmvs' -> + thing_inside names (RuleBndrs { rb_ext = noExtField + , rb_tyvs = tyvs', rb_tmvs = tmvs' }) } + where + get_var :: RuleBndr GhcPs -> LocatedN RdrName + get_var (RuleBndrSig _ v _) = v + get_var (RuleBndr _ v) = v + + +bindRuleTmVars :: HsDocContext -> Maybe ty_bndrs + -> [LRuleBndr GhcPs] -> [Name] + -> ([LRuleBndr GhcRn] -> RnM (a, FreeVars)) + -> RnM (a, FreeVars) +bindRuleTmVars doc tyvs vars names thing_inside + = go vars names $ \ vars' -> + bindLocalNamesFV names (thing_inside vars') + where + go ((L l (RuleBndr _ (L loc _))) : vars) (n : ns) thing_inside + = go vars ns $ \ vars' -> + thing_inside (L l (RuleBndr noAnn (L loc n)) : vars') + + go ((L l (RuleBndrSig _ (L loc _) bsig)) : vars) + (n : ns) thing_inside + = rnHsPatSigType bind_free_tvs doc bsig $ \ bsig' -> + go vars ns $ \ vars' -> + thing_inside (L l (RuleBndrSig noAnn (L loc n) bsig') : vars') + + go [] [] thing_inside = thing_inside [] + go vars names _ = pprPanic "bindRuleVars" (ppr vars $$ ppr names) + + bind_free_tvs = case tyvs of Nothing -> AlwaysBind + Just _ -> NeverBind + +bindRuleTyVars :: HsDocContext -> Maybe [LHsTyVarBndr () GhcPs] + -> (Maybe [LHsTyVarBndr () GhcRn] -> RnM (b, FreeVars)) + -> RnM (b, FreeVars) +bindRuleTyVars doc (Just bndrs) thing_inside + = bindLHsTyVarBndrs doc WarnUnusedForalls Nothing bndrs (thing_inside . Just) +bindRuleTyVars _ _ thing_inside = thing_inside Nothing {- ************************************************************************ diff --git a/compiler/GHC/Rename/HsType.hs b/compiler/GHC/Rename/HsType.hs index e1e0c0a05b3..d7ad77dd671 100644 --- a/compiler/GHC/Rename/HsType.hs +++ b/compiler/GHC/Rename/HsType.hs @@ -825,6 +825,7 @@ wildCardsAllowed env ExprWithTySigCtx {} -> True PatCtx {} -> True RuleCtx {} -> True + SpecECtx {} -> True FamPatCtx {} -> True -- Not named wildcards though GHCiCtx {} -> True HsTypeCtx {} -> True diff --git a/compiler/GHC/Rename/Module.hs b/compiler/GHC/Rename/Module.hs index b4ef1359ddd..f48b0ae770e 100644 --- a/compiler/GHC/Rename/Module.hs +++ b/compiler/GHC/Rename/Module.hs @@ -24,25 +24,23 @@ import {-# SOURCE #-} GHC.Rename.Expr( rnLExpr ) import {-# SOURCE #-} GHC.Rename.Splice ( rnSpliceDecl, rnTopSpliceDecls ) import GHC.Hs -import GHC.Types.FieldLabel -import GHC.Types.Name.Reader + import GHC.Rename.HsType import GHC.Rename.Bind import GHC.Rename.Doc import GHC.Rename.Env import GHC.Rename.Utils ( mapFvRn, bindLocalNames , checkDupRdrNames, bindLocalNamesFV - , checkShadowedRdrNames, warnUnusedTypePatterns - , newLocalBndrsRn + , warnUnusedTypePatterns , noNestedForallsContextsErr , addNoNestedForallsContextsErr, checkInferredVars ) import GHC.Rename.Unbound ( mkUnboundName, notInScopeErr, WhereLooking(WL_Global) ) import GHC.Rename.Names + import GHC.Tc.Errors.Types import GHC.Tc.Utils.Monad import GHC.Tc.Types.Origin ( TypedThing(..) ) -import GHC.Types.ForeignCall ( CCallTarget(..) ) import GHC.Unit import GHC.Unit.Module.Warnings import GHC.Builtin.Names( applicativeClassName, pureAName, thenAName @@ -50,23 +48,29 @@ import GHC.Builtin.Names( applicativeClassName, pureAName, thenAName , semigroupClassName, sappendName , monoidClassName, mappendName ) + +import GHC.Types.FieldLabel +import GHC.Types.Name.Reader +import GHC.Types.ForeignCall ( CCallTarget(..) ) import GHC.Types.Name import GHC.Types.Name.Set import GHC.Types.Name.Env -import GHC.Utils.Outputable -import GHC.Types.Basic (VisArity) -import GHC.Types.Basic ( TypeOrKind(..) ) -import GHC.Data.FastString +import GHC.Types.Basic ( VisArity, TypeOrKind(..), RuleName ) +import GHC.Types.GREInfo (ConLikeInfo (..), ConInfo, mkConInfo, conInfoFields) +import GHC.Types.Unique.Set import GHC.Types.SrcLoc as SrcLoc + import GHC.Driver.DynFlags +import GHC.Driver.Env ( HscEnv(..), hsc_home_unit) + import GHC.Utils.Misc ( lengthExceeds, partitionWith ) import GHC.Utils.Panic -import GHC.Driver.Env ( HscEnv(..), hsc_home_unit) +import GHC.Utils.Outputable + +import GHC.Data.FastString import GHC.Data.List.SetOps ( findDupsEq, removeDupsOn, equivClasses ) import GHC.Data.Graph.Directed ( SCC, flattenSCC, flattenSCCs, Node(..) , stronglyConnCompFromEdgedVerticesUniq ) -import GHC.Types.GREInfo (ConLikeInfo (..), ConInfo, mkConInfo, conInfoFields) -import GHC.Types.Unique.Set import GHC.Data.OrdList import qualified GHC.LanguageExtensions as LangExt import GHC.Core.DataCon ( isSrcStrict ) @@ -1159,65 +1163,23 @@ rnHsRuleDecls (HsRules { rds_ext = (_, src) , rds_rules = rn_rules }, fvs) } rnHsRuleDecl :: RuleDecl GhcPs -> RnM (RuleDecl GhcRn, FreeVars) -rnHsRuleDecl (HsRule { rd_ext = (_, st) - , rd_name = rule_name - , rd_act = act - , rd_tyvs = tyvs - , rd_tmvs = tmvs - , rd_lhs = lhs - , rd_rhs = rhs }) - = do { let rdr_names_w_loc = map (get_var . unLoc) tmvs - ; checkDupRdrNames rdr_names_w_loc - ; checkShadowedRdrNames rdr_names_w_loc - ; names <- newLocalBndrsRn rdr_names_w_loc - ; let doc = RuleCtx (unLoc rule_name) - ; bindRuleTyVars doc tyvs $ \ tyvs' -> - bindRuleTmVars doc tyvs' tmvs names $ \ tmvs' -> +rnHsRuleDecl (HsRule { rd_ext = (_, st) + , rd_name = lrule_name@(L _ rule_name) + , rd_act = act + , rd_bndrs = bndrs + , rd_lhs = lhs + , rd_rhs = rhs }) + = bindRuleBndrs (RuleCtx rule_name) bndrs $ \tm_names bndrs' -> do { (lhs', fv_lhs') <- rnLExpr lhs ; (rhs', fv_rhs') <- rnLExpr rhs - ; checkValidRule (unLoc rule_name) names lhs' fv_lhs' - ; return (HsRule { rd_ext = (HsRuleRn fv_lhs' fv_rhs', st) - , rd_name = rule_name - , rd_act = act - , rd_tyvs = tyvs' - , rd_tmvs = tmvs' - , rd_lhs = lhs' - , rd_rhs = rhs' }, fv_lhs' `plusFV` fv_rhs') } } - where - get_var :: RuleBndr GhcPs -> LocatedN RdrName - get_var (RuleBndrSig _ v _) = v - get_var (RuleBndr _ v) = v - -bindRuleTmVars :: HsDocContext -> Maybe ty_bndrs - -> [LRuleBndr GhcPs] -> [Name] - -> ([LRuleBndr GhcRn] -> RnM (a, FreeVars)) - -> RnM (a, FreeVars) -bindRuleTmVars doc tyvs vars names thing_inside - = go vars names $ \ vars' -> - bindLocalNamesFV names (thing_inside vars') - where - go ((L l (RuleBndr _ (L loc _))) : vars) (n : ns) thing_inside - = go vars ns $ \ vars' -> - thing_inside (L l (RuleBndr noAnn (L loc n)) : vars') - - go ((L l (RuleBndrSig _ (L loc _) bsig)) : vars) - (n : ns) thing_inside - = rnHsPatSigType bind_free_tvs doc bsig $ \ bsig' -> - go vars ns $ \ vars' -> - thing_inside (L l (RuleBndrSig noAnn (L loc n) bsig') : vars') - - go [] [] thing_inside = thing_inside [] - go vars names _ = pprPanic "bindRuleVars" (ppr vars $$ ppr names) - - bind_free_tvs = case tyvs of Nothing -> AlwaysBind - Just _ -> NeverBind - -bindRuleTyVars :: HsDocContext -> Maybe [LHsTyVarBndr () GhcPs] - -> (Maybe [LHsTyVarBndr () GhcRn] -> RnM (b, FreeVars)) - -> RnM (b, FreeVars) -bindRuleTyVars doc (Just bndrs) thing_inside - = bindLHsTyVarBndrs doc WarnUnusedForalls Nothing bndrs (thing_inside . Just) -bindRuleTyVars _ _ thing_inside = thing_inside Nothing + ; checkValidRule rule_name tm_names lhs' fv_lhs' + ; return (HsRule { rd_ext = (HsRuleRn fv_lhs' fv_rhs', st) + , rd_name = lrule_name + , rd_act = act + , rd_bndrs = bndrs' + , rd_lhs = lhs' + , rd_rhs = rhs' } + , fv_lhs' `plusFV` fv_rhs') } {- Note [Rule LHS validity checking] @@ -1244,7 +1206,7 @@ with the LHS wrapped in parens. But Template Haskell does (#24621)! So we should accommodate them. -} -checkValidRule :: FastString -> [Name] -> LHsExpr GhcRn -> NameSet -> RnM () +checkValidRule :: RuleName -> [Name] -> LHsExpr GhcRn -> NameSet -> RnM () checkValidRule rule_name ids lhs' fv_lhs' = do { -- Check for the form of the LHS case (validRuleLhs ids lhs') of diff --git a/compiler/GHC/Rename/Names.hs b/compiler/GHC/Rename/Names.hs index a4850a1ff17..f71008b297d 100644 --- a/compiler/GHC/Rename/Names.hs +++ b/compiler/GHC/Rename/Names.hs @@ -54,7 +54,7 @@ import GHC.Iface.Load ( loadSrcInterface ) import GHC.Iface.Syntax ( IfaceDefault, fromIfaceWarnings ) import GHC.Builtin.Names import GHC.Parser.PostProcess ( setRdrNameSpace ) -import GHC.Core.Type +import GHC.Core.TyCo.Tidy import GHC.Core.PatSyn import GHC.Core.TyCon ( TyCon, tyConName ) import qualified GHC.LanguageExtensions as LangExt diff --git a/compiler/GHC/Runtime/Debugger.hs b/compiler/GHC/Runtime/Debugger.hs index 37413a68f1d..ca09a673049 100644 --- a/compiler/GHC/Runtime/Debugger.hs +++ b/compiler/GHC/Runtime/Debugger.hs @@ -33,6 +33,7 @@ import GHC.Runtime.Context import GHC.Iface.Syntax ( showToHeader ) import GHC.Iface.Env ( newInteractiveBinder ) import GHC.Core.Type +import GHC.Core.TyCo.Tidy( tidyOpenType ) import GHC.Utils.Outputable import GHC.Utils.Error diff --git a/compiler/GHC/Runtime/Eval.hs b/compiler/GHC/Runtime/Eval.hs index 9420a9e2c2e..dcb6b98410e 100644 --- a/compiler/GHC/Runtime/Eval.hs +++ b/compiler/GHC/Runtime/Eval.hs @@ -72,6 +72,7 @@ import GHC.Core.FamInstEnv ( FamInst, orphNamesOfFamInst ) import GHC.Core.InstEnv import GHC.Core.Predicate import GHC.Core.TyCo.Ppr +import GHC.Core.TyCo.Tidy( tidyType, tidyOpenTypes ) import GHC.Core.TyCon import GHC.Core.Type hiding( typeKind ) import qualified GHC.Core.Type as Type @@ -121,12 +122,11 @@ import GHC.Unit.Module.ModSummary import GHC.Unit.Home.ModInfo import GHC.Unit.Home.PackageTable -import GHC.Tc.Module ( runTcInteractive, tcRnType, loadUnqualIfaces ) +import GHC.Tc.Module ( runTcInteractive, tcRnTypeSkolemising, loadUnqualIfaces ) import GHC.Tc.Solver (simplifyWantedsTcM) import GHC.Tc.Utils.Env (tcGetInstEnvs) import GHC.Tc.Utils.Instantiate (instDFunType) import GHC.Tc.Utils.Monad -import GHC.Tc.Zonk.Env ( ZonkFlexi (SkolemiseFlexi) ) import GHC.IfaceToCore @@ -1072,8 +1072,9 @@ parseInstanceHead str = withSession $ \hsc_env0 -> do (ty, _) <- liftIO $ runInteractiveHsc hsc_env0 $ do hsc_env <- getHscEnv ty <- hscParseType str - ioMsgMaybe $ hoistTcRnMessage $ tcRnType hsc_env SkolemiseFlexi True ty - + ioMsgMaybe $ hoistTcRnMessage $ + tcRnTypeSkolemising hsc_env ty + -- I'm not sure what to do about those zonked skolems return ty -- Get all the constraints required of a dictionary binding diff --git a/compiler/GHC/Runtime/Heap/Inspect.hs b/compiler/GHC/Runtime/Heap/Inspect.hs index b340760bb0c..0bac82d34cd 100644 --- a/compiler/GHC/Runtime/Heap/Inspect.hs +++ b/compiler/GHC/Runtime/Heap/Inspect.hs @@ -44,6 +44,7 @@ import GHCi.Message ( fromSerializableException ) import GHC.Core.DataCon import GHC.Core.Type +import GHC.Core.Predicate( tyCoVarsOfTypeWellScoped ) import GHC.Types.RepType import GHC.Core.Multiplicity import qualified GHC.Core.Unify as U diff --git a/compiler/GHC/StgToByteCode.hs b/compiler/GHC/StgToByteCode.hs index e0521ddcd9c..6ebd599e50b 100644 --- a/compiler/GHC/StgToByteCode.hs +++ b/compiler/GHC/StgToByteCode.hs @@ -43,6 +43,7 @@ import GHC.Types.Literal import GHC.Builtin.PrimOps import GHC.Builtin.PrimOps.Ids (primOpId) import GHC.Core.Type +import GHC.Core.Predicate( tyCoVarsOfTypesWellScoped ) import GHC.Core.TyCo.Compare (eqType) import GHC.Types.RepType import GHC.Core.DataCon diff --git a/compiler/GHC/Tc/Deriv.hs b/compiler/GHC/Tc/Deriv.hs index d4e19259955..45412cb0b32 100644 --- a/compiler/GHC/Tc/Deriv.hs +++ b/compiler/GHC/Tc/Deriv.hs @@ -20,52 +20,56 @@ import GHC.Hs import GHC.Driver.Session import GHC.Tc.Errors.Types -import GHC.Tc.Utils.Monad import GHC.Tc.Instance.Family import GHC.Tc.Types.Origin import GHC.Tc.Deriv.Infer import GHC.Tc.Deriv.Utils -import GHC.Tc.TyCl.Class( instDeclCtxt3, tcATDefault ) -import GHC.Tc.Utils.Env import GHC.Tc.Deriv.Generate +import GHC.Tc.TyCl.Class( instDeclCtxt3, tcATDefault ) import GHC.Tc.Validity( checkValidInstHead ) -import GHC.Core.InstEnv -import GHC.Tc.Utils.Instantiate -import GHC.Core.FamInstEnv import GHC.Tc.Gen.HsType -import GHC.Core.TyCo.Rep -import GHC.Core.TyCo.Ppr ( pprTyVars ) -import GHC.Unit.Module.Warnings +import GHC.Tc.Utils.Monad +import GHC.Tc.Utils.Instantiate +import GHC.Tc.Utils.TcType +import GHC.Tc.Utils.Env import GHC.Rename.Bind import GHC.Rename.Env import GHC.Rename.Module ( addTcgDUs ) import GHC.Rename.Utils +import GHC.Core.TyCo.Ppr ( pprTyVars ) +import GHC.Core.FamInstEnv +import GHC.Core.InstEnv import GHC.Core.Unify( tcUnifyTy ) import GHC.Core.Class import GHC.Core.Type -import GHC.Utils.Error import GHC.Core.DataCon -import GHC.Data.Maybe +import GHC.Core.TyCon +import GHC.Core.Predicate( tyCoVarsOfTypesWellScoped ) + import GHC.Types.Hint (AssumedDerivingStrategy(..)) import GHC.Types.Name.Reader import GHC.Types.Name import GHC.Types.Name.Set as NameSet -import GHC.Core.TyCon -import GHC.Tc.Utils.TcType import GHC.Types.Var as Var import GHC.Types.Var.Env import GHC.Types.Var.Set -import GHC.Builtin.Names import GHC.Types.SrcLoc + +import GHC.Unit.Module.Warnings +import GHC.Builtin.Names + +import GHC.Utils.Error import GHC.Utils.Misc import GHC.Utils.Outputable as Outputable import GHC.Utils.Panic import GHC.Utils.Logger -import GHC.Data.Bag import GHC.Utils.FV as FV (fvVarList, unionFV, mkFVs) import qualified GHC.LanguageExtensions as LangExt + +import GHC.Data.Bag +import GHC.Data.Maybe import GHC.Data.BooleanFormula ( isUnsatisfied ) import Control.Monad diff --git a/compiler/GHC/Tc/Errors.hs b/compiler/GHC/Tc/Errors.hs index b6e8fa8cdd5..cdf14f10719 100644 --- a/compiler/GHC/Tc/Errors.hs +++ b/compiler/GHC/Tc/Errors.hs @@ -60,7 +60,7 @@ import GHC.Core.Predicate import GHC.Core.Type import GHC.Core.Coercion import GHC.Core.TyCo.Ppr ( pprTyVars ) -import GHC.Core.TyCo.Tidy ( tidyAvoiding ) +import GHC.Core.TyCo.Tidy import GHC.Core.InstEnv import GHC.Core.TyCon import GHC.Core.DataCon diff --git a/compiler/GHC/Tc/Errors/Ppr.hs b/compiler/GHC/Tc/Errors/Ppr.hs index 16009860037..c500d6c2baa 100644 --- a/compiler/GHC/Tc/Errors/Ppr.hs +++ b/compiler/GHC/Tc/Errors/Ppr.hs @@ -64,6 +64,7 @@ import GHC.Core.TyCo.Rep (Type(..)) import GHC.Core.TyCo.Ppr (pprWithInvisibleBitsWhen, pprSourceTyCon, pprTyVars, pprWithTYPE, pprTyVar, pprTidiedType, pprForAll) import GHC.Core.PatSyn ( patSynName, pprPatSynType ) +import GHC.Core.TyCo.Tidy import GHC.Core.Predicate import GHC.Core.Type import GHC.Core.FVs( orphNamesOfTypes ) @@ -1355,6 +1356,7 @@ instance Diagnostic TcRnMessage where PatSynBind {} -> text "Pattern synonyms" -- Associated pattern synonyms are not implemented yet _ -> pprPanic "rnMethodBind" (ppr bind) + TcRnOrphanCompletePragma -> mkSimpleDecorated $ text "Orphan COMPLETE pragmas not supported" $$ text "A COMPLETE pragma must mention at least one data constructor" $$ @@ -1403,6 +1405,9 @@ instance Diagnostic TcRnMessage where , text "Combine alternative minimal complete definitions with `|'" ] where sigs = sig1 : sig2 : otherSigs + TcRnSpecSigShape spec_e -> mkSimpleDecorated $ + hang (text "Illegal form of SPECIALISE pragma:") + 2 (ppr spec_e) TcRnUnexpectedStandaloneDerivingDecl -> mkSimpleDecorated $ text "Illegal standalone deriving declaration" TcRnUnusedVariableInRuleDecl name var -> mkSimpleDecorated $ @@ -2397,6 +2402,8 @@ instance Diagnostic TcRnMessage where -> ErrorWithoutFlag TcRnOrphanCompletePragma{} -> ErrorWithoutFlag + TcRnSpecSigShape{} + -> ErrorWithoutFlag TcRnEmptyCase{} -> ErrorWithoutFlag TcRnNonStdGuards{} @@ -3070,6 +3077,8 @@ instance Diagnostic TcRnMessage where EmptyCaseWithoutFlag{} -> [suggestExtension LangExt.EmptyCase] EmptyCaseDisallowedCtxt{} -> noHints EmptyCaseForall{} -> noHints + TcRnSpecSigShape{} + -> noHints TcRnNonStdGuards{} -> [suggestExtension LangExt.PatternGuards] TcRnDuplicateSigDecl{} @@ -5591,6 +5600,7 @@ pprHsDocContext SpecInstSigCtx = text "a SPECIALISE instance pragma" pprHsDocContext DefaultDeclCtx = text "a `default' declaration" pprHsDocContext DerivDeclCtx = text "a deriving declaration" pprHsDocContext (RuleCtx name) = text "the rewrite rule" <+> doubleQuotes (ftext name) +pprHsDocContext (SpecECtx name) = text "the SPECIALISE pragma for" <+> quotes (ppr name) pprHsDocContext (TyDataCtx tycon) = text "the data type declaration for" <+> quotes (ppr tycon) pprHsDocContext (FamPatCtx tycon) = text "a type pattern of family instance for" <+> quotes (ppr tycon) pprHsDocContext (TySynCtx name) = text "the declaration for type synonym" <+> quotes (ppr name) diff --git a/compiler/GHC/Tc/Errors/Types.hs b/compiler/GHC/Tc/Errors/Types.hs index 5a7840ba16c..67faa2199c4 100644 --- a/compiler/GHC/Tc/Errors/Types.hs +++ b/compiler/GHC/Tc/Errors/Types.hs @@ -2606,6 +2606,7 @@ data TcRnMessage where typecheck/should_compile/T10504 -} TcRnNonOverloadedSpecialisePragma :: !(LIdP GhcRn) -> TcRnMessage + -- NB: this constructor is deprecated and will be removed in GHC 9.18 (#25540) {-| TcRnSpecialiseNotVisible is a warning that occurs when the subject of a SPECIALISE pragma has a definition that is not visible from the current module. @@ -3187,6 +3188,14 @@ data TcRnMessage where -} TcRnDuplicateMinimalSig :: LSig GhcPs -> LSig GhcPs -> [LSig GhcPs] -> TcRnMessage + {-| TcRnSpecSigShape is an error that occurs when the user writes a SPECIALISE + pragma that isn't just a function application. + + Example: + {-# SPECIALISE let x=True in x #-} + -} + TcRnSpecSigShape :: LHsExpr GhcPs -> TcRnMessage + {-| 'TcRnIllegalInvisTyVarBndr' is an error that occurs when invisible type variable binders in type declarations are used without enabling the @TypeAbstractions@ extension. @@ -5994,6 +6003,7 @@ data HsDocContext | ForeignDeclCtx (LocatedN RdrName) | DerivDeclCtx | RuleCtx FastString + | SpecECtx RdrName | TyDataCtx (LocatedN RdrName) | TySynCtx (LocatedN RdrName) | TyFamilyCtx (LocatedN RdrName) diff --git a/compiler/GHC/Tc/Gen/Bind.hs b/compiler/GHC/Tc/Gen/Bind.hs index b102607bc09..eb4ea696b71 100644 --- a/compiler/GHC/Tc/Gen/Bind.hs +++ b/compiler/GHC/Tc/Gen/Bind.hs @@ -59,8 +59,9 @@ import GHC.Core.Multiplicity import GHC.Core.FamInstEnv( normaliseType ) import GHC.Core.Class ( Class ) import GHC.Core.Coercion( mkSymCo ) -import GHC.Core.Type (mkStrLitTy, tidyOpenTypeX, mkCastTy) +import GHC.Core.Type (mkStrLitTy, mkCastTy) import GHC.Core.TyCo.Ppr( pprTyVars ) +import GHC.Core.TyCo.Tidy( tidyOpenTypeX ) import GHC.Builtin.Types ( mkConstraintTupleTy, multiplicityTy, oneDataConTy ) import GHC.Builtin.Types.Prim @@ -912,9 +913,11 @@ mkExport prag_fn residual insoluble qtvs theta ; poly_id <- mkInferredPolyId residual insoluble qtvs theta poly_name mb_sig mono_ty -- NB: poly_id has a zonked type - ; poly_id <- addInlinePrags poly_id prag_sigs - ; spec_prags <- tcSpecPrags poly_id prag_sigs - -- tcPrags requires a zonked poly_id + ; poly_id <- addInlinePrags poly_id prag_sigs + ; spec_prags <- tcExtendIdEnv1 poly_name poly_id $ + tcSpecPrags poly_id prag_sigs + -- tcSpecPrags requires a zonked poly_id. It also needs poly_id to + -- be in the type env (so we can typecheck the SPECIALISE expression) -- See Note [Impedance matching] -- NB: we have already done checkValidType, including an ambiguity check, @@ -1280,27 +1283,6 @@ The impedance matcher can do defaulting: in the above example, we default to Integer because of Num. See #7173. If we're dealing with a nondefaultable class, impedance matching can fail. See #23427. -Note [SPECIALISE pragmas] -~~~~~~~~~~~~~~~~~~~~~~~~~ -There is no point in a SPECIALISE pragma for a non-overloaded function: - reverse :: [a] -> [a] - {-# SPECIALISE reverse :: [Int] -> [Int] #-} - -But SPECIALISE INLINE *can* make sense for GADTS: - data Arr e where - ArrInt :: !Int -> ByteArray# -> Arr Int - ArrPair :: !Int -> Arr e1 -> Arr e2 -> Arr (e1, e2) - - (!:) :: Arr e -> Int -> e - {-# SPECIALISE INLINE (!:) :: Arr Int -> Int -> Int #-} - {-# SPECIALISE INLINE (!:) :: Arr (a, b) -> Int -> (a, b) #-} - (ArrInt _ ba) !: (I# i) = I# (indexIntArray# ba i) - (ArrPair _ a1 a2) !: i = (a1 !: i, a2 !: i) - -When (!:) is specialised it becomes non-recursive, and can usefully -be inlined. Scary! So we only warn for SPECIALISE *without* INLINE -for a non-overloaded function. - ************************************************************************ * * tcMonoBinds diff --git a/compiler/GHC/Tc/Gen/HsType.hs b/compiler/GHC/Tc/Gen/HsType.hs index ad9964d161f..d95e53693ff 100644 --- a/compiler/GHC/Tc/Gen/HsType.hs +++ b/compiler/GHC/Tc/Gen/HsType.hs @@ -68,7 +68,7 @@ module GHC.Tc.Gen.HsType ( tcMult, -- Pattern type signatures - tcHsPatSigType, tcHsTyPat, + tcHsPatSigType, tcHsTyPat, tcRuleBndrSig, HoleMode(..), -- Utils @@ -4399,8 +4399,26 @@ tcHsPatSigType ctxt hole_mode (HsPS { hsps_ext = HsPSRn { hsps_nwcs = sig_wcs, hsps_imp_tvs = sig_ns } , hsps_body = hs_ty }) ctxt_kind - = tc_type_in_pat ctxt hole_mode hs_ty sig_wcs sig_ns ctxt_kind - + = tc_type_in_pat ctxt Nothing hole_mode hs_ty sig_wcs sig_ns ctxt_kind + +tcRuleBndrSig :: Name + -> SkolemInfo + -> HsPatSigType GhcRn -- The type signature + -> TcM ( [(Name, TcTyVar)] -- Wildcards + , [(Name, TcTyVar)] -- The new bit of type environment, binding + -- the scoped type variables + , TcType) -- The type +-- Used for type-checking type signatures in +-- RULE forall bndrs e.g. forall (x::Int). f x = x +-- See Note [Pattern signature binders and scoping] in GHC.Hs.Type +-- +-- This may emit constraints +-- See Note [Recipe for checking a signature] +tcRuleBndrSig name skol_info + (HsPS { hsps_ext = HsPSRn { hsps_nwcs = sig_wcs, hsps_imp_tvs = sig_ns } + , hsps_body = hs_ty }) + = tc_type_in_pat (RuleBndrTypeCtxt name) (Just skol_info) + HM_Sig hs_ty sig_wcs sig_ns OpenKind -- Typecheck type patterns, in data constructor patterns, e.g -- f (MkT @a @(Maybe b) ...) = ... @@ -4423,7 +4441,7 @@ tcHsTyPat hs_pat@(HsTP{hstp_ext = hstp_rn, hstp_body = hs_ty}) expected_kind where all_ns = imp_ns ++ exp_ns HsTPRn{hstp_nwcs = wcs, hstp_imp_tvs = imp_ns, hstp_exp_tvs = exp_ns} = hstp_rn - tc_unif_in_pat = tc_type_in_pat TypeAppCtxt HM_TyAppPat + tc_unif_in_pat = tc_type_in_pat TypeAppCtxt Nothing HM_TyAppPat -- `tc_bndr_in_pat` is used in type patterns to handle the binders case. -- See Note [Type patterns: binders and unifiers] @@ -4476,6 +4494,7 @@ tc_bndr_in_pat bndr wcs imp_ns expected_kind = do -- -- * In patterns `tc_type_in_pat` is used to check pattern signatures. tc_type_in_pat :: UserTypeCtxt + -> Maybe SkolemInfo -- Just sk for RULE and SPECIALISE pragmas only -> HoleMode -- HM_Sig when in a SigPat, HM_TyAppPat when in a ConPat checking type applications. -> LHsType GhcRn -- The type in pattern -> [Name] -- All named wildcards in type @@ -4485,9 +4504,10 @@ tc_type_in_pat :: UserTypeCtxt , [(Name, TcTyVar)] -- The new bit of type environment, binding -- the scoped type variables , TcType) -- The type -tc_type_in_pat ctxt hole_mode hs_ty wcs ns ctxt_kind +tc_type_in_pat ctxt mb_skol hole_mode hs_ty wcs ns ctxt_kind = addSigCtxt ctxt (UserLHsType hs_ty) $ - do { tkv_prs <- mapM new_implicit_tv ns + do { tkvs <- mapM new_implicit_tv ns + ; let tkv_prs = ns `zip` tkvs ; mode <- mkHoleMode TypeLevel hole_mode ; (wcs, ty) <- addTypeCtxt hs_ty $ @@ -4517,14 +4537,11 @@ tc_type_in_pat ctxt hole_mode hs_ty wcs ns ctxt_kind where new_implicit_tv name = do { kind <- newMetaKindVar - ; tv <- case ctxt of - RuleSigCtxt rname _ -> do - skol_info <- mkSkolemInfo (RuleSkol rname) - newSkolemTyVar skol_info name kind - _ -> newPatTyVar name kind - -- See Note [Typechecking pattern signature binders] - -- NB: tv's Name may be fresh (in the case of newPatTyVar) - ; return (name, tv) } + ; case mb_skol of + Just skol_info -> newSkolemTyVar skol_info name kind + Nothing -> newPatTyVar name kind } + -- See Note [Typechecking pattern signature binders] + -- NB: tv's Name may be fresh (in the case of newPatTyVar) -- See Note [Type patterns: binders and unifiers] tyPatToBndr :: HsTyPat GhcRn -> Maybe (HsTyVarBndr () GhcRn) diff --git a/compiler/GHC/Tc/Gen/Rule.hs b/compiler/GHC/Tc/Gen/Rule.hs deleted file mode 100644 index cc3253e7c47..00000000000 --- a/compiler/GHC/Tc/Gen/Rule.hs +++ /dev/null @@ -1,525 +0,0 @@ -{-# LANGUAGE TypeFamilies #-} - -{- -(c) The University of Glasgow 2006 -(c) The AQUA Project, Glasgow University, 1993-1998 - --} - --- | Typechecking rewrite rules -module GHC.Tc.Gen.Rule ( tcRules ) where - -import GHC.Prelude - -import GHC.Hs -import GHC.Tc.Types -import GHC.Tc.Errors.Types ( ErrCtxtMsg(RuleCtxt) ) -import GHC.Tc.Utils.Monad -import GHC.Tc.Solver.Solve( solveWanteds ) -import GHC.Tc.Solver.Monad ( runTcS ) -import GHC.Tc.Types.Constraint -import GHC.Tc.Types.Origin -import GHC.Tc.Utils.TcMType -import GHC.Tc.Utils.TcType -import GHC.Tc.Gen.HsType -import GHC.Tc.Gen.Expr -import GHC.Tc.Utils.Env -import GHC.Tc.Utils.Unify( buildImplicationFor ) -import GHC.Tc.Zonk.TcType - -import GHC.Core.Type -import GHC.Core.Coercion( mkCoVarCo ) -import GHC.Core.TyCon( isTypeFamilyTyCon ) -import GHC.Core.Predicate - -import GHC.Types.Id -import GHC.Types.Var( EvVar, tyVarName ) -import GHC.Types.Var.Set -import GHC.Types.Basic ( RuleName, NonStandardDefaultingStrategy(..) ) -import GHC.Types.SrcLoc -import GHC.Utils.Outputable -import GHC.Utils.Panic -import GHC.Data.FastString -import GHC.Data.Bag - -{- -Note [Typechecking rules] -~~~~~~~~~~~~~~~~~~~~~~~~~ -We *infer* the typ of the LHS, and use that type to *check* the type of -the RHS. That means that higher-rank rules work reasonably well. Here's -an example (test simplCore/should_compile/rule2.hs) produced by Roman: - - foo :: (forall m. m a -> m b) -> m a -> m b - foo f = ... - - bar :: (forall m. m a -> m a) -> m a -> m a - bar f = ... - - {-# RULES "foo/bar" foo = bar #-} - -He wanted the rule to typecheck. - -Note [TcLevel in type checking rules] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Bringing type variables into scope naturally bumps the TcLevel. Thus, we type -check the term-level binders in a bumped level, and we must accordingly bump -the level whenever these binders are in scope. - -Note [Re-quantify type variables in rules] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Consider this example from #17710: - - foo :: forall k (a :: k) (b :: k). Proxy a -> Proxy b - foo x = Proxy - {-# RULES "foo" forall (x :: Proxy (a :: k)). foo x = Proxy #-} - -Written out in more detail, the "foo" rewrite rule looks like this: - - forall k (a :: k). forall (x :: Proxy (a :: k)). foo @k @a @b0 x = Proxy @k @b0 - -Where b0 is a unification variable. Where should b0 be quantified? We have to -quantify it after k, since (b0 :: k). But generalization usually puts inferred -type variables (such as b0) at the /front/ of the telescope! This creates a -conflict. - -One option is to simply throw an error, per the principles of -Note [Naughty quantification candidates] in GHC.Tc.Utils.TcMType. This is what would happen -if we were generalising over a normal type signature. On the other hand, the -types in a rewrite rule aren't quite "normal", since the notions of specified -and inferred type variables aren't applicable. - -A more permissive design (and the design that GHC uses) is to simply requantify -all of the type variables. That is, we would end up with this: - - forall k (a :: k) (b :: k). forall (x :: Proxy (a :: k)). foo @k @a @b x = Proxy @k @b - -It's a bit strange putting the generalized variable `b` after the user-written -variables `k` and `a`. But again, the notion of specificity is not relevant to -rewrite rules, since one cannot "visibly apply" a rewrite rule. This design not -only makes "foo" typecheck, but it also makes the implementation simpler. - -See also Note [Generalising in tcTyFamInstEqnGuts] in GHC.Tc.TyCl, which -explains a very similar design when generalising over a type family instance -equation. --} - -tcRules :: [LRuleDecls GhcRn] -> TcM [LRuleDecls GhcTc] -tcRules decls = mapM (wrapLocMA tcRuleDecls) decls - -tcRuleDecls :: RuleDecls GhcRn -> TcM (RuleDecls GhcTc) -tcRuleDecls (HsRules { rds_ext = src - , rds_rules = decls }) - = do { maybe_tc_decls <- mapM (wrapLocMA tcRule) decls - ; let tc_decls = [L loc rule | (L loc (Just rule)) <- maybe_tc_decls] - ; return $ HsRules { rds_ext = src - , rds_rules = tc_decls } } - - -tcRule :: RuleDecl GhcRn -> TcM (Maybe (RuleDecl GhcTc)) -tcRule (HsRule { rd_ext = ext - , rd_name = rname@(L _ name) - , rd_act = act - , rd_tyvs = ty_bndrs - , rd_tmvs = tm_bndrs - , rd_lhs = lhs - , rd_rhs = rhs }) - = addErrCtxt (RuleCtxt name) $ - do { traceTc "---- Rule ------" (pprFullRuleName (snd ext) rname) - ; skol_info <- mkSkolemInfo (RuleSkol name) - -- Note [Typechecking rules] - ; (tc_lvl, stuff) <- pushTcLevelM $ - generateRuleConstraints name ty_bndrs tm_bndrs lhs rhs - - ; let (id_bndrs, lhs', lhs_wanted - , rhs', rhs_wanted, rule_ty) = stuff - - ; traceTc "tcRule 1" (vcat [ pprFullRuleName (snd ext) rname - , ppr lhs_wanted - , ppr rhs_wanted ]) - - ; (lhs_evs, residual_lhs_wanted, dont_default) - <- simplifyRule name tc_lvl lhs_wanted rhs_wanted - - -- SimplifyRule Plan, step 4 - -- Now figure out what to quantify over - -- c.f. GHC.Tc.Solver.simplifyInfer - -- We quantify over any tyvars free in *either* the rule - -- *or* the bound variables. The latter is important. Consider - -- ss (x,(y,z)) = (x,z) - -- RULE: forall v. fst (ss v) = fst v - -- The type of the rhs of the rule is just a, but v::(a,(b,c)) - -- - -- We also need to get the completely-unconstrained tyvars of - -- the LHS, lest they otherwise get defaulted to Any; but we do that - -- during zonking (see GHC.Tc.Zonk.Type.zonkRule) - - ; let tpl_ids = lhs_evs ++ id_bndrs - - -- See Note [Re-quantify type variables in rules] - ; forall_tkvs <- candidateQTyVarsOfTypes (rule_ty : map idType tpl_ids) - ; let weed_out = (`dVarSetMinusVarSet` dont_default) - quant_cands = forall_tkvs { dv_kvs = weed_out (dv_kvs forall_tkvs) - , dv_tvs = weed_out (dv_tvs forall_tkvs) } - ; qtkvs <- quantifyTyVars skol_info DefaultNonStandardTyVars quant_cands - ; traceTc "tcRule" (vcat [ pprFullRuleName (snd ext) rname - , text "forall_tkvs:" <+> ppr forall_tkvs - , text "quant_cands:" <+> ppr quant_cands - , text "dont_default:" <+> ppr dont_default - , text "residual_lhs_wanted:" <+> ppr residual_lhs_wanted - , text "qtkvs:" <+> ppr qtkvs - , text "rule_ty:" <+> ppr rule_ty - , text "ty_bndrs:" <+> ppr ty_bndrs - , text "qtkvs ++ tpl_ids:" <+> ppr (qtkvs ++ tpl_ids) - , text "tpl_id info:" <+> - vcat [ ppr id <+> dcolon <+> ppr (idType id) | id <- tpl_ids ] - ]) - - -- SimplfyRule Plan, step 5 - -- Simplify the LHS and RHS constraints: - -- For the LHS constraints we must solve the remaining constraints - -- (a) so that we report insoluble ones - -- (b) so that we bind any soluble ones - ; (lhs_implic, lhs_binds) <- buildImplicationFor tc_lvl (getSkolemInfo skol_info) qtkvs - lhs_evs residual_lhs_wanted - ; (rhs_implic, rhs_binds) <- buildImplicationFor tc_lvl (getSkolemInfo skol_info) qtkvs - lhs_evs rhs_wanted - ; emitImplications (lhs_implic `unionBags` rhs_implic) - - -- A type error on the LHS of a rule will be reported earlier while solving for - -- lhs_implic. However, we should also drop the rule entirely for cases where - -- compilation continues regardless of the error. For example with - -- `-fdefer-type-errors`, where this ill-typed LHS rule may cause follow-on errors - -- (#24026). - ; if anyBag insolubleImplic lhs_implic - then - return Nothing -- The RULE LHS does not type-check and will be dropped. - else - return . Just $ HsRule { rd_ext = ext - , rd_name = rname - , rd_act = act - , rd_tyvs = ty_bndrs -- preserved for ppr-ing - , rd_tmvs = map (noLocA . RuleBndr noAnn . noLocA) - (qtkvs ++ tpl_ids) - , rd_lhs = mkHsDictLet lhs_binds lhs' - , rd_rhs = mkHsDictLet rhs_binds rhs' } } - -generateRuleConstraints :: FastString - -> Maybe [LHsTyVarBndr () GhcRn] -> [LRuleBndr GhcRn] - -> LHsExpr GhcRn -> LHsExpr GhcRn - -> TcM ( [TcId] - , LHsExpr GhcTc, WantedConstraints - , LHsExpr GhcTc, WantedConstraints - , TcType ) -generateRuleConstraints rule_name ty_bndrs tm_bndrs lhs rhs - = do { ((tv_bndrs, id_bndrs), bndr_wanted) <- captureConstraints $ - tcRuleBndrs rule_name ty_bndrs tm_bndrs - -- bndr_wanted constraints can include wildcard hole - -- constraints, which we should not forget about. - -- It may mention the skolem type variables bound by - -- the RULE. c.f. #10072 - ; tcExtendNameTyVarEnv [(tyVarName tv, tv) | tv <- tv_bndrs] $ - tcExtendIdEnv id_bndrs $ - do { -- See Note [Solve order for RULES] - ((lhs', rule_ty), lhs_wanted) <- captureConstraints (tcInferRho lhs) - ; (rhs', rhs_wanted) <- captureConstraints $ - tcCheckMonoExpr rhs rule_ty - ; let all_lhs_wanted = bndr_wanted `andWC` lhs_wanted - ; return (id_bndrs, lhs', all_lhs_wanted, rhs', rhs_wanted, rule_ty) } } - --- See Note [TcLevel in type checking rules] -tcRuleBndrs :: FastString -> Maybe [LHsTyVarBndr () GhcRn] -> [LRuleBndr GhcRn] - -> TcM ([TcTyVar], [Id]) -tcRuleBndrs rule_name (Just bndrs) xs - = do { skol_info <- mkSkolemInfo (RuleSkol rule_name) - ; (tybndrs1,(tys2,tms)) <- bindExplicitTKBndrs_Skol skol_info bndrs $ - tcRuleTmBndrs rule_name xs - ; let tys1 = binderVars tybndrs1 - ; return (tys1 ++ tys2, tms) } - -tcRuleBndrs rule_name Nothing xs - = tcRuleTmBndrs rule_name xs - --- See Note [TcLevel in type checking rules] -tcRuleTmBndrs :: FastString -> [LRuleBndr GhcRn] -> TcM ([TcTyVar],[Id]) -tcRuleTmBndrs _ [] = return ([],[]) -tcRuleTmBndrs rule_name (L _ (RuleBndr _ (L _ name)) : rule_bndrs) - = do { ty <- newOpenFlexiTyVarTy - ; (tyvars, tmvars) <- tcRuleTmBndrs rule_name rule_bndrs - ; return (tyvars, mkLocalId name ManyTy ty : tmvars) } -tcRuleTmBndrs rule_name (L _ (RuleBndrSig _ (L _ name) rn_ty) : rule_bndrs) --- e.g x :: a->a --- The tyvar 'a' is brought into scope first, just as if you'd written --- a::*, x :: a->a --- If there's an explicit forall, the renamer would have already reported an --- error for each out-of-scope type variable used - = do { let ctxt = RuleSigCtxt rule_name name - ; (_ , tvs, id_ty) <- tcHsPatSigType ctxt HM_Sig rn_ty OpenKind - ; let id = mkLocalId name ManyTy id_ty - -- See Note [Typechecking pattern signature binders] in GHC.Tc.Gen.HsType - - -- The type variables scope over subsequent bindings; yuk - ; (tyvars, tmvars) <- tcExtendNameTyVarEnv tvs $ - tcRuleTmBndrs rule_name rule_bndrs - ; return (map snd tvs ++ tyvars, id : tmvars) } - -{- -********************************************************************************* -* * - Constraint simplification for rules -* * -*********************************************************************************** - -Note [The SimplifyRule Plan] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Example. Consider the following left-hand side of a rule - f (x == y) (y > z) = ... -If we typecheck this expression we get constraints - d1 :: Ord a, d2 :: Eq a -We do NOT want to "simplify" to the LHS - forall x::a, y::a, z::a, d1::Ord a. - f ((==) (eqFromOrd d1) x y) ((>) d1 y z) = ... -Instead we want - forall x::a, y::a, z::a, d1::Ord a, d2::Eq a. - f ((==) d2 x y) ((>) d1 y z) = ... - -Here is another example: - fromIntegral :: (Integral a, Num b) => a -> b - {-# RULES "foo" fromIntegral = id :: Int -> Int #-} -In the rule, a=b=Int, and Num Int is a superclass of Integral Int. But -we *dont* want to get - forall dIntegralInt. - fromIntegral Int Int dIntegralInt (scsel dIntegralInt) = id Int -because the scsel will mess up RULE matching. Instead we want - forall dIntegralInt, dNumInt. - fromIntegral Int Int dIntegralInt dNumInt = id Int - -Even if we have - g (x == y) (y == z) = .. -where the two dictionaries are *identical*, we do NOT WANT - forall x::a, y::a, z::a, d1::Eq a - f ((==) d1 x y) ((>) d1 y z) = ... -because that will only match if the dict args are (visibly) equal. -Instead we want to quantify over the dictionaries separately. - -In short, simplifyRuleLhs must *only* squash equalities, leaving -all dicts unchanged, with absolutely no sharing. - -Also note that we can't solve the LHS constraints in isolation: -Example foo :: Ord a => a -> a - foo_spec :: Int -> Int - {-# RULE "foo" foo = foo_spec #-} -Here, it's the RHS that fixes the type variable - -HOWEVER, under a nested implication things are different -Consider - f :: (forall a. Eq a => a->a) -> Bool -> ... - {-# RULES "foo" forall (v::forall b. Eq b => b->b). - f b True = ... - #-} -Here we *must* solve the wanted (Eq a) from the given (Eq a) -resulting from skolemising the argument type of g. So we -revert to SimplCheck when going under an implication. - - ---------- So the SimplifyRule Plan is this ----------------------- - -* Step 0: typecheck the LHS and RHS to get constraints from each - -* Step 1: Simplify the LHS and RHS constraints all together in one bag, - but /discarding/ the simplified constraints. We do this only - to discover all unification equalities. - -* Step 2: Zonk the ORIGINAL (unsimplified) LHS constraints, to take - advantage of those unifications - -* Setp 3: Partition the LHS constraints into the ones we will - quantify over, and the others. - See Note [RULE quantification over equalities] - -* Step 4: Decide on the type variables to quantify over - -* Step 5: Simplify the LHS and RHS constraints separately, using the - quantified constraints as givens - -Note [Solve order for RULES] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In step 1 above, we need to be a bit careful about solve order. -Consider - f :: Int -> T Int - type instance T Int = Bool - - RULE f 3 = True - -From the RULE we get - lhs-constraints: T Int ~ alpha - rhs-constraints: Bool ~ alpha -where 'alpha' is the type that connects the two. If we glom them -all together, and solve the RHS constraint first, we might solve -with alpha := Bool. But then we'd end up with a RULE like - - RULE: f 3 |> (co :: T Int ~ Bool) = True - -which is terrible. We want - - RULE: f 3 = True |> (sym co :: Bool ~ T Int) - -So we are careful to solve the LHS constraints first, and *then* the -RHS constraints. Actually much of this is done by the on-the-fly -constraint solving, so the same order must be observed in -tcRule. - - -Note [RULE quantification over equalities] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Deciding which equalities to quantify over is tricky: - * We do not want to quantify over insoluble equalities (Int ~ Bool) - (a) because we prefer to report a LHS type error - (b) because if such things end up in 'givens' we get a bogus - "inaccessible code" error - - * But we do want to quantify over things like (a ~ F b), where - F is a type function. - -The difficulty is that it's hard to tell what is insoluble! -So we see whether the simplification step yielded any type errors, -and if so refrain from quantifying over *any* equalities. - -Note [Quantifying over coercion holes] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Equality constraints from the LHS will emit coercion hole Wanteds. -These don't have a name, so we can't quantify over them directly. -Instead, because we really do want to quantify here, invent a new -EvVar for the coercion, fill the hole with the invented EvVar, and -then quantify over the EvVar. Not too tricky -- just some -impedance matching, really. - -Note [Simplify cloned constraints] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -At this stage, we're simplifying constraints only for insolubility -and for unification. Note that all the evidence is quickly discarded. -We use a clone of the real constraint. If we don't do this, -then RHS coercion-hole constraints get filled in, only to get filled -in *again* when solving the implications emitted from tcRule. That's -terrible, so we avoid the problem by cloning the constraints. - --} - -simplifyRule :: RuleName - -> TcLevel -- Level at which to solve the constraints - -> WantedConstraints -- Constraints from LHS - -> WantedConstraints -- Constraints from RHS - -> TcM ( [EvVar] -- Quantify over these LHS vars - , WantedConstraints -- Residual un-quantified LHS constraints - , TcTyVarSet ) -- Don't default these --- See Note [The SimplifyRule Plan] --- NB: This consumes all simple constraints on the LHS, but not --- any LHS implication constraints. -simplifyRule name tc_lvl lhs_wanted rhs_wanted - = do { - -- Note [The SimplifyRule Plan] step 1 - -- First solve the LHS and *then* solve the RHS - -- Crucially, this performs unifications - -- Why clone? See Note [Simplify cloned constraints] - ; lhs_clone <- cloneWC lhs_wanted - ; rhs_clone <- cloneWC rhs_wanted - ; (dont_default, _) - <- setTcLevel tc_lvl $ - runTcS $ - do { lhs_wc <- solveWanteds lhs_clone - ; _rhs_wc <- solveWanteds rhs_clone - -- Why do them separately? - -- See Note [Solve order for RULES] - - ; let dont_default = nonDefaultableTyVarsOfWC lhs_wc - -- If lhs_wanteds has - -- (a[sk] :: TYPE rr[sk]) ~ (b0[tau] :: TYPE r0[conc]) - -- we want r0 to be non-defaultable; - -- see nonDefaultableTyVarsOfWC. Simplest way to get - -- this is to look at the post-simplified lhs_wc, which - -- will contain (rr[sk] ~ r0[conc)]. An example is in - -- test rep-poly/RepPolyRule1 - ; return dont_default } - - -- Note [The SimplifyRule Plan] step 2 - ; lhs_wanted <- liftZonkM $ zonkWC lhs_wanted - ; let (quant_cts, residual_lhs_wanted) = getRuleQuantCts lhs_wanted - - -- Note [The SimplifyRule Plan] step 3 - ; quant_evs <- mapM mk_quant_ev (bagToList quant_cts) - - ; traceTc "simplifyRule" $ - vcat [ text "LHS of rule" <+> doubleQuotes (ftext name) - , text "lhs_wanted" <+> ppr lhs_wanted - , text "rhs_wanted" <+> ppr rhs_wanted - , text "quant_cts" <+> ppr quant_cts - , text "residual_lhs_wanted" <+> ppr residual_lhs_wanted - , text "dont_default" <+> ppr dont_default - ] - - ; return (quant_evs, residual_lhs_wanted, dont_default) } - - where - mk_quant_ev :: Ct -> TcM EvVar - mk_quant_ev ct - | CtWanted { ctev_dest = dest, ctev_pred = pred } <- ctEvidence ct - = case dest of - EvVarDest ev_id -> return ev_id - HoleDest hole -> -- See Note [Quantifying over coercion holes] - do { ev_id <- newEvVar pred - ; fillCoercionHole hole (mkCoVarCo ev_id) - ; return ev_id } - mk_quant_ev ct = pprPanic "mk_quant_ev" (ppr ct) - - -getRuleQuantCts :: WantedConstraints -> (Cts, WantedConstraints) --- Extract all the constraints we can quantify over, --- also returning the depleted WantedConstraints --- --- NB: we must look inside implications, because with --- -fdefer-type-errors we generate implications rather eagerly; --- see GHC.Tc.Utils.Unify.implicationNeeded. Not doing so caused #14732. --- --- Unlike simplifyInfer, we don't leave the WantedConstraints unchanged, --- and attempt to solve them from the quantified constraints. That --- nearly works, but fails for a constraint like (d :: Eq Int). --- We /do/ want to quantify over it, but the short-cut solver --- (see GHC.Tc.Solver.Dict Note [Shortcut solving]) ignores the quantified --- and instead solves from the top level. --- --- So we must partition the WantedConstraints ourselves --- Not hard, but tiresome. - -getRuleQuantCts wc - = float_wc emptyVarSet wc - where - float_wc :: TcTyCoVarSet -> WantedConstraints -> (Cts, WantedConstraints) - float_wc skol_tvs (WC { wc_simple = simples, wc_impl = implics, wc_errors = errs }) - = ( simple_yes `andCts` implic_yes - , emptyWC { wc_simple = simple_no, wc_impl = implics_no, wc_errors = errs }) - where - (simple_yes, simple_no) = partitionBag (rule_quant_ct skol_tvs) simples - (implic_yes, implics_no) = mapAccumBagL (float_implic skol_tvs) - emptyBag implics - - float_implic :: TcTyCoVarSet -> Cts -> Implication -> (Cts, Implication) - float_implic skol_tvs yes1 imp - = (yes1 `andCts` yes2, imp { ic_wanted = no }) - where - (yes2, no) = float_wc new_skol_tvs (ic_wanted imp) - new_skol_tvs = skol_tvs `extendVarSetList` ic_skols imp - - rule_quant_ct :: TcTyCoVarSet -> Ct -> Bool - rule_quant_ct skol_tvs ct = case classifyPredType (ctPred ct) of - EqPred _ t1 t2 - | not (ok_eq t1 t2) - -> False -- Note [RULE quantification over equalities] - _ -> tyCoVarsOfCt ct `disjointVarSet` skol_tvs - - ok_eq t1 t2 - | t1 `tcEqType` t2 = False - | otherwise = is_fun_app t1 || is_fun_app t2 - - is_fun_app ty -- ty is of form (F tys) where F is a type function - = case tyConAppTyCon_maybe ty of - Just tc -> isTypeFamilyTyCon tc - Nothing -> False diff --git a/compiler/GHC/Tc/Gen/Sig.hs b/compiler/GHC/Tc/Gen/Sig.hs index bbd47757b8e..2676708ed65 100644 --- a/compiler/GHC/Tc/Gen/Sig.hs +++ b/compiler/GHC/Tc/Gen/Sig.hs @@ -19,7 +19,9 @@ module GHC.Tc.Gen.Sig( TcPragEnv, emptyPragEnv, lookupPragEnv, extendPragEnv, mkPragEnv, tcSpecPrags, tcSpecWrapper, tcImpPrags, - addInlinePrags, addInlinePragArity + addInlinePrags, addInlinePragArity, + + tcRules ) where import GHC.Prelude @@ -30,29 +32,39 @@ import GHC.Driver.Backend import GHC.Hs +import {-# SOURCE #-} GHC.Tc.Gen.Expr ( tcInferRho, tcCheckMonoExpr ) import GHC.Tc.Errors.Types import GHC.Tc.Gen.HsType -import GHC.Tc.Types -import GHC.Tc.Solver( pushLevelAndSolveEqualitiesX, reportUnsolvedEqualities ) +import GHC.Tc.Solver( reportUnsolvedEqualities, pushLevelAndSolveEqualitiesX + , emitResidualConstraints ) +import GHC.Tc.Solver.Solve( solveWanteds ) +import GHC.Tc.Solver.Monad( runTcS, runTcSSpecPrag ) +import GHC.Tc.Validity ( checkValidType ) + import GHC.Tc.Utils.Monad -import GHC.Tc.Utils.TcMType ( checkTypeHasFixedRuntimeRep, newOpenTypeKind ) -import GHC.Tc.Zonk.Type -import GHC.Tc.Types.Origin import GHC.Tc.Utils.TcType -import GHC.Tc.Validity ( checkValidType ) -import GHC.Tc.Utils.Unify( DeepSubsumptionFlag(..), tcSkolemise, unifyType ) +import GHC.Tc.Utils.TcMType +import GHC.Tc.Utils.Unify( DeepSubsumptionFlag(..), tcSkolemise, unifyType, buildImplicationFor ) import GHC.Tc.Utils.Instantiate( topInstantiate, tcInstTypeBndrs ) -import GHC.Tc.Utils.Env( tcLookupId ) -import GHC.Tc.Types.Evidence( HsWrapper, (<.>) ) +import GHC.Tc.Utils.Env + +import GHC.Tc.Types.Origin +import GHC.Tc.Types.Evidence +import GHC.Tc.Types.Constraint + +import GHC.Tc.Zonk.TcType +import GHC.Tc.Zonk.Type import GHC.Core( hasSomeUnfolding ) -import GHC.Core.Type ( mkTyVarBinders ) +import GHC.Core.Type import GHC.Core.Multiplicity +import GHC.Core.Predicate import GHC.Core.TyCo.Rep( mkNakedFunTy ) -import GHC.Types.Var ( TyVar, Specificity(..), tyVarKind, binderVars, invisArgTypeLike ) -import GHC.Types.Id ( Id, idName, idType, setInlinePragma +import GHC.Types.Var +import GHC.Types.Var.Set +import GHC.Types.Id ( idName, idType, setInlinePragma , mkLocalId, realIdUnfolding ) import GHC.Types.Basic import GHC.Types.Name @@ -60,19 +72,19 @@ import GHC.Types.Name.Env import GHC.Types.SrcLoc import GHC.Builtin.Names( mkUnboundName ) -import GHC.Unit.Module( getModule ) +import GHC.Unit.Module( Module, getModule ) import GHC.Utils.Misc as Utils ( singleton ) import GHC.Utils.Outputable import GHC.Utils.Panic +import GHC.Data.Bag import GHC.Data.Maybe( orElse, whenIsJust ) import Data.Maybe( mapMaybe ) import qualified Data.List.NonEmpty as NE import Control.Monad( unless ) - {- ------------------------------------------------------------- Note [Overview of type signatures] ---------------------------------------------------------------- @@ -567,8 +579,9 @@ mkPragEnv sigs binds prs = mapMaybe get_sig sigs get_sig :: LSig GhcRn -> Maybe (Name, LSig GhcRn) - get_sig sig@(L _ (SpecSig _ (L _ nm) _ _)) = Just (nm, add_arity nm sig) - get_sig sig@(L _ (InlineSig _ (L _ nm) _)) = Just (nm, add_arity nm sig) + get_sig sig@(L _ (SpecSig _ (L _ nm) _ _)) = Just (nm, add_arity nm sig) + get_sig sig@(L _ (SpecSigE nm _ _ _)) = Just (nm, add_arity nm sig) + get_sig sig@(L _ (InlineSig _ (L _ nm) _)) = Just (nm, add_arity nm sig) get_sig sig@(L _ (SCCFunSig _ (L _ nm) _)) = Just (nm, sig) get_sig _ = Nothing @@ -584,6 +597,7 @@ mkPragEnv sigs binds addInlinePragArity :: Arity -> LSig GhcRn -> LSig GhcRn addInlinePragArity ar (L l (InlineSig x nm inl)) = L l (InlineSig x nm (add_inl_arity ar inl)) addInlinePragArity ar (L l (SpecSig x nm ty inl)) = L l (SpecSig x nm ty (add_inl_arity ar inl)) +addInlinePragArity ar (L l (SpecSigE n x e inl)) = L l (SpecSigE n x e (add_inl_arity ar inl)) addInlinePragArity _ sig = sig add_inl_arity :: Arity -> InlinePragma -> InlinePragma @@ -650,13 +664,204 @@ should add the arity later for all binders. But it works fine like this. * * ************************************************************************ -Note [Handling SPECIALISE pragmas] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Note [Overview of SPECIALISE pragmas] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The basic idea is this: foo :: Num a => a -> b -> a - {-# SPECIALISE foo :: Int -> b -> Int #-} + foo = rhs + {-# SPECIALISE foo :: Int -> b -> Int #-} -- Old form + {-# SPECIALISE foo @Float #-} -- New form + +Generally: +* Rename as usual +* Typecheck, attaching info to the ABExport record of the AbsBinds for foo +* Desugar by generating + - a specialised binding $sfoo = rhs @Float + - a rewrite rule like RULE "USPEC foo" foo @Float = $sfoo + +There are two major routes: + +* Old form + - Handled by `SpecSig` and `SpecPrag` + - Deals with SPECIALISE pragmas have multiple signatures + {-# SPECIALISE f :: Int -> Int, Float -> Float #-} + - See Note [Handling old-form SPECIALISE pragmas] + - Deprecated, to be removed in GHC 9.18 as per #25540. + +* New form, described in GHC Proposal #493 + - Handled by `SpecSigE` and `SpecPragE` + - Deals with SPECIALISE pragmas which may have value arguments + {-# SPECIALISE f @Int 3 #-} + - See Note [Handling new-form SPECIALISE pragmas] + +Note [Handling new-form SPECIALISE pragmas] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +New-form SPECIALISE pragmas are described by GHC Proposal #493. + +The pragma takes the form of a function application, possibly with intervening +parens and type signatures, with a variable at the head: + + {-# SPECIALISE f1 @Int 3 #-} + {-# SPECIALISE f2 :: Int -> Int #-} + {-# SPECIALISE (f3 :: Int -> Int) 5 #-} + +It may also have rule for-alls at the top, e.g. + + {-# SPECIALISE forall x xs. f4 (x:xs) #-} + {-# SPECIALISE forall a. forall x xs. f5 @a @a (x:xs) #-} + +See `GHC.Rename.Bind.checkSpecESigShape` for the shape-check. + +Example: + f :: forall a b. (Eq a, Eq b, Eq c) => a -> b -> c -> Bool -> blah + {-# SPECIALISE forall x y. f (x::Int) y y True #-} + -- y::p + +We want to generate: + + RULE forall @p (d1::Eq Int) (d2::Eq p) (d3::Eq p) (x::Int) (y::p). + f @Int @p @p d1 d2 d3 x y y True + = $sf @p d2 x y + $sf @p (d2::Eq p) (x::Int) (y::p) + = let d1 = $fEqInt + d3 = d2 + in <f-rhs> @p @p @Int (d1::Eq p) (d2::Eq p) (d3::Eq p) x y y True + +Note that + +* The `rule_bndrs`, over which the RULE is quantified, are all the variables + free in the call to `f`, /ignoring/ all dictionary simplification. Why? + Because we want to make the rule maximally applicable; provided the types + match, the dictionaries should match. + + rule_bndrs = @p (d1::Eq Int) (d2::Eq p) (d3::Eq p) (x::Int) (y::p). + + Note that we have separate binders for `d1` and `d2` even though they are + the same (Eq p) dictionary. Reason: we don't want to force them to be visibly + equal at the call site. + +* The `spec_bnrs`, which are lambda-bound in the specialised function `$sf`, + are a subset of `rule_bndrs`. + + spec_bndrs = @p (d2::Eq p) (x::Int) (y::p) + +* The `spec_const_binds` make up the difference between `rule_bndrs` and + `spec_bndrs`. They communicate the specialisation! + If `spec_bndrs` = `rule_bndrs`, no specialisation has happened. + + spec_const_binds = let d1 = $fEqInt + d3 = d2 + +This is done in three parts. + + A. Typechecker: `GHC.Tc.Gen.Sig.tcSpecPrag` + + (1) Typecheck the expression, capturing its constraints + + (2) Solve these constraints, but in special TcSSpecPrag mode which ensures + each original Wanted is either fully solved or left untouched. + See Note [Fully solving constraints for specialisation]. + + (3) Compute the constraints to quantify over, using `getRuleQuantCts` on + the unsolved constraints returned by (2). + + (4) Emit the residual (non-solved, non-quantified) constraints, and wrap the + expression in a let binding for those constraints. + + (5) Wrap the call in the combined evidence bindings from steps (2) and (4) + + (6) Store all the information in a 'SpecPragE' record, to be consumed + by the desugarer. + + B. Zonker: `GHC.Tc.Zonk.Type.zonkLTcSpecPrags` + + The zonker does a little extra work to collect any free type variables + of the LHS. See Note [Free tyvars on rule LHS] in GHC.Tc.Zonk.Type. + These weren't conveniently available earlier. + + C. Desugarer: `GHC.HsToCore.Binds.dsSpec`. + + See Note [Desugaring new-form SPECIALISE pragmas] in GHC.HsToCore.Binds for details, + but in brief: + + (1) Simplify the expression. This is important because a type signature in + the expression will have led to type/dictionary abstractions/applications. + After simplification it should look like + let <dict-binds> in f d1 d2 d3 + + (2) `prepareSpecLHS` identifies the `spec_const_binds`, discards the other + dictionary bindings, and decomposes the call. + + (3) Then we build the specialised function $sf, and concoct a RULE + of the form: + forall @a @b d1 d2 d3. f d1 d2 d3 = $sf d1 d2 d3 + +Note [Fully solving constraints for specialisation] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +As far as specialisation is concerned, it is actively harmful to simplify +constraints without /fully/ solving them. + +Example: + + f :: ∀ a t. (Eq a, ∀x. Eq x => Eq (t x)). t a -> Char + {-# SPECIALISE f @Int #-} + + Typechecking 'f' will result in [W] Eq Int, [W] ∀x. Eq x => Eq (t x). + We absolutely MUST leave the quantified constraint alone, because we want to + quantify over it. If we were to try to simplify it, we would emit an + implication and would thereafter never be able to quantify over the original + quantified constraint. + + However, we still need to simplify quantified constraints that can be + /fully solved/ from instances, otherwise we would never be able to + specialise them away. Example: {-# SPECIALISE f @a @[] #-}. + +The conclusion is this: + + when solving the constraints that arise from a specialise pragma, following + the recipe described in Note [Handling new-form SPECIALISE pragmas], all + Wanted quantified constraints should either be: + - fully solved (no free evidence variables), or + - left untouched. + +To achieve this, we run the solver in a special "all-or-nothing" solving mode, +described in Note [TcSSpecPrag] in GHC.Tc.Solver.Monad. + +Note that a similar problem arises in other situations in which the solver takes +an irreversible step, such as using a top-level class instance. This is currently +less important, as the desugarer can handle these cases. To explain, consider: + g :: ∀ a. Eq a => a -> Bool + {-# SPECIALISE g @[e] #-} + + Typechecking 'g' will result in [W] Eq [e]. Were we to simplify this to + [W] Eq e, we would have difficulty generating a RULE for the specialisation: + + $sg :: Eq e => [e] -> Bool + + RULE ∀ e (d :: Eq [e]). g @[e] d = $sg @e (??? :: Eq e) + -- Can't fill in ??? because we can't run instances in reverse. + + RULE ∀ e (d :: Eq e). g @[e] ($fEqList @e d) = $sg @e d + -- Bad RULE matching template: matches on the structure of a dictionary + + Moreover, there is no real benefit to any of this, because the specialiser + can't do anything useful from the knowledge that a dictionary for 'Eq [e]' is + constructed from a dictionary for 'Eq e' using the 'Eq' instance for lists. + +Here, it would make sense to also use the "solve completely" mechanism in the +typechecker to avoid producing evidence terms that we can't "run in reverse". +However, the current implementation tackles this issue in the desugarer, as is +explained in Note [prepareSpecLHS] in GHC.HsToCore.Binds. +So, for the time being at least, in TcSSpecPrag mode, we don't attempt to "fully solve" +constraints when we use a top-level instance. This might change in the future, +were we to decide to attempt to address [Shortcoming] in Note [prepareSpecLHS] +in GHC.HsToCore.Binds. + +Note [Handling old-form SPECIALISE pragmas] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +NB: this code path is deprecated, and is scheduled to be removed in GHC 9.18, as per #25440. We check that (forall a b. Num a => a -> b -> a) is more polymorphic than @@ -749,6 +954,27 @@ Some wrinkles regardless of XXX. It's sort of polymorphic in XXX. This is useful: we use the same wrapper to transform each of the class ops, as well as the dict. That's what goes on in GHC.Tc.TyCl.Instance.mk_meth_spec_prags + +Note [SPECIALISE pragmas] +~~~~~~~~~~~~~~~~~~~~~~~~~ +There is no point in a SPECIALISE pragma for a non-overloaded function: + reverse :: [a] -> [a] + {-# SPECIALISE reverse :: [Int] -> [Int] #-} + +But SPECIALISE INLINE *can* make sense for GADTS: + data Arr e where + ArrInt :: !Int -> ByteArray# -> Arr Int + ArrPair :: !Int -> Arr e1 -> Arr e2 -> Arr (e1, e2) + + (!:) :: Arr e -> Int -> e + {-# SPECIALISE INLINE (!:) :: Arr Int -> Int -> Int #-} + {-# SPECIALISE INLINE (!:) :: Arr (a, b) -> Int -> (a, b) #-} + (ArrInt _ ba) !: (I# i) = I# (indexIntArray# ba i) + (ArrPair _ a1 a2) !: i = (a1 !: i, a2 !: i) + +When (!:) is specialised it becomes non-recursive, and can usefully +be inlined. Scary! So we only warn for SPECIALISE *without* INLINE +for a non-overloaded function. -} tcSpecPrags :: Id -> [LSig GhcRn] @@ -775,7 +1001,7 @@ tcSpecPrags poly_id prag_sigs -------------- tcSpecPrag :: TcId -> Sig GhcRn -> TcM [TcSpecPrag] tcSpecPrag poly_id prag@(SpecSig _ fun_name hs_tys inl) --- See Note [Handling SPECIALISE pragmas] +-- See Note [Handling old-form SPECIALISE pragmas] -- -- The Name fun_name in the SpecSig may not be the same as that of the poly_id -- Example: SPECIALISE for a class method: the Name in the SpecSig is @@ -798,19 +1024,80 @@ tcSpecPrag poly_id prag@(SpecSig _ fun_name hs_tys inl) ; wrap <- tcSpecWrapper (FunSigCtxt name (lhsSigTypeContextSpan hs_ty)) poly_ty spec_ty ; return (SpecPrag poly_id wrap inl) } +tcSpecPrag poly_id (SpecSigE nm rule_bndrs spec_e inl) + -- For running commentary, see Note [Handling new-form SPECIALISE pragmas] + = do { -- (1) Typecheck the expression, spec_e, capturing its constraints + let skol_info_anon = SpecESkol nm + ; traceTc "tcSpecPrag SpecSigE {" (ppr nm $$ ppr spec_e) + ; skol_info <- mkSkolemInfo skol_info_anon + ; (rhs_tclvl, spec_e_wanted, (rule_bndrs', (tc_spec_e, _rho))) + <- tcRuleBndrs skol_info rule_bndrs $ + tcInferRho spec_e + + -- (2) Solve the resulting wanteds in TcSSpecPrag mode. + ; ev_binds_var <- newTcEvBinds + ; spec_e_wanted <- setTcLevel rhs_tclvl $ + runTcSSpecPrag ev_binds_var $ + solveWanteds spec_e_wanted + ; spec_e_wanted <- liftZonkM $ zonkWC spec_e_wanted + + -- (3) Compute which constraints to quantify over, by looking + -- at the unsolved constraints from (2) + ; (quant_cands, residual_wc) <- getRuleQuantCts spec_e_wanted + + -- (4) Emit the residual constraints (i.e. ones that we have + -- not solved in (2) nor quantified in (3) + -- NB: use the same `ev_binds_var` as (2), so the bindings + -- for (2) and (4) are combined + ; let tv_bndrs = filter isTyVar rule_bndrs' + qevs = map ctEvId (bagToList quant_cands) + ; emitResidualConstraints rhs_tclvl skol_info_anon ev_binds_var + emptyVarSet tv_bndrs qevs + residual_wc + + -- (5) Wrap the call in the combined evidence bindings + -- from steps (2) and (4) + ; let lhs_call = mkLHsWrap (WpLet (TcEvBinds ev_binds_var)) tc_spec_e + + ; ev_binds <- getTcEvBindsMap ev_binds_var + + ; traceTc "tcSpecPrag SpecSigE }" $ + vcat [ text "nm:" <+> ppr nm + , text "rule_bndrs':" <+> ppr rule_bndrs' + , text "qevs:" <+> ppr qevs + , text "spec_e:" <+> ppr tc_spec_e + , text "inl:" <+> ppr inl + , text "spec_e_wanted:" <+> ppr spec_e_wanted + , text "quant_cands:" <+> ppr quant_cands + , text "residual_wc:" <+> ppr residual_wc + , text (replicate 80 '-') + , text "ev_binds_var:" <+> ppr ev_binds_var + , text "ev_binds:" <+> ppr ev_binds + ] + + -- (6) Store the results in a SpecPragE record, which will be + -- zonked and then consumed by the desugarer. + + ; return [SpecPragE { spe_fn_nm = nm + , spe_fn_id = poly_id + , spe_bndrs = qevs ++ rule_bndrs' -- Dependency order + -- does not matter + , spe_call = lhs_call + , spe_inl = inl }] } + tcSpecPrag _ prag = pprPanic "tcSpecPrag" (ppr prag) -------------- tcSpecWrapper :: UserTypeCtxt -> TcType -> TcType -> TcM HsWrapper -- A simpler variant of tcSubType, used for SPECIALISE pragmas --- See Note [Handling SPECIALISE pragmas], wrinkle 1 +-- See Note [Handling old-form SPECIALISE pragmas], wrinkle 1 tcSpecWrapper ctxt poly_ty spec_ty = do { (sk_wrap, inst_wrap) <- tcSkolemise Shallow ctxt spec_ty $ \spec_tau -> do { (inst_wrap, tau) <- topInstantiate orig poly_ty ; _ <- unifyType Nothing spec_tau tau -- Deliberately ignore the evidence - -- See Note [Handling SPECIALISE pragmas], + -- See Note [Handling old-form SPECIALISE pragmas], -- wrinkle (2) ; return inst_wrap } ; return (sk_wrap <.> inst_wrap) } @@ -821,15 +1108,13 @@ tcSpecWrapper ctxt poly_ty spec_ty tcImpPrags :: [LSig GhcRn] -> TcM [LTcSpecPrag] -- SPECIALISE pragmas for imported things tcImpPrags prags - = do { this_mod <- getModule - ; dflags <- getDynFlags + = do { dflags <- getDynFlags + ; traceTc "tcImpPrags1" (ppr prags) ; if (not_specialising dflags) then return [] else do - { pss <- mapAndRecoverM (wrapLocMA tcImpSpec) - [L loc (name,prag) - | (L loc prag@(SpecSig _ (L _ name) _ _)) <- prags - , not (nameIsLocalOrFrom this_mod name) ] + { this_mod <- getModule + ; pss <- mapAndRecoverM (wrapLocMA (tcImpSpec this_mod)) prags ; return $ concatMap (\(L l ps) -> map (L (locA l)) ps) pss } } where -- Ignore SPECIALISE pragmas for imported things @@ -839,8 +1124,10 @@ tcImpPrags prags not_specialising dflags = not (gopt Opt_Specialise dflags) || not (backendRespectsSpecialise (backend dflags)) -tcImpSpec :: (Name, Sig GhcRn) -> TcM [TcSpecPrag] -tcImpSpec (name, prag) +tcImpSpec :: Module -> Sig GhcRn -> TcM [TcSpecPrag] +tcImpSpec this_mod prag + | Just name <- is_spec_prag prag -- It's a specialisation pragma + , not (nameIsLocalOrFrom this_mod name) -- The Id is imported = do { id <- tcLookupId name ; if hasSomeUnfolding (realIdUnfolding id) -- See Note [SPECIALISE pragmas for imported Ids] @@ -848,6 +1135,12 @@ tcImpSpec (name, prag) else do { let dia = TcRnSpecialiseNotVisible name ; addDiagnosticTc dia ; return [] } } + | otherwise + = return [] + where + is_spec_prag (SpecSig _ (L _ nm) _ _) = Just nm + is_spec_prag (SpecSigE nm _ _ _) = Just nm + is_spec_prag _ = Nothing {- Note [SPECIALISE pragmas for imported Ids] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -866,3 +1159,459 @@ longer has an unfolding in the future. But then you'll get a helpful error message suggesting an INLINABLE pragma, which you can follow. That seems enough for now. -} + + +{- ********************************************************************* +* * + Rules +* * +************************************************************************ + +Note [Typechecking rules] +~~~~~~~~~~~~~~~~~~~~~~~~~ +We *infer* the type of the LHS, and use that type to *check* the type of +the RHS. That means that higher-rank rules work reasonably well. Here's +an example (test simplCore/should_compile/rule2.hs) produced by Roman: + + foo :: (forall m. m a -> m b) -> m a -> m b + foo f = ... + + bar :: (forall m. m a -> m a) -> m a -> m a + bar f = ... + + {-# RULES "foo/bar" foo = bar #-} + +He wanted the rule to typecheck. + +Note [TcLevel in type checking rules] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Bringing type variables into scope naturally bumps the TcLevel. Thus, we type +check the term-level binders in a bumped level, and we must accordingly bump +the level whenever these binders are in scope. + +Note [Re-quantify type variables in rules] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Consider this example from #17710: + + foo :: forall k (a :: k) (b :: k). Proxy a -> Proxy b + foo x = Proxy + {-# RULES "foo" forall (x :: Proxy (a :: k)). foo x = Proxy #-} + +Written out in more detail, the "foo" rewrite rule looks like this: + + forall k (a :: k). forall (x :: Proxy (a :: k)). foo @k @a @b0 x = Proxy @k @b0 + +Where b0 is a unification variable. Where should b0 be quantified? We have to +quantify it after k, since (b0 :: k). But generalization usually puts inferred +type variables (such as b0) at the /front/ of the telescope! This creates a +conflict. + +One option is to simply throw an error, per the principles of +Note [Naughty quantification candidates] in GHC.Tc.Utils.TcMType. This is what would happen +if we were generalising over a normal type signature. On the other hand, the +types in a rewrite rule aren't quite "normal", since the notions of specified +and inferred type variables aren't applicable. + +A more permissive design (and the design that GHC uses) is to simply requantify +all of the type variables. That is, we would end up with this: + + forall k (a :: k) (b :: k). forall (x :: Proxy (a :: k)). foo @k @a @b x = Proxy @k @b + +It's a bit strange putting the generalized variable `b` after the user-written +variables `k` and `a`. But again, the notion of specificity is not relevant to +rewrite rules, since one cannot "visibly apply" a rewrite rule. This design not +only makes "foo" typecheck, but it also makes the implementation simpler. + +See also Note [Generalising in tcTyFamInstEqnGuts] in GHC.Tc.TyCl, which +explains a very similar design when generalising over a type family instance +equation. +-} + +tcRules :: [LRuleDecls GhcRn] -> TcM [LRuleDecls GhcTc] +tcRules decls = mapM (wrapLocMA tcRuleDecls) decls + +tcRuleDecls :: RuleDecls GhcRn -> TcM (RuleDecls GhcTc) +tcRuleDecls (HsRules { rds_ext = src + , rds_rules = decls }) + = do { maybe_tc_decls <- mapM (wrapLocMA tcRule) decls + ; let tc_decls = [L loc rule | (L loc (Just rule)) <- maybe_tc_decls] + ; return $ HsRules { rds_ext = src + , rds_rules = tc_decls } } + +tcRule :: RuleDecl GhcRn -> TcM (Maybe (RuleDecl GhcTc)) +tcRule (HsRule { rd_ext = ext + , rd_name = rname@(L _ name) + , rd_act = act + , rd_bndrs = bndrs + , rd_lhs = lhs + , rd_rhs = rhs }) + = addErrCtxt (RuleCtxt name) $ + do { traceTc "---- Rule ------" (pprFullRuleName (snd ext) rname) + ; skol_info <- mkSkolemInfo (RuleSkol name) + -- Note [Typechecking rules] + ; (tc_lvl, lhs_wanted, stuff) + <- tcRuleBndrs skol_info bndrs $ + do { (lhs', rule_ty) <- tcInferRho lhs + ; (rhs', rhs_wanted) <- captureConstraints $ + tcCheckMonoExpr rhs rule_ty + ; return (lhs', rule_ty, rhs', rhs_wanted) } + + ; let (bndrs', (lhs', rule_ty, rhs', rhs_wanted)) = stuff + + ; traceTc "tcRule 1" (vcat [ pprFullRuleName (snd ext) rname + , ppr lhs_wanted + , ppr rhs_wanted ]) + + ; (lhs_evs, residual_lhs_wanted, dont_default) + <- simplifyRule name tc_lvl lhs_wanted rhs_wanted + + -- SimplifyRule Plan, step 4 + -- Now figure out what to quantify over + -- c.f. GHC.Tc.Solver.simplifyInfer + -- We quantify over any tyvars free in *either* the rule + -- *or* the bound variables. The latter is important. Consider + -- ss (x,(y,z)) = (x,z) + -- RULE: forall v. fst (ss v) = fst v + -- The type of the rhs of the rule is just a, but v::(a,(b,c)) + -- + -- We also need to get the completely-unconstrained tyvars of + -- the LHS, lest they otherwise get defaulted to Any; but we do that + -- during zonking (see GHC.Tc.Zonk.Type.zonkRule) + + ; let tpl_ids = lhs_evs ++ filter isId bndrs' + + -- See Note [Re-quantify type variables in rules] + ; dvs <- candidateQTyVarsOfTypes (rule_ty : map idType tpl_ids) + ; let weed_out = (`dVarSetMinusVarSet` dont_default) + weeded_dvs = weedOutCandidates weed_out dvs + ; qtkvs <- quantifyTyVars skol_info DefaultNonStandardTyVars weeded_dvs + ; traceTc "tcRule" (vcat [ pprFullRuleName (snd ext) rname + , text "dvs:" <+> ppr dvs + , text "weeded_dvs:" <+> ppr weeded_dvs + , text "dont_default:" <+> ppr dont_default + , text "residual_lhs_wanted:" <+> ppr residual_lhs_wanted + , text "qtkvs:" <+> ppr qtkvs + , text "rule_ty:" <+> ppr rule_ty + , text "bndrs:" <+> ppr bndrs + , text "qtkvs ++ tpl_ids:" <+> ppr (qtkvs ++ tpl_ids) + , text "tpl_id info:" <+> + vcat [ ppr id <+> dcolon <+> ppr (idType id) | id <- tpl_ids ] + ]) + + -- SimplfyRule Plan, step 5 + -- Simplify the LHS and RHS constraints: + -- For the LHS constraints we must solve the remaining constraints + -- (a) so that we report insoluble ones + -- (b) so that we bind any soluble ones + ; (lhs_implic, lhs_binds) <- buildImplicationFor tc_lvl (getSkolemInfo skol_info) qtkvs + lhs_evs residual_lhs_wanted + ; (rhs_implic, rhs_binds) <- buildImplicationFor tc_lvl (getSkolemInfo skol_info) qtkvs + lhs_evs rhs_wanted + ; emitImplications (lhs_implic `unionBags` rhs_implic) + + ; return $ Just $ HsRule { rd_ext = ext + , rd_name = rname + , rd_act = act + , rd_bndrs = bndrs { rb_ext = qtkvs ++ tpl_ids } + , rd_lhs = mkHsDictLet lhs_binds lhs' + , rd_rhs = mkHsDictLet rhs_binds rhs' } } + +{- ******************************************************************************** +* * + tcRuleBndrs +* * +******************************************************************************** -} + +tcRuleBndrs :: SkolemInfo -> RuleBndrs GhcRn + -> TcM a -- Typecheck this with the rule binders in scope + -> TcM (TcLevel, WantedConstraints, ([Var], a)) + -- The [Var] are the explicitly-quantified variables, + -- both type variables and term variables +tcRuleBndrs skol_info (RuleBndrs { rb_tyvs = mb_tv_bndrs, rb_tmvs = tm_bndrs }) + thing_inside + = pushLevelAndCaptureConstraints $ + case mb_tv_bndrs of + Nothing -> go_tms tm_bndrs thing_inside + Just tv_bndrs -> do { (bndrs1, (bndrs2, res)) <- go_tvs tv_bndrs $ + go_tms tm_bndrs $ + thing_inside + ; return (binderVars bndrs1 ++ bndrs2, res) } + where + -------------- + go_tvs tvs thing_inside = bindExplicitTKBndrs_Skol skol_info tvs thing_inside + + -------------- + go_tms [] thing_inside + = do { res <- thing_inside; return ([], res) } + go_tms (L _ (RuleBndr _ (L _ name)) : rule_bndrs) thing_inside + = do { ty <- newOpenFlexiTyVarTy + ; let bndr_id = mkLocalId name ManyTy ty + ; (bndrs, res) <- tcExtendIdEnv [bndr_id] $ + go_tms rule_bndrs thing_inside + ; return (bndr_id : bndrs, res) } + + go_tms (L _ (RuleBndrSig _ (L _ name) rn_ty) : rule_bndrs) thing_inside + -- e.g x :: a->a + -- The tyvar 'a' is brought into scope first, just as if you'd written + -- a::*, x :: a->a + -- If there's an explicit forall, the renamer would have already reported an + -- error for each out-of-scope type variable used + = do { (_ , tv_prs, id_ty) <- tcRuleBndrSig name skol_info rn_ty + ; let bndr_id = mkLocalId name ManyTy id_ty + -- See Note [Typechecking pattern signature binders] in GHC.Tc.Gen.HsType + + -- The type variables scope over subsequent bindings; yuk + ; (bndrs, res) <- tcExtendNameTyVarEnv tv_prs $ + tcExtendIdEnv [bndr_id] $ + go_tms rule_bndrs thing_inside + ; return (map snd tv_prs ++ bndr_id : bndrs, res) } + + +{- +********************************************************************************* +* * + Constraint simplification for rules +* * +*********************************************************************************** + +Note [The SimplifyRule Plan] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Example. Consider the following left-hand side of a rule + f (x == y) (y > z) = ... +If we typecheck this expression we get constraints + d1 :: Ord a, d2 :: Eq a +We do NOT want to "simplify" to the LHS + forall x::a, y::a, z::a, d1::Ord a. + f ((==) (eqFromOrd d1) x y) ((>) d1 y z) = ... +Instead we want + forall x::a, y::a, z::a, d1::Ord a, d2::Eq a. + f ((==) d2 x y) ((>) d1 y z) = ... + +Here is another example: + fromIntegral :: (Integral a, Num b) => a -> b + {-# RULES "foo" fromIntegral = id :: Int -> Int #-} +In the rule, a=b=Int, and Num Int is a superclass of Integral Int. But +we *dont* want to get + forall dIntegralInt. + fromIntegral Int Int dIntegralInt (scsel dIntegralInt) = id Int +because the scsel will mess up RULE matching. Instead we want + forall dIntegralInt, dNumInt. + fromIntegral Int Int dIntegralInt dNumInt = id Int + +Even if we have + g (x == y) (y == z) = .. +where the two dictionaries are *identical*, we do NOT WANT + forall x::a, y::a, z::a, d1::Eq a + f ((==) d1 x y) ((>) d1 y z) = ... +because that will only match if the dict args are (visibly) equal. +Instead we want to quantify over the dictionaries separately. + +In short, simplifyRuleLhs must *only* squash equalities, leaving +all dicts unchanged, with absolutely no sharing. + +Also note that we can't solve the LHS constraints in isolation: +Example foo :: Ord a => a -> a + foo_spec :: Int -> Int + {-# RULE "foo" foo = foo_spec #-} +Here, it's the RHS that fixes the type variable + +HOWEVER, under a nested implication things are different +Consider + f :: (forall a. Eq a => a->a) -> Bool -> ... + {-# RULES "foo" forall (v::forall b. Eq b => b->b). + f b True = ... + #-} +Here we *must* solve the wanted (Eq a) from the given (Eq a) +resulting from skolemising the argument type of g. So we +revert to SimplCheck when going under an implication. + + +--------- So the SimplifyRule Plan is this ----------------------- + +* Step 0: typecheck the LHS and RHS to get constraints from each + +* Step 1: Simplify the LHS and RHS constraints all together in one bag, + but /discarding/ the simplified constraints. We do this only + to discover all unification equalities. + +* Step 2: Zonk the ORIGINAL (unsimplified) LHS constraints, to take + advantage of those unifications + +* Step 3: Partition the LHS constraints into the ones we will + quantify over, and the others. + See Note [RULE quantification over equalities] + +* Step 4: Decide on the type variables to quantify over + +* Step 5: Simplify the LHS and RHS constraints separately, using the + quantified constraints as givens + +Note [Solve order for RULES] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In step 1 above, we need to be a bit careful about solve order. +Consider + f :: Int -> T Int + type instance T Int = Bool + + RULE f 3 = True + +From the RULE we get + lhs-constraints: T Int ~ alpha + rhs-constraints: Bool ~ alpha +where 'alpha' is the type that connects the two. If we glom them +all together, and solve the RHS constraint first, we might solve +with alpha := Bool. But then we'd end up with a RULE like + + RULE: f 3 |> (co :: T Int ~ Bool) = True + +which is terrible. We want + + RULE: f 3 = True |> (sym co :: Bool ~ T Int) + +So we are careful to solve the LHS constraints first, and *then* the +RHS constraints. Actually much of this is done by the on-the-fly +constraint solving, so the same order must be observed in +tcRule. + + +Note [RULE quantification over equalities] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +At the moment a RULE never quantifies over an equality; see `rule_quant_ct` +in `getRuleQuantCts`. Why not? + + * It's not clear why we would want to do so (see Historical Note + below) + + * We do not want to quantify over insoluble equalities (Int ~ Bool) + (a) because we prefer to report a LHS type error + (b) because if such things end up in 'givens' we get a bogus + "inaccessible code" error + + * Equality constraints are unboxed, and that leads to complications + For example equality constraints from the LHS will emit coercion hole + Wanteds. These don't have a name, so we can't quantify over them directly. + Instead, in `getRuleQuantCts`, we'd have to invent a new EvVar for the + coercion, fill the hole with the invented EvVar, and then quantify over the + EvVar. Here is old code from `mk_one` + do { ev_id <- newEvVar pred + ; fillCoercionHole hole (mkCoVarCo ev_id) + ; return ev_id } + But that led to new complications becuase of the side effect on the coercion + hole. Much easier just to side-step the issue entirely by not quantifying over + equalities. + +Historical Note: + Back in 2012 (5aa1ae24567) we started quantifying over some equality + constraints, saying + * But we do want to quantify over things like (a ~ F b), + where F is a type function. + It is not clear /why/ we did so, and we don't do so any longer. +End of historical note. + +Note [Simplify cloned constraints] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +At this stage, we're simplifying constraints only for insolubility +and for unification. Note that all the evidence is quickly discarded. +We use a clone of the real constraint. If we don't do this, +then RHS coercion-hole constraints get filled in, only to get filled +in *again* when solving the implications emitted from tcRule. That's +terrible, so we avoid the problem by cloning the constraints. + +-} + +simplifyRule :: RuleName + -> TcLevel -- Level at which to solve the constraints + -> WantedConstraints -- Constraints from LHS + -> WantedConstraints -- Constraints from RHS + -> TcM ( [EvVar] -- Quantify over these LHS vars + , WantedConstraints -- Residual un-quantified LHS constraints + , TcTyVarSet ) -- Don't default these +-- See Note [The SimplifyRule Plan] +-- NB: This consumes all simple constraints on the LHS, but not +-- any LHS implication constraints. +simplifyRule name tc_lvl lhs_wanted rhs_wanted + = do { + -- Note [The SimplifyRule Plan] step 1 + -- First solve the LHS and *then* solve the RHS + -- Crucially, this performs unifications + -- Why clone? See Note [Simplify cloned constraints] + ; lhs_clone <- cloneWC lhs_wanted + ; rhs_clone <- cloneWC rhs_wanted + ; (dont_default, _) + <- setTcLevel tc_lvl $ + runTcS $ + do { lhs_wc <- solveWanteds lhs_clone + ; _rhs_wc <- solveWanteds rhs_clone + -- Why do them separately? + -- See Note [Solve order for RULES] + + ; let dont_default = nonDefaultableTyVarsOfWC lhs_wc + -- If lhs_wanteds has + -- (a[sk] :: TYPE rr[sk]) ~ (b0[tau] :: TYPE r0[conc]) + -- we want r0 to be non-defaultable; + -- see nonDefaultableTyVarsOfWC. Simplest way to get + -- this is to look at the post-simplified lhs_wc, which + -- will contain (rr[sk] ~ r0[conc)]. An example is in + -- test rep-poly/RepPolyRule1 + ; return dont_default } + + -- Note [The SimplifyRule Plan] step 2 + ; lhs_wanted <- liftZonkM $ zonkWC lhs_wanted + + -- Note [The SimplifyRule Plan] step 3 + ; (quant_cts, residual_lhs_wanted) <- getRuleQuantCts lhs_wanted + ; let quant_evs = map ctEvId (bagToList quant_cts) + + ; traceTc "simplifyRule" $ + vcat [ text "LHS of rule" <+> doubleQuotes (ftext name) + , text "lhs_wanted" <+> ppr lhs_wanted + , text "rhs_wanted" <+> ppr rhs_wanted + , text "quant_cts" <+> ppr quant_evs + , text "residual_lhs_wanted" <+> ppr residual_lhs_wanted + , text "dont_default" <+> ppr dont_default + ] + + ; return (quant_evs, residual_lhs_wanted, dont_default) } + +getRuleQuantCts :: WantedConstraints -> TcM (Cts, WantedConstraints) +-- Extract all the constraints that we can quantify over, +-- also returning the depleted WantedConstraints +-- +-- Unlike simplifyInfer, we don't leave the WantedConstraints unchanged, +-- and attempt to solve them from the quantified constraints. Instead +-- we /partition/ the WantedConstraints into ones to quantify and ones +-- we can't quantify. We could use approximateWC instead, and leave +-- `wanted` unchanged; but then we'd have to clone fresh binders and +-- generate silly identity bindings. Seems more direct to do this. +-- Probably not a big deal wither way. +-- +-- NB: we must look inside implications, because with +-- -fdefer-type-errors we generate implications rather eagerly; +-- see GHC.Tc.Utils.Unify.implicationNeeded. Not doing so caused #14732. + +getRuleQuantCts wc + = return $ float_wc emptyVarSet wc + where + float_wc :: TcTyCoVarSet -> WantedConstraints -> (Cts, WantedConstraints) + float_wc skol_tvs (WC { wc_simple = simples, wc_impl = implics, wc_errors = errs }) + = ( simple_yes `andCts` implic_yes + , emptyWC { wc_simple = simple_no, wc_impl = implics_no, wc_errors = errs }) + where + (simple_yes, simple_no) = partitionBag (rule_quant_ct skol_tvs) simples + (implic_yes, implics_no) = mapAccumBagL (float_implic skol_tvs) emptyBag implics + + float_implic :: TcTyCoVarSet -> Cts -> Implication -> (Cts, Implication) + float_implic skol_tvs yes1 imp + = (yes1 `andCts` yes2, imp { ic_wanted = no }) + where + (yes2, no) = float_wc new_skol_tvs (ic_wanted imp) + new_skol_tvs = skol_tvs `extendVarSetList` ic_skols imp + + rule_quant_ct :: TcTyCoVarSet -> Ct -> Bool + rule_quant_ct skol_tvs ct + | insolubleWantedCt ct + = False + | otherwise + = case classifyPredType (ctPred ct) of + EqPred {} -> False -- Note [RULE quantification over equalities] + _ -> tyCoVarsOfCt ct `disjointVarSet` skol_tvs diff --git a/compiler/GHC/Tc/Module.hs b/compiler/GHC/Tc/Module.hs index 75b57149324..3a178d73db3 100644 --- a/compiler/GHC/Tc/Module.hs +++ b/compiler/GHC/Tc/Module.hs @@ -16,7 +16,8 @@ -- -- https://gitlab.haskell.org/ghc/ghc/wikis/commentary/compiler/type-checker module GHC.Tc.Module ( - tcRnStmt, tcRnExpr, TcRnExprMode(..), tcRnType, + tcRnStmt, tcRnExpr, TcRnExprMode(..), + tcRnType, tcRnTypeSkolemising, tcRnImportDecls, tcRnLookupRdrName, getModuleInterface, @@ -71,7 +72,7 @@ import GHC.Tc.Gen.Annotation import GHC.Tc.Gen.Bind import GHC.Tc.Gen.Default import GHC.Tc.Utils.Env -import GHC.Tc.Gen.Rule +import GHC.Tc.Gen.Sig( tcRules ) import GHC.Tc.Gen.Foreign import GHC.Tc.TyCl.Instance import GHC.Tc.Utils.TcMType @@ -115,6 +116,7 @@ import GHC.Core.Class import GHC.Core.Coercion.Axiom import GHC.Core.Reduction ( Reduction(..) ) import GHC.Core.TyCo.Ppr( debugPprType ) +import GHC.Core.TyCo.Tidy( tidyTopType ) import GHC.Core.FamInstEnv ( FamInst, pprFamInst, famInstsRepTyCons, orphNamesOfFamInst , famInstEnvElts, extendFamInstEnvList, normaliseType ) @@ -184,6 +186,7 @@ import qualified Data.Set as S import qualified Data.Map as M import Data.Foldable ( for_ ) import Data.Traversable ( for ) +import Data.IORef( newIORef ) @@ -2680,6 +2683,16 @@ tcRnImportDecls hsc_env import_decls where zap_rdr_env gbl_env = gbl_env { tcg_rdr_env = emptyGlobalRdrEnv } + +tcRnTypeSkolemising :: HscEnv + -> LHsType GhcPs + -> IO (Messages TcRnMessage, Maybe (Type, Kind)) +-- tcRnTypeSkolemising skolemisese any free unification variables, +-- and normalises the type +tcRnTypeSkolemising env ty + = do { skol_tv_ref <- liftIO (newIORef []) + ; tcRnType env (SkolemiseFlexi skol_tv_ref) True ty } + -- tcRnType just finds the kind of a type tcRnType :: HscEnv -> ZonkFlexi diff --git a/compiler/GHC/Tc/Solver.hs b/compiler/GHC/Tc/Solver.hs index 0bdf9bdf735..c4771cc79d5 100644 --- a/compiler/GHC/Tc/Solver.hs +++ b/compiler/GHC/Tc/Solver.hs @@ -16,7 +16,7 @@ module GHC.Tc.Solver( tcNormalise, approximateWC, -- Exported for plugins to use - captureTopConstraints, + captureTopConstraints, emitResidualConstraints, simplifyTopWanteds, @@ -231,7 +231,7 @@ simplifyAndEmitFlatConstraints wanted -- it's OK to use unkSkol | we must increase the TcLevel, -- because we don't bind | as explained in -- any skolem variables here | Note [Wrapping failing kind equalities] - ; emitImplication implic + ; TcM.emitImplication implic ; failM } Just (simples, errs) -> do { _ <- promoteTyVarSet (tyCoVarsOfCts simples) @@ -968,14 +968,17 @@ simplifyInfer top_lvl rhs_tclvl infer_mode sigs name_taus wanteds ; bound_theta_vars <- mapM TcM.newEvVar bound_theta ; let full_theta = map idType bound_theta_vars - ; skol_info <- mkSkolemInfo (InferSkol [ (name, mkPhiTy full_theta ty) - | (name, ty) <- name_taus ]) + skol_info = InferSkol [ (name, mkPhiTy full_theta ty) + | (name, ty) <- name_taus ] + -- mkPhiTy: we don't add the quantified variables here, because + -- they are also bound in ic_skols and we want them to be tidied + -- uniformly. } -- Now emit the residual constraint - ; emitResidualConstraints rhs_tclvl ev_binds_var - name_taus co_vars qtvs bound_theta_vars + ; emitResidualConstraints rhs_tclvl skol_info ev_binds_var + co_vars qtvs bound_theta_vars wanted_transformed -- All done! @@ -992,13 +995,12 @@ simplifyInfer top_lvl rhs_tclvl infer_mode sigs name_taus wanteds partial_sigs = filter isPartialSig sigs -------------------- -emitResidualConstraints :: TcLevel -> EvBindsVar - -> [(Name, TcTauType)] +emitResidualConstraints :: TcLevel -> SkolemInfoAnon -> EvBindsVar -> CoVarSet -> [TcTyVar] -> [EvVar] -> WantedConstraints -> TcM () -- Emit the remaining constraints from the RHS. -emitResidualConstraints rhs_tclvl ev_binds_var - name_taus co_vars qtvs full_theta_vars wanteds +emitResidualConstraints rhs_tclvl skol_info ev_binds_var + co_vars qtvs full_theta_vars wanteds | isEmptyWC wanteds = return () @@ -1031,13 +1033,6 @@ emitResidualConstraints rhs_tclvl ev_binds_var ; emitConstraints (emptyWC { wc_simple = outer_simple , wc_impl = implics }) } - where - full_theta = map idType full_theta_vars - skol_info = InferSkol [ (name, mkPhiTy full_theta ty) - | (name, ty) <- name_taus ] - -- We don't add the quantified variables here, because they are - -- also bound in ic_skols and we want them to be tidied - -- uniformly. -------------------- findInferredDiff :: TcThetaType -> TcThetaType -> TcM TcThetaType @@ -1286,7 +1281,7 @@ decideQuantification :: TopLevelFlag -> TcLevel -> InferMode - -> SkolemInfo + -> SkolemInfoAnon -> [(Name, TcTauType)] -- Variables to be generalised -> [TcIdSigInst] -- Partial type signatures (if any) -> WantedConstraints -- Candidate theta; already zonked @@ -1818,13 +1813,13 @@ defaultTyVarsAndSimplify rhs_tclvl candidates ------------------ decideQuantifiedTyVars - :: SkolemInfo + :: SkolemInfoAnon -> [(Name,TcType)] -- Annotated theta and (name,tau) pairs -> [TcIdSigInst] -- Partial signatures -> [PredType] -- Candidates, zonked -> TcM [TyVar] -- Fix what tyvars we are going to quantify over, and quantify them -decideQuantifiedTyVars skol_info name_taus psigs candidates +decideQuantifiedTyVars skol_info_anon name_taus psigs candidates = do { -- Why psig_tys? We try to quantify over everything free in here -- See Note [Quantification and partial signatures] -- Wrinkles 2 and 3 @@ -1843,20 +1838,19 @@ decideQuantifiedTyVars skol_info name_taus psigs candidates -- The psig_tys are first in seed_tys, then candidates, then tau_tvs. -- This makes candidateQTyVarsOfTypes produces them in that order, so that the -- final qtvs quantifies in the same- order as the partial signatures do (#13524) - ; dv@DV {dv_kvs = cand_kvs, dv_tvs = cand_tvs} - <- candidateQTyVarsOfTypes $ - psig_tys ++ candidates ++ tau_tys - ; let pick = (`dVarSetIntersectVarSet` grown_tcvs) - dvs_plus = dv { dv_kvs = pick cand_kvs, dv_tvs = pick cand_tvs } + ; dvs <- candidateQTyVarsOfTypes (psig_tys ++ candidates ++ tau_tys) + ; let dvs_plus = weedOutCandidates (`dVarSetIntersectVarSet` grown_tcvs) dvs ; traceTc "decideQuantifiedTyVars" (vcat - [ text "candidates =" <+> ppr candidates - , text "cand_kvs =" <+> ppr cand_kvs - , text "cand_tvs =" <+> ppr cand_tvs - , text "seed_tys =" <+> ppr seed_tvs + [ text "tau_tys =" <+> ppr tau_tys + , text "candidates =" <+> ppr candidates + , text "dvs =" <+> ppr dvs + , text "tau_tys =" <+> ppr tau_tys + , text "seed_tvs =" <+> ppr seed_tvs , text "grown_tcvs =" <+> ppr grown_tcvs , text "dvs =" <+> ppr dvs_plus]) + ; skol_info <- mkSkolemInfo skol_info_anon ; quantifyTyVars skol_info DefaultNonStandardTyVars dvs_plus } ------------------ diff --git a/compiler/GHC/Tc/Solver/Default.hs b/compiler/GHC/Tc/Solver/Default.hs index 2e16ddabb97..d1320e051eb 100644 --- a/compiler/GHC/Tc/Solver/Default.hs +++ b/compiler/GHC/Tc/Solver/Default.hs @@ -33,6 +33,7 @@ import GHC.Core.Unify ( tcMatchTyKis ) import GHC.Core.Predicate import GHC.Core.Type import GHC.Core.TyCon ( TyCon ) +import GHC.Core.TyCo.Tidy import GHC.Types.DefaultEnv ( ClassDefaults (..), defaultList ) import GHC.Types.Unique.Set diff --git a/compiler/GHC/Tc/Solver/Dict.hs b/compiler/GHC/Tc/Solver/Dict.hs index c14e032d449..36ad82c0837 100644 --- a/compiler/GHC/Tc/Solver/Dict.hs +++ b/compiler/GHC/Tc/Solver/Dict.hs @@ -62,7 +62,6 @@ import Control.Monad.Trans.Maybe( MaybeT, runMaybeT ) import Control.Monad.Trans.Class( lift ) import Control.Monad - {- ********************************************************************* * * * Class Canonicalization @@ -101,26 +100,6 @@ solveDict dict_ct@(DictCt { di_ev = ev, di_cls = cls, di_tys = tys }) ; simpleStage (updInertDicts dict_ct) ; stopWithStage (dictCtEvidence dict_ct) "Kept inert DictCt" } -updInertDicts :: DictCt -> TcS () -updInertDicts dict_ct@(DictCt { di_cls = cls, di_ev = ev, di_tys = tys }) - = do { traceTcS "Adding inert dict" (ppr dict_ct $$ ppr cls <+> ppr tys) - - ; if | isGiven ev, Just (str_ty, _) <- isIPPred_maybe cls tys - -> -- See (SIP1) and (SIP2) in Note [Shadowing of implicit parameters] - -- Update /both/ inert_cans /and/ inert_solved_dicts. - updInertSet $ \ inerts@(IS { inert_cans = ics, inert_solved_dicts = solved }) -> - inerts { inert_cans = updDicts (filterDicts (not_ip_for str_ty)) ics - , inert_solved_dicts = filterDicts (not_ip_for str_ty) solved } - | otherwise - -> return () - - -- Add the new constraint to the inert set - ; updInertCans (updDicts (addDict dict_ct)) } - where - not_ip_for :: Type -> DictCt -> Bool - not_ip_for str_ty (DictCt { di_cls = cls, di_tys = tys }) - = not (mentionsIP str_ty cls tys) - canDictCt :: CtEvidence -> Class -> [Type] -> SolverStage DictCt -- Once-only processing of Dict constraints: -- * expand superclasses diff --git a/compiler/GHC/Tc/Solver/InertSet.hs b/compiler/GHC/Tc/Solver/InertSet.hs index 3d3f8b295f5..fda755da648 100644 --- a/compiler/GHC/Tc/Solver/InertSet.hs +++ b/compiler/GHC/Tc/Solver/InertSet.hs @@ -1,4 +1,5 @@ {-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeApplications #-} @@ -24,6 +25,8 @@ module GHC.Tc.Solver.InertSet ( InertEqs, foldTyEqs, delEq, findEq, partitionInertEqs, partitionFunEqs, + filterInertEqs, filterFunEqs, + inertGivens, foldFunEqs, addEqToCans, -- * Inert Dicts @@ -73,10 +76,9 @@ import GHC.Utils.Outputable import GHC.Utils.Panic import GHC.Data.Bag -import Data.List.NonEmpty ( NonEmpty(..), (<|) ) -import Data.Function ( on ) - import Control.Monad ( forM_ ) +import Data.List.NonEmpty ( NonEmpty(..), (<|) ) +import Data.Function ( on ) {- ************************************************************************ @@ -390,7 +392,6 @@ emptyInert , inert_famapp_cache = emptyFunEqs , inert_solved_dicts = emptyDictMap } - {- Note [Solved dictionaries] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When we apply a top-level instance declaration, we add the "solved" @@ -1377,6 +1378,17 @@ addInertEqs :: EqCt -> InertEqs -> InertEqs addInertEqs eq_ct@(EqCt { eq_lhs = TyVarLHS tv }) eqs = addTyEq eqs tv eq_ct addInertEqs other _ = pprPanic "extendInertEqs" (ppr other) +-- | Filter InertEqs according to a predicate +filterInertEqs :: (EqCt -> Bool) -> InertEqs -> InertEqs +filterInertEqs f = mapMaybeDVarEnv g + where + g xs = + let filtered = filter f xs + in + if null filtered + then Nothing + else Just filtered + ------------------------ addCanFunEq :: InertFunEqs -> TyCon -> [TcType] -> EqCt -> InertFunEqs @@ -1400,7 +1412,16 @@ addFunEqs eq_ct@(EqCt { eq_lhs = TyFamLHS tc args }) fun_eqs = addCanFunEq fun_eqs tc args eq_ct addFunEqs other _ = pprPanic "extendFunEqs" (ppr other) - +-- | Filter entries in InertFunEqs that satisfy the predicate +filterFunEqs :: (EqCt -> Bool) -> InertFunEqs -> InertFunEqs +filterFunEqs f = mapMaybeTcAppMap g + where + g xs = + let filtered = filter f xs + in + if null filtered + then Nothing + else Just filtered {- ********************************************************************* * * @@ -2214,3 +2235,44 @@ Wrong! The level-check ensures that the inner implicit parameter wins. (Actually I think that the order in which the work-list is processed means that this chain of events won't happen, but that's very fragile.) -} + +{- ********************************************************************* +* * + Extracting Givens from the inert set +* * +********************************************************************* -} + + +-- | Extract only Given constraints from the inert set. +inertGivens :: InertSet -> InertSet +inertGivens is@(IS { inert_cans = cans }) = + is { inert_cans = givens_cans + , inert_solved_dicts = emptyDictMap + } + where + + isGivenEq :: EqCt -> Bool + isGivenEq eq = isGiven (ctEvidence (CEqCan eq)) + isGivenDict :: DictCt -> Bool + isGivenDict dict = isGiven (ctEvidence (CDictCan dict)) + isGivenIrred :: IrredCt -> Bool + isGivenIrred irred = isGiven (ctEvidence (CIrredCan irred)) + + -- Filter the inert constraints for Givens + (eq_givens_list, _) = partitionInertEqs isGivenEq (inert_eqs cans) + (funeq_givens_list, _) = partitionFunEqs isGivenEq (inert_funeqs cans) + dict_givens = filterDicts isGivenDict (inert_dicts cans) + safehask_givens = filterDicts isGivenDict (inert_safehask cans) + irreds_givens = filterBag isGivenIrred (inert_irreds cans) + + eq_givens = foldr addInertEqs emptyTyEqs eq_givens_list + funeq_givens = foldr addFunEqs emptyFunEqs funeq_givens_list + + givens_cans = + cans + { inert_eqs = eq_givens + , inert_funeqs = funeq_givens + , inert_dicts = dict_givens + , inert_safehask = safehask_givens + , inert_irreds = irreds_givens + } diff --git a/compiler/GHC/Tc/Solver/Irred.hs b/compiler/GHC/Tc/Solver/Irred.hs index 63f35127637..08751882fed 100644 --- a/compiler/GHC/Tc/Solver/Irred.hs +++ b/compiler/GHC/Tc/Solver/Irred.hs @@ -39,10 +39,6 @@ solveIrred irred ; simpleStage (updInertIrreds irred) ; stopWithStage (irredCtEvidence irred) "Kept inert IrredCt" } -updInertIrreds :: IrredCt -> TcS () -updInertIrreds irred - = do { tc_lvl <- getTcLevel - ; updInertCans $ addIrredToCans tc_lvl irred } {- ********************************************************************* * * diff --git a/compiler/GHC/Tc/Solver/Monad.hs b/compiler/GHC/Tc/Solver/Monad.hs index 387263ff5d7..b15feebf5c7 100644 --- a/compiler/GHC/Tc/Solver/Monad.hs +++ b/compiler/GHC/Tc/Solver/Monad.hs @@ -14,11 +14,14 @@ module GHC.Tc.Solver.Monad ( -- The TcS monad - TcS, runTcS, runTcSEarlyAbort, runTcSWithEvBinds, runTcSInerts, + TcS(..), TcSEnv(..), TcSMode(..), + runTcS, runTcSEarlyAbort, runTcSWithEvBinds, runTcSInerts, + runTcSSpecPrag, failTcS, warnTcS, addErrTcS, wrapTcS, ctLocWarnTcS, runTcSEqualities, nestTcS, nestImplicTcS, setEvBindsTcS, emitImplicationTcS, emitTvImplicationTcS, + emitImplication, emitFunDepWanteds, selectNextWorkItem, @@ -76,6 +79,7 @@ module GHC.Tc.Solver.Monad ( getUnsolvedInerts, removeInertCts, getPendingGivenScs, insertFunEq, addInertForAll, + updInertDicts, updInertIrreds, emitWorkNC, emitWork, lookupInertDict, @@ -170,6 +174,7 @@ import GHC.Builtin.Names ( unsatisfiableClassNameKey ) import GHC.Core.Type import GHC.Core.TyCo.Rep as Rep +import GHC.Core.TyCo.Tidy import GHC.Core.Coercion import GHC.Core.Coercion.Axiom( TypeEqn ) import GHC.Core.Predicate @@ -210,6 +215,7 @@ import Data.Maybe ( isJust ) import qualified Data.Semigroup as S import GHC.Types.SrcLoc import GHC.Rename.Env +--import GHC.Tc.Solver.Solve (solveWanteds) #if defined(DEBUG) import GHC.Types.Unique.Set (nonDetEltsUniqSet) @@ -370,6 +376,31 @@ duplicates, is explained in Note [Use only the best matching quantified constrai in GHC.Tc.Solver.Dict. -} +updInertDicts :: DictCt -> TcS () +updInertDicts dict_ct@(DictCt { di_cls = cls, di_ev = ev, di_tys = tys }) + = do { traceTcS "Adding inert dict" (ppr dict_ct $$ ppr cls <+> ppr tys) + + ; if | isGiven ev, Just (str_ty, _) <- isIPPred_maybe cls tys + -> -- See (SIP1) and (SIP2) in Note [Shadowing of implicit parameters] + -- Update /both/ inert_cans /and/ inert_solved_dicts. + updInertSet $ \ inerts@(IS { inert_cans = ics, inert_solved_dicts = solved }) -> + inerts { inert_cans = updDicts (filterDicts (not_ip_for str_ty)) ics + , inert_solved_dicts = filterDicts (not_ip_for str_ty) solved } + | otherwise + -> return () + + -- Add the new constraint to the inert set + ; updInertCans (updDicts (addDict dict_ct)) } + where + not_ip_for :: Type -> DictCt -> Bool + not_ip_for str_ty (DictCt { di_cls = cls, di_tys = tys }) + = not (mentionsIP str_ty cls tys) + +updInertIrreds :: IrredCt -> TcS () +updInertIrreds irred + = do { tc_lvl <- getTcLevel + ; updInertCans $ addIrredToCans tc_lvl irred } + {- ********************************************************************* * * Kicking out @@ -580,7 +611,7 @@ getInertGivens :: TcS [Ct] getInertGivens = do { inerts <- getInertCans ; let all_cts = foldIrreds ((:) . CIrredCan) (inert_irreds inerts) - $ foldDicts ((:) . CDictCan) (inert_dicts inerts) + $ foldDicts ((:) . CDictCan) (inert_dicts inerts) $ foldFunEqs ((:) . CEqCan) (inert_funeqs inerts) $ foldTyEqs ((:) . CEqCan) (inert_eqs inerts) $ [] @@ -680,6 +711,7 @@ getUnsolvedInerts where ct = mk_ct thing + getHasGivenEqs :: TcLevel -- TcLevel of this implication -> TcS ( HasGivenEqs -- are there Given equalities? , InertIrreds ) -- Insoluble equalities arising from givens @@ -825,6 +857,31 @@ for it, so TcS carries a mutable location where the binding can be added. This is initialised from the innermost implication constraint. -} +-- | See Note [TcSMode] +data TcSMode + = TcSVanilla -- ^ Normal constraint solving + | TcSEarlyAbort -- ^ Abort early on insoluble constraints + | TcSSpecPrag -- ^ Fully solve all constraints + deriving (Eq) + +{- Note [TcSMode] +~~~~~~~~~~~~~~~~~ +The constraint solver can operate in different modes: + +* TcSVanilla: Normal constraint solving mode. This is the default. + +* TcSEarlyAbort: Abort (fail in the monad) as soon as we come across an + insoluble constraint. This is used to fail-fast when checking for hole-fits. + See Note [Speeding up valid hole-fits]. + +* TcSSpecPrag: Solve constraints fully or not at all. This is described in + Note [TcSSpecPrag]. + + This mode is currently used in one place only: when solving constraints + arising from specialise pragmas. + See Note [Fully solving constraints for specialisation] in GHC.Tc.Gen.Sig. +-} + data TcSEnv = TcSEnv { tcs_ev_binds :: EvBindsVar, @@ -844,13 +901,11 @@ data TcSEnv tcs_inerts :: IORef InertSet, -- Current inert set - -- Whether to throw an exception if we come across an insoluble constraint. - -- Used to fail-fast when checking for hole-fits. See Note [Speeding up - -- valid hole-fits]. - tcs_abort_on_insoluble :: Bool, + -- | The mode of operation for the constraint solver. + -- See Note [TcSMode] + tcs_mode :: TcSMode, - -- See Note [WorkList priorities] in GHC.Tc.Solver.InertSet - tcs_worklist :: IORef WorkList -- Current worklist + tcs_worklist :: IORef WorkList } --------------- @@ -921,9 +976,9 @@ addErrTcS = wrapTcS . TcM.addErr panicTcS doc = pprPanic "GHC.Tc.Solver.Monad" doc tryEarlyAbortTcS :: TcS () --- Abort (fail in the monad) if the abort_on_insoluble flag is on +-- Abort (fail in the monad) if the mode is TcSEarlyAbort tryEarlyAbortTcS - = mkTcS (\env -> when (tcs_abort_on_insoluble env) TcM.failM) + = mkTcS (\env -> when (tcs_mode env == TcSEarlyAbort) TcM.failM) -- | Emit a warning within the 'TcS' monad at the location given by the 'CtLoc'. ctLocWarnTcS :: CtLoc -> TcRnMessage -> TcS () @@ -993,7 +1048,59 @@ runTcS tcs runTcSEarlyAbort :: TcS a -> TcM a runTcSEarlyAbort tcs = do { ev_binds_var <- TcM.newTcEvBinds - ; runTcSWithEvBinds' True True ev_binds_var tcs } + ; runTcSWithEvBinds' True TcSEarlyAbort ev_binds_var tcs } + +-- | Run the 'TcS' monad in 'TcSSpecPrag' mode, which either fully solves +-- individual Wanted quantified constraints or leaves them alone. +-- +-- See Note [TcSSpecPrag]. +runTcSSpecPrag :: EvBindsVar -> TcS a -> TcM a +runTcSSpecPrag ev_binds_var tcs + = runTcSWithEvBinds' True TcSSpecPrag ev_binds_var tcs + +{- Note [TcSSpecPrag] +~~~~~~~~~~~~~~~~~~~~~ +The TcSSpecPrag mode is a specialized constraint solving mode that guarantees +that Wanted quantified constraints are either: + - Fully solved with no free evidence variables, or + - Left completely untouched (no simplification at all) + +Examples: + + * [W] forall x. Eq x => Eq (f x). + + In TcSSpecPrag mode, we **do not** process this quantified constraint by + creating a new implication constraint; we leave it alone instead. + + * [W] Eq (Maybe Int). + + This constraint is solved fully, using two top-level Eq instances. + + * [W] forall x. Eq x => Eq [x]. + + This constraint is solved fully as well, using the Eq instance for lists. + +This functionality is crucially used by the specialiser, for which taking an +irreversible constraint solving steps is actively harmful, as described in +Note [Fully solving constraints for specialisation] in GHC.Tc.Gen.Sig. + +Note that currently we **do not** refrain from using top-level instances, +even though we also can't run them in reverse; this isn't a problem for the +specialiser (which is currently the sole consumer of this functionality). + +The implementation is as follows: in TcSFullySolveMode, when we are about to +solve a Wanted quantified constraint by emitting an implication, we call the +special function `solveCompletelyIfRequired`. This function recursively calls +the solver but in TcSVanilla mode (i.e. full-blown solving, with no restrictions). +If this recursive call manages to solve all the remaining constraints fully, +then we proceed with that outcome (i.e. we continue with that inert set, etc). +Otherwise, we discard everything that happened in the recursive call, and +continue with the original quantified constraint unchanged. + +In the future, we could consider re-using this functionality for the short-cut +solver (see Note [Shortcut solving] in GHC.Tc.Solver.Dict), but we would have to +be wary of the performance implications. +-} -- | This can deal only with equality constraints. runTcSEqualities :: TcS a -> TcM a @@ -1006,7 +1113,7 @@ runTcSEqualities thing_inside runTcSInerts :: InertSet -> TcS a -> TcM (a, InertSet) runTcSInerts inerts tcs = do ev_binds_var <- TcM.newTcEvBinds - runTcSWithEvBinds' False False ev_binds_var $ do + runTcSWithEvBinds' False TcSVanilla ev_binds_var $ do setInertSet inerts a <- tcs new_inerts <- getInertSet @@ -1015,17 +1122,17 @@ runTcSInerts inerts tcs = do runTcSWithEvBinds :: EvBindsVar -> TcS a -> TcM a -runTcSWithEvBinds = runTcSWithEvBinds' True False +runTcSWithEvBinds = runTcSWithEvBinds' True TcSVanilla -runTcSWithEvBinds' :: Bool -- ^ Restore type variable cycles afterwards? +runTcSWithEvBinds' :: Bool -- True <=> restore type equality cycles -- Don't if you want to reuse the InertSet. -- See also Note [Type equality cycles] -- in GHC.Tc.Solver.Equality - -> Bool + -> TcSMode -> EvBindsVar -> TcS a -> TcM a -runTcSWithEvBinds' restore_cycles abort_on_insoluble ev_binds_var tcs +runTcSWithEvBinds' restore_cycles mode ev_binds_var tcs = do { unified_var <- TcM.newTcRef 0 ; step_count <- TcM.newTcRef 0 ; inert_var <- TcM.newTcRef emptyInert @@ -1036,7 +1143,7 @@ runTcSWithEvBinds' restore_cycles abort_on_insoluble ev_binds_var tcs , tcs_unif_lvl = unif_lvl_var , tcs_count = step_count , tcs_inerts = inert_var - , tcs_abort_on_insoluble = abort_on_insoluble + , tcs_mode = mode , tcs_worklist = wl_var } -- Run the computation @@ -1098,7 +1205,7 @@ nestImplicTcS ref inner_tclvl (TcS thing_inside) , tcs_inerts = old_inert_var , tcs_count = count , tcs_unif_lvl = unif_lvl - , tcs_abort_on_insoluble = abort_on_insoluble + , tcs_mode = mode } -> do { inerts <- TcM.readTcRef old_inert_var ; let nest_inert = inerts { inert_cycle_breakers = pushCycleBreakerVarStack @@ -1113,7 +1220,7 @@ nestImplicTcS ref inner_tclvl (TcS thing_inside) , tcs_ev_binds = ref , tcs_unified = unified_var , tcs_inerts = new_inert_var - , tcs_abort_on_insoluble = abort_on_insoluble + , tcs_mode = mode , tcs_worklist = new_wl_var } ; res <- TcM.setTcLevel inner_tclvl $ thing_inside nest_env @@ -1128,7 +1235,7 @@ nestImplicTcS ref inner_tclvl (TcS thing_inside) #endif ; return res } -nestTcS :: TcS a -> TcS a +nestTcS :: TcS a -> TcS a -- Use the current untouchables, augmenting the current -- evidence bindings, and solved dictionaries -- But have no effect on the InertCans, or on the inert_famapp_cache diff --git a/compiler/GHC/Tc/Solver/Solve.hs b/compiler/GHC/Tc/Solver/Solve.hs index 630137ff0ad..8ab06ccae5a 100644 --- a/compiler/GHC/Tc/Solver/Solve.hs +++ b/compiler/GHC/Tc/Solver/Solve.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE RecursiveDo #-} module GHC.Tc.Solver.Solve ( @@ -5,6 +6,7 @@ module GHC.Tc.Solver.Solve ( solveWanteds, -- Solves WantedConstraints solveSimpleGivens, -- Solves [Ct] solveSimpleWanteds, -- Solves Cts + solveCompletelyIfRequired, setImplicationStatus ) where @@ -18,7 +20,7 @@ import GHC.Tc.Solver.Rewrite( rewrite, rewriteType ) import GHC.Tc.Errors.Types import GHC.Tc.Utils.TcType import GHC.Tc.Types.Evidence -import GHC.Tc.Types.CtLoc( ctLocEnv, ctLocOrigin, setCtLocOrigin, CtLoc ) +import GHC.Tc.Types.CtLoc( ctLocEnv, ctLocOrigin, setCtLocOrigin ) import GHC.Tc.Types import GHC.Tc.Types.Origin import GHC.Tc.Types.Constraint @@ -51,7 +53,8 @@ import GHC.Driver.Session import Data.List( deleteFirstsBy ) import Control.Monad -import Data.Maybe (mapMaybe) +import Data.Foldable ( traverse_ ) +import Data.Maybe ( mapMaybe ) import qualified Data.Semigroup as S import Data.Void( Void ) @@ -1053,7 +1056,7 @@ solveCt (CNonCanonical ev) = solveNC ev solveCt (CIrredCan (IrredCt { ir_ev = ev })) = solveNC ev solveCt (CEqCan (EqCt { eq_ev = ev, eq_eq_rel = eq_rel - , eq_lhs = lhs, eq_rhs = rhs })) + , eq_lhs = lhs, eq_rhs = rhs })) = solveEquality ev eq_rel (canEqLHSType lhs) rhs solveCt (CQuantCan (QCI { qci_ev = ev, qci_pend_sc = pend_sc })) @@ -1062,7 +1065,8 @@ solveCt (CQuantCan (QCI { qci_ev = ev, qci_pend_sc = pend_sc })) -- rewrite the pieces and build a Reduction that will rewrite -- the whole constraint ; case classifyPredType (ctEvPred ev) of - ForAllPred tvs th p -> Stage $ solveForAll ev tvs th p pend_sc + ForAllPred tvs theta body_pred -> + Stage $ solveForAll ev tvs theta body_pred pend_sc _ -> pprPanic "SolveCt" (ppr ev) } solveCt (CDictCan (DictCt { di_ev = ev, di_pend_sc = pend_sc })) @@ -1100,7 +1104,7 @@ solveNC ev IrredPred {} -> solveIrred (IrredCt { ir_ev = ev, ir_reason = IrredShapeReason }) EqPred eq_rel ty1 ty2 -> solveEquality ev eq_rel ty1 ty2 -- EqPred only happens if (say) `c` is unified with `a ~# b`, - -- but that is rare becuase it requires c :: CONSTRAINT UnliftedRep + -- but that is rare because it requires c :: CONSTRAINT UnliftedRep }} @@ -1188,8 +1192,8 @@ type signature. -- Precondition: the constraint has already been rewritten by the inert set. solveForAllNC :: CtEvidence -> [TcTyVar] -> TcThetaType -> TcPredType -> TcS (StopOrContinue Void) -solveForAllNC ev tvs theta pred - | Just (cls,tys) <- getClassPredTys_maybe pred +solveForAllNC ev tvs theta body_pred + | Just (cls,tys) <- getClassPredTys_maybe body_pred , classHasSCs cls = do { dflags <- getDynFlags -- Either expand superclasses (Givens) or provide fuel to do so (Wanteds) @@ -1199,38 +1203,37 @@ solveForAllNC ev tvs theta pred -- givensFuel dflags: See Note [Expanding Recursive Superclasses and ExpansionFuel] do { sc_cts <- mkStrictSuperClasses (givensFuel dflags) ev tvs theta cls tys ; emitWork (listToBag sc_cts) - ; solveForAll ev tvs theta pred doNotExpand } + ; solveForAll ev tvs theta body_pred doNotExpand } else -- See invariants (a) and (b) in QCI.qci_pend_sc -- qcsFuel dflags: See Note [Expanding Recursive Superclasses and ExpansionFuel] -- See Note [Quantified constraints] - do { solveForAll ev tvs theta pred (qcsFuel dflags) } + do { solveForAll ev tvs theta body_pred (qcsFuel dflags) } } | otherwise - = solveForAll ev tvs theta pred doNotExpand + = solveForAll ev tvs theta body_pred doNotExpand -- | Solve a canonical quantified constraint. -- -- Precondition: the constraint has already been rewritten by the inert set. solveForAll :: CtEvidence -> [TcTyVar] -> TcThetaType -> PredType -> ExpansionFuel -> TcS (StopOrContinue Void) -solveForAll ev tvs theta pred fuel = +solveForAll ev tvs theta body_pred fuel = case ev of CtGiven {} -> -- See Note [Solving a Given forall-constraint] do { addInertForAll qci ; stopWith ev "Given forall-constraint" } - CtWanted { ctev_dest = dest, ctev_rewriters = rewriters, ctev_loc = loc } -> + CtWanted {} -> -- See Note [Solving a Wanted forall-constraint] runSolverStage $ do { tryInertQCs qci - ; simpleStage $ solveWantedForAll_implic dest rewriters loc tvs theta pred - ; stopWithStage ev "Wanted forall-constraint (implication)" + ; Stage $ solveWantedForAll_implic ev tvs theta body_pred } where qci = QCI { qci_ev = ev, qci_tvs = tvs - , qci_pred = pred, qci_pend_sc = fuel } + , qci_pred = body_pred, qci_pend_sc = fuel } tryInertQCs :: QCInst -> SolverStage () @@ -1257,14 +1260,22 @@ try_inert_qcs (QCI { qci_ev = ev_w }) inerts = -- | Solve a (canonical) Wanted quantified constraint by emitting an implication. -- -- See Note [Solving a Wanted forall-constraint] -solveWantedForAll_implic :: TcEvDest -> RewriterSet -> CtLoc -> [TcTyVar] -> TcThetaType -> PredType -> TcS () -solveWantedForAll_implic dest rewriters loc tvs theta pred - = TcS.setSrcSpan (getCtLocEnvLoc $ ctLocEnv loc) $ +solveWantedForAll_implic :: CtEvidence -> [TcTyVar] -> TcThetaType -> PredType -> TcS (StopOrContinue Void) +solveWantedForAll_implic + ev@(CtWanted { ctev_dest = dest, ctev_loc = loc, ctev_rewriters = rewriters }) + tvs theta body_pred = + -- We are about to do something irreversible (turning a quantified constraint + -- into an implication), so wrap the inner call in solveCompletelyIfRequired + -- to ensure we can roll back if we can't solve the implication fully. + -- See Note [TcSSpecPrag] in GHC.Tc.Solver.Monad. + solveCompletelyIfRequired (mkNonCanonical ev) $ + -- This setSrcSpan is important: the emitImplicationTcS uses that -- TcLclEnv for the implication, and that in turn sets the location -- for the Givens when solving the constraint (#21006) + TcS.setSrcSpan (getCtLocEnvLoc $ ctLocEnv loc) $ do { let empty_subst = mkEmptySubst $ mkInScopeSet $ - tyCoVarsOfTypes (pred:theta) `delVarSetList` tvs + tyCoVarsOfTypes (body_pred:theta) `delVarSetList` tvs is_qc = IsQC (ctLocOrigin loc) -- rec {..}: see Note [Keeping SkolemInfo inside a SkolemTv] @@ -1272,7 +1283,7 @@ solveWantedForAll_implic dest rewriters loc tvs theta pred -- Very like the code in tcSkolDFunType ; rec { skol_info <- mkSkolemInfo skol_info_anon ; (subst, skol_tvs) <- tcInstSkolTyVarsX skol_info empty_subst tvs - ; let inst_pred = substTy subst pred + ; let inst_pred = substTy subst body_pred inst_theta = substTheta subst theta skol_info_anon = InstSkol is_qc (get_size inst_pred) } @@ -1294,6 +1305,8 @@ solveWantedForAll_implic dest rewriters loc tvs theta pred ; setWantedEvTerm dest EvCanonical $ EvFun { et_tvs = skol_tvs, et_given = given_ev_vars , et_binds = ev_binds, et_body = w_id } + + ; stopWith ev "Wanted forall-constraint (implication)" } where -- Getting the size of the head is a bit horrible @@ -1301,6 +1314,8 @@ solveWantedForAll_implic dest rewriters loc tvs theta pred get_size pred = case classifyPredType pred of ClassPred cls tys -> pSizeClassPred cls tys _ -> pSizeType pred +solveWantedForAll_implic (CtGiven {}) _ _ _ = + panic "solveWantedForAll_implic: CtGiven" {- Note [Solving a Wanted forall-constraint] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1342,7 +1357,7 @@ Note [Solving a Given forall-constraint] For a Given constraint [G] df :: forall ab. (Eq a, Ord b) => C x a b we just add it to TcS's local InstEnv of known instances, -via addInertForall. Then, if we look up (C x Int Bool), say, +via addInertForAll. Then, if we look up (C x Int Bool), say, we'll find a match in the InstEnv. Note [Solving Wanted QCs from Given QCs] @@ -1615,3 +1630,105 @@ runTcPluginSolvers solvers all_cts CtGiven {} -> (ct:givens, wanteds) CtWanted {} -> (givens, (ev,ct):wanteds) +-------------------------------------------------------------------------------- + +-- | If the mode is 'TcSSpecPrag', attempt to fully solve the Wanted +-- constraints that arise from solving 'Ct'. +-- +-- If not in 'TcSSpecPrag' mode, simply run 'thing_inside'. +-- +-- See Note [TcSSpecPrag] in GHC.Tc.Solver.Monad. +solveCompletelyIfRequired :: Ct -> TcS (StopOrContinue a) -> TcS (StopOrContinue a) +solveCompletelyIfRequired ct (TcS thing_inside) + = TcS $ \ env@(TcSEnv { tcs_ev_binds = outer_ev_binds_var + , tcs_unified = outer_unified_var + , tcs_unif_lvl = outer_unif_lvl_var + , tcs_inerts = outer_inert_var + , tcs_count = outer_count + , tcs_mode = mode + }) -> + case mode of + TcSSpecPrag -> + do { traceTc "solveCompletelyIfRequired {" empty + -- Create a fresh environment for the inner computation + ; outer_inerts <- TcM.readTcRef outer_inert_var + ; let outer_givens = inertGivens outer_inerts + -- Keep the ambient Given inerts, but drop the Wanteds. + ; new_inert_var <- TcM.newTcRef outer_givens + ; new_wl_var <- TcM.newTcRef emptyWorkList + ; new_ev_binds_var <- TcM.newTcEvBinds + + ; let + inner_env = + TcSEnv + -- KEY part: recur with TcSVanilla + { tcs_mode = TcSVanilla + + -- Use new variables for evidence bindings, inerts; and + -- the work list. We may want to discard all of these if the + -- inner computation doesn't fully solve all the constraints. + , tcs_ev_binds = new_ev_binds_var + , tcs_inerts = new_inert_var + , tcs_worklist = new_wl_var + + -- Inherit the other variables. In particular, inherit the + -- variables to do with unification, as filling metavariables + -- is a side-effect that we are not reverting, even when we + -- discard the result of the inner computation. + , tcs_unif_lvl = outer_unif_lvl_var + , tcs_unified = outer_unified_var + , tcs_count = outer_count + } + + -- Solve the constraint + ; let wc = emptyWC { wc_simple = unitBag ct } + ; traceTc "solveCompletelyIfRequired solveWanteds" $ + vcat [ text "ct:" <+> ppr ct + ] + ; solved_wc <- unTcS (solveWanteds wc) inner_env + -- NB: it would probably make more sense to call 'thing_inside', + -- collecting all constraints that were added to the work list as + -- a result, and calling 'solveWanteds' on that. This would avoid + -- restarting from the top of the solver pipeline. + -- For the time being, we just call 'solveWanteds' on the original + -- constraint, which is simpler + + ; if isSolvedWC solved_wc + then + do { -- The constraint was fully solved. Continue with + -- the inner solver state. + ; traceTc "solveCompletelyIfRequired: fully solved }" $ + vcat [ text "ct:" <+> ppr ct + , text "solved_wc:" <+> ppr solved_wc ] + + -- Add new evidence bindings to the existing ones + ; inner_ev_binds <- TcM.getTcEvBindsMap new_ev_binds_var + ; addTcEvBinds outer_ev_binds_var inner_ev_binds + + -- Keep the outer inert set and work list: the inner work + -- list is empty, and there are no leftover unsolved + -- Wanteds. + -- However, we **must not** drop solved implications, due + -- to Note [Free vars of EvFun] in GHC.Tc.Types.Evidence; + -- so we re-emit them here. + ; let re_emit_implic impl = unTcS ( TcS.emitImplication impl ) env + ; traverse_ re_emit_implic $ wc_impl solved_wc + ; return $ Stop (ctEvidence ct) (text "Fully solved:" <+> ppr ct) + } + else + do { traceTc "solveCompletelyIfRequired: unsolved }" $ + vcat [ text "ct:" <+> ppr ct + , text "solved_wc:" <+> ppr solved_wc ] + -- Failed to fully solve the constraint: + -- + -- - discard the inner solver state, + -- - add the original constraint as an inert. + ; unTcS (updInertIrreds (IrredCt (ctEvidence ct) IrredShapeReason)) env + -- NB: currently we only call 'solveCompletelyIfRequired' + -- from 'solveForAll'; so we just stash the unsolved quantified + -- constraint in the irreds. + + ; return $ Stop (ctEvidence ct) (text "Not fully solved; kept as inert:" <+> ppr ct) + } } + _notFullySolveMode -> + thing_inside env \ No newline at end of file diff --git a/compiler/GHC/Tc/TyCl.hs b/compiler/GHC/Tc/TyCl.hs index d4ace7b6196..a175e857a39 100644 --- a/compiler/GHC/Tc/TyCl.hs +++ b/compiler/GHC/Tc/TyCl.hs @@ -3376,7 +3376,7 @@ without treating the explicitly-quantified ones specially. Wrinkles: no role. See also Note [Re-quantify type variables in rules] in -GHC.Tc.Gen.Rule, which explains a /very/ similar design when +GHC.Tc.Gen.Sig, which explains a /very/ similar design when generalising over the type of a rewrite rule. -} diff --git a/compiler/GHC/Tc/TyCl/Instance.hs b/compiler/GHC/Tc/TyCl/Instance.hs index 280661f2907..5f817313909 100644 --- a/compiler/GHC/Tc/TyCl/Instance.hs +++ b/compiler/GHC/Tc/TyCl/Instance.hs @@ -2043,7 +2043,17 @@ tcMethodBody skol_info clas tyvars dfun_ev_vars inst_tys tcMethodBodyHelp sig_fn sel_id local_meth_id (L bind_loc lm_bind) ; global_meth_id <- addInlinePrags global_meth_id prags - ; spec_prags <- tcSpecPrags global_meth_id prags + ; spec_prags <- tcExtendIdEnv1 (idName sel_id) global_meth_id $ + -- tcExtendIdEnv1: tricky point: a SPECIALISE pragma in prags + -- mentions sel_name but the pragma is really for global_meth_id. + -- So we bind sel_name to global_meth_id, just in the pragmas. + -- Example: + -- instance C [a] where + -- op :: forall b. Ord b => b -> a -> a + -- {-# SPECIALISE op @Int #-} + -- The specialisation is for the `op` for this instance decl, not + -- for the global selector-id, of course. + tcSpecPrags global_meth_id prags ; let specs = mk_meth_spec_prags global_meth_id spec_inst_prags spec_prags export = ABE { abe_poly = global_meth_id @@ -2215,7 +2225,7 @@ mk_meth_spec_prags :: Id -> [LTcSpecPrag] -> [LTcSpecPrag] -> TcSpecPrags -- * spec_prags_from_inst: derived from {-# SPECIALISE instance :: <blah> #-} -- These ones have the dfun inside, but [perhaps surprisingly] -- the correct wrapper. - -- See Note [Handling SPECIALISE pragmas] in GHC.Tc.Gen.Bind + -- See Note [Handling old-form SPECIALISE pragmas] in GHC.Tc.Gen.Bind mk_meth_spec_prags meth_id spec_inst_prags spec_prags_for_me = SpecPrags (spec_prags_for_me ++ spec_prags_from_inst) where diff --git a/compiler/GHC/Tc/TyCl/PatSyn.hs b/compiler/GHC/Tc/TyCl/PatSyn.hs index ff1886ea777..57fea9b152b 100644 --- a/compiler/GHC/Tc/TyCl/PatSyn.hs +++ b/compiler/GHC/Tc/TyCl/PatSyn.hs @@ -37,8 +37,9 @@ import GHC.Tc.Types.Origin import GHC.Tc.TyCl.Build import GHC.Core.Multiplicity -import GHC.Core.Type ( typeKind, tidyForAllTyBinders, tidyTypes, tidyType, isManyTy, mkTYPEapp ) +import GHC.Core.Type ( typeKind, isManyTy, mkTYPEapp ) import GHC.Core.TyCo.Subst( extendTvSubstWithClone ) +import GHC.Core.TyCo.Tidy( tidyForAllTyBinders, tidyTypes, tidyType ) import GHC.Core.Predicate import GHC.Types.Name diff --git a/compiler/GHC/Tc/Types/Constraint.hs b/compiler/GHC/Tc/Types/Constraint.hs index d505260370d..5f5d1247fc5 100644 --- a/compiler/GHC/Tc/Types/Constraint.hs +++ b/compiler/GHC/Tc/Types/Constraint.hs @@ -2068,6 +2068,7 @@ checkSkolInfoAnon sk1 sk2 = go sk1 sk2 go FamInstSkol FamInstSkol = True go BracketSkol BracketSkol = True go (RuleSkol n1) (RuleSkol n2) = n1==n2 + go (SpecESkol n1) (SpecESkol n2) = n1==n2 go (PatSkol c1 _) (PatSkol c2 _) = getName c1 == getName c2 -- Too tedious to compare the HsMatchContexts go (InferSkol ids1) (InferSkol ids2) = equalLength ids1 ids2 && diff --git a/compiler/GHC/Tc/Types/Evidence.hs b/compiler/GHC/Tc/Types/Evidence.hs index b234c4627f7..032902e775c 100644 --- a/compiler/GHC/Tc/Types/Evidence.hs +++ b/compiler/GHC/Tc/Types/Evidence.hs @@ -15,7 +15,7 @@ module GHC.Tc.Types.Evidence ( -- * Evidence bindings TcEvBinds(..), EvBindsVar(..), - EvBindMap(..), emptyEvBindMap, extendEvBinds, + EvBindMap(..), emptyEvBindMap, extendEvBinds, unionEvBindMap, lookupEvBind, evBindMapBinds, foldEvBindMap, nonDetStrictFoldEvBindMap, filterEvBindMap, @@ -373,10 +373,15 @@ data EvBindsVar } instance Data.Data TcEvBinds where - -- Placeholder; we can't travers into TcEvBinds + -- Placeholder; we can't traverse into TcEvBinds toConstr _ = abstractConstr "TcEvBinds" gunfold _ _ = error "gunfold" dataTypeOf _ = Data.mkNoRepType "TcEvBinds" +instance Data.Data EvBind where + -- Placeholder; we can't traverse into EvBind + toConstr _ = abstractConstr "TcEvBind" + gunfold _ _ = error "gunfold" + dataTypeOf _ = Data.mkNoRepType "EvBind" {- Note [Coercion evidence only] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -428,6 +433,11 @@ extendEvBinds bs ev_bind (eb_lhs ev_bind) ev_bind } +-- | Union two evidence binding maps +unionEvBindMap :: EvBindMap -> EvBindMap -> EvBindMap +unionEvBindMap (EvBindMap env1) (EvBindMap env2) = + EvBindMap { ev_bind_varenv = plusDVarEnv env1 env2 } + isEmptyEvBindMap :: EvBindMap -> Bool isEmptyEvBindMap (EvBindMap m) = isEmptyDVarEnv m diff --git a/compiler/GHC/Tc/Types/Origin.hs b/compiler/GHC/Tc/Types/Origin.hs index 4220ef17672..4ed282188b7 100644 --- a/compiler/GHC/Tc/Types/Origin.hs +++ b/compiler/GHC/Tc/Types/Origin.hs @@ -130,8 +130,6 @@ data UserTypeCtxt | PatSigCtxt -- Type sig in pattern -- eg f (x::t) = ... -- or (x::t, y) = e - | RuleSigCtxt FastString Name -- LHS of a RULE forall - -- RULE "foo" forall (x :: a -> a). f (Just x) = ... | ForSigCtxt Name -- Foreign import or export signature | DefaultDeclCtxt -- Class or types in a default declaration | InstDeclCtxt Bool -- An instance declaration @@ -154,6 +152,9 @@ data UserTypeCtxt -- data <S> => T a = MkT a | DerivClauseCtxt -- A 'deriving' clause | TyVarBndrKindCtxt Name -- The kind of a type variable being bound + | RuleBndrTypeCtxt Name -- The type of a term variable being bound in a RULE + -- or SPECIALISE pragma + -- RULE "foo" forall (x :: a -> a). f (Just x) = ... | DataKindCtxt Name -- The kind of a data/newtype (instance) | TySynKindCtxt Name -- The kind of the RHS of a type synonym | TyFamResKindCtxt Name -- The result kind of a type family @@ -195,11 +196,10 @@ redundantConstraintsSpan _ = noSrcSpan pprUserTypeCtxt :: UserTypeCtxt -> SDoc -pprUserTypeCtxt (FunSigCtxt n _) = text "the type signature for" <+> quotes (ppr n) -pprUserTypeCtxt (InfSigCtxt n) = text "the inferred type for" <+> quotes (ppr n) -pprUserTypeCtxt (RuleSigCtxt _ n) = text "the type signature for" <+> quotes (ppr n) -pprUserTypeCtxt (ExprSigCtxt _) = text "an expression type signature" -pprUserTypeCtxt KindSigCtxt = text "a kind signature" +pprUserTypeCtxt (FunSigCtxt n _) = text "the type signature for" <+> quotes (ppr n) +pprUserTypeCtxt (InfSigCtxt n) = text "the inferred type for" <+> quotes (ppr n) +pprUserTypeCtxt (ExprSigCtxt _) = text "an expression type signature" +pprUserTypeCtxt KindSigCtxt = text "a kind signature" pprUserTypeCtxt (StandaloneKindSigCtxt n) = text "a standalone kind signature for" <+> quotes (ppr n) pprUserTypeCtxt TypeAppCtxt = text "a type argument" pprUserTypeCtxt (ConArgCtxt c) = text "the type of the constructor" <+> quotes (ppr c) @@ -218,6 +218,7 @@ pprUserTypeCtxt (DataTyCtxt tc) = text "the context of the data type declarati pprUserTypeCtxt (PatSynCtxt n) = text "the signature for pattern synonym" <+> quotes (ppr n) pprUserTypeCtxt (DerivClauseCtxt) = text "a `deriving' clause" pprUserTypeCtxt (TyVarBndrKindCtxt n) = text "the kind annotation on the type variable" <+> quotes (ppr n) +pprUserTypeCtxt (RuleBndrTypeCtxt n) = text "the type signature for" <+> quotes (ppr n) pprUserTypeCtxt (DataKindCtxt n) = text "the kind annotation on the declaration for" <+> quotes (ppr n) pprUserTypeCtxt (TySynKindCtxt n) = text "the kind annotation on the declaration for" <+> quotes (ppr n) pprUserTypeCtxt (TyFamResKindCtxt n) = text "the result kind for" <+> quotes (ppr n) @@ -299,6 +300,7 @@ data SkolemInfoAnon | IPSkol [HsIPName] -- Binding site of an implicit parameter | RuleSkol RuleName -- The LHS of a RULE + | SpecESkol Name -- A SPECIALISE pragma | InferSkol [(Name,TcType)] -- We have inferred a type for these (mutually recursive) @@ -370,6 +372,7 @@ pprSkolInfo (InstSkol (IsQC {}) sz) = vcat [ text "a quantified context" pprSkolInfo FamInstSkol = text "a family instance declaration" pprSkolInfo BracketSkol = text "a Template Haskell bracket" pprSkolInfo (RuleSkol name) = text "the RULE" <+> pprRuleName name +pprSkolInfo (SpecESkol name) = text "a SPECIALISE pragma for" <+> quotes (ppr name) pprSkolInfo (PatSkol cl mc) = sep [ pprPatSkolInfo cl , text "in" <+> pprMatchContext mc ] pprSkolInfo (InferSkol ids) = hang (text "the inferred type" <> plural ids <+> text "of") diff --git a/compiler/GHC/Tc/Utils/Instantiate.hs b/compiler/GHC/Tc/Utils/Instantiate.hs index d362c37d38e..1e5da18eaa4 100644 --- a/compiler/GHC/Tc/Utils/Instantiate.hs +++ b/compiler/GHC/Tc/Utils/Instantiate.hs @@ -55,6 +55,7 @@ import GHC.Core.FamInstEnv import GHC.Core ( isOrphan ) -- For the Coercion constructor import GHC.Core.Type import GHC.Core.TyCo.Ppr ( debugPprType ) +import GHC.Core.TyCo.Tidy ( tidyType ) import GHC.Core.Class( Class ) import GHC.Core.Coercion.Axiom diff --git a/compiler/GHC/Tc/Utils/Monad.hs b/compiler/GHC/Tc/Utils/Monad.hs index 2458cd62bba..756649b3451 100644 --- a/compiler/GHC/Tc/Utils/Monad.hs +++ b/compiler/GHC/Tc/Utils/Monad.hs @@ -98,7 +98,7 @@ module GHC.Tc.Utils.Monad( -- * Type constraints newTcEvBinds, newNoTcEvBinds, cloneEvBindsVar, - addTcEvBind, addTopEvBinds, + addTcEvBind, addTcEvBinds, addTopEvBinds, getTcEvTyCoVars, getTcEvBindsMap, setTcEvBindsMap, chooseUniqueOccTc, getConstraintVar, setConstraintVar, @@ -1794,6 +1794,19 @@ addTcEvBind (EvBindsVar { ebv_binds = ev_ref, ebv_uniq = u }) ev_bind addTcEvBind (CoEvBindsVar { ebv_uniq = u }) ev_bind = pprPanic "addTcEvBind CoEvBindsVar" (ppr ev_bind $$ ppr u) +addTcEvBinds :: EvBindsVar -> EvBindMap -> TcM () +-- ^ Add a collection of binding to the TcEvBinds by side effect +addTcEvBinds _ new_ev_binds + | isEmptyEvBindMap new_ev_binds + = return () +addTcEvBinds (EvBindsVar { ebv_binds = ev_ref, ebv_uniq = u }) new_ev_binds + = do { traceTc "addTcEvBinds" $ ppr u $$ + ppr new_ev_binds + ; old_bnds <- readTcRef ev_ref + ; writeTcRef ev_ref (old_bnds `unionEvBindMap` new_ev_binds) } +addTcEvBinds (CoEvBindsVar { ebv_uniq = u }) new_ev_binds + = pprPanic "addTcEvBinds CoEvBindsVar" (ppr new_ev_binds $$ ppr u) + chooseUniqueOccTc :: (OccSet -> OccName) -> TcM OccName chooseUniqueOccTc fn = do { env <- getGblEnv diff --git a/compiler/GHC/Tc/Utils/TcMType.hs b/compiler/GHC/Tc/Utils/TcMType.hs index fb3d2ffcc33..0bba873b65c 100644 --- a/compiler/GHC/Tc/Utils/TcMType.hs +++ b/compiler/GHC/Tc/Utils/TcMType.hs @@ -83,7 +83,7 @@ module GHC.Tc.Utils.TcMType ( candidateQTyVarsOfType, candidateQTyVarsOfKind, candidateQTyVarsOfTypes, candidateQTyVarsOfKinds, - candidateQTyVarsWithBinders, + candidateQTyVarsWithBinders, weedOutCandidates, CandidatesQTvs(..), delCandidates, candidateKindVars, partitionCandidates, @@ -123,6 +123,7 @@ import GHC.Core.ConLike import GHC.Core.DataCon import GHC.Core.TyCo.Rep import GHC.Core.TyCo.Ppr +import GHC.Core.TyCo.Tidy import GHC.Core.Type import GHC.Core.TyCon import GHC.Core.Coercion @@ -1342,6 +1343,10 @@ instance Outputable CandidatesQTvs where , text "dv_tvs =" <+> ppr tvs , text "dv_cvs =" <+> ppr cvs ]) +weedOutCandidates :: (DTyVarSet -> DTyVarSet) -> CandidatesQTvs -> CandidatesQTvs +weedOutCandidates weed_out dv@(DV { dv_kvs = kvs, dv_tvs = tvs }) + = dv { dv_kvs = weed_out kvs, dv_tvs = weed_out tvs } + isEmptyCandidates :: CandidatesQTvs -> Bool isEmptyCandidates (DV { dv_kvs = kvs, dv_tvs = tvs }) = isEmptyDVarSet kvs && isEmptyDVarSet tvs diff --git a/compiler/GHC/Tc/Validity.hs b/compiler/GHC/Tc/Validity.hs index 1f1826b99f7..174cb33f91e 100644 --- a/compiler/GHC/Tc/Validity.hs +++ b/compiler/GHC/Tc/Validity.hs @@ -49,6 +49,7 @@ import GHC.Core.Predicate import GHC.Core.TyCo.FVs import GHC.Core.TyCo.Rep import GHC.Core.TyCo.Ppr +import GHC.Core.TyCo.Tidy import GHC.Core.FamInstEnv ( isDominatedBy, injectiveBranches , InjectivityCheckResult(..) ) @@ -371,7 +372,6 @@ checkValidType ctxt ty = case ctxt of DefaultDeclCtxt-> MustBeMonoType PatSigCtxt -> rank0 - RuleSigCtxt {} -> rank1 TySynCtxt _ -> rank0 ExprSigCtxt {} -> rank1 @@ -395,10 +395,11 @@ checkValidType ctxt ty SpecInstCtxt -> rank1 GhciCtxt {} -> ArbitraryRank - TyVarBndrKindCtxt _ -> rank0 - DataKindCtxt _ -> rank1 - TySynKindCtxt _ -> rank1 - TyFamResKindCtxt _ -> rank1 + TyVarBndrKindCtxt {} -> rank0 + RuleBndrTypeCtxt{} -> rank1 + DataKindCtxt _ -> rank1 + TySynKindCtxt _ -> rank1 + TyFamResKindCtxt _ -> rank1 _ -> panic "checkValidType" -- Can't happen; not used for *user* sigs @@ -532,7 +533,7 @@ typeOrKindCtxt (ExprSigCtxt {}) = OnlyTypeCtxt typeOrKindCtxt (TypeAppCtxt {}) = OnlyTypeCtxt typeOrKindCtxt (PatSynCtxt {}) = OnlyTypeCtxt typeOrKindCtxt (PatSigCtxt {}) = OnlyTypeCtxt -typeOrKindCtxt (RuleSigCtxt {}) = OnlyTypeCtxt +typeOrKindCtxt (RuleBndrTypeCtxt {})= OnlyTypeCtxt typeOrKindCtxt (ForSigCtxt {}) = OnlyTypeCtxt typeOrKindCtxt (DefaultDeclCtxt {}) = OnlyTypeCtxt typeOrKindCtxt (InstDeclCtxt {}) = OnlyTypeCtxt @@ -1454,7 +1455,7 @@ okIPCtxt (StandaloneKindSigCtxt {}) = False okIPCtxt (ClassSCCtxt {}) = False okIPCtxt (InstDeclCtxt {}) = False okIPCtxt (SpecInstCtxt {}) = False -okIPCtxt (RuleSigCtxt {}) = False +okIPCtxt (RuleBndrTypeCtxt {}) = False okIPCtxt DefaultDeclCtxt = False okIPCtxt DerivClauseCtxt = False okIPCtxt (TyVarBndrKindCtxt {}) = False diff --git a/compiler/GHC/Tc/Zonk/Env.hs b/compiler/GHC/Tc/Zonk/Env.hs index d31f6d30023..0f8255c4a54 100644 --- a/compiler/GHC/Tc/Zonk/Env.hs +++ b/compiler/GHC/Tc/Zonk/Env.hs @@ -61,10 +61,14 @@ data ZonkEnv -- -- See Note [Un-unified unification variables] data ZonkFlexi - = DefaultFlexi -- ^ Default unbound unification variables to Any - | SkolemiseFlexi -- ^ Skolemise unbound unification variables - -- See Note [Zonking the LHS of a RULE] + = DefaultFlexi -- ^ Default unbound unification variables to Any + + | SkolemiseFlexi -- ^ Skolemise unbound unification variables + (IORef [TyVar]) -- See Note [Zonking the LHS of a RULE] + -- Records the tyvars thus skolemised + | RuntimeUnkFlexi -- ^ Used in the GHCi debugger + | NoFlexi -- ^ Panic on unfilled meta-variables -- See Note [Error on unconstrained meta-variables] -- in GHC.Tc.Utils.TcMType diff --git a/compiler/GHC/Tc/Zonk/TcType.hs b/compiler/GHC/Tc/Zonk/TcType.hs index f6fd4c73325..68d957bac07 100644 --- a/compiler/GHC/Tc/Zonk/TcType.hs +++ b/compiler/GHC/Tc/Zonk/TcType.hs @@ -75,6 +75,7 @@ import GHC.Tc.Zonk.Monad import GHC.Core.InstEnv (ClsInst(is_tys)) import GHC.Core.TyCo.Rep +import GHC.Core.TyCo.Tidy import GHC.Core.TyCon import GHC.Core.Type import GHC.Core.Coercion diff --git a/compiler/GHC/Tc/Zonk/Type.hs b/compiler/GHC/Tc/Zonk/Type.hs index dd77e25039c..07a70244d6e 100644 --- a/compiler/GHC/Tc/Zonk/Type.hs +++ b/compiler/GHC/Tc/Zonk/Type.hs @@ -381,7 +381,7 @@ zonkTyVarBinderX (Bndr tv vis) zonkTyVarOcc :: HasDebugCallStack => TcTyVar -> ZonkTcM Type zonkTyVarOcc tv - = do { ZonkEnv { ze_tv_env = tv_env } <- getZonkEnv + = do { ZonkEnv { ze_tv_env = tv_env, ze_flexi = zonk_flexi } <- getZonkEnv ; let lookup_in_tv_env -- Look up in the env just as we do for Ids = case lookupVarEnv tv_env tv of @@ -395,7 +395,7 @@ zonkTyVarOcc tv zonk_meta ref Flexi = do { kind <- zonkTcTypeToTypeX (tyVarKind tv) - ; ty <- commitFlexi tv kind + ; ty <- lift $ commitFlexi zonk_flexi tv kind ; lift $ liftZonkM $ writeMetaTyVarRef tv ref ty -- Belt and braces ; finish_meta ty } @@ -443,50 +443,47 @@ lookupTyVarX tv Nothing -> pprPanic "lookupTyVarOcc" (ppr tv $$ ppr tv_env) ; return res } -commitFlexi :: TcTyVar -> Kind -> ZonkTcM Type -commitFlexi tv zonked_kind - = do { flexi <- ze_flexi <$> getZonkEnv - ; lift $ case flexi of - SkolemiseFlexi -> return (mkTyVarTy (mkTyVar name zonked_kind)) - - DefaultFlexi - -- Normally, RuntimeRep variables are defaulted in GHC.Tc.Utils.TcMType.defaultTyVar - -- But that sees only type variables that appear in, say, an inferred type. - -- Defaulting here, in the zonker, is needed to catch e.g. - -- y :: Bool - -- y = (\x -> True) undefined - -- We need *some* known RuntimeRep for the x and undefined, but no one - -- will choose it until we get here, in the zonker. - | isRuntimeRepTy zonked_kind - -> do { traceTc "Defaulting flexi tyvar to LiftedRep:" (pprTyVar tv) - ; return liftedRepTy } - | isLevityTy zonked_kind - -> do { traceTc "Defaulting flexi tyvar to Lifted:" (pprTyVar tv) - ; return liftedDataConTy } - | isMultiplicityTy zonked_kind - -> do { traceTc "Defaulting flexi tyvar to Many:" (pprTyVar tv) - ; return manyDataConTy } - | Just (ConcreteFRR origin) <- isConcreteTyVar_maybe tv - -> do { addErr $ TcRnZonkerMessage (ZonkerCannotDefaultConcrete origin) - ; return (anyTypeOfKind zonked_kind) } - | otherwise - -> do { traceTc "Defaulting flexi tyvar to ZonkAny:" (pprTyVar tv) - -- See Note [Any types] in GHC.Builtin.Types, esp wrinkle (Any4) - ; newZonkAnyType zonked_kind } - - RuntimeUnkFlexi - -> do { traceTc "Defaulting flexi tyvar to RuntimeUnk:" (pprTyVar tv) - ; return (mkTyVarTy (mkTcTyVar name zonked_kind RuntimeUnk)) } - -- This is where RuntimeUnks are born: - -- otherwise-unconstrained unification variables are - -- turned into RuntimeUnks as they leave the - -- typechecker's monad - - NoFlexi -> pprPanic "NoFlexi" (ppr tv <+> dcolon <+> ppr zonked_kind) } - - where - name = tyVarName tv - +commitFlexi :: ZonkFlexi -> TcTyVar -> Kind -> TcM Type +commitFlexi NoFlexi tv zonked_kind + = pprPanic "NoFlexi" (ppr tv <+> dcolon <+> ppr zonked_kind) + +commitFlexi (SkolemiseFlexi tvs_ref) tv zonked_kind + = do { let skol_tv = mkTyVar (tyVarName tv) zonked_kind + ; updTcRef tvs_ref (skol_tv :) + ; return (mkTyVarTy skol_tv) } + +commitFlexi RuntimeUnkFlexi tv zonked_kind + = do { traceTc "Defaulting flexi tyvar to RuntimeUnk:" (pprTyVar tv) + ; return (mkTyVarTy (mkTcTyVar (tyVarName tv) zonked_kind RuntimeUnk)) } + -- This is where RuntimeUnks are born: + -- otherwise-unconstrained unification variables are + -- turned into RuntimeUnks as they leave the + -- typechecker's monad + +commitFlexi DefaultFlexi tv zonked_kind + -- Normally, RuntimeRep variables are defaulted in GHC.Tc.Utils.TcMType.defaultTyVar + -- But that sees only type variables that appear in, say, an inferred type. + -- Defaulting here, in the zonker, is needed to catch e.g. + -- y :: Bool + -- y = (\x -> True) undefined + -- We need *some* known RuntimeRep for the x and undefined, but no one + -- will choose it until we get here, in the zonker. + | isRuntimeRepTy zonked_kind + = do { traceTc "Defaulting flexi tyvar to LiftedRep:" (pprTyVar tv) + ; return liftedRepTy } + | isLevityTy zonked_kind + = do { traceTc "Defaulting flexi tyvar to Lifted:" (pprTyVar tv) + ; return liftedDataConTy } + | isMultiplicityTy zonked_kind + = do { traceTc "Defaulting flexi tyvar to Many:" (pprTyVar tv) + ; return manyDataConTy } + | Just (ConcreteFRR origin) <- isConcreteTyVar_maybe tv + = do { addErr $ TcRnZonkerMessage (ZonkerCannotDefaultConcrete origin) + ; return (anyTypeOfKind zonked_kind) } + | otherwise + = do { traceTc "Defaulting flexi tyvar to ZonkAny:" (pprTyVar tv) + -- See Note [Any types] in GHC.Builtin.Types, esp wrinkle (Any4) + ; newZonkAnyType zonked_kind } zonkCoVarOcc :: CoVar -> ZonkTcM Coercion zonkCoVarOcc cv @@ -855,9 +852,25 @@ zonkLTcSpecPrags ps = mapM zonk_prag ps where zonk_prag (L loc (SpecPrag id co_fn inl)) - = do { co_fn' <- don'tBind $ zonkCoFn co_fn - ; id' <- zonkIdOcc id - ; return (L loc (SpecPrag id' co_fn' inl)) } + = do { co_fn' <- don'tBind $ zonkCoFn co_fn + ; id' <- zonkIdOcc id + ; return (L loc (SpecPrag id' co_fn' inl)) } + zonk_prag (L loc prag@(SpecPragE { spe_fn_id = poly_id + , spe_bndrs = bndrs + , spe_call = spec_e })) + = do { poly_id' <- zonkIdOcc poly_id + + ; skol_tvs_ref <- lift $ newTcRef [] + ; setZonkType (SkolemiseFlexi skol_tvs_ref) $ + -- SkolemiseFlexi: see Note [Free tyvars on rule LHS] + runZonkBndrT (zonkCoreBndrsX bndrs) $ \ bndrs' -> + do { spec_e' <- zonkLExpr spec_e + ; skol_tvs <- lift $ readTcRef skol_tvs_ref + ; return (L loc (prag { spe_fn_id = poly_id' + , spe_bndrs = skol_tvs ++ bndrs' + , spe_call = spec_e' + })) + }} {- ************************************************************************ @@ -1671,30 +1684,73 @@ zonkRules :: [LRuleDecl GhcTc] -> ZonkTcM [LRuleDecl GhcTc] zonkRules rs = mapM (wrapLocZonkMA zonkRule) rs zonkRule :: RuleDecl GhcTc -> ZonkTcM (RuleDecl GhcTc) -zonkRule rule@(HsRule { rd_tmvs = tm_bndrs{-::[RuleBndr TcId]-} +zonkRule rule@(HsRule { rd_bndrs = bndrs , rd_lhs = lhs , rd_rhs = rhs }) - = runZonkBndrT (traverse zonk_tm_bndr tm_bndrs) $ \ new_tm_bndrs -> - do { -- See Note [Zonking the LHS of a RULE] - ; new_lhs <- setZonkType SkolemiseFlexi $ zonkLExpr lhs - ; new_rhs <- zonkLExpr rhs - ; return $ rule { rd_tmvs = new_tm_bndrs - , rd_lhs = new_lhs - , rd_rhs = new_rhs } } + = do { skol_tvs_ref <- lift $ newTcRef [] + ; setZonkType (SkolemiseFlexi skol_tvs_ref) $ + -- setZonkType: see Note [Free tyvars on rule LHS] + zonkRuleBndrs bndrs $ \ new_bndrs -> + do { new_lhs <- zonkLExpr lhs + ; skol_tvs <- lift $ readTcRef skol_tvs_ref + ; new_rhs <- setZonkType DefaultFlexi $ zonkLExpr rhs + ; return $ rule { rd_bndrs = add_tvs skol_tvs new_bndrs + , rd_lhs = new_lhs + , rd_rhs = new_rhs } } } + where + add_tvs :: [TyVar] -> RuleBndrs GhcTc -> RuleBndrs GhcTc + add_tvs tvs rbs@(RuleBndrs { rb_ext = bndrs }) = rbs { rb_ext = tvs ++ bndrs } + + +zonkRuleBndrs :: RuleBndrs GhcTc -> (RuleBndrs GhcTc -> ZonkTcM a) -> ZonkTcM a +zonkRuleBndrs rb@(RuleBndrs { rb_ext = bndrs }) thing_inside + = runZonkBndrT (traverse zonk_it bndrs) $ \ new_bndrs -> + thing_inside (rb { rb_ext = new_bndrs }) where - zonk_tm_bndr :: LRuleBndr GhcTc -> ZonkBndrTcM (LRuleBndr GhcTc) - zonk_tm_bndr (L l (RuleBndr x (L loc v))) - = do { v' <- zonk_it v - ; return (L l (RuleBndr x (L loc v'))) } - zonk_tm_bndr (L _ (RuleBndrSig {})) = panic "zonk_tm_bndr RuleBndrSig" - - zonk_it v - | isId v = zonkIdBndrX v - | otherwise = assert (isImmutableTyVar v) - zonkTyBndrX v - -- DV: used to be "return v", but that is plain - -- wrong because we may need to go inside the kind - -- of v and zonk there! + zonk_it v + | isId v = zonkIdBndrX v + | otherwise = assert (isImmutableTyVar v) $ + zonkTyBndrX v + -- We may need to go inside the kind of v and zonk there! + +{- Note [Free tyvars on rule LHS] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Consider + data T a = C + + foo :: T a -> Int + foo C = 1 + + {-# RULES "myrule" foo C = 1 #-} + +After type checking the LHS becomes (foo alpha (C alpha)), where alpha +is an unbound meta-tyvar. The zonker in GHC.Tc.Zonk.Type is careful not to +turn the free alpha into Any (as it usually does). Instead we want to quantify +over it. Here is how: + +* We set the ze_flexi field of ZonkEnv to (SkolemiseFlexi ref), to tell the + zonker to zonk a Flexi meta-tyvar to a TyVar, not to Any. See the + SkolemiseFlexi case of `commitFlexi`. + +* Here (ref :: TcRef [TyVar]) collects the type variables thus skolemised; + again see `commitFlexi`. + +* When zonking a RULE, in `zonkRule` we + - make a fresh ref-cell to collect the skolemised type variables, + - zonk the binders and LHS with ze_flexi = SkolemiseFlexi ref + - read the ref-cell to get all the skolemised TyVars + - add them to the binders + +All this applies for SPECIALISE pragmas too. + +Wrinkles: + +(FTV1) We just add the new tyvars to the front of the binder-list, but + that may make the list not be in dependency order. Example (T12925): + the existing list is [k:Type, b:k], and we add (a:k) to the front. + Also we just collect the new skolemised type variables in any old order, + so they may not be ordered with respect to each other. +-} {- ************************************************************************ diff --git a/compiler/GHC/ThToHs.hs b/compiler/GHC/ThToHs.hs index 58da4b053b9..e8d3a878aa2 100644 --- a/compiler/GHC/ThToHs.hs +++ b/compiler/GHC/ThToHs.hs @@ -913,21 +913,7 @@ cvtPragmaD (OpaqueP nm) cvtPragmaD (SpecialiseP nm ty inline phases) = do { nm' <- vNameN nm ; ty' <- cvtSigType ty - ; let src TH.NoInline = fsLit "{-# SPECIALISE NOINLINE" - src TH.Inline = fsLit "{-# SPECIALISE INLINE" - src TH.Inlinable = fsLit "{-# SPECIALISE INLINE" - ; let (inline', dflt, srcText) = case inline of - Just inline1 -> (cvtInline inline1 (toSrcTxt inline1), dfltActivation inline1, - toSrcTxt inline1) - Nothing -> (NoUserInlinePrag, AlwaysActive, - SourceText $ fsLit "{-# SPECIALISE") - where - toSrcTxt a = SourceText $ src a - ; let ip = InlinePragma { inl_src = srcText - , inl_inline = inline' - , inl_rule = Hs.FunLike - , inl_act = cvtPhases phases dflt - , inl_sat = Nothing } + ; let ip = cvtInlinePhases inline phases ; returnJustLA $ Hs.SigD noExtField $ SpecSig noAnn nm' [ty'] ip } cvtPragmaD (SpecialiseInstP ty) @@ -935,6 +921,16 @@ cvtPragmaD (SpecialiseInstP ty) ; returnJustLA $ Hs.SigD noExtField $ SpecInstSig (noAnn, (SourceText $ fsLit "{-# SPECIALISE")) ty' } +cvtPragmaD (SpecialiseEP ty_bndrs tm_bndrs exp inline phases) + = do { ty_bndrs' <- traverse cvtTvs ty_bndrs + ; tm_bndrs' <- mapM cvtRuleBndr tm_bndrs + ; let ip = cvtInlinePhases inline phases + ; exp' <- cvtl exp + ; let bndrs' = RuleBndrs { rb_ext = noAnn, rb_tyvs = ty_bndrs', rb_tmvs = tm_bndrs' } + ; returnJustLA $ Hs.SigD noExtField $ + SpecSigE noAnn bndrs' exp' ip + } + cvtPragmaD (RuleP nm ty_bndrs tm_bndrs lhs rhs phases) = do { let nm' = mkFastString nm ; rd_name' <- returnLA nm' @@ -947,8 +943,7 @@ cvtPragmaD (RuleP nm ty_bndrs tm_bndrs lhs rhs phases) HsRule { rd_ext = (noAnn, quotedSourceText nm) , rd_name = rd_name' , rd_act = act - , rd_tyvs = ty_bndrs' - , rd_tmvs = tm_bndrs' + , rd_bndrs = RuleBndrs { rb_ext = noAnn, rb_tyvs = ty_bndrs', rb_tmvs = tm_bndrs' } , rd_lhs = lhs' , rd_rhs = rhs' } ; returnJustLA $ Hs.RuleD noExtField @@ -1016,6 +1011,24 @@ cvtRuleBndr (TypedRuleVar n ty) ; ty' <- cvtType ty ; returnLA $ Hs.RuleBndrSig noAnn n' $ mkHsPatSigType noAnn ty' } +cvtInlinePhases :: Maybe Inline -> Phases -> InlinePragma +cvtInlinePhases inline phases = + let src TH.NoInline = fsLit "{-# SPECIALISE NOINLINE" + src TH.Inline = fsLit "{-# SPECIALISE INLINE" + src TH.Inlinable = fsLit "{-# SPECIALISE INLINE" + (inline', dflt, srcText) = case inline of + Just inline1 -> (cvtInline inline1 (toSrcTxt inline1), dfltActivation inline1, + toSrcTxt inline1) + Nothing -> (NoUserInlinePrag, AlwaysActive, + SourceText $ fsLit "{-# SPECIALISE") + where + toSrcTxt a = SourceText $ src a + in InlinePragma { inl_src = srcText + , inl_inline = inline' + , inl_rule = Hs.FunLike + , inl_act = cvtPhases phases dflt + , inl_sat = Nothing } + --------------------------------------------------- -- Declarations --------------------------------------------------- diff --git a/compiler/GHC/Types/Basic.hs b/compiler/GHC/Types/Basic.hs index 5e542cf6662..efe560cb76e 100644 --- a/compiler/GHC/Types/Basic.hs +++ b/compiler/GHC/Types/Basic.hs @@ -1785,12 +1785,7 @@ inlinePragmaSpec :: InlinePragma -> InlineSpec inlinePragmaSpec = inl_inline inlinePragmaSource :: InlinePragma -> SourceText -inlinePragmaSource prag = case inl_inline prag of - Inline x -> x - Inlinable y -> y - NoInline z -> z - Opaque q -> q - NoUserInlinePrag -> NoSourceText +inlinePragmaSource prag = inlineSpecSource (inl_inline prag) inlineSpecSource :: InlineSpec -> SourceText inlineSpecSource spec = case spec of diff --git a/compiler/GHC/Types/Error/Codes.hs b/compiler/GHC/Types/Error/Codes.hs index 0b68e4cf8fa..c2c09c04bf5 100644 --- a/compiler/GHC/Types/Error/Codes.hs +++ b/compiler/GHC/Types/Error/Codes.hs @@ -31,15 +31,17 @@ import GHC.Core.InstEnv ( LookupInstanceErrReason ) import GHC.Hs.Extension ( GhcRn ) import GHC.Types.Error ( DiagnosticCode(..), UnknownDiagnostic (..) , diagnosticCode, UnknownDiagnosticFor ) -import GHC.Unit.Module.Warnings ( WarningTxt ) -import GHC.Utils.Panic.Plain --- Import all the structured error data types -import GHC.Driver.Errors.Types ( DriverMessage, GhcMessage ) -import GHC.HsToCore.Errors.Types ( DsMessage ) import GHC.Iface.Errors.Types +import GHC.Driver.Errors.Types ( DriverMessage ) import GHC.Parser.Errors.Types ( PsMessage, PsHeaderMessage ) +import GHC.HsToCore.Errors.Types ( DsMessage, UselessSpecialisePragmaReason ) import GHC.Tc.Errors.Types +import GHC.Unit.Module.Warnings ( WarningTxt ) +import GHC.Utils.Panic.Plain + +-- Import all the structured error data types +import GHC.Driver.Errors.Types ( GhcMessage ) import Data.Kind ( Type, Constraint ) import GHC.Exts ( proxy# ) @@ -220,8 +222,6 @@ type family GhcDiagnosticCode c = n | n -> c where GhcDiagnosticCode "DsMaxPmCheckModelsReached" = 61505 GhcDiagnosticCode "DsNonExhaustivePatterns" = 62161 GhcDiagnosticCode "DsTopLevelBindsNotAllowed" = 48099 - GhcDiagnosticCode "DsUselessSpecialiseForClassMethodSelector" = 93315 - GhcDiagnosticCode "DsUselessSpecialiseForNoInlineFunction" = 38524 GhcDiagnosticCode "DsOrphanRule" = 58181 GhcDiagnosticCode "DsRuleLhsTooComplicated" = 69441 GhcDiagnosticCode "DsRuleIgnoredDueToConstructor" = 00828 @@ -238,6 +238,10 @@ type family GhcDiagnosticCode c = n | n -> c where GhcDiagnosticCode "DsAnotherRuleMightFireFirst" = 87502 GhcDiagnosticCode "DsIncompleteRecordSelector" = 17335 + -- Constructors of 'UselessSpecialisePragmaReason' + GhcDiagnosticCode "UselessSpecialiseForClassMethodSelector" = 93315 + GhcDiagnosticCode "UselessSpecialiseForNoInlineFunction" = 38524 + GhcDiagnosticCode "UselessSpecialiseNoSpecialisation" = 66582 -- Parser diagnostic codes GhcDiagnosticCode "PsErrParseLanguagePragma" = 68686 @@ -363,6 +367,8 @@ type family GhcDiagnosticCode c = n | n -> c where GhcDiagnosticCode "PsErrInvalidPun" = 52943 GhcDiagnosticCode "PsErrIllegalOrPat" = 29847 GhcDiagnosticCode "PsErrTypeSyntaxInPat" = 32181 + GhcDiagnosticCode "PsErrSpecExprMultipleTypeAscription" = 62037 + GhcDiagnosticCode "PsWarnSpecMultipleTypeAscription" = 73026 -- Driver diagnostic codes GhcDiagnosticCode "DriverMissingHomeModules" = 32850 @@ -598,6 +604,7 @@ type family GhcDiagnosticCode c = n | n -> c where GhcDiagnosticCode "TcRnMisplacedSigDecl" = 87866 GhcDiagnosticCode "TcRnUnexpectedDefaultSig" = 40700 GhcDiagnosticCode "TcRnDuplicateMinimalSig" = 85346 + GhcDiagnosticCode "TcRnSpecSigShape" = 93944 GhcDiagnosticCode "TcRnLoopySuperclassSolve" = Outdated 36038 GhcDiagnosticCode "TcRnUnexpectedStandaloneDerivingDecl" = 95159 GhcDiagnosticCode "TcRnUnusedVariableInRuleDecl" = 65669 @@ -1050,6 +1057,11 @@ type family ConRecursInto con where ConRecursInto "PsUnknownMessage" = 'Just (UnknownDiagnosticFor PsMessage) ConRecursInto "PsHeaderMessage" = 'Just PsHeaderMessage + ---------------------------------- + -- Constructors of DsMessage + + ConRecursInto "DsUselessSpecialisePragma" = 'Just UselessSpecialisePragmaReason + ---------------------------------- -- Constructors of TcRnMessage diff --git a/compiler/GHC/Types/Hint.hs b/compiler/GHC/Types/Hint.hs index 33bfc23db4e..2c5db7b1815 100644 --- a/compiler/GHC/Types/Hint.hs +++ b/compiler/GHC/Types/Hint.hs @@ -504,6 +504,11 @@ data GhcHint {-| Suggest add parens to pattern `e -> p :: t` -} | SuggestParenthesizePatternRHS + {-| Suggest splitting up a SPECIALISE pragmas with multiple type ascriptions + into several individual SPECIALISE pragmas. + -} + | SuggestSplittingIntoSeveralSpecialisePragmas + -- | The deriving strategy that was assumed when not explicitly listed in the -- source. This is used solely by the missing-deriving-strategies warning. -- There's no `Via` case because we never assume that. diff --git a/compiler/GHC/Types/Hint/Ppr.hs b/compiler/GHC/Types/Hint/Ppr.hs index 25a17e32a07..a20fe7475cd 100644 --- a/compiler/GHC/Types/Hint/Ppr.hs +++ b/compiler/GHC/Types/Hint/Ppr.hs @@ -288,6 +288,8 @@ instance Outputable GhcHint where (hsep [text "deriving", ppr strat, text "instance", ppr deriv_sig]) SuggestParenthesizePatternRHS -> text "Parenthesize the RHS of the view pattern" + SuggestSplittingIntoSeveralSpecialisePragmas + -> text "Split the SPECIALISE pragma into multiple pragmas, one for each type signature" perhapsAsPat :: SDoc perhapsAsPat = text "Perhaps you meant an as-pattern, which must not be surrounded by whitespace" diff --git a/compiler/GHC/Types/Var.hs b/compiler/GHC/Types/Var.hs index 51000d7ccfd..b337f82d2d5 100644 --- a/compiler/GHC/Types/Var.hs +++ b/compiler/GHC/Types/Var.hs @@ -572,7 +572,7 @@ information. In (FunTy { ft_af = af, ft_arg = t1, ft_res = t2 }) False True FTF_T_C True False FTF_C_T True True FTF_C_C -where isPredTy is defined in GHC.Core.Type, and sees if t1's +where isPredTy is defined in GHC.Core.Predicate, and sees if t1's kind is Constraint. See GHC.Core.Type.chooseFunTyFlag, and GHC.Core.TyCo.Rep Note [Types for coercions, predicates, and evidence] diff --git a/compiler/Language/Haskell/Syntax/Binds.hs b/compiler/Language/Haskell/Syntax/Binds.hs index ef917518093..b298916c8e9 100644 --- a/compiler/Language/Haskell/Syntax/Binds.hs +++ b/compiler/Language/Haskell/Syntax/Binds.hs @@ -355,9 +355,11 @@ data Sig pass (LIdP pass) -- Function name InlinePragma -- Never defaultInlinePragma - -- | A specialisation pragma + -- | An old-form specialisation pragma -- -- > {-# SPECIALISE f :: Int -> Int #-} + -- + -- NB: this constructor is deprecated and will be removed in GHC 9.18 (#25540) | SpecSig (XSpecSig pass) (LIdP pass) -- Specialise a function or datatype ... [LHsSigType pass] -- ... to these types @@ -365,6 +367,17 @@ data Sig pass -- If it's just defaultInlinePragma, then we said -- SPECIALISE, not SPECIALISE_INLINE + -- | A new-form specialisation pragma (see GHC Proposal #493) + -- e.g. {-# SPECIALISE f @Int 1 :: Int -> Int #-} + -- See Note [Overview of SPECIALISE pragmas] + | SpecSigE (XSpecSigE pass) + (RuleBndrs pass) + (LHsExpr pass) -- Expression to specialise + InlinePragma + -- The expression should be of form + -- f a1 ... an [ :: sig ] + -- with an optional type signature + -- | A specialisation pragma for instance declarations only -- -- > {-# SPECIALISE instance Eq [Int] #-} @@ -419,8 +432,9 @@ isTypeLSig (unXRec @p -> XSig {}) = True isTypeLSig _ = False isSpecLSig :: forall p. UnXRec p => LSig p -> Bool -isSpecLSig (unXRec @p -> SpecSig {}) = True -isSpecLSig _ = False +isSpecLSig (unXRec @p -> SpecSig {}) = True +isSpecLSig (unXRec @p -> SpecSigE {}) = True +isSpecLSig _ = False isSpecInstLSig :: forall p. UnXRec p => LSig p -> Bool isSpecInstLSig (unXRec @p -> SpecInstSig {}) = True @@ -429,6 +443,7 @@ isSpecInstLSig _ = False isPragLSig :: forall p. UnXRec p => LSig p -> Bool -- Identifies pragmas isPragLSig (unXRec @p -> SpecSig {}) = True +isPragLSig (unXRec @p -> SpecSigE {}) = True isPragLSig (unXRec @p -> InlineSig {}) = True isPragLSig (unXRec @p -> SCCFunSig {}) = True isPragLSig (unXRec @p -> CompleteMatchSig {}) = True @@ -451,6 +466,40 @@ isCompleteMatchSig :: forall p. UnXRec p => LSig p -> Bool isCompleteMatchSig (unXRec @p -> CompleteMatchSig {} ) = True isCompleteMatchSig _ = False +{- ********************************************************************* +* * + Rule binders +* * +********************************************************************* -} + +data RuleBndrs pass = RuleBndrs + { rb_ext :: XCRuleBndrs pass + -- After typechecking rb_ext contains /all/ the quantified variables + -- both term variables and type varibles + , rb_tyvs :: Maybe [LHsTyVarBndr () (NoGhcTc pass)] + -- ^ User-written forall'd type vars; preserved for pretty-printing + , rb_tmvs :: [LRuleBndr (NoGhcTc pass)] + -- ^ User-written forall'd term vars; preserved for pretty-printing + } + | XRuleBndrs !(XXRuleBndrs pass) + +-- | Located Rule Binder +type LRuleBndr pass = XRec pass (RuleBndr pass) + +-- | Rule Binder +data RuleBndr pass + = RuleBndr (XCRuleBndr pass) (LIdP pass) + | RuleBndrSig (XRuleBndrSig pass) (LIdP pass) (HsPatSigType pass) + | XRuleBndr !(XXRuleBndr pass) + -- ^ + -- - 'GHC.Parser.Annotation.AnnKeywordId' : 'GHC.Parser.Annotation.AnnOpen', + -- 'GHC.Parser.Annotation.AnnDcolon','GHC.Parser.Annotation.AnnClose' + + -- For details on above see Note [exact print annotations] in GHC.Parser.Annotation + +collectRuleBndrSigTys :: [RuleBndr pass] -> [HsPatSigType pass] +collectRuleBndrSigTys bndrs = [ty | RuleBndrSig _ _ ty <- bndrs] + {- ************************************************************************ * * diff --git a/compiler/Language/Haskell/Syntax/Decls.hs b/compiler/Language/Haskell/Syntax/Decls.hs index 84e14b427ff..7d9a6adb04b 100644 --- a/compiler/Language/Haskell/Syntax/Decls.hs +++ b/compiler/Language/Haskell/Syntax/Decls.hs @@ -1477,29 +1477,13 @@ data RuleDecl pass -- ^ After renamer, free-vars from the LHS and RHS , rd_name :: XRec pass RuleName -- ^ Note [Pragma source text] in "GHC.Types.SourceText" - , rd_act :: Activation - , rd_tyvs :: Maybe [LHsTyVarBndr () (NoGhcTc pass)] - -- ^ Forall'd type vars - , rd_tmvs :: [LRuleBndr pass] - -- ^ Forall'd term vars, before typechecking; after typechecking - -- this includes all forall'd vars - , rd_lhs :: XRec pass (HsExpr pass) - , rd_rhs :: XRec pass (HsExpr pass) + , rd_act :: Activation + , rd_bndrs :: RuleBndrs pass + , rd_lhs :: XRec pass (HsExpr pass) + , rd_rhs :: XRec pass (HsExpr pass) } | XRuleDecl !(XXRuleDecl pass) --- | Located Rule Binder -type LRuleBndr pass = XRec pass (RuleBndr pass) - --- | Rule Binder -data RuleBndr pass - = RuleBndr (XCRuleBndr pass) (LIdP pass) - | RuleBndrSig (XRuleBndrSig pass) (LIdP pass) (HsPatSigType pass) - | XRuleBndr !(XXRuleBndr pass) - -collectRuleBndrSigTys :: [RuleBndr pass] -> [HsPatSigType pass] -collectRuleBndrSigTys bndrs = [ty | RuleBndrSig _ _ ty <- bndrs] - {- ************************************************************************ * * diff --git a/compiler/Language/Haskell/Syntax/Extension.hs b/compiler/Language/Haskell/Syntax/Extension.hs index 4d8fbaec0f3..8cd02485265 100644 --- a/compiler/Language/Haskell/Syntax/Extension.hs +++ b/compiler/Language/Haskell/Syntax/Extension.hs @@ -208,6 +208,7 @@ type family XIdSig x type family XFixSig x type family XInlineSig x type family XSpecSig x +type family XSpecSigE x type family XSpecInstSig x type family XMinimalSig x type family XSCCFunSig x @@ -363,6 +364,11 @@ type family XXRuleDecls x type family XHsRule x type family XXRuleDecl x +-- ------------------------------------- +-- RuleBndrs type families +type family XCRuleBndrs x +type family XXRuleBndrs x + -- ------------------------------------- -- RuleBndr type families type family XCRuleBndr x diff --git a/compiler/ghc.cabal.in b/compiler/ghc.cabal.in index 97918c7242f..138963cbfff 100644 --- a/compiler/ghc.cabal.in +++ b/compiler/ghc.cabal.in @@ -824,7 +824,6 @@ Library GHC.Tc.Gen.HsType GHC.Tc.Gen.Match GHC.Tc.Gen.Pat - GHC.Tc.Gen.Rule GHC.Tc.Gen.Sig GHC.Tc.Gen.Splice GHC.Tc.Instance.Class diff --git a/docs/users_guide/9.14.1-notes.rst b/docs/users_guide/9.14.1-notes.rst index af991ef04c2..de757db1a4a 100644 --- a/docs/users_guide/9.14.1-notes.rst +++ b/docs/users_guide/9.14.1-notes.rst @@ -11,6 +11,23 @@ for specific guidance on migrating programs to this release. Language ~~~~~~~~ +* `GHC proposal 493: allow expressions in SPECIALISE pragmas <https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0493-specialise-expressions.rst>`_ + has been implemented. SPECIALISE pragmas now allow arbitrary expressions such as: :: + + {-# SPECIALISE f @Int False :: Int -> Char #-} + + The ability to specify multiple specialisations in a single SPECIALISE pragma, + with syntax of the form (note the comma between the type signatures): :: + + {-# SPECIALISE g : Int -> Int, Float -> Float #-} + + has been deprecated, and is scheduled to be removed in GHC 9.18. + This deprecation is controlled by the newly introduced ``-Wdeprecated-pragmas`` + flag in ``-Wdefault``. + +* A new flag, `-Wuseless-specialisations`, controls warnings emitted when GHC + determines that a SPECIALISE pragma would have no effect. + * ``-Wincomplete-record-selectors`` is now part of `-Wall`, as specified by `GHC Proposal 516: add warning for incomplete record selectors <https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0516-incomplete-record-selectors.rst>`_. Hence, if a library is compiled with ``-Werror``, compilation may now fail. Solution: fix the library. @@ -102,6 +119,11 @@ Cmm - The `ParStmtBlock` list argument of the `ParStmt` constructor of `StmtLR` is now `NonEmpty`. +* As part of the implementation of `GHC proposal 493 <https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0493-specialise-expressions.rst>`_, + the `SpecSig` constructor of `Sig` has been deprecated. It is replaced by + the constructor `SpecSigE` which supports expressions at the head, rather than + a lone variable. + ``ghc-heap`` library ~~~~~~~~~~~~~~~~~~~~ @@ -126,6 +148,13 @@ Cmm ``template-haskell`` library ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- As part of the implementation of `GHC proposal 493 <https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0493-specialise-expressions.rst>`_, + the ``SpecialiseP`` constructor of the Template Haskell ``Pragma`` type, as + well as the helpers ``pragSpecD`` and ``pragSpecInlD``, have been deprecated. + + They are replaced, respectively, by ``SpecialiseEP``, ``pragSpecED`` and + ``pragSpecInlED``. + Included libraries ~~~~~~~~~~~~~~~~~~ diff --git a/docs/users_guide/exts/pragmas.rst b/docs/users_guide/exts/pragmas.rst index 749b52a1578..545e339ebb8 100644 --- a/docs/users_guide/exts/pragmas.rst +++ b/docs/users_guide/exts/pragmas.rst @@ -692,24 +692,32 @@ The :pragma:`RULES` pragma lets you specify rewrite rules. It is described in single: pragma, SPECIALIZE single: overloading, death to -.. pragma:: SPECIALIZE ⟨name⟩ :: ⟨type⟩ +.. pragma:: SPECIALIZE ⟨expr⟩ - Ask that GHC specialize a polymorphic value to a particular type. + Ask that GHC create a copy of a function with specific arguments; most + commonly, a copy of an overloaded function with specific class + dictionary arguments. (UK spelling also accepted.) For key overloaded functions, you can -create extra versions (NB: at the cost of larger code) specialised to particular -types. Thus, if you have an overloaded function: +create extra versions (NB: at the cost of larger code), specialised to specific +arguments. Thus, if you have an overloaded function: :: hammeredLookup :: Ord key => [(key, value)] -> key -> value If it is heavily used on lists with ``Widget`` keys, you could -specialise it as follows: +specialise it with either of the following forms (the second syntax, +introduced in GHC 9.14, additionally requires :extension:`TypeApplications`): :: {-# SPECIALIZE hammeredLookup :: [(Widget, value)] -> Widget -> value #-} + {-# SPECIALIZE hammeredLookup @Widget #-} + +Instead of taking an unknown ``Ord key`` dictionary at runtime, the specialised +version of ``hammeredLookup`` will use the specific implementation of ``Ord Widget``, +which is likely to produce more efficient code. - A ``SPECIALIZE`` pragma for a function can be put anywhere its type signature could be put. Moreover, you can also ``SPECIALIZE`` an @@ -755,15 +763,14 @@ specialise it as follows: specialisation is done too early, the optimisation rules might fail to fire. -- The type in a ``SPECIALIZE`` pragma can be any type that is less - polymorphic than the type of the original function. In concrete - terms, if the original function is ``f`` then the pragma +- The ``SPECIALIZE`` pragma is valid only if the expression is well-typed. + For example, a specialize pragma of the form :: {-# SPECIALIZE f :: <type> #-} - is valid if and only if the definition + is valid only if the definition :: @@ -777,6 +784,7 @@ specialise it as follows: f :: Eq a => a -> b -> b {-# SPECIALISE f :: Int -> b -> b #-} + {-# SPECIALISE f @Float #-} g :: (Eq a, Ix b) => a -> b -> b {-# SPECIALISE g :: (Eq a) => a -> Int -> Int #-} @@ -789,12 +797,28 @@ specialise it as follows: fire very well. If you use this kind of specialisation, let us know how well it works. +- Since GHC 9.14, it is also possible to specialise a function at specific + value arguments, e.g.: :: + + fn :: Bool -> Int -> Double + fn b i = ... + where + ... = if b then helper1 else helper2 + {-# SPECIALISE fn True #-} + {-# SPECIALISE fn False #-} + + This will make two copies of ``fn``, one for ``True`` and one for ``False``. + These will then be optimised to make direct calls to ``helper1`` or ``helper2``, + respectively, instead of dispatching on ``b`` at runtime. + Call sites (that use a literal ``True`` or ``False``) will be rewritten + to use the specialised versions. + .. _specialize-inline: ``SPECIALIZE INLINE`` ~~~~~~~~~~~~~~~~~~~~~ -.. pragma:: SPECIALIZE INLINE ⟨name⟩ :: ⟨type⟩ +.. pragma:: SPECIALIZE INLINE ⟨expr⟩ :where: top-level diff --git a/docs/users_guide/using-warnings.rst b/docs/users_guide/using-warnings.rst index df5738ef3f5..299276ef6b3 100644 --- a/docs/users_guide/using-warnings.rst +++ b/docs/users_guide/using-warnings.rst @@ -422,6 +422,20 @@ of ``-W(no-)*``. such as a `LANGUAGE` or `OPTIONS_GHC` pragma, appears in the body of the module instead. +.. ghc-flag:: -Wdeprecated-pragmas + :shortdesc: warn about deprecated pragmas + :type: dynamic + :reverse: -Wno-deprecated-pragmas + :category: + + :since: 9.14 + + :default: on + + Emits a warning when using a deprecated form of a SPECIALISE pragma which + uses multiple comma-separated type signatures (deprecated and scheduled + to be removed in GHC 9.18). + .. ghc-flag:: -Wmissed-specialisations :shortdesc: warn when specialisation of an imported, overloaded function fails. @@ -476,6 +490,27 @@ of ``-W(no-)*``. Alias for :ghc-flag:`-Wall-missed-specialisations` +.. ghc-flag:: -Wuseless-specialisations + :shortdesc: warn on useless SPECIALISE pragmas + :type: dynamic + :reverse: -Wno-useless-specialisations + :category: + + :since: 9.14 + + :default: on + + Emits a warning if GHC detects a useless SPECIALISE pragma, such as a + SPECIALISE pragma on a non-overloaded function, for example + ``{-# SPECIALISE id :: Int -> Int #-}``. + +.. ghc-flag:: -Wuseless-specializations + :shortdesc: alias for :ghc-flag:`-Wuseless-specialisations` + :type: dynamic + :reverse: -Wno-useless-specializations + + Alias for :ghc-flag:`-Wuseless-specialisations` + .. ghc-flag:: -Wextended-warnings :shortdesc: warn about uses of functions & types that have WARNING or DEPRECATED pragmas, across all categories diff --git a/libraries/array b/libraries/array index e7ffb82fd40..b362edee437 160000 --- a/libraries/array +++ b/libraries/array @@ -1 +1 @@ -Subproject commit e7ffb82fd40134da21d7642a41568f32c77c1a04 +Subproject commit b362edee437c88f2ac38971b66631ed782caa275 diff --git a/libraries/ghc-boot-th/GHC/Boot/TH/Ppr.hs b/libraries/ghc-boot-th/GHC/Boot/TH/Ppr.hs index ce3b1d44aad..f1d1471ba0a 100644 --- a/libraries/ghc-boot-th/GHC/Boot/TH/Ppr.hs +++ b/libraries/ghc-boot-th/GHC/Boot/TH/Ppr.hs @@ -653,21 +653,20 @@ instance Ppr Pragma where <+> sep [ pprName' Applied n <+> dcolon , nest 2 $ ppr ty ] <+> text "#-}" + ppr (SpecialiseEP ty_bndrs tm_bndrs spec_e inline phases) + = sep [ text "{-# SPECIALISE" + <+> maybe empty ppr inline + <+> ppr phases + , nest 2 $ sep [ ppr_ty_forall ty_bndrs <+> ppr_tm_forall ty_bndrs tm_bndrs + , nest 2 (ppr spec_e) ] + <+> text "#-}" ] ppr (SpecialiseInstP inst) = text "{-# SPECIALISE instance" <+> ppr inst <+> text "#-}" ppr (RuleP n ty_bndrs tm_bndrs lhs rhs phases) = sep [ text "{-# RULES" <+> pprString n <+> ppr phases - , nest 4 $ ppr_ty_forall ty_bndrs <+> ppr_tm_forall ty_bndrs + , nest 4 $ ppr_ty_forall ty_bndrs <+> ppr_tm_forall ty_bndrs tm_bndrs <+> ppr lhs , nest 4 $ char '=' <+> ppr rhs <+> text "#-}" ] - where ppr_ty_forall Nothing = empty - ppr_ty_forall (Just bndrs) = text "forall" - <+> fsep (map ppr bndrs) - <+> char '.' - ppr_tm_forall Nothing | null tm_bndrs = empty - ppr_tm_forall _ = text "forall" - <+> fsep (map ppr tm_bndrs) - <+> char '.' ppr (AnnP tgt expr) = text "{-# ANN" <+> target1 tgt <+> ppr expr <+> text "#-}" where target1 ModuleAnnotation = text "module" @@ -681,6 +680,17 @@ instance Ppr Pragma where ppr (SCCP nm str) = text "{-# SCC" <+> pprName' Applied nm <+> maybe empty pprString str <+> text "#-}" +ppr_ty_forall :: Maybe [TyVarBndr ()] -> Doc +ppr_ty_forall Nothing = empty +ppr_ty_forall (Just bndrs) = text "forall" + <+> fsep (map ppr bndrs) + <+> char '.' + +ppr_tm_forall :: Maybe [TyVarBndr ()] -> [RuleBndr] -> Doc +ppr_tm_forall Nothing [] = empty +ppr_tm_forall _ tm_bndrs = text "forall" + <+> fsep (map ppr tm_bndrs) + <+> char '.' ------------------------------ instance Ppr Inline where ppr NoInline = text "NOINLINE" diff --git a/libraries/ghc-internal/src/GHC/Internal/Float.hs b/libraries/ghc-internal/src/GHC/Internal/Float.hs index 626ae94d9cc..0e5883428ba 100644 --- a/libraries/ghc-internal/src/GHC/Internal/Float.hs +++ b/libraries/ghc-internal/src/GHC/Internal/Float.hs @@ -1217,8 +1217,8 @@ floatToDigits base x = -- Converting from an Integer to a RealFloat ------------------------------------------------------------------------ -{-# SPECIALISE integerToBinaryFloat' :: Integer -> Float, - Integer -> Double #-} +{-# SPECIALISE integerToBinaryFloat' :: Integer -> Float #-} +{-# SPECIALISE integerToBinaryFloat' :: Integer -> Double #-} -- | Converts a positive integer to a floating-point value. -- -- The value nearest to the argument will be returned. @@ -1386,8 +1386,8 @@ Float or Double exploiting the known floatRadix and avoiding divisions as much as possible. -} -{-# SPECIALISE fromRat'' :: Int -> Int -> Integer -> Integer -> Float, - Int -> Int -> Integer -> Integer -> Double #-} +{-# SPECIALISE fromRat'' :: Int -> Int -> Integer -> Integer -> Float #-} +{-# SPECIALISE fromRat'' :: Int -> Int -> Integer -> Integer -> Double #-} fromRat'' :: RealFloat a => Int -> Int -> Integer -> Integer -> a -- Invariant: n and d strictly positive fromRat'' minEx@(I# me#) mantDigs@(I# md#) n d = diff --git a/libraries/ghc-internal/src/GHC/Internal/Numeric.hs b/libraries/ghc-internal/src/GHC/Internal/Numeric.hs index 9e9c5da6a15..7650e08ad7c 100644 --- a/libraries/ghc-internal/src/GHC/Internal/Numeric.hs +++ b/libraries/ghc-internal/src/GHC/Internal/Numeric.hs @@ -174,13 +174,16 @@ showInt n0 cs0 -- mutual module deps. {-# SPECIALIZE showEFloat :: - Maybe Int -> Float -> ShowS, + Maybe Int -> Float -> ShowS #-} +{-# SPECIALIZE showEFloat :: Maybe Int -> Double -> ShowS #-} {-# SPECIALIZE showFFloat :: - Maybe Int -> Float -> ShowS, + Maybe Int -> Float -> ShowS #-} +{-# SPECIALIZE showFFloat :: Maybe Int -> Double -> ShowS #-} {-# SPECIALIZE showGFloat :: - Maybe Int -> Float -> ShowS, + Maybe Int -> Float -> ShowS #-} +{-# SPECIALIZE showGFloat :: Maybe Int -> Double -> ShowS #-} -- | Show a signed 'RealFloat' value diff --git a/libraries/ghc-internal/src/GHC/Internal/Real.hs b/libraries/ghc-internal/src/GHC/Internal/Real.hs index 7855911de3b..e6f668b8fa9 100644 --- a/libraries/ghc-internal/src/GHC/Internal/Real.hs +++ b/libraries/ghc-internal/src/GHC/Internal/Real.hs @@ -746,10 +746,9 @@ x0 ^ y0 | y0 < 0 = errorWithoutStackTrace "Negative exponent" | y0 == 0 = 1 | otherwise = powImpl x0 y0 -{-# SPECIALISE powImpl :: - Integer -> Integer -> Integer, - Integer -> Int -> Integer, - Int -> Int -> Int #-} +{-# SPECIALISE powImpl :: Integer -> Integer -> Integer #-} +{-# SPECIALISE powImpl :: Integer -> Int -> Integer #-} +{-# SPECIALISE powImpl :: Int -> Int -> Int #-} {-# INLINABLE powImpl #-} -- See Note [Inlining (^)] powImpl :: (Num a, Integral b) => a -> b -> a -- powImpl : x0 ^ y0 = (x ^ y) @@ -757,10 +756,9 @@ powImpl x y | even y = powImpl (x * x) (y `quot` 2) | y == 1 = x | otherwise = powImplAcc (x * x) (y `quot` 2) x -- See Note [Half of y - 1] -{-# SPECIALISE powImplAcc :: - Integer -> Integer -> Integer -> Integer, - Integer -> Int -> Integer -> Integer, - Int -> Int -> Int -> Int #-} +{-# SPECIALISE powImplAcc :: Integer -> Integer -> Integer -> Integer #-} +{-# SPECIALISE powImplAcc :: Integer -> Int -> Integer -> Integer #-} +{-# SPECIALISE powImplAcc :: Int -> Int -> Int -> Int #-} {-# INLINABLE powImplAcc #-} -- See Note [Inlining (^)] powImplAcc :: (Num a, Integral b) => a -> b -> a -> a -- powImplAcc : x0 ^ y0 = (x ^ y) * z diff --git a/libraries/ghc-internal/src/GHC/Internal/TH/Lib.hs b/libraries/ghc-internal/src/GHC/Internal/TH/Lib.hs index 89cfad79313..063ed3db28b 100644 --- a/libraries/ghc-internal/src/GHC/Internal/TH/Lib.hs +++ b/libraries/ghc-internal/src/GHC/Internal/TH/Lib.hs @@ -555,18 +555,45 @@ pragInlD name inline rm phases pragOpaqueD :: Quote m => Name -> m Dec pragOpaqueD name = pure $ PragmaD $ OpaqueP name +{-# DEPRECATED pragSpecD "Please use 'pragSpecED' instead. 'pragSpecD' will be removed in GHC 9.18." #-} pragSpecD :: Quote m => Name -> m Type -> Phases -> m Dec pragSpecD n ty phases = do ty1 <- ty pure $ PragmaD $ SpecialiseP n ty1 Nothing phases +{-# DEPRECATED pragSpecInlD "Please use 'pragSpecInlED' instead. 'pragSpecInlD' will be removed in GHC 9.18." #-} pragSpecInlD :: Quote m => Name -> m Type -> Inline -> Phases -> m Dec pragSpecInlD n ty inline phases = do ty1 <- ty pure $ PragmaD $ SpecialiseP n ty1 (Just inline) phases +pragSpecED :: Quote m + => Maybe [m (TyVarBndr ())] -> [m RuleBndr] + -> m Exp + -> Phases + -> m Dec +pragSpecED ty_bndrs tm_bndrs expr phases + = do + ty_bndrs1 <- traverse sequenceA ty_bndrs + tm_bndrs1 <- sequenceA tm_bndrs + expr1 <- expr + pure $ PragmaD $ SpecialiseEP ty_bndrs1 tm_bndrs1 expr1 Nothing phases + +pragSpecInlED :: Quote m + => Maybe [m (TyVarBndr ())] -> [m RuleBndr] + -> m Exp + -> Inline + -> Phases + -> m Dec +pragSpecInlED ty_bndrs tm_bndrs expr inl phases + = do + ty_bndrs1 <- traverse sequenceA ty_bndrs + tm_bndrs1 <- sequenceA tm_bndrs + expr1 <- expr + pure $ PragmaD $ SpecialiseEP ty_bndrs1 tm_bndrs1 expr1 (Just inl) phases + pragSpecInstD :: Quote m => m Type -> m Dec pragSpecInstD ty = do diff --git a/libraries/ghc-internal/src/GHC/Internal/TH/Syntax.hs b/libraries/ghc-internal/src/GHC/Internal/TH/Syntax.hs index a2e2e1ebd84..e77e37db7ef 100644 --- a/libraries/ghc-internal/src/GHC/Internal/TH/Syntax.hs +++ b/libraries/ghc-internal/src/GHC/Internal/TH/Syntax.hs @@ -2165,7 +2165,11 @@ data Pragma = InlineP Name Inline RuleMatch Phases | OpaqueP Name -- ^ @{ {\-\# OPAQUE T #-} }@ | SpecialiseP Name Type (Maybe Inline) Phases - -- ^ @{ {\-\# SPECIALISE [INLINE] [phases] T #-} }@ + -- ^ @{ {\-\# SPECIALISE [INLINE] [phases] nm :: ty #-} }@ + -- + -- NB: this constructor is deprecated and will be removed in GHC 9.18 + | SpecialiseEP (Maybe [TyVarBndr ()]) [RuleBndr] Exp (Maybe Inline) Phases + -- ^ @{ {\-\# SPECIALISE [INLINE] [phases] exp #-} }@ | SpecialiseInstP Type -- ^ @{ {\-\# SPECIALISE instance I #-} }@ | RuleP String (Maybe [TyVarBndr ()]) [RuleBndr] Exp Exp Phases diff --git a/libraries/template-haskell/Language/Haskell/TH/Lib.hs b/libraries/template-haskell/Language/Haskell/TH/Lib.hs index 21f1c47e9a5..7e45b40b76b 100644 --- a/libraries/template-haskell/Language/Haskell/TH/Lib.hs +++ b/libraries/template-haskell/Language/Haskell/TH/Lib.hs @@ -122,7 +122,9 @@ module Language.Haskell.TH.Lib ( -- **** Pragmas ruleVar, typedRuleVar, valueAnnotation, typeAnnotation, moduleAnnotation, - pragInlD, pragSpecD, pragSpecInlD, pragSpecInstD, pragRuleD, pragAnnD, + pragInlD, pragSpecD, pragSpecInlD, + pragSpecED, pragSpecInlED, + pragSpecInstD, pragRuleD, pragAnnD, pragLineD, pragCompleteD, -- **** Pattern Synonyms diff --git a/libraries/template-haskell/changelog.md b/libraries/template-haskell/changelog.md index 9b9d9bb082d..62959eae085 100644 --- a/libraries/template-haskell/changelog.md +++ b/libraries/template-haskell/changelog.md @@ -4,6 +4,13 @@ * Introduce `dataToCodeQ` and `liftDataTyped`, typed variants of `dataToExpQ` and `liftData` respectively. + * As part of the implementation of [GHC proposal 493](https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0493-specialise-expressions.rst), + the ``SpecialiseP`` constructor of `Pragma`, as well as the helper functions + `pragSpecD` and `pragSpecInlD`, have been deprecated. + + They are replaced, respectively, by `SpecialiseEP`, `pragSpecED` and + `pragSpecInlED`. + ## 2.23.0.0 * Extend `Exp` with `ForallE`, `ForallVisE`, `ConstraintedE`, diff --git a/testsuite/tests/deSugar/should_compile/T10251.stderr b/testsuite/tests/deSugar/should_compile/T10251.stderr new file mode 100644 index 00000000000..60804a72fe1 --- /dev/null +++ b/testsuite/tests/deSugar/should_compile/T10251.stderr @@ -0,0 +1,5 @@ +T10251.hs:19:5: warning: [GHC-66582] [-Wuseless-specialisations (in -Wdefault)] + Dubious SPECIALISE instance pragma. + The pragma does not specialise away any class dictionaries, + and neither is there any value specialisation. + diff --git a/testsuite/tests/diagnostic-codes/codes.stdout b/testsuite/tests/diagnostic-codes/codes.stdout index f76653c468c..4f70f801859 100644 --- a/testsuite/tests/diagnostic-codes/codes.stdout +++ b/testsuite/tests/diagnostic-codes/codes.stdout @@ -9,10 +9,10 @@ - add test cases to exercise any newly uncovered diagnostic codes, - accept the expected output of the 'codes' test by passing the '-a' flag to Hadrian. -[GHC-93315] is untested (constructor = DsUselessSpecialiseForClassMethodSelector) [GHC-58181] is untested (constructor = DsOrphanRule) [GHC-69441] is untested (constructor = DsRuleLhsTooComplicated) [GHC-19551] is untested (constructor = DsAggregatedViewExpressions) +[GHC-93315] is untested (constructor = UselessSpecialiseForClassMethodSelector) [GHC-09848] is untested (constructor = PsErrCmmParser) [GHC-95644] is untested (constructor = PsErrBangPatWithoutSpace) [GHC-45106] is untested (constructor = PsErrInvalidInfixHole) @@ -61,6 +61,7 @@ [GHC-01570] is untested (constructor = TcRnExpectedValueId) [GHC-96665] is untested (constructor = TcRnMultipleInlinePragmas) [GHC-88293] is untested (constructor = TcRnUnexpectedPragmas) +[GHC-35827] is untested (constructor = TcRnNonOverloadedSpecialisePragma) [GHC-85337] is untested (constructor = TcRnSpecialiseNotVisible) [GHC-91382] is untested (constructor = TcRnIllegalKindSignature) [GHC-72520] is untested (constructor = TcRnIgnoreSpecialisePragmaOnDefMethod) @@ -119,3 +120,5 @@ [GHC-75721] is untested (constructor = CannotRepresentType) [GHC-17599] is untested (constructor = AddTopDeclsUnexpectedDeclarationSplice) [GHC-86934] is untested (constructor = ClassPE) + + diff --git a/testsuite/tests/ghc-api/T18522-dbg-ppr.hs b/testsuite/tests/ghc-api/T18522-dbg-ppr.hs index a683eb9ea87..e02a56c1b74 100644 --- a/testsuite/tests/ghc-api/T18522-dbg-ppr.hs +++ b/testsuite/tests/ghc-api/T18522-dbg-ppr.hs @@ -20,6 +20,7 @@ import GHC import qualified GHC.LanguageExtensions as LangExt import Data.Either (fromRight) +import Data.IORef import Control.Monad.IO.Class (liftIO) import System.Environment (getArgs) @@ -46,8 +47,9 @@ main = do let exts = extensionFlags dflags hs_t = fromRight (error "convertToHsType") $ convertToHsType exts (Generated OtherExpansion SkipPmc) noSrcSpan th_t + skol_tv_ref <- liftIO (newIORef []) (messages, mres) <- - tcRnType hsc_env SkolemiseFlexi True hs_t + tcRnType hsc_env (SkolemiseFlexi skol_tv_ref) True hs_t let (warnings, errors) = partitionMessages messages case mres of Nothing -> do diff --git a/testsuite/tests/interface-stability/template-haskell-exports.stdout b/testsuite/tests/interface-stability/template-haskell-exports.stdout index ba10b03b241..4104320282d 100644 --- a/testsuite/tests/interface-stability/template-haskell-exports.stdout +++ b/testsuite/tests/interface-stability/template-haskell-exports.stdout @@ -345,7 +345,7 @@ module Language.Haskell.TH where ppr_list :: [a] -> GHC.Boot.TH.PprLib.Doc {-# MINIMAL ppr #-} type Pragma :: * - data Pragma = InlineP Name Inline RuleMatch Phases | OpaqueP Name | SpecialiseP Name Type (GHC.Internal.Maybe.Maybe Inline) Phases | SpecialiseInstP Type | RuleP GHC.Internal.Base.String (GHC.Internal.Maybe.Maybe [TyVarBndr ()]) [RuleBndr] Exp Exp Phases | AnnP AnnTarget Exp | LineP GHC.Internal.Types.Int GHC.Internal.Base.String | CompleteP [Name] (GHC.Internal.Maybe.Maybe Name) | SCCP Name (GHC.Internal.Maybe.Maybe GHC.Internal.Base.String) + data Pragma = InlineP Name Inline RuleMatch Phases | OpaqueP Name | SpecialiseP Name Type (GHC.Internal.Maybe.Maybe Inline) Phases | SpecialiseEP (GHC.Internal.Maybe.Maybe [TyVarBndr ()]) [RuleBndr] Exp (GHC.Internal.Maybe.Maybe Inline) Phases | SpecialiseInstP Type | RuleP GHC.Internal.Base.String (GHC.Internal.Maybe.Maybe [TyVarBndr ()]) [RuleBndr] Exp Exp Phases | AnnP AnnTarget Exp | LineP GHC.Internal.Types.Int GHC.Internal.Base.String | CompleteP [Name] (GHC.Internal.Maybe.Maybe Name) | SCCP Name (GHC.Internal.Maybe.Maybe GHC.Internal.Base.String) type Pred :: * type Pred = Type type PredQ :: * @@ -612,7 +612,9 @@ module Language.Haskell.TH where pragLineD :: forall (m :: * -> *). Quote m => GHC.Internal.Types.Int -> GHC.Internal.Base.String -> m Dec pragRuleD :: forall (m :: * -> *). Quote m => GHC.Internal.Base.String -> [m RuleBndr] -> m Exp -> m Exp -> Phases -> m Dec pragSpecD :: forall (m :: * -> *). Quote m => Name -> m Type -> Phases -> m Dec + pragSpecED :: forall (m :: * -> *). Quote m => GHC.Internal.Maybe.Maybe [m (TyVarBndr ())] -> [m RuleBndr] -> m Exp -> Phases -> m Dec pragSpecInlD :: forall (m :: * -> *). Quote m => Name -> m Type -> Inline -> Phases -> m Dec + pragSpecInlED :: forall (m :: * -> *). Quote m => GHC.Internal.Maybe.Maybe [m (TyVarBndr ())] -> [m RuleBndr] -> m Exp -> Inline -> Phases -> m Dec pragSpecInstD :: forall (m :: * -> *). Quote m => m Type -> m Dec prefixPatSyn :: forall (m :: * -> *). Quote m => [Name] -> m PatSynArgs prim :: Callconv @@ -1117,7 +1119,9 @@ module Language.Haskell.TH.Lib where pragLineD :: forall (m :: * -> *). GHC.Internal.TH.Syntax.Quote m => GHC.Internal.Types.Int -> GHC.Internal.Base.String -> m GHC.Internal.TH.Syntax.Dec pragRuleD :: forall (m :: * -> *). GHC.Internal.TH.Syntax.Quote m => GHC.Internal.Base.String -> [m GHC.Internal.TH.Syntax.RuleBndr] -> m GHC.Internal.TH.Syntax.Exp -> m GHC.Internal.TH.Syntax.Exp -> GHC.Internal.TH.Syntax.Phases -> m GHC.Internal.TH.Syntax.Dec pragSpecD :: forall (m :: * -> *). GHC.Internal.TH.Syntax.Quote m => GHC.Internal.TH.Syntax.Name -> m GHC.Internal.TH.Syntax.Type -> GHC.Internal.TH.Syntax.Phases -> m GHC.Internal.TH.Syntax.Dec + pragSpecED :: forall (m :: * -> *). GHC.Internal.TH.Syntax.Quote m => GHC.Internal.Maybe.Maybe [m (GHC.Internal.TH.Syntax.TyVarBndr ())] -> [m GHC.Internal.TH.Syntax.RuleBndr] -> m GHC.Internal.TH.Syntax.Exp -> GHC.Internal.TH.Syntax.Phases -> m GHC.Internal.TH.Syntax.Dec pragSpecInlD :: forall (m :: * -> *). GHC.Internal.TH.Syntax.Quote m => GHC.Internal.TH.Syntax.Name -> m GHC.Internal.TH.Syntax.Type -> GHC.Internal.TH.Syntax.Inline -> GHC.Internal.TH.Syntax.Phases -> m GHC.Internal.TH.Syntax.Dec + pragSpecInlED :: forall (m :: * -> *). GHC.Internal.TH.Syntax.Quote m => GHC.Internal.Maybe.Maybe [m (GHC.Internal.TH.Syntax.TyVarBndr ())] -> [m GHC.Internal.TH.Syntax.RuleBndr] -> m GHC.Internal.TH.Syntax.Exp -> GHC.Internal.TH.Syntax.Inline -> GHC.Internal.TH.Syntax.Phases -> m GHC.Internal.TH.Syntax.Dec pragSpecInstD :: forall (m :: * -> *). GHC.Internal.TH.Syntax.Quote m => m GHC.Internal.TH.Syntax.Type -> m GHC.Internal.TH.Syntax.Dec prefixPatSyn :: forall (m :: * -> *). GHC.Internal.TH.Syntax.Quote m => [GHC.Internal.TH.Syntax.Name] -> m GHC.Internal.TH.Syntax.PatSynArgs prim :: GHC.Internal.TH.Syntax.Callconv @@ -1459,7 +1463,9 @@ module Language.Haskell.TH.Lib.Internal where pragSCCFunD :: forall (m :: * -> *). GHC.Internal.TH.Syntax.Quote m => GHC.Internal.TH.Syntax.Name -> m GHC.Internal.TH.Syntax.Dec pragSCCFunNamedD :: forall (m :: * -> *). GHC.Internal.TH.Syntax.Quote m => GHC.Internal.TH.Syntax.Name -> GHC.Internal.Base.String -> m GHC.Internal.TH.Syntax.Dec pragSpecD :: forall (m :: * -> *). GHC.Internal.TH.Syntax.Quote m => GHC.Internal.TH.Syntax.Name -> m GHC.Internal.TH.Syntax.Type -> GHC.Internal.TH.Syntax.Phases -> m GHC.Internal.TH.Syntax.Dec + pragSpecED :: forall (m :: * -> *). GHC.Internal.TH.Syntax.Quote m => GHC.Internal.Maybe.Maybe [m (GHC.Internal.TH.Syntax.TyVarBndr ())] -> [m GHC.Internal.TH.Syntax.RuleBndr] -> m GHC.Internal.TH.Syntax.Exp -> GHC.Internal.TH.Syntax.Phases -> m GHC.Internal.TH.Syntax.Dec pragSpecInlD :: forall (m :: * -> *). GHC.Internal.TH.Syntax.Quote m => GHC.Internal.TH.Syntax.Name -> m GHC.Internal.TH.Syntax.Type -> GHC.Internal.TH.Syntax.Inline -> GHC.Internal.TH.Syntax.Phases -> m GHC.Internal.TH.Syntax.Dec + pragSpecInlED :: forall (m :: * -> *). GHC.Internal.TH.Syntax.Quote m => GHC.Internal.Maybe.Maybe [m (GHC.Internal.TH.Syntax.TyVarBndr ())] -> [m GHC.Internal.TH.Syntax.RuleBndr] -> m GHC.Internal.TH.Syntax.Exp -> GHC.Internal.TH.Syntax.Inline -> GHC.Internal.TH.Syntax.Phases -> m GHC.Internal.TH.Syntax.Dec pragSpecInstD :: forall (m :: * -> *). GHC.Internal.TH.Syntax.Quote m => m GHC.Internal.TH.Syntax.Type -> m GHC.Internal.TH.Syntax.Dec prefixPatSyn :: forall (m :: * -> *). GHC.Internal.TH.Syntax.Quote m => [GHC.Internal.TH.Syntax.Name] -> m GHC.Internal.TH.Syntax.PatSynArgs prim :: GHC.Internal.TH.Syntax.Callconv @@ -2023,7 +2029,7 @@ module Language.Haskell.TH.Syntax where type PkgName :: * newtype PkgName = PkgName GHC.Internal.Base.String type Pragma :: * - data Pragma = InlineP Name Inline RuleMatch Phases | OpaqueP Name | SpecialiseP Name Type (GHC.Internal.Maybe.Maybe Inline) Phases | SpecialiseInstP Type | RuleP GHC.Internal.Base.String (GHC.Internal.Maybe.Maybe [TyVarBndr ()]) [RuleBndr] Exp Exp Phases | AnnP AnnTarget Exp | LineP GHC.Internal.Types.Int GHC.Internal.Base.String | CompleteP [Name] (GHC.Internal.Maybe.Maybe Name) | SCCP Name (GHC.Internal.Maybe.Maybe GHC.Internal.Base.String) + data Pragma = InlineP Name Inline RuleMatch Phases | OpaqueP Name | SpecialiseP Name Type (GHC.Internal.Maybe.Maybe Inline) Phases | SpecialiseEP (GHC.Internal.Maybe.Maybe [TyVarBndr ()]) [RuleBndr] Exp (GHC.Internal.Maybe.Maybe Inline) Phases | SpecialiseInstP Type | RuleP GHC.Internal.Base.String (GHC.Internal.Maybe.Maybe [TyVarBndr ()]) [RuleBndr] Exp Exp Phases | AnnP AnnTarget Exp | LineP GHC.Internal.Types.Int GHC.Internal.Base.String | CompleteP [Name] (GHC.Internal.Maybe.Maybe Name) | SCCP Name (GHC.Internal.Maybe.Maybe GHC.Internal.Base.String) type Pred :: * type Pred = Type type role Q nominal diff --git a/testsuite/tests/parser/should_compile/OpaqueParseWarn1.stderr b/testsuite/tests/parser/should_compile/OpaqueParseWarn1.stderr index 01f62eb39d1..a3b566f42ad 100644 --- a/testsuite/tests/parser/should_compile/OpaqueParseWarn1.stderr +++ b/testsuite/tests/parser/should_compile/OpaqueParseWarn1.stderr @@ -1,3 +1,3 @@ - -OpaqueParseWarn1.hs:6:1: warning: [GHC-38524] +OpaqueParseWarn1.hs:6:1: warning: [GHC-38524] [-Wuseless-specialisations (in -Wdefault)] Ignoring useless SPECIALISE pragma for NOINLINE function: ‘f’ + diff --git a/testsuite/tests/parser/should_fail/T7848.stderr b/testsuite/tests/parser/should_fail/T7848.stderr index ff24ae7b375..8ff99b1f1ab 100644 --- a/testsuite/tests/parser/should_fail/T7848.stderr +++ b/testsuite/tests/parser/should_fail/T7848.stderr @@ -1,11 +1,10 @@ - -T7848.hs:10:9: error: [GHC-25897] - • Couldn't match expected type ‘Char’ with actual type ‘a’ +T7848.hs:10:24: error: [GHC-25897] + • Couldn't match expected type ‘a’ with actual type ‘Char’ ‘a’ is a rigid type variable bound by - the type signature for: - (&) :: forall a. a - at T7848.hs:10:9-35 - • In the pragma: {-# SPECIALIZE (&) :: a #-} + an expression type signature: + forall a. a + at T7848.hs:10:31 + • In the expression: (&) :: a In an equation for ‘x’: x (+) ((&)@z) ((:&&) a b) (c :&& d) (e `A` f) (A g h) = y @@ -15,3 +14,4 @@ T7848.hs:10:9: error: [GHC-25897] {-# INLINE (&) #-} {-# SPECIALIZE (&) :: a #-} (&) = 'c' + diff --git a/testsuite/tests/simplCore/should_compile/DsSpecPragmas.hs b/testsuite/tests/simplCore/should_compile/DsSpecPragmas.hs new file mode 100644 index 00000000000..9cd4fcfef7d --- /dev/null +++ b/testsuite/tests/simplCore/should_compile/DsSpecPragmas.hs @@ -0,0 +1,147 @@ +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE FunctionalDependencies #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TypeFamilyDependencies #-} +{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE QuantifiedConstraints #-} + +module DsSpecPragmas where + +import Control.Monad.ST + ( ST ) +import Data.Kind + ( Constraint, Type ) +import Data.Proxy + ( Proxy(..) ) + +-- Some specialise pragmas that are tricky to generate the correct RULE for. + +-------------------------------------------------------------------------------- + +f1 :: ( Num a, Eq b ) => a -> b -> Int +f1 _ _ = 111 +-- Make sure we don't generate a rule with an LHS of the form +-- +-- forall @e (d :: Eq e). f1 @[e] ($fEqList d) = ... +-- +-- but rather +-- +-- forall @e (d :: Eq [e]). f1 @[e] d = ... +{-# SPECIALISE f1 :: Eq [e] => Word -> [e] -> Int #-} + +f1_qc :: ( forall x. Eq x => Eq ( f x ), Eq a, Num b ) => Proxy f -> a -> b -> Char +f1_qc _ _ _ = 'q' + +-- Like 'f1', but with a local instance (quantified constraint). +{-# SPECIALISE f1_qc :: ( forall y. Eq y => Eq ( g y ), Eq ( g e ) ) => Proxy g -> g e -> Word -> Char #-} + +-------------------------------------------------------------------------------- + +f2 :: ( Eq a, Eq b, Num c ) => a -> b -> c -> Int +f2 _ _ _ = 2 + +-- Make sure the rule LHS is of the form +-- +-- f2 @c @c d1 d2 and not f2 @c @c d d +{-# SPECIALISE f2 :: Eq c => c -> c -> Word -> Int #-} + +-------------------------------------------------------------------------------- + +f3 :: ( Eq a, forall x. Eq x => Eq ( f x ) ) => f a -> Bool +f3 z = z == z + +-- Discharge the quantified constraint but keep the 'Eq' constraint +{-# SPECIALISE f3 :: Eq c => [ c ] -> Bool #-} + +-- Discharge the 'Eq' constraint but keep the quantified constraint +{-# SPECIALISE f3 :: ( forall y. Eq y => Eq ( g y ) ) => g Int -> Bool #-} + +-------------------------------------------------------------------------------- + +f4 :: (Eq a, Monad m) => a -> m a +f4 = return + +-- Check we can deal with locally quantified variables in constraints, +-- in this case 'Monad (ST s)'. +{-# SPECIALISE f4 :: forall s b. Eq b => b -> ST s b #-} + +f4_qc :: (Eq a, forall m. Monad m => Monad (t m)) => t m a -> () +f4_qc _ = () + +-- Like 'f4' but with a quantified constraint. +{-# SPECIALISE f4_qc :: forall r n b. (forall m. Monad m => Monad (r m)) => r n Int -> () #-} + +-------------------------------------------------------------------------------- + +type family T a where + T Int = Word +data D a = D a (T a) +deriving stock instance (Eq a, Eq (T a)) => Eq (D a) + +f5 :: Eq a => a -> Bool +f5 x = x == x + +-- Discharge a dictionary constraint using a top-level instance +-- whose context contains a type family application. +{-# SPECIALISE f5 :: D Int -> Bool #-} + + +f5_qc :: ( Eq a, Eq ( T a ), forall x. ( Eq x, Eq ( T x ) ) => Eq ( f x ) ) => f a -> Bool +f5_qc z = z == z + +-- Discharge a quantified constraint using a top-level instance +-- whose context includes a type family application. +{-# SPECIALISE f5_qc :: D Int -> Bool #-} + +-- Quantify over this same quantified constraint, but discharge the +-- other dictionary constraints. +{-# SPECIALISE f5_qc :: ( forall y. ( Eq y, Eq ( T y ) ) => Eq ( g y ) ) => g Int -> Bool #-} + +-------------------------------------------------------------------------------- + +f6 :: ( Eq a, Ord b, Num c ) => a -> b -> c -> Char +f6 _ _ _ = 'c' + +-- Check that we do perform simplification among Wanteds that we quantify over. +{-# SPECIALISE f6 :: Ord c => c -> c -> Word -> Char #-} + + +f6_qc :: ( forall x. Eq x => Eq ( f x ), forall y. Eq y => Ord ( g y ), Num c ) => Proxy f -> Proxy g -> c -> Char +f6_qc _ _ _ = 'd' + +-- Like 'f6', but with quantified constraints. +{-# SPECIALISE f6_qc :: ( forall z. Eq z => Ord ( h z ) ) => Proxy h -> Proxy h -> Word -> Char #-} + +-------------------------------------------------------------------------------- + +type Cls :: Type -> Constraint +class Cls a where {} + +type TF :: Type -> Type +type family TF a = r | r -> a where + TF Int = Bool + +f7 :: ( Cls ( TF a ), Eq a ) => a -> a +f7 x = x +{-# SPECIALISE f7 :: Cls Bool => Int -> Int #-} + +-------------------------------------------------------------------------------- +-- An example taken from the Cabal library (buildInfoFieldGrammar). + +type F :: ( Type -> Constraint ) -> ( Type -> Type ) -> Constraint +class F c g | g -> c where {} + +type C :: Type -> Constraint +class C a where +instance C (a, b) where {} + +type G :: Type -> Type +data G a where + +instance F C G + +qcfd :: ( F c g, forall a b. c (a, b) ) => g () +qcfd = let x = x in x +{-# SPECIALISE qcfd :: G () #-} + +-------------------------------------------------------------------------------- diff --git a/testsuite/tests/simplCore/should_compile/DsSpecPragmas.stderr b/testsuite/tests/simplCore/should_compile/DsSpecPragmas.stderr new file mode 100644 index 00000000000..194810d914a --- /dev/null +++ b/testsuite/tests/simplCore/should_compile/DsSpecPragmas.stderr @@ -0,0 +1,75 @@ + +==================== Tidy Core rules ==================== +"USPEC f1 @Word @[e]" + forall (@e) ($dEq :: Eq [e]) ($dNum :: Num Word). + f1 @Word @[e] $dNum $dEq + = \ _ [Occ=Dead] _ [Occ=Dead] -> I# 111# +"USPEC f1_qc @_ @(g e) @Word" + forall (@e) + (@(g :: * -> *)) + ($dNum :: Num Word) + ($dEq :: Eq (g e)) + (df :: forall x. Eq x => Eq (g x)). + f1_qc @g @(g e) @Word df $dEq $dNum + = \ _ [Occ=Dead] _ [Occ=Dead] _ [Occ=Dead] -> C# 'q'# +"USPEC f2 @_ @_ @Word" + forall (@c) ($dNum :: Num Word) ($dEq :: Eq c) ($dEq1 :: Eq c). + f2 @c @c @Word $dEq1 $dEq $dNum + = \ _ [Occ=Dead] _ [Occ=Dead] _ [Occ=Dead] -> I# 2# +"USPEC f3 @Int @_" + forall (@(g :: * -> *)) + (df :: forall x. Eq x => Eq (g x)) + ($dEq :: Eq Int). + f3 @Int @g $dEq df + = f3_$sf3 @g df +"USPEC f3 @_ @[]" + forall (@c) (df :: forall x. Eq x => Eq [x]) ($dEq :: Eq c). + f3 @c @[] $dEq df + = f3_$sf1 @c $dEq +"USPEC f4 @_ @(ST s)" + forall (@s) (@b) ($dMonad :: Monad (ST s)) ($dEq :: Eq b). + f4 @b @(ST s) $dEq $dMonad + = $fApplicativeST_$cpure @s @b +"USPEC f4_qc @Int @_ @_" + forall (@(n :: * -> *)) + (@(r :: (* -> *) -> * -> *)) + (df :: forall (m :: * -> *). Monad m => Monad (r m)) + ($dEq :: Eq Int). + f4_qc @Int @r @n $dEq df + = \ _ [Occ=Dead] -> () +"USPEC f5 @(D Int)" + forall ($dEq :: Eq (D Int)). f5 @(D Int) $dEq = f5_$sf5 +"USPEC f5_qc @Int @D" + forall (df :: forall x. (Eq x, Eq (T x)) => Eq (D x)) + ($dEq :: Eq (T Int)) + ($dEq1 :: Eq Int). + f5_qc @Int @D $dEq1 $dEq df + = f5_$sf5 +"USPEC f5_qc @Int @_" + forall (@(g :: * -> *)) + (df :: forall x. (Eq x, Eq (T x)) => Eq (g x)) + ($dEq :: Eq (T Int)) + ($dEq1 :: Eq Int). + f5_qc @Int @g $dEq1 $dEq df + = f5_qc_$sf5_qc @g df +"USPEC f6 @_ @_ @Word" + forall (@c) ($dNum :: Num Word) ($dOrd :: Ord c) ($dEq :: Eq c). + f6 @c @c @Word $dEq $dOrd $dNum + = \ _ [Occ=Dead] _ [Occ=Dead] _ [Occ=Dead] -> C# 'c'# +"USPEC f6_qc @_ @_ @Word" + forall (@(h :: * -> *)) + ($dNum :: Num Word) + (df :: forall y. Eq y => Ord (h y)) + (df1 :: forall x. Eq x => Eq (h x)). + f6_qc @h @h @Word df1 df $dNum + = \ _ [Occ=Dead] _ [Occ=Dead] _ [Occ=Dead] -> C# 'd'# +"USPEC f7 @Int" + forall ($dEq :: Eq Int) ($dCls :: Cls (TF Int)). + f7 @Int $dCls $dEq + = \ (x [Occ=Once1] :: Int) -> x +"USPEC qcfd @C @G" + forall (df :: forall a b. C (a, b)) ($dF :: F C G). + qcfd @C @G $dF df + = qcfd_x + + diff --git a/testsuite/tests/simplCore/should_compile/T12603.stdout b/testsuite/tests/simplCore/should_compile/T12603.stdout index b5423adf1a2..229db972cf9 100644 --- a/testsuite/tests/simplCore/should_compile/T12603.stdout +++ b/testsuite/tests/simplCore/should_compile/T12603.stdout @@ -1 +1 @@ - = case GHC.Internal.Real.$w$spowImpl1 2# 8# of v { __DEFAULT -> + = case GHC.Internal.Real.$w$spowImpl 2# 8# of v { __DEFAULT -> diff --git a/testsuite/tests/simplCore/should_compile/T15445.stderr b/testsuite/tests/simplCore/should_compile/T15445.stderr index 7aab8340496..eaa4f2aee3a 100644 --- a/testsuite/tests/simplCore/should_compile/T15445.stderr +++ b/testsuite/tests/simplCore/should_compile/T15445.stderr @@ -8,6 +8,7 @@ Rule fired: Class op show (BUILTIN) Rule fired: USPEC plusTwoRec @Int (T15445a) Rule fired: Class op enumFromTo (BUILTIN) Rule fired: Class op show (BUILTIN) +Rule fired: USPEC plusTwoRec @Int (T15445a) Rule fired: Class op enumFromTo (BUILTIN) Rule fired: eftIntList (GHC.Internal.Enum) Rule fired: ># (BUILTIN) diff --git a/testsuite/tests/simplCore/should_compile/T24359a.hs b/testsuite/tests/simplCore/should_compile/T24359a.hs new file mode 100644 index 00000000000..cf2513f63f5 --- /dev/null +++ b/testsuite/tests/simplCore/should_compile/T24359a.hs @@ -0,0 +1,18 @@ +{-# LANGUAGE TypeApplications, ExplicitForAll #-} + +module T24359a where + +data UA i = UA !i + +class IArray a where + bounds :: a i -> i + +showsIArray :: (IArray a, Show i) => a i -> String +showsIArray a = show (bounds a) + +{-# SPECIALISE + showsIArray :: (Show i) => UA i -> String + #-} + +instance IArray UA where + bounds (UA u) = u diff --git a/testsuite/tests/simplCore/should_compile/T24359a.stderr b/testsuite/tests/simplCore/should_compile/T24359a.stderr new file mode 100644 index 00000000000..6d7905155cf --- /dev/null +++ b/testsuite/tests/simplCore/should_compile/T24359a.stderr @@ -0,0 +1,7 @@ + +==================== Tidy Core rules ==================== +"USPEC showsIArray @UA @_" + forall (@i) ($dShow :: Show i) ($dIArray :: IArray UA). + showsIArray @UA @i $dIArray $dShow + = showsIArray_$sshowsIArray @i $dShow + diff --git a/testsuite/tests/simplCore/should_compile/T25389.hs b/testsuite/tests/simplCore/should_compile/T25389.hs new file mode 100644 index 00000000000..d248f5b1a03 --- /dev/null +++ b/testsuite/tests/simplCore/should_compile/T25389.hs @@ -0,0 +1,19 @@ +{-# LANGUAGE GADTs #-} +{-# LANGUAGE DataKinds #-} + +{-# OPTIONS_GHC -Wno-useless-specialisations #-} + +module T25389 where + +data Example (b :: Bool) where + Ex1 :: Int -> Example True + Ex2 :: Example False + +expensive :: Int -> Int +expensive = (*2) + +{-# SPECIALISE INLINE op :: Example False -> Int #-} +op :: Example b -> Int +op e = case e of + Ex1 i -> expensive i + Ex2 -> 0 diff --git a/testsuite/tests/simplCore/should_compile/T25389.stderr b/testsuite/tests/simplCore/should_compile/T25389.stderr new file mode 100644 index 00000000000..26db8c66e45 --- /dev/null +++ b/testsuite/tests/simplCore/should_compile/T25389.stderr @@ -0,0 +1,103 @@ + +==================== Tidy Core ==================== +Result size of Tidy Core + = {terms: 34, types: 46, coercions: 2, joins: 0/0} + +-- RHS size: {terms: 3, types: 2, coercions: 1, joins: 0/0} +T25389.$WEx1 [InlPrag=INLINE[final] CONLIKE] + :: Int %1 -> Example True +[GblId[DataConWrapper], + Arity=1, + Str=<L>, + Unf=Unf{Src=StableSystem, TopLvl=True, + Value=True, ConLike=True, WorkFree=True, Expandable=True, + Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=True) + Tmpl= \ (conrep [Occ=Once1] :: Int) -> + T25389.Ex1 + @True @~(<True>_N :: True GHC.Internal.Prim.~# True) conrep}] +T25389.$WEx1 + = \ (conrep [Occ=Once1] :: Int) -> + T25389.Ex1 + @True @~(<True>_N :: True GHC.Internal.Prim.~# True) conrep + +-- RHS size: {terms: 1, types: 1, coercions: 1, joins: 0/0} +T25389.$WEx2 [InlPrag=INLINE[final] CONLIKE] :: Example False +[GblId[DataConWrapper], + Unf=Unf{Src=StableSystem, TopLvl=True, + Value=True, ConLike=True, WorkFree=True, Expandable=True, + Guidance=ALWAYS_IF(arity=0,unsat_ok=True,boring_ok=False) + Tmpl= T25389.Ex2 + @False @~(<False>_N :: False GHC.Internal.Prim.~# False)}] +T25389.$WEx2 + = T25389.Ex2 + @False @~(<False>_N :: False GHC.Internal.Prim.~# False) + +-- RHS size: {terms: 8, types: 3, coercions: 0, joins: 0/0} +expensive :: Int -> Int +[GblId, + Arity=1, + Str=<1!P(L)>, + Cpr=1, + Unf=Unf{Src=StableSystem, TopLvl=True, + Value=True, ConLike=True, WorkFree=True, Expandable=True, + Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False) + Tmpl= \ (v [Occ=Once1!] :: Int) -> + case v of { GHC.Internal.Types.I# x [Occ=Once1] -> + GHC.Internal.Types.I# (GHC.Internal.Prim.*# x 2#) + }}] +expensive + = \ (v :: Int) -> + case v of { GHC.Internal.Types.I# x -> + GHC.Internal.Types.I# (GHC.Internal.Prim.*# x 2#) + } + +-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0} +T25389.op1 :: Int +[GblId, + Unf=Unf{Src=<vanilla>, TopLvl=True, + Value=True, ConLike=True, WorkFree=True, Expandable=True, + Guidance=IF_ARGS [] 10 10}] +T25389.op1 = GHC.Internal.Types.I# 0# + +-- RHS size: {terms: 5, types: 9, coercions: 0, joins: 0/0} +T25389.op_$sop [InlPrag=INLINE (sat-args=1)] + :: Example False -> Int +[GblId, + Arity=1, + Str=<1L>, + Cpr=1, + Unf=Unf{Src=StableSystem, TopLvl=True, + Value=True, ConLike=True, WorkFree=True, Expandable=True, + Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=True) + Tmpl= \ (e [Occ=Once1!] :: Example False) -> + case e of { Ex2 _ [Occ=Dead] -> T25389.op1 }}] +T25389.op_$sop + = \ (e :: Example False) -> + case e of { Ex2 co [Dmd=B] -> T25389.op1 } + +-- RHS size: {terms: 9, types: 16, coercions: 0, joins: 0/0} +op :: forall (b :: Bool). Example b -> Int +[GblId, + Arity=1, + Str=<1L>, + Cpr=1, + Unf=Unf{Src=StableSystem, TopLvl=True, + Value=True, ConLike=True, WorkFree=True, Expandable=True, + Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False) + Tmpl= \ (@(b :: Bool)) (e [Occ=Once1!] :: Example b) -> + case e of { + Ex1 _ [Occ=Dead] i [Occ=Once1] -> expensive i; + Ex2 _ [Occ=Dead] -> T25389.op1 + }}] +op + = \ (@(b :: Bool)) (e :: Example b) -> + case e of { + Ex1 co [Dmd=B] i -> expensive i; + Ex2 co [Dmd=B] -> T25389.op1 + } + + +------ Local rules for imported ids -------- +"USPEC op @'False" forall. op @False = T25389.op_$sop + + diff --git a/testsuite/tests/simplCore/should_compile/T4398.stderr b/testsuite/tests/simplCore/should_compile/T4398.stderr index 04b2db98e60..575703df874 100644 --- a/testsuite/tests/simplCore/should_compile/T4398.stderr +++ b/testsuite/tests/simplCore/should_compile/T4398.stderr @@ -1,6 +1,6 @@ - T4398.hs:6:11: warning: [GHC-40548] Forall'd constraint ‘Ord a’ is not bound in RULE lhs Orig bndrs: [a, $dOrd, x, y] Orig lhs: f @a ((\ ($dOrd :: Ord a) -> x) $dOrd) y - optimised lhs: f @a x y + Optimised lhs: f @a x y + diff --git a/testsuite/tests/simplCore/should_compile/T5821.hs b/testsuite/tests/simplCore/should_compile/T5821.hs index 762254cb548..5fceb605a4d 100644 --- a/testsuite/tests/simplCore/should_compile/T5821.hs +++ b/testsuite/tests/simplCore/should_compile/T5821.hs @@ -9,3 +9,6 @@ foo :: Num a => a -> T a foo = undefined {-# SPECIALISE foo :: Int -> Bool #-} +{- # SPECIALISE (foo :: Int -> Bool) # -} +{- # SPECIALISE forall x. foo (x::Int) :: Bool # -} +{- # SPECIALISE forall x. (foo :: Int -> Bool) x # -} diff --git a/testsuite/tests/simplCore/should_compile/T8537.stderr b/testsuite/tests/simplCore/should_compile/T8537.stderr index 8d85318f06d..805d9bd5e2e 100644 --- a/testsuite/tests/simplCore/should_compile/T8537.stderr +++ b/testsuite/tests/simplCore/should_compile/T8537.stderr @@ -1,3 +1,5 @@ +T8537.hs:20:5: warning: [GHC-66582] [-Wuseless-specialisations (in -Wdefault)] + Dubious SPECIALISE pragma for ‘fmap’. + The pragma does not specialise away any class dictionaries, + and neither is there any value specialisation. -T8537.hs:20:5: warning: [GHC-35827] - SPECIALISE pragma for non-overloaded function ‘fmap’ diff --git a/testsuite/tests/simplCore/should_compile/T9578b.hs b/testsuite/tests/simplCore/should_compile/T9578b.hs new file mode 100644 index 00000000000..05534c1b803 --- /dev/null +++ b/testsuite/tests/simplCore/should_compile/T9578b.hs @@ -0,0 +1,84 @@ +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE RoleAnnotations #-} + +module Main where + +import Data.Array.Byte + ( ByteArray(..) ) +import Data.Ix + ( Ix, range, index, rangeSize ) +import GHC.Show + ( appPrec ) +import GHC.Exts + ( Int(..), ByteArray#, indexWord64Array# ) +import GHC.IsList + ( fromList ) +import GHC.Word + ( Word8, Word64(..) ) + +data UArray i e = UArray !i !i !Int ByteArray# +type role UArray nominal nominal + +class IArray a e where + bounds :: Ix i => a i e -> (i,i) + numElements :: Ix i => a i e -> Int + unsafeAt :: Ix i => a i e -> Int -> e + +{-# INLINE safeRangeSize #-} +safeRangeSize :: Ix i => (i, i) -> Int +safeRangeSize (l,u) = let r = rangeSize (l, u) + in if r < 0 then error "Negative range size" + else r + +{-# INLINE safeIndex #-} +safeIndex :: Ix i => (i, i) -> Int -> i -> Int +safeIndex (l,u) n i = let i' = index (l,u) i + in if (0 <= i') && (i' < n) + then i' + else error ("Error in array index; " ++ show i' ++ + " not in range [0.." ++ show n ++ ")") + +{-# INLINE (!) #-} +-- | Returns the element of an immutable array at the specified index, +-- or throws an exception if the index is out of bounds. +(!) :: (IArray a e, Ix i) => a i e -> i -> e +(!) arr i = case bounds arr of + (l,u) -> unsafeAt arr $ safeIndex (l,u) (numElements arr) i + + +{-# INLINE assocs #-} +-- | Returns the contents of an array as a list of associations. +assocs :: (IArray a e, Ix i) => a i e -> [(i, e)] +assocs arr = case bounds arr of + (l,u) -> [(i, arr ! i) | i <- range (l,u)] + +showsIArray :: (IArray a e, Ix i, Show i, Show e) => Int -> a i e -> ShowS +showsIArray p a = + showParen (p > appPrec) $ + showString "array " . + shows (bounds a) . + showChar ' ' . + shows (assocs a) + +{-# SPECIALISE + showsIArray :: (IArray UArray e, Ix i, Show i, Show e) => + Int -> UArray i e -> ShowS + #-} + +instance IArray UArray Word64 where + {-# INLINE bounds #-} + bounds (UArray l u _ _) = (l,u) + {-# INLINE numElements #-} + numElements (UArray _ _ n _) = n + {-# INLINE unsafeAt #-} + unsafeAt (UArray _ _ _ arr#) (I# i#) = W64# (indexWord64Array# arr# i#) + +instance (Ix ix, Show ix, Show e, IArray UArray e) => Show (UArray ix e) where + showsPrec = showsIArray + + +main :: IO () +main = do + let ba :: ByteArray# + ByteArray ba = fromList ( replicate (2 * 8) 0 :: [ Word8 ] ) + print (UArray 0 1 2 ba :: UArray Int Word64) diff --git a/testsuite/tests/simplCore/should_compile/all.T b/testsuite/tests/simplCore/should_compile/all.T index 9fb64293a68..8a2d21c8c42 100644 --- a/testsuite/tests/simplCore/should_compile/all.T +++ b/testsuite/tests/simplCore/should_compile/all.T @@ -502,6 +502,7 @@ test('T23491d', [extra_files(['T23491.hs']), grep_errmsg(r'Static argument')], m test('T23074', normal, compile, ['-O -ddump-rules']) test('T23272', [only_ways(['ghci']), extra_hc_opts('-fno-unoptimized-core-for-interpreter -O')], ghci_script, ['T23272.script']) test('T23567', [extra_files(['T23567A.hs'])], multimod_compile, ['T23567', '-O -v0']) +test('DsSpecPragmas', normal, compile, ['-O -ddump-rules']) # The -ddump-simpl of T22404 should have no let-bindings test('T22404', [only_ways(['optasm']), check_errmsg(r'let') ], compile, ['-ddump-simpl -dsuppress-uniques']) @@ -532,4 +533,6 @@ test('T24725a', [ grep_errmsg(r'testedRule')], compile, ['-O -ddump-rule-firings test('T25033', normal, compile, ['-O']) test('T25160', normal, compile, ['-O -ddump-rules']) test('T25197', [req_th, extra_files(["T25197_TH.hs"]), only_ways(['optasm'])], multimod_compile, ['T25197', '-O2 -v0']) +test('T25389', normal, compile, ['-O -ddump-simpl -dsuppress-uniques -dno-typeable-binds']) +test('T24359a', normal, compile, ['-O -ddump-rules']) test('T25713', [grep_errmsg('W:::')], compile, ['-O -ddump-simpl']) diff --git a/testsuite/tests/simplCore/should_compile/simpl016.stderr b/testsuite/tests/simplCore/should_compile/simpl016.stderr deleted file mode 100644 index 2c6e508c73e..00000000000 --- a/testsuite/tests/simplCore/should_compile/simpl016.stderr +++ /dev/null @@ -1,10 +0,0 @@ -simpl016.hs:7:1: warning: [GHC-40548] - Forall'd constraint ‘Num b’ is not bound in RULE lhs - Orig bndrs: [b, $dNum] - Orig lhs: let { - $dEq :: Eq Int - [LclId] - $dEq = GHC.Internal.Classes.$fEqInt } in - delta' @Int @b $dEq - optimised lhs: delta' @Int @b $dEq - diff --git a/testsuite/tests/simplCore/should_fail/T25117a.hs b/testsuite/tests/simplCore/should_fail/T25117a.hs new file mode 100644 index 00000000000..3ab2bce9ebe --- /dev/null +++ b/testsuite/tests/simplCore/should_fail/T25117a.hs @@ -0,0 +1,6 @@ +module T25117a where + +f :: Ord a => a -> a +f = f + +{-# SPECIALISE let x = 2 in f x #-} diff --git a/testsuite/tests/simplCore/should_fail/T25117a.stderr b/testsuite/tests/simplCore/should_fail/T25117a.stderr new file mode 100644 index 00000000000..f4131cb7a28 --- /dev/null +++ b/testsuite/tests/simplCore/should_fail/T25117a.stderr @@ -0,0 +1,2 @@ +T25117a.hs:6:1: error: [GHC-93944] + Illegal form of SPECIALISE pragma: let x = 2 in f x diff --git a/testsuite/tests/simplCore/should_fail/T25117b.hs b/testsuite/tests/simplCore/should_fail/T25117b.hs new file mode 100644 index 00000000000..8d89c5c4908 --- /dev/null +++ b/testsuite/tests/simplCore/should_fail/T25117b.hs @@ -0,0 +1,7 @@ +module T25117b where + +f :: Num a => a -> a +f = f + +-- We don't allow old-form multiple type ascriptions +{-# SPECIALISE forall . f :: Int->Int, Float->Float #-} diff --git a/testsuite/tests/simplCore/should_fail/T25117b.stderr b/testsuite/tests/simplCore/should_fail/T25117b.stderr new file mode 100644 index 00000000000..b68403c0348 --- /dev/null +++ b/testsuite/tests/simplCore/should_fail/T25117b.stderr @@ -0,0 +1,5 @@ +T25117b.hs:7:27: error: [GHC-62037] + SPECIALISE expression doesn't support multiple type ascriptions + Suggested fix: + Split the SPECIALISE pragma into multiple pragmas, one for each type signature + diff --git a/testsuite/tests/simplCore/should_fail/all.T b/testsuite/tests/simplCore/should_fail/all.T index 85411e69dbb..87781924811 100644 --- a/testsuite/tests/simplCore/should_fail/all.T +++ b/testsuite/tests/simplCore/should_fail/all.T @@ -2,5 +2,7 @@ test('T7411', [expect_broken_for(7411, ['optasm', 'optllvm', 'threaded2', 'dyn']), exit_code(1)], compile_and_run, ['']) +test('T25117a', normal, compile_fail, ['']) +test('T25117b', normal, compile_fail, ['']) # This one produces a warning test('T25672', normal, compile, ['-O']) diff --git a/testsuite/tests/simplCore/should_run/T24359b.hs b/testsuite/tests/simplCore/should_run/T24359b.hs new file mode 100644 index 00000000000..c742d8c9822 --- /dev/null +++ b/testsuite/tests/simplCore/should_run/T24359b.hs @@ -0,0 +1,24 @@ +-- At some point when developing the new capability for +-- #24359 (SEPECIALISE pragmas on values), this program +-- went into a in infinite loop. So just keeping it here +-- as a rgression test + +module Main where + +data UA i = UA !i + +class IArray a where + bounds :: a i -> i + +showsIArray :: (IArray a, Show i) => a i -> String +showsIArray a = show (bounds a) + +{-# SPECIALISE + showsIArray :: (Show i) => UA i -> String + #-} + +instance IArray UA where + bounds (UA u) = u + +main :: IO () +main = putStrLn $ showsIArray (UA 1 :: UA Int) diff --git a/testsuite/tests/simplCore/should_run/T24359b.stdout b/testsuite/tests/simplCore/should_run/T24359b.stdout new file mode 100644 index 00000000000..d00491fd7e5 --- /dev/null +++ b/testsuite/tests/simplCore/should_run/T24359b.stdout @@ -0,0 +1 @@ +1 diff --git a/testsuite/tests/simplCore/should_run/all.T b/testsuite/tests/simplCore/should_run/all.T index f662cdf9dfa..8857537c403 100644 --- a/testsuite/tests/simplCore/should_run/all.T +++ b/testsuite/tests/simplCore/should_run/all.T @@ -118,3 +118,4 @@ test('T23056', [only_ways(['ghci-opt'])], ghci_script, ['T23056.script']) test('T24725', normal, compile_and_run, ['-O -dcore-lint']) test('T25096', normal, compile_and_run, ['-O -dcore-lint']) test('AppIsHNF', normal, compile_and_run, ['-O']) +test('T24359b', normal, compile_and_run, ['-O']) diff --git a/testsuite/tests/th/T13123.stderr b/testsuite/tests/th/T13123.stderr new file mode 100644 index 00000000000..df56637b0c9 --- /dev/null +++ b/testsuite/tests/th/T13123.stderr @@ -0,0 +1,5 @@ +T13123.hs:15:2: warning: [GHC-66582] [-Wuseless-specialisations (in -Wdefault)] + Dubious SPECIALISE pragma for ‘id2’. + The pragma does not specialise away any class dictionaries, + and neither is there any value specialisation. + diff --git a/testsuite/tests/th/T19363.stdout b/testsuite/tests/th/T19363.stdout index a6f3fee5b59..555ef4eeeed 100644 --- a/testsuite/tests/th/T19363.stdout +++ b/testsuite/tests/th/T19363.stdout @@ -9,9 +9,9 @@ infix 5 `f_6` (%%_7) :: [a_8] -> [a_8] -> [a_8] (%%_7) = (GHC.Internal.Base.++) {-# INLINE (%%_7) #-} -{-# SPECIALISE (%%_7) :: - GHC.Internal.Base.String -> - GHC.Internal.Base.String -> GHC.Internal.Base.String #-} +{-# SPECIALISE + (%%_7) :: GHC.Internal.Base.String -> + GHC.Internal.Base.String -> GHC.Internal.Base.String #-} {-# ANN (%%_7) "blah" #-} g_9 ((:**_2) {(^**_3) = x_10}) = x_10 pattern a_11 `H_12` b_13 = (a_11, b_13) diff --git a/testsuite/tests/th/T7064.stdout b/testsuite/tests/th/T7064.stdout index 0d37f3cd108..e6dd25ab715 100644 --- a/testsuite/tests/th/T7064.stdout +++ b/testsuite/tests/th/T7064.stdout @@ -7,12 +7,12 @@ f3_0 x_1 = 3 g1_0 x_1 = 1 g2_0 x_1 = 2 g3_0 x_1 = 3 -{-# SPECIALISE g1_0 :: - GHC.Internal.Types.Int -> GHC.Internal.Types.Int #-} -{-# SPECIALISE [2] g2_0 :: - GHC.Internal.Types.Int -> GHC.Internal.Types.Int #-} -{-# SPECIALISE INLINE [~2] g3_0 :: - GHC.Internal.Types.Int -> GHC.Internal.Types.Int #-} +{-# SPECIALISE + g1_0 :: GHC.Internal.Types.Int -> GHC.Internal.Types.Int #-} +{-# SPECIALISE [2] + g2_0 :: GHC.Internal.Types.Int -> GHC.Internal.Types.Int #-} +{-# SPECIALISE INLINE [~2] + g3_0 :: GHC.Internal.Types.Int -> GHC.Internal.Types.Int #-} data T_0 a_1 = T_2 a_1 instance GHC.Internal.Classes.Eq a_0 => GHC.Internal.Classes.Eq (T_1 a_0) where {{-# SPECIALISE instance GHC.Internal.Classes.Eq (T_1 GHC.Internal.Types.Int) #-}; diff --git a/testsuite/tests/th/TH_pragma.hs b/testsuite/tests/th/TH_pragma.hs index a78b5483fe5..6f53a896c58 100644 --- a/testsuite/tests/th/TH_pragma.hs +++ b/testsuite/tests/th/TH_pragma.hs @@ -10,3 +10,7 @@ $( [d| foo :: Int -> Int $( [d| bar :: Num a => a -> a {-# SPECIALISE INLINE [~1] bar :: Float -> Float #-} bar x = x * 10 |] ) + +$( [d| baz :: Num a => a -> a + {-# SPECIALISE INLINE [~1] baz @Double #-} + baz x = x * 10 |] ) diff --git a/testsuite/tests/th/TH_pragma.stderr b/testsuite/tests/th/TH_pragma.stderr index 0baf21c564c..978d333bde7 100644 --- a/testsuite/tests/th/TH_pragma.stderr +++ b/testsuite/tests/th/TH_pragma.stderr @@ -14,3 +14,11 @@ TH_pragma.hs:(10,2)-(12,33): Splicing declarations bar :: Num a => a -> a {-# SPECIALISE INLINE [~1] bar :: Float -> Float #-} bar x = (x * 10) +TH_pragma.hs:(14,2)-(16,33): Splicing declarations + [d| baz :: Num a => a -> a + {-# SPECIALISE INLINE [~1] baz @Double #-} + baz x = x * 10 |] + ======> + baz :: Num a => a -> a + {-# SPECIALISE INLINE [~1] baz @Double #-} + baz x = (x * 10) diff --git a/testsuite/tests/typecheck/should_compile/T10504.stderr b/testsuite/tests/typecheck/should_compile/T10504.stderr index ed8b83514b6..8eb4f037a63 100644 --- a/testsuite/tests/typecheck/should_compile/T10504.stderr +++ b/testsuite/tests/typecheck/should_compile/T10504.stderr @@ -1,3 +1,5 @@ +T10504.hs:5:1: warning: [GHC-66582] [-Wuseless-specialisations (in -Wdefault)] + Dubious SPECIALISE pragma for ‘myfun’. + The pragma does not specialise away any class dictionaries, + and neither is there any value specialisation. -T10504.hs:5:1: warning: [GHC-35827] - SPECIALISE pragma for non-overloaded function ‘myfun’ diff --git a/testsuite/tests/typecheck/should_compile/T2494.stderr b/testsuite/tests/typecheck/should_compile/T2494.stderr index 931cd5874f8..06457c45301 100644 --- a/testsuite/tests/typecheck/should_compile/T2494.stderr +++ b/testsuite/tests/typecheck/should_compile/T2494.stderr @@ -1,4 +1,3 @@ - T2494.hs:15:14: error: [GHC-25897] • Couldn't match type ‘b’ with ‘a’ Expected: Maybe (m a) -> Maybe (m a) @@ -13,11 +12,11 @@ T2494.hs:15:14: error: [GHC-25897] In the second argument of ‘foo’, namely ‘(foo g x)’ In the expression: foo f (foo g x) • Relevant bindings include - f :: forall (m :: * -> *). Monad m => Maybe (m a) -> Maybe (m a) - (bound at T2494.hs:13:11) + x :: Maybe a (bound at T2494.hs:14:65) g :: forall (m :: * -> *). Monad m => Maybe (m b) -> Maybe (m b) (bound at T2494.hs:14:11) - x :: Maybe a (bound at T2494.hs:14:65) + f :: forall (m :: * -> *). Monad m => Maybe (m a) -> Maybe (m a) + (bound at T2494.hs:13:11) T2494.hs:15:30: error: [GHC-25897] • Couldn't match type ‘b’ with ‘a’ @@ -33,8 +32,9 @@ T2494.hs:15:30: error: [GHC-25897] In the first argument of ‘foo’, namely ‘(f . g)’ In the expression: foo (f . g) x • Relevant bindings include - f :: forall (m :: * -> *). Monad m => Maybe (m a) -> Maybe (m a) - (bound at T2494.hs:13:11) + x :: Maybe a (bound at T2494.hs:14:65) g :: forall (m :: * -> *). Monad m => Maybe (m b) -> Maybe (m b) (bound at T2494.hs:14:11) - x :: Maybe a (bound at T2494.hs:14:65) + f :: forall (m :: * -> *). Monad m => Maybe (m a) -> Maybe (m a) + (bound at T2494.hs:13:11) + diff --git a/testsuite/tests/typecheck/should_compile/TcSpecPragmas.hs b/testsuite/tests/typecheck/should_compile/TcSpecPragmas.hs new file mode 100644 index 00000000000..67e7e23e63c --- /dev/null +++ b/testsuite/tests/typecheck/should_compile/TcSpecPragmas.hs @@ -0,0 +1,56 @@ + +{-# LANGUAGE GADTs, NamedWildCards, PartialTypeSignatures #-} + +module SpecPragmas where + +import Data.Proxy + ( Proxy(..) ) +import Data.Type.Equality + ( (:~~:)(HRefl) ) +import Data.Typeable + ( Typeable, heqT ) + +-------------------------------------------------------------------------------- + +foo :: Num a => a -> a +foo x = x + 1 + +{-# SPECIALISE foo @Int #-} + +{-# SPECIALISE foo @Float :: Float -> Float #-} + +{-# SPECIALISE foo (3 :: Int) #-} +{-# SPECIALISE foo @Int 4 #-} + + +{-# SPECIALISE INLINE foo @Double #-} + +bar :: ( Num a, Integral i ) => a -> i -> a +bar x y = x + fromIntegral y + +{-# SPECIALISE bar @Float :: Float -> Int -> Float #-} + +{-# SPECIALISE bar @Double 3 :: Integer -> Double #-} + +{-# SPECIALISE [1] bar @_ @Int #-} + +{-# SPECIALISE bar @_a @_a #-} + +baz :: (Real a, Integral b, Fractional c) => a -> b -> c +baz a b = realToFrac a + fromIntegral b + +{-# SPECIALISE [~1] forall a. forall. baz @a @_ @a #-} + +-------------------------------------------------------------------------------- + +tyEq :: ( Typeable a, Typeable b ) => Proxy a -> Proxy b -> Float +tyEq ( _ :: Proxy a ) ( _ :: Proxy b ) = + case heqT @a @b of + Nothing -> 17.9 + Just HRefl -> 1.1 + +-- Check that we don't emit a "useless specialisation" warning, as the +-- specialisation allows us to drop dead code in the body of 'tyEq'. +{-# SPECIALISE tyEq :: Typeable c => Proxy c -> Proxy c -> Float #-} + +-------------------------------------------------------------------------------- diff --git a/testsuite/tests/typecheck/should_compile/TcSpecPragmas.stderr b/testsuite/tests/typecheck/should_compile/TcSpecPragmas.stderr new file mode 100644 index 00000000000..b39e35fd274 --- /dev/null +++ b/testsuite/tests/typecheck/should_compile/TcSpecPragmas.stderr @@ -0,0 +1,15 @@ +TcSpecPragmas.hs:37:1: warning: [GHC-66582] [-Wuseless-specialisations (in -Wdefault)] + Dubious SPECIALISE pragma for ‘bar’. + The pragma does not specialise away any class dictionaries, + and neither is there any value specialisation. + +TcSpecPragmas.hs:42:1: warning: [GHC-66582] [-Wuseless-specialisations (in -Wdefault)] + Dubious SPECIALISE pragma for ‘baz’. + The pragma does not specialise away any class dictionaries, + and neither is there any value specialisation. + +TcSpecPragmas.hs:54:1: warning: [GHC-66582] [-Wuseless-specialisations (in -Wdefault)] + Dubious SPECIALISE pragma for ‘tyEq’. + The pragma does not specialise away any class dictionaries, + and neither is there any value specialisation. + diff --git a/testsuite/tests/typecheck/should_compile/all.T b/testsuite/tests/typecheck/should_compile/all.T index 2958e845fb9..1b68ab6de76 100644 --- a/testsuite/tests/typecheck/should_compile/all.T +++ b/testsuite/tests/typecheck/should_compile/all.T @@ -738,6 +738,7 @@ test('T19042', normal, compile, ['']) test('ExplicitSpecificityA1', normal, compile, ['']) test('ExplicitSpecificityA2', normal, compile, ['']) test('ExplicitSpecificity4', normal, compile, ['']) +test('TcSpecPragmas', normal, compile, ['']) test('T17775-viewpats-a', normal, compile, ['']) test('T17775-viewpats-b', normal, compile_fail, ['']) test('T17775-viewpats-c', normal, compile_fail, ['']) diff --git a/testsuite/tests/typecheck/should_compile/tc186.hs b/testsuite/tests/typecheck/should_compile/tc186.hs index a99b956098f..b1db254080e 100644 --- a/testsuite/tests/typecheck/should_compile/tc186.hs +++ b/testsuite/tests/typecheck/should_compile/tc186.hs @@ -2,7 +2,7 @@ -- Killed 6.2.2 -- The trouble was that 1 was instantiated to a type (t::?) -- and the constraint (Foo (t::? -> s::*)) didn't match Foo (a::* -> b::*). --- Solution is to zap the expected type in TcEpxr.tc_expr(HsOverLit). +-- Solution is to zap the expected type in TcExpr.tc_expr(HsOverLit). module ShouldCompile where diff --git a/testsuite/tests/typecheck/should_compile/tc212.hs b/testsuite/tests/typecheck/should_compile/tc212.hs index ad408fbdaf4..1903fd3961c 100644 --- a/testsuite/tests/typecheck/should_compile/tc212.hs +++ b/testsuite/tests/typecheck/should_compile/tc212.hs @@ -4,5 +4,6 @@ module ShouldCompile where -- A specialise pragma with no type signature +-- fac :: Num a => a -> a fac n = fac (n + 1) {-# SPECIALISE fac :: Int -> Int #-} diff --git a/testsuite/tests/typecheck/should_fail/SpecPragmasFail.hs b/testsuite/tests/typecheck/should_fail/SpecPragmasFail.hs new file mode 100644 index 00000000000..f403876a0df --- /dev/null +++ b/testsuite/tests/typecheck/should_fail/SpecPragmasFail.hs @@ -0,0 +1,14 @@ + +module SpecPragmasFail where + +foo :: Num a => a -> a +foo x = x + 1 + +{-# SPECIALISE foo @Integer :: Int -> Int #-} + +{-# SPECIALISE foo @Bool #-} + +bar :: a ~ Int => a +bar = 3 + +{-# SPECIALISE bar @Char #-} diff --git a/testsuite/tests/typecheck/should_fail/SpecPragmasFail.stderr b/testsuite/tests/typecheck/should_fail/SpecPragmasFail.stderr new file mode 100644 index 00000000000..ef4c4d22c0b --- /dev/null +++ b/testsuite/tests/typecheck/should_fail/SpecPragmasFail.stderr @@ -0,0 +1,6 @@ +SpecPragmasFail.hs:7:16: error: [GHC-83865] + • Couldn't match type ‘Integer’ with ‘Int’ + Expected: Int -> Int + Actual: Integer -> Integer + • In the expression: foo @Integer :: Int -> Int + diff --git a/testsuite/tests/typecheck/should_fail/T5853.stderr b/testsuite/tests/typecheck/should_fail/T5853.stderr index 1e71808b4ec..857147a7de7 100644 --- a/testsuite/tests/typecheck/should_fail/T5853.stderr +++ b/testsuite/tests/typecheck/should_fail/T5853.stderr @@ -1,4 +1,3 @@ - T5853.hs:15:52: error: [GHC-25897] • Could not deduce ‘Subst fa2 (Elem fb) ~ fb’ arising from a use of ‘<$>’ @@ -13,6 +12,7 @@ T5853.hs:15:52: error: [GHC-25897] • In the expression: (f . g) <$> xs When checking the rewrite rule "map/map" • Relevant bindings include - f :: Elem fa1 -> Elem fb (bound at T5853.hs:15:19) - g :: Elem fa2 -> Elem fa1 (bound at T5853.hs:15:21) xs :: fa2 (bound at T5853.hs:15:23) + g :: Elem fa2 -> Elem fa1 (bound at T5853.hs:15:21) + f :: Elem fa1 -> Elem fb (bound at T5853.hs:15:19) + diff --git a/testsuite/tests/warnings/should_compile/SpecMultipleTys.hs b/testsuite/tests/warnings/should_compile/SpecMultipleTys.hs new file mode 100644 index 00000000000..233949bcf2c --- /dev/null +++ b/testsuite/tests/warnings/should_compile/SpecMultipleTys.hs @@ -0,0 +1,12 @@ +{-# LANGUAGE GADTs #-} +{-# LANGUAGE DataKinds #-} + +module SpecMultipleTys where + +-- NB: this program should be rejected starting from GHC 9.18. +-- See GHC ticket #25540. + +foo :: Num a => a -> a +foo x = 2 * ( x + 1 ) + +{-# SPECIALISE foo :: Float -> Float, Double -> Double #-} diff --git a/testsuite/tests/warnings/should_compile/SpecMultipleTys.stderr b/testsuite/tests/warnings/should_compile/SpecMultipleTys.stderr new file mode 100644 index 00000000000..848a98c9a3f --- /dev/null +++ b/testsuite/tests/warnings/should_compile/SpecMultipleTys.stderr @@ -0,0 +1,5 @@ +SpecMultipleTys.hs:12:20: warning: [GHC-73026] [-Wdeprecated-pragmas (in -Wdefault)] + SPECIALISE pragmas with multiple type ascriptions are deprecated, and will be removed in GHC 9.18 + Suggested fix: + Split the SPECIALISE pragma into multiple pragmas, one for each type signature + diff --git a/testsuite/tests/warnings/should_compile/T19296.stderr b/testsuite/tests/warnings/should_compile/T19296.stderr index cfa7cb31324..e8eaa754d77 100644 --- a/testsuite/tests/warnings/should_compile/T19296.stderr +++ b/testsuite/tests/warnings/should_compile/T19296.stderr @@ -24,24 +24,11 @@ T19296.hs:13:6: warning: [GHC-30606] [-Wredundant-constraints] 13 | h :: (Eq a, Ord b) => a -> b -> b | ^^^^^^^^^^^^^ -T19296.hs:21:1: warning: [GHC-40548] - Forall'd constraint ‘Eq a’ is not bound in RULE lhs - Orig bndrs: [a, $dEq] - Orig lhs: let { - $dOrd :: Ord Int - [LclId] - $dOrd = GHC.Internal.Classes.$fOrdInt } in - spec @Int @a $dOrd - optimised lhs: spec @Int @a $dOrd - | -21 | {-# SPECIALISE spec :: Eq a => a -> Int -> Int #-} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - T19296.hs:21:24: warning: [GHC-30606] [-Wredundant-constraints] • Redundant constraint: Eq a - In the type signature for: - spec :: forall a. Eq a => a -> Int -> Int - • In the pragma: {-# SPECIALISE spec :: Eq a => a -> Int -> Int #-} + In an expression type signature: + forall a. Eq a => a -> Int -> Int + • In the expression: spec :: Eq a => a -> Int -> Int | 21 | {-# SPECIALISE spec :: Eq a => a -> Int -> Int #-} | ^^^^ diff --git a/testsuite/tests/warnings/should_compile/all.T b/testsuite/tests/warnings/should_compile/all.T index daed779ae82..9d321516f36 100644 --- a/testsuite/tests/warnings/should_compile/all.T +++ b/testsuite/tests/warnings/should_compile/all.T @@ -71,3 +71,4 @@ test('T23573', [extra_files(["T23573.hs", "T23573A.hs", "T23573B.hs"])], multimo test('T23465', normal, compile, ['-ddump-parsed']) test('WarnNoncanonical', normal, compile, ['']) test('T24396', [extra_files(["T24396a.hs", "T24396b.hs"])], multimod_compile, ['T24396b', '']) +test('SpecMultipleTys', normal, compile, ['']) # compile_fail from GHC 9.18 diff --git a/testsuite/tests/warnings/should_fail/SpecEMultipleTys.hs b/testsuite/tests/warnings/should_fail/SpecEMultipleTys.hs new file mode 100644 index 00000000000..66312775ac8 --- /dev/null +++ b/testsuite/tests/warnings/should_fail/SpecEMultipleTys.hs @@ -0,0 +1,10 @@ +{-# LANGUAGE GADTs #-} +{-# LANGUAGE DataKinds #-} + +module SpecEMultipleTys where + +bar :: Num a => a -> a -> a +bar x y = 2 * ( x + y ) + +-- The "specialise expression" syntax doesn't support multiple type ascriptions. +{-# SPECIALISE bar 3 :: Float -> Float, Double -> Double #-} diff --git a/testsuite/tests/warnings/should_fail/SpecEMultipleTys.stderr b/testsuite/tests/warnings/should_fail/SpecEMultipleTys.stderr new file mode 100644 index 00000000000..95faf254dc1 --- /dev/null +++ b/testsuite/tests/warnings/should_fail/SpecEMultipleTys.stderr @@ -0,0 +1,5 @@ +SpecEMultipleTys.hs:10:22: error: [GHC-62037] + SPECIALISE expression doesn't support multiple type ascriptions + Suggested fix: + Split the SPECIALISE pragma into multiple pragmas, one for each type signature + diff --git a/testsuite/tests/warnings/should_fail/all.T b/testsuite/tests/warnings/should_fail/all.T index aafc92e6dca..4f08dffd1f8 100644 --- a/testsuite/tests/warnings/should_fail/all.T +++ b/testsuite/tests/warnings/should_fail/all.T @@ -27,3 +27,4 @@ test('WarningCategory6', [extra_files(['WarningCategory1.hs', 'WarningCategory1_ test('WarningCategory7', [extra_files(['WarningCategory1.hs', 'WarningCategory1_B.hs', 'WarningCategoryModule.hs'])], multimod_compile_fail, ['WarningCategory1', '-v0 -Werror -w -Wall']) test('WarningCategoryInvalid', normal, compile_fail, ['']) test('T24396c', normal, compile_fail, ['']) +test('SpecEMultipleTys', normal, compile_fail, ['']) diff --git a/utils/check-exact/ExactPrint.hs b/utils/check-exact/ExactPrint.hs index 74979771158..451cb7cde84 100644 --- a/utils/check-exact/ExactPrint.hs +++ b/utils/check-exact/ExactPrint.hs @@ -330,7 +330,7 @@ instance HasTrailing AnnSig where trailing _ = [] setTrailing a _ = a -instance HasTrailing HsRuleAnn where +instance HasTrailing HsRuleBndrsAnn where trailing _ = [] setTrailing a _ = a @@ -1140,17 +1140,15 @@ lhsCaseAnnOf k parent = fmap (\new -> parent { hsCaseAnnOf = new }) -- --------------------------------------------------------------------- --- data HsRuleAnn --- = HsRuleAnn --- { ra_tyanns :: Maybe (TokForall, EpToken ".") --- , ra_tmanns :: Maybe (TokForall, EpToken ".") --- , ra_equal :: EpToken "=" --- , ra_rest :: ActivationAnn --- } deriving (Data, Eq) +-- data HsRuleBndrsAnn +-- = HsRuleBndrsAnn +-- { rb_tyanns :: Maybe (TokForall, EpToken ".") +-- , rb_tmanns :: Maybe (TokForall, EpToken ".") +-- } -lra_tyanns :: Lens HsRuleAnn (Maybe (TokForall, EpToken ".")) -lra_tyanns k parent = fmap (\new -> parent { ra_tyanns = new }) - (k (ra_tyanns parent)) +lrb_tyanns :: Lens HsRuleBndrsAnn (Maybe (TokForall, EpToken ".")) +lrb_tyanns k parent = fmap (\new -> parent { rb_tyanns = new }) + (k (rb_tyanns parent)) ff :: Maybe (a,b) -> (Maybe a,Maybe b) ff Nothing = (Nothing, Nothing) @@ -1167,30 +1165,21 @@ lff k parent = fmap (\new -> gg new) (k (ff parent)) -- (.) :: Lens' a b -> Lens' b c -> Lens' a c -lra_tyanns_fst :: Lens HsRuleAnn (Maybe TokForall) -lra_tyanns_fst = lra_tyanns . lff . lfst +lrb_tyanns_fst :: Lens HsRuleBndrsAnn (Maybe TokForall) +lrb_tyanns_fst = lrb_tyanns . lff . lfst -lra_tyanns_snd :: Lens HsRuleAnn (Maybe (EpToken ".")) -lra_tyanns_snd = lra_tyanns . lff . lsnd +lrb_tyanns_snd :: Lens HsRuleBndrsAnn (Maybe (EpToken ".")) +lrb_tyanns_snd = lrb_tyanns . lff . lsnd -lra_tmanns :: Lens HsRuleAnn (Maybe (TokForall, EpToken ".")) -lra_tmanns k parent = fmap (\new -> parent { ra_tmanns = new }) - (k (ra_tmanns parent)) +lrb_tmanns :: Lens HsRuleBndrsAnn (Maybe (TokForall, EpToken ".")) +lrb_tmanns k parent = fmap (\new -> parent { rb_tmanns = new }) + (k (rb_tmanns parent)) -lra_tmanns_fst :: Lens HsRuleAnn (Maybe TokForall) -lra_tmanns_fst = lra_tmanns . lff . lfst - -lra_tmanns_snd :: Lens HsRuleAnn (Maybe (EpToken ".")) -lra_tmanns_snd = lra_tmanns . lff . lsnd - -lra_equal :: Lens HsRuleAnn (EpToken "=") -lra_equal k parent = fmap (\new -> parent { ra_equal = new }) - (k (ra_equal parent)) - -lra_rest :: Lens HsRuleAnn ActivationAnn -lra_rest k parent = fmap (\new -> parent { ra_rest = new }) - (k (ra_rest parent)) +lrb_tmanns_fst :: Lens HsRuleBndrsAnn (Maybe TokForall) +lrb_tmanns_fst = lrb_tmanns . lff . lfst +lrb_tmanns_snd :: Lens HsRuleBndrsAnn (Maybe (EpToken ".")) +lrb_tmanns_snd = lrb_tmanns . lff . lsnd -- --------------------------------------------------------------------- -- data GrhsAnn @@ -2016,33 +2005,14 @@ instance ExactPrint (RuleDecl GhcPs) where getAnnotationEntry _ = NoEntryVal setAnnotationAnchor a _ _ _ = a - exact (HsRule (an,nsrc) (L ln n) act mtybndrs termbndrs lhs rhs) = do + exact (HsRule ((ann_act, ann_eq),nsrc) (L ln n) act bndrs lhs rhs) = do (L ln' _) <- markAnnotated (L ln (nsrc, n)) - an0 <- markActivationL an lra_rest act - (an1, mtybndrs') <- - case mtybndrs of - Nothing -> return (an0, Nothing) - Just bndrs -> do - an1 <- markLensFun an0 lra_tyanns_fst (\mt -> mapM markEpUniToken mt) -- AnnForall - bndrs' <- mapM markAnnotated bndrs - an2 <- markLensFun an1 lra_tyanns_snd (\mt -> mapM markEpToken mt) -- AnnDot - return (an2, Just bndrs') - - an2 <- markLensFun an1 lra_tmanns_fst (\mt -> mapM markEpUniToken mt) -- AnnForall - termbndrs' <- mapM markAnnotated termbndrs - an3 <- markLensFun an2 lra_tmanns_snd (\mt -> mapM markEpToken mt) -- AnnDot - + ann_act' <- markActivation ann_act act + bndrs' <- markAnnotated bndrs lhs' <- markAnnotated lhs - an4 <- markLensFun an3 lra_equal markEpToken + ann_eq' <- markEpToken ann_eq rhs' <- markAnnotated rhs - return (HsRule (an4,nsrc) (L ln' n) act mtybndrs' termbndrs' lhs' rhs') - - -markActivationL :: (Monad m, Monoid w) - => a -> Lens a ActivationAnn -> Activation -> EP w m a -markActivationL a l act = do - new <- markActivation (view l a) act - return (set l new a) + return (HsRule ((ann_act', ann_eq'),nsrc) (L ln' n) act bndrs' lhs' rhs') markActivation :: (Monad m, Monoid w) => ActivationAnn -> Activation -> EP w m ActivationAnn @@ -2113,6 +2083,26 @@ instance ExactPrint Role where -- --------------------------------------------------------------------- +instance ExactPrint (RuleBndrs GhcPs) where + getAnnotationEntry = const NoEntryVal + setAnnotationAnchor a _ _ _ = a + exact (RuleBndrs an0 mtybndrs termbndrs) = do + (an2, mtybndrs') <- + case mtybndrs of + Nothing -> return (an0, Nothing) + Just bndrs -> do + an1 <- markLensFun an0 lrb_tyanns_fst (traverse markEpUniToken) -- AnnForall + bndrs' <- mapM markAnnotated bndrs + an2 <- markLensFun an1 lrb_tyanns_snd (traverse markEpToken) -- AnnDot + return (an2, Just bndrs') + + an3 <- markLensFun an2 lrb_tmanns_fst (traverse markEpUniToken) -- AnnForall + termbndrs' <- mapM markAnnotated termbndrs + an4 <- markLensFun an3 lrb_tmanns_snd (traverse markEpToken) -- AnnDot + return (RuleBndrs an4 mtybndrs' termbndrs') + +-- --------------------------------------------------------------------- + instance ExactPrint (RuleBndr GhcPs) where getAnnotationEntry = const NoEntryVal setAnnotationAnchor a _ _ _ = a @@ -2676,11 +2666,20 @@ instance ExactPrint (Sig GhcPs) where o' <- markAnnOpen'' o (inl_src inl) "{-# SPECIALISE" -- Note: may be {-# SPECIALISE_INLINE act' <- markActivation act (inl_act inl) ln' <- markAnnotated ln - dc' <- markEpUniToken dc + dc' <- traverse markEpUniToken dc typs' <- markAnnotated typs c' <- markEpToken c return (SpecSig (AnnSpecSig o' c' dc' act') ln' typs' inl) + exact (SpecSigE (AnnSpecSig o c dc act) bndrs expr inl) = do + o' <- markAnnOpen'' o (inl_src inl) "{-# SPECIALISE" -- Note: may be {-# SPECIALISE_INLINE + act' <- markActivation act (inl_act inl) + bndrs' <- markAnnotated bndrs + dc' <- traverse markEpUniToken dc + expr' <- markAnnotated expr + c' <- markEpToken c + return (SpecSigE (AnnSpecSig o' c' dc' act') bndrs' expr' inl) + exact (SpecInstSig ((o,i,c),src) typ) = do o' <- markAnnOpen'' o src "{-# SPECIALISE" i' <- markEpToken i -- GitLab