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

Cata tactic should generalize let and ensure unifiability #1938

Merged
merged 5 commits into from
Jun 17, 2021
Merged
Show file tree
Hide file tree
Changes from 4 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
4 changes: 3 additions & 1 deletion plugins/hls-tactics-plugin/src/Wingman/CodeGen.hs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import GhcPlugins (isSymOcc, mkVarOccFS)
import OccName (occName)
import PatSyn
import Type hiding (Var)
import TysPrim (alphaTy)
import Wingman.CodeGen.Utils
import Wingman.GHC
import Wingman.Judgements
Expand Down Expand Up @@ -309,7 +310,8 @@ letForEach rename solve (unHypothesis -> hy) jdg = do
let g = jGoal jdg
terms <- fmap sequenceA $ for hy $ \hi -> do
let name = rename $ hi_name hi
res <- tacticToRule jdg $ solve hi
let generalized_let_ty = CType alphaTy
res <- tacticToRule (withNewGoal generalized_let_ty jdg) $ solve hi
pure $ fmap ((name,) . unLoc) res
let hy' = fmap (g <$) $ syn_val terms
matches = fmap (fmap (\(occ, expr) -> valBind (occNameToStr occ) expr)) terms
Expand Down
27 changes: 26 additions & 1 deletion plugins/hls-tactics-plugin/src/Wingman/Machinery.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Control.Applicative (empty)
import Control.Lens ((<>~))
import Control.Monad.Error.Class
import Control.Monad.Reader
import Control.Monad.State.Class (gets, modify)
import Control.Monad.State.Class (gets, modify, MonadState)
import Control.Monad.State.Strict (StateT (..), execStateT)
import Control.Monad.Trans.Maybe
import Data.Coerce
Expand Down Expand Up @@ -217,6 +217,20 @@ unify goal inst = do
Nothing -> throwError (UnificationError inst goal)


------------------------------------------------------------------------------
-- | Attempt to unify two types.
canUnify
:: MonadState TacticState m
=> CType -- ^ The goal type
-> CType -- ^ The type we are trying unify the goal type with
-> m Bool
canUnify goal inst = do
skolems <- gets ts_skolems
case tryUnifyUnivarsButNotSkolems skolems goal inst of
Just _ -> pure True
Nothing -> pure False


------------------------------------------------------------------------------
-- | Prefer the first tactic to the second, if the bool is true. Otherwise, just run the second tactic.
--
Expand Down Expand Up @@ -312,6 +326,17 @@ lookupNameInContext name = do
Nothing -> empty


getDefiningType
:: (MonadError TacticError m, MonadReader Context m)
=> m CType
getDefiningType = do
calling_fun_name <- fst . head <$> asks ctxDefiningFuncs
maybe
(throwError $ NotInScope calling_fun_name)
pure
=<< lookupNameInContext calling_fun_name


------------------------------------------------------------------------------
-- | Build a 'HyInfo' for an imported term.
createImportedHyInfo :: OccName -> CType -> HyInfo CType
Expand Down
23 changes: 21 additions & 2 deletions plugins/hls-tactics-plugin/src/Wingman/Tactics.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ module Wingman.Tactics
import ConLike (ConLike(RealDataCon))
import Control.Applicative (Alternative(empty))
import Control.Lens ((&), (%~), (<>~))
import Control.Monad (filterM)
import Control.Monad (unless)
import Control.Monad.Except (throwError)
import Control.Monad.Extra (anyM)
import Control.Monad.Reader.Class (MonadReader (ask))
import Control.Monad.State.Strict (StateT(..), runStateT, gets)
import Data.Bool (bool)
Expand Down Expand Up @@ -475,21 +477,38 @@ nary n =
mkInvForAllTys [alphaTyVar, betaTyVar] $
mkFunTys' (replicate n alphaTy) betaTy


self :: TacticsM ()
self =
fmap listToMaybe getCurrentDefinitions >>= \case
Just (self, _) -> useNameFromContext apply self
Nothing -> throwError $ TacticPanic "no defining function"


------------------------------------------------------------------------------
-- | Perform a catamorphism when destructing the given 'HyInfo'. This will
-- result in let binding, making values that call the defining function on each
-- destructed value.
cata :: HyInfo CType -> TacticsM ()
cata hi = do
(_, _, calling_args, _)
<- tacticsSplitFunTy . unCType <$> getDefiningType
freshened_args <- traverse freshTyvars calling_args
diff <- hyDiff $ destruct hi

-- For for every destructed term, check to see if it can unify with any of
-- the arguments to the calling function. If it doesn't, we don't try to
-- perform a cata on it.
unifiable_diff <- flip filterM (unHypothesis diff) $ \hi ->
flip anyM freshened_args $ \ty ->
canUnify (hi_type hi) $ CType ty

rule $
letForEach
(mkVarOcc . flip mappend "_c" . occNameString)
(\hi -> self >> commit (apply hi) assumption)
diff
(\hi -> self >> commit (assume $ hi_name hi) assumption)
$ Hypothesis unifiable_diff


collapse :: TacticsM ()
collapse = do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@ spec = do
metaTest 11 11 "MetaUseMethod"
metaTest 9 38 "MetaCataCollapse"
metaTest 7 16 "MetaCataCollapseUnary"
metaTest 21 32 "MetaCataAST"
metaTest 6 46 "MetaPointwise"
metaTest 4 28 "MetaUseSymbol"
34 changes: 34 additions & 0 deletions plugins/hls-tactics-plugin/test/golden/MetaCataAST.expected.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{-# LANGUAGE GADTs #-}

data AST a where
BoolLit :: Bool -> AST Bool
IntLit :: Int -> AST Int
If :: AST Bool -> AST a -> AST a -> AST a
Equal :: AST a -> AST a -> AST Bool

eval :: AST a -> a
-- NOTE(sandy): There is an unrelated bug that is shown off in this test
-- namely, that
--
-- @eval (IntLit n) = _@
--
-- but should be
--
-- @eval (IntLit n) = n@
--
-- https://github.com/haskell/haskell-language-server/issues/1937

eval (BoolLit b) = b
eval (IntLit n) = _
eval (If ast ast' ast_a)
= let
ast_c = eval ast
ast'_c = eval ast'
ast_a_c = eval ast_a
in _ ast_c ast'_c ast_a_c
eval (Equal ast ast')
= let
ast_c = eval ast
ast'_c = eval ast'
in _ ast_c ast'_c

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

data AST a where
BoolLit :: Bool -> AST Bool
IntLit :: Int -> AST Int
If :: AST Bool -> AST a -> AST a -> AST a
Equal :: AST a -> AST a -> AST Bool

eval :: AST a -> a
-- NOTE(sandy): There is an unrelated bug that is shown off in this test
-- namely, that
--
-- @eval (IntLit n) = _@
--
-- but should be
--
-- @eval (IntLit n) = n@
--
-- https://github.com/haskell/haskell-language-server/issues/1937

eval = [wingman| intros x, cata x; collapse |]