1
+ {-# LANGUAGE ScopedTypeVariables #-}
1
2
{-# LANGUAGE DeriveFunctor #-}
2
3
{-# LANGUAGE DeriveGeneric #-}
3
4
{-# LANGUAGE DerivingStrategies #-}
@@ -17,6 +18,7 @@ module Ide.Plugin.Tactic.Machinery
17
18
( module Ide.Plugin.Tactic.Machinery
18
19
) where
19
20
21
+ import Class (Class (classTyVars ))
20
22
import Control.Arrow
21
23
import Control.Monad.Error.Class
22
24
import Control.Monad.Reader
@@ -25,12 +27,15 @@ import Control.Monad.State.Class (gets, modify)
25
27
import Control.Monad.State.Strict (StateT (.. ))
26
28
import Data.Coerce
27
29
import Data.Either
28
- import Data.List (intercalate , sortBy )
30
+ import Data.Functor ((<&>) )
31
+ import Data.Generics (mkQ , everything , gcount )
32
+ import Data.List (sortBy )
29
33
import Data.Ord (comparing , Down (.. ))
30
34
import qualified Data.Set as S
31
35
import Development.IDE.GHC.Compat
32
36
import Ide.Plugin.Tactic.Judgements
33
37
import Ide.Plugin.Tactic.Types
38
+ import OccName (HasOccName (occName ))
34
39
import Refinery.ProofState
35
40
import Refinery.Tactic
36
41
import Refinery.Tactic.Internal
@@ -74,7 +79,8 @@ runTactic ctx jdg t =
74
79
(errs, [] ) -> Left $ take 50 $ errs
75
80
(_, fmap assoc23 -> solns) -> do
76
81
let sorted =
77
- sortBy (comparing $ Down . uncurry scoreSolution . snd ) solns
82
+ flip sortBy solns $ comparing $ \ ((_, ext), (jdg, holes)) ->
83
+ Down $ scoreSolution ext jdg holes
78
84
case sorted of
79
85
(((tr, ext), _) : _) ->
80
86
Right
@@ -121,56 +127,97 @@ setRecursionFrameData b = do
121
127
122
128
123
129
scoreSolution
124
- :: TacticState
130
+ :: LHsExpr GhcPs
131
+ -> TacticState
125
132
-> [Judgement ]
126
133
-> ( Penalize Int -- number of holes
127
134
, Reward Bool -- all bindings used
128
135
, Penalize Int -- number of introduced bindings
129
136
, Reward Int -- number used bindings
137
+ , Penalize Int -- size of extract
130
138
)
131
- scoreSolution TacticState {.. } holes
139
+ scoreSolution ext TacticState {.. } holes
132
140
= ( Penalize $ length holes
133
- , Reward $ S. null $ ts_intro_vals S. \\ ts_used_vals
141
+ , Reward $ S. null $ ts_intro_vals S. \\ ts_used_vals
134
142
, Penalize $ S. size ts_intro_vals
135
- , Reward $ S. size ts_used_vals
143
+ , Reward $ S. size ts_used_vals
144
+ , Penalize $ solutionSize ext
136
145
)
137
146
138
147
148
+ ------------------------------------------------------------------------------
149
+ -- | Compute the number of 'LHsExpr' nodes; used as a rough metric for code
150
+ -- size.
151
+ solutionSize :: LHsExpr GhcPs -> Int
152
+ solutionSize = everything (+) $ gcount $ mkQ False $ \ case
153
+ (_ :: LHsExpr GhcPs ) -> True
154
+
155
+
139
156
newtype Penalize a = Penalize a
140
157
deriving (Eq , Ord , Show ) via (Down a )
141
158
142
159
newtype Reward a = Reward a
143
160
deriving (Eq , Ord , Show ) via a
144
161
145
162
163
+ ------------------------------------------------------------------------------
164
+ -- | Like 'tcUnifyTy', but takes a list of skolems to prevent unification of.
165
+ tryUnifyUnivarsButNotSkolems :: [TyVar ] -> CType -> CType -> Maybe TCvSubst
166
+ tryUnifyUnivarsButNotSkolems skolems goal inst =
167
+ case tcUnifyTysFG (skolemsOf skolems) [unCType inst] [unCType goal] of
168
+ Unifiable subst -> pure subst
169
+ _ -> Nothing
170
+
146
171
147
172
------------------------------------------------------------------------------
148
- -- | We need to make sure that we don't try to unify any skolems.
149
- -- To see why, consider the case:
150
- --
151
- -- uhh :: (Int -> Int) -> a
152
- -- uhh f = _
153
- --
154
- -- If we were to apply 'f', then we would try to unify 'Int' and 'a'.
155
- -- This is fine from the perspective of 'tcUnifyTy', but will cause obvious
156
- -- type errors in our use case. Therefore, we need to ensure that our
157
- -- 'TCvSubst' doesn't try to unify skolems.
158
- checkSkolemUnification :: CType -> CType -> TCvSubst -> RuleM ()
159
- checkSkolemUnification t1 t2 subst = do
160
- skolems <- gets ts_skolems
161
- unless (all (flip notElemTCvSubst subst) skolems) $
162
- throwError (UnificationError t1 t2)
173
+ -- | Helper method for 'tryUnifyUnivarsButNotSkolems'
174
+ skolemsOf :: [TyVar ] -> TyVar -> BindFlag
175
+ skolemsOf tvs tv =
176
+ case elem tv tvs of
177
+ True -> Skolem
178
+ False -> BindMe
163
179
164
180
165
181
------------------------------------------------------------------------------
166
182
-- | Attempt to unify two types.
167
183
unify :: CType -- ^ The goal type
168
184
-> CType -- ^ The type we are trying unify the goal type with
169
185
-> RuleM ()
170
- unify goal inst =
171
- case tcUnifyTy (unCType inst) (unCType goal) of
172
- Just subst -> do
173
- checkSkolemUnification inst goal subst
174
- modify (\ s -> s { ts_unifier = unionTCvSubst subst (ts_unifier s) })
175
- Nothing -> throwError (UnificationError inst goal)
186
+ unify goal inst = do
187
+ skolems <- gets ts_skolems
188
+ case tryUnifyUnivarsButNotSkolems skolems goal inst of
189
+ Just subst ->
190
+ modify (\ s -> s { ts_unifier = unionTCvSubst subst (ts_unifier s) })
191
+ Nothing -> throwError (UnificationError inst goal)
192
+
193
+
194
+ ------------------------------------------------------------------------------
195
+ -- | Get the class methods of a 'PredType', correctly dealing with
196
+ -- instantiation of quantified class types.
197
+ methodHypothesis :: PredType -> Maybe [(OccName , CType )]
198
+ methodHypothesis ty = do
199
+ (tc, apps) <- splitTyConApp_maybe ty
200
+ cls <- tyConClass_maybe tc
201
+ let methods = classMethods cls
202
+ tvs = classTyVars cls
203
+ subst = zipTvSubst tvs apps
204
+ sc_methods <- fmap join
205
+ $ traverse (methodHypothesis . substTy subst)
206
+ $ classSCTheta cls
207
+ pure $ mappend sc_methods $ methods <&> \ method ->
208
+ let (_, _, ty) = tcSplitSigmaTy $ idType method
209
+ in (occName method, CType $ substTy subst ty)
210
+
211
+
212
+ ------------------------------------------------------------------------------
213
+ -- | Run the given tactic iff the current hole contains no univars. Skolems and
214
+ -- already decided univars are OK though.
215
+ requireConcreteHole :: TacticsM a -> TacticsM a
216
+ requireConcreteHole m = do
217
+ jdg <- goal
218
+ skolems <- gets $ S. fromList . ts_skolems
219
+ let vars = S. fromList $ tyCoVarsOfTypeWellScoped $ unCType $ jGoal jdg
220
+ case S. size $ vars S. \\ skolems of
221
+ 0 -> m
222
+ _ -> throwError TooPolymorphic
176
223
0 commit comments