Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix unification pertaining to evidence #1885

Merged
merged 7 commits into from
Jun 4, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions plugins/hls-tactics-plugin/src/Wingman/CodeGen.hs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import Data.Bool (bool)
import Data.Functor ((<&>))
import Data.Generics.Labels ()
import Data.List
import Data.Monoid (Endo(..))
import qualified Data.Set as S
import Data.Traversable
import DataCon
Expand Down Expand Up @@ -67,7 +66,7 @@ destructMatches use_field_puns f scrut t jdg = do
-- #syn_scoped
method_hy = foldMap evidenceToHypothesis ev
args = conLikeInstOrigArgTys' con apps
modify $ appEndo $ foldMap (Endo . evidenceToSubst) ev
modify $ evidenceToSubst ev
subst <- gets ts_unifier

let names_in_scope = hyNamesInScope hy
Expand Down
52 changes: 40 additions & 12 deletions plugins/hls-tactics-plugin/src/Wingman/Judgements/Theta.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ module Wingman.Judgements.Theta

import Class (classTyVars)
import Control.Applicative (empty)
import Control.Lens (preview)
import Data.Maybe (fromMaybe, mapMaybe, maybeToList)
import Data.Generics.Sum (_Ctor)
import Data.Set (Set)
import qualified Data.Set as S
import Development.IDE.Core.UseStale
import Development.IDE.GHC.Compat
import Generics.SYB hiding (tyConName, empty)
import GhcPlugins (mkVarOcc, splitTyConApp_maybe, getTyVar_maybe, zipTvSubst)
import Generics.SYB hiding (tyConName, empty, Generic)
import GHC.Generics
import GhcPlugins (mkVarOcc, splitTyConApp_maybe, getTyVar_maybe, zipTvSubst, unionTCvSubst, emptyTCvSubst, TCvSubst)
#if __GLASGOW_HASKELL__ > 806
import GhcPlugins (eqTyCon)
#else
Expand All @@ -40,7 +43,7 @@ data Evidence
= EqualityOfTypes Type Type
-- | We have an instance in scope
| HasInstance PredType
deriving (Show)
deriving (Show, Generic)


------------------------------------------------------------------------------
Expand Down Expand Up @@ -75,21 +78,46 @@ getEvidenceAtHole (unTrack -> dst)
. unTrack


------------------------------------------------------------------------------
-- | Update our knowledge of which types are equal.
evidenceToSubst :: Evidence -> TacticState -> TacticState
evidenceToSubst (EqualityOfTypes a b) ts =
mkSubst :: Set TyVar -> Type -> Type -> TCvSubst
mkSubst skolems a b =
let tyvars = S.fromList $ mapMaybe getTyVar_maybe [a, b]
-- If we can unify our skolems, at least one is no longer a skolem.
-- Removing them from this set ensures we can get a subtitution between
-- the two. But it's okay to leave them in 'ts_skolems' in general, since
-- they won't exist after running this substitution.
skolems = ts_skolems ts S.\\ tyvars
skolems' = skolems S.\\ tyvars
in
case tryUnifyUnivarsButNotSkolems skolems (CType a) (CType b) of
Just subst -> updateSubst subst ts
Nothing -> ts
evidenceToSubst HasInstance{} ts = ts
case tryUnifyUnivarsButNotSkolems skolems' (CType a) (CType b) of
Just subst -> subst
Nothing -> emptyTCvSubst


substPair :: TCvSubst -> (Type, Type) -> (Type, Type)
substPair subst (ty, ty') = (substTy subst ty, substTy subst ty')


------------------------------------------------------------------------------
-- | Construct a substitution given a list of types that are equal to one
-- another. This is more subtle than it seems, since there might be several
-- equalities for the same type. We must be careful to push the accumulating
-- substitution through each pair of types before adding their equalities.
allEvidenceToSubst :: Set TyVar -> [(Type, Type)] -> TCvSubst
allEvidenceToSubst _ [] = emptyTCvSubst
allEvidenceToSubst skolems ((a, b) : evs) =
let subst = mkSubst skolems a b
in unionTCvSubst subst
$ allEvidenceToSubst skolems
$ fmap (substPair subst) evs

------------------------------------------------------------------------------
-- | Update our knowledge of which types are equal.
evidenceToSubst :: [Evidence] -> TacticState -> TacticState
evidenceToSubst evs ts =
updateSubst
(allEvidenceToSubst (ts_skolems ts)
$ mapMaybe (preview $ _Ctor @"EqualityOfTypes")
$ evs)
ts


------------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ mkJudgementAndContext cfg g (TrackedStale binds bmap) rss (TrackedStale tcg tcgm
$ hypothesisFromBindings binds_rss binds
evidence = getEvidenceAtHole (fmap RealSrcSpan tcg_rss) tcs
cls_hy = foldMap evidenceToHypothesis evidence
subst = ts_unifier $ appEndo (foldMap (Endo . evidenceToSubst) evidence) defaultTacticState
subst = ts_unifier $ evidenceToSubst evidence defaultTacticState
pure $
( disallowing AlreadyDestructed already_destructed
$ fmap (CType . substTyAddInScope subst . unCType) $ mkFirstJudgement
Expand Down
4 changes: 3 additions & 1 deletion plugins/hls-tactics-plugin/src/Wingman/Plugin.hs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ tacticCmd tac pId state (TacticParams uri range var_name)

timingOut (cfg_timeout_seconds cfg * seconds) $ join $
case runTactic hj_ctx hj_jdg t of
Left _ -> Left TacticErrors
Left errs -> do
traceMX "errs" errs
Left TacticErrors
Right rtr ->
case rtr_extract rtr of
L _ (HsVar _ (L _ rdr)) | isHole (occName rdr) ->
Expand Down
16 changes: 9 additions & 7 deletions plugins/hls-tactics-plugin/src/Wingman/Tactics.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Control.Lens ((&), (%~), (<>~))
import Control.Monad (unless)
import Control.Monad.Except (throwError)
import Control.Monad.Reader.Class (MonadReader (ask))
import Control.Monad.State.Strict (StateT(..), runStateT)
import Control.Monad.State.Strict (StateT(..), runStateT, gets)
import Data.Bool (bool)
import Data.Foldable
import Data.Functor ((<&>))
Expand Down Expand Up @@ -160,7 +160,7 @@ destructOrHomoAuto hi = tracing "destructOrHomoAuto" $ do
attemptWhen
(rule $ destruct' False (\dc jdg ->
buildDataCon False jdg dc $ snd $ splitAppTys g) hi)
(rule $ destruct' False (const subgoal) hi)
(rule $ destruct' False (const newSubgoal) hi)
$ case (splitTyConApp_maybe g, splitTyConApp_maybe ty) of
(Just (gtc, _), Just (tytc, _)) -> gtc == tytc
_ -> False
Expand All @@ -170,14 +170,14 @@ destructOrHomoAuto hi = tracing "destructOrHomoAuto" $ do
-- | Case split, and leave holes in the matches.
destruct :: HyInfo CType -> TacticsM ()
destruct hi = requireConcreteHole $ tracing "destruct(user)" $
rule $ destruct' False (const subgoal) hi
rule $ destruct' False (const newSubgoal) hi


------------------------------------------------------------------------------
-- | Case split, and leave holes in the matches. Performs record punning.
destructPun :: HyInfo CType -> TacticsM ()
destructPun hi = requireConcreteHole $ tracing "destructPun(user)" $
rule $ destruct' True (const subgoal) hi
rule $ destruct' True (const newSubgoal) hi


------------------------------------------------------------------------------
Expand All @@ -191,7 +191,7 @@ homo = requireConcreteHole . tracing "homo" . rule . destruct' False (\dc jdg ->
-- | LambdaCase split, and leave holes in the matches.
destructLambdaCase :: TacticsM ()
destructLambdaCase =
tracing "destructLambdaCase" $ rule $ destructLambdaCase' False (const subgoal)
tracing "destructLambdaCase" $ rule $ destructLambdaCase' False (const newSubgoal)


------------------------------------------------------------------------------
Expand Down Expand Up @@ -335,7 +335,7 @@ destructAll :: TacticsM ()
destructAll = do
jdg <- goal
let args = fmap fst
$ sort
$ sortOn snd
$ mapMaybe (\(hi, prov) ->
case prov of
TopLevelArgPrv _ idx _ -> pure (hi, idx)
Expand All @@ -345,7 +345,9 @@ destructAll = do
$ filter (isAlgType . unCType . hi_type)
$ unHypothesis
$ jHypothesis jdg
for_ args destruct
for_ args $ \arg -> do
subst <- gets ts_unifier
destruct $ fmap (coerce substTy subst) arg

--------------------------------------------------------------------------------
-- | User-facing tactic to implement "Use constructor <x>"
Expand Down
1 change: 1 addition & 0 deletions plugins/hls-tactics-plugin/test/CodeAction/AutoSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ spec = do
autoTest 6 8 "AutoThetaEqGADTDestruct"
autoTest 6 10 "AutoThetaRefl"
autoTest 6 8 "AutoThetaReflDestruct"
autoTest 19 30 "AutoThetaMultipleUnification"

describe "known" $ do
autoTest 25 13 "GoldenArbitrary"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ spec = do
destructAllTest 4 23 "DestructAllMany"
destructAllTest 2 18 "DestructAllNonVarTopMatch"
destructAllTest 2 18 "DestructAllFunc"
destructAllTest 19 18 "DestructAllGADTEvidence"

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}

import Data.Kind

data Nat = Z | S Nat

data HList (ls :: [Type]) where
HNil :: HList '[]
HCons :: t -> HList ts -> HList (t ': ts)

data ElemAt (n :: Nat) t (ts :: [Type]) where
AtZ :: ElemAt 'Z t (t ': ts)
AtS :: ElemAt k t ts -> ElemAt ('S k) t (u ': ts)

lookMeUp :: ElemAt i ty tys -> HList tys -> ty
lookMeUp AtZ (HCons t _) = t
lookMeUp (AtS ea') (HCons t hl') = _

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}

import Data.Kind

data Nat = Z | S Nat

data HList (ls :: [Type]) where
HNil :: HList '[]
HCons :: t -> HList ts -> HList (t ': ts)

data ElemAt (n :: Nat) t (ts :: [Type]) where
AtZ :: ElemAt 'Z t (t ': ts)
AtS :: ElemAt k t ts -> ElemAt ('S k) t (u ': ts)

lookMeUp :: ElemAt i ty tys -> HList tys -> ty
lookMeUp AtZ (HCons t hl') = _
lookMeUp (AtS ea') (HCons t hl') = _

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}

import Data.Kind

data Nat = Z | S Nat

data HList (ls :: [Type]) where
HNil :: HList '[]
HCons :: t -> HList ts -> HList (t ': ts)

data ElemAt (n :: Nat) t (ts :: [Type]) where
AtZ :: ElemAt 'Z t (t ': ts)
AtS :: ElemAt k t ts -> ElemAt ('S k) t (u ': ts)

lookMeUp :: ElemAt i ty tys -> HList tys -> ty
lookMeUp AtZ (HCons t hl') = _
lookMeUp (AtS ea') (HCons t hl') = _

20 changes: 20 additions & 0 deletions plugins/hls-tactics-plugin/test/golden/DestructAllGADTEvidence.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}

import Data.Kind

data Nat = Z | S Nat

data HList (ls :: [Type]) where
HNil :: HList '[]
HCons :: t -> HList ts -> HList (t ': ts)

data ElemAt (n :: Nat) t (ts :: [Type]) where
AtZ :: ElemAt 'Z t (t ': ts)
AtS :: ElemAt k t ts -> ElemAt ('S k) t (u ': ts)

lookMeUp :: ElemAt i ty tys -> HList tys -> ty
lookMeUp ea hl = _

30 changes: 15 additions & 15 deletions plugins/hls-tactics-plugin/test/golden/DestructAllMany.expected.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,26 @@ data ABC = A | B | C

many :: () -> Either a b -> Bool -> Maybe ABC -> ABC -> ()
many () (Left a) False Nothing A = _
many () (Left a) False (Just abc') A = _
many () (Right b') False Nothing A = _
many () (Right b') False (Just abc') A = _
many () (Left a) True Nothing A = _
many () (Left a) True (Just abc') A = _
many () (Right b') True Nothing A = _
many () (Right b') True (Just abc') A = _
many () (Left a) False Nothing B = _
many () (Left a) False Nothing C = _
many () (Left a) False (Just abc') A = _
many () (Left a) False (Just abc') B = _
many () (Right b') False Nothing B = _
many () (Right b') False (Just abc') B = _
many () (Left a) False (Just abc') C = _
many () (Left a) True Nothing A = _
many () (Left a) True Nothing B = _
many () (Left a) True Nothing C = _
many () (Left a) True (Just abc') A = _
many () (Left a) True (Just abc') B = _
many () (Right b') True Nothing B = _
many () (Right b') True (Just abc') B = _
many () (Left a) False Nothing C = _
many () (Left a) False (Just abc') C = _
many () (Left a) True (Just abc') C = _
many () (Right b') False Nothing A = _
many () (Right b') False Nothing B = _
many () (Right b') False Nothing C = _
many () (Right b') False (Just abc') A = _
many () (Right b') False (Just abc') B = _
many () (Right b') False (Just abc') C = _
many () (Left a) True Nothing C = _
many () (Left a) True (Just abc') C = _
many () (Right b') True Nothing A = _
many () (Right b') True Nothing B = _
many () (Right b') True Nothing C = _
many () (Right b') True (Just abc') A = _
many () (Right b') True (Just abc') B = _
many () (Right b') True (Just abc') C = _
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
maybeAp :: Maybe (a -> b) -> Maybe a -> Maybe b
maybeAp Nothing Nothing = Nothing
maybeAp (Just _) Nothing = Nothing
maybeAp Nothing (Just _) = Nothing
maybeAp (Just _) Nothing = Nothing
maybeAp (Just fab) (Just a) = Just (fab a)