packages feed

g2-0.1.0.0: src/G2/Liquid/AddCFBranch.hs

{-# LANGUAGE OverloadedStrings #-}

module G2.Liquid.AddCFBranch (addCounterfactualBranch) where

import G2.Language
import G2.Language.Monad
import G2.Liquid.Types

-- Enables finding abstract counterexamples, by adding counterfactual branches
-- with two states.
-- (a) One state is exactly the same as the current reduce function: we lookup
--     up the var, and set the curr expr to its definition.
-- (b) [StateB] The other branch introduces a new symbolic variable x_s,
--     with the same type of s, and sets the curr expr to
--     let x = x_s in Assert (a x'_1 ... x'_n x_s) x_s
--     appropriately binding x'_i to x_i in the expression environment
--
--     This allows us to choose any value for the return type of the function.
--     In this rule, we also return a b, oldb `mappend` [(f, [x_1, ..., x_n], x)]
--     This is essentially abstracting away the function definition, leaving
--     only the information that LH also knows (that is, the information in the
--     refinment type.)
addCounterfactualBranch :: [Name] -> LHStateM Name
addCounterfactualBranch ns = do
    cfn <- freshSeededStringN "cf"
    mapWithKeyME (addCounterfactualBranch' cfn ns)
    return cfn

addCounterfactualBranch' :: Name -> [Name]-> Name -> Expr -> LHStateM Expr
addCounterfactualBranch' cfn ns n =
    if n `elem` ns then insertInLamsE (\_ -> addCounterfactualBranch'' cfn) else return

addCounterfactualBranch'' :: Name -> Expr -> LHStateM Expr
addCounterfactualBranch'' cfn
    orig_e@(Let 
        [(b, _)]
        (Assert (Just (FuncCall { funcName = fn, arguments = ars })) a _)) = do
    let t = returnType orig_e
        sg = SymGen t

    -- Create lambdas, to gobble up any ApplyFrames left on the stack
    lams <- tyBindings orig_e

    -- If the type of b is not the same as e's type, we have no assumption,
    -- so we get a new b.  Otherwise, we just keep our current b,
    -- in case it is used in the assertion
    b' <- if typeOf b == t then return b else freshIdN t

    let fc = FuncCall { funcName = fn, arguments = ars', returns = (Var b')}
        e' = lams $ Let [(b', sg)] $ Tick (NamedLoc cfn) $ Assume (Just fc) a (Var b')
        -- We add the Id's from the newly created Lambdas to the arguments list
        lamI = map Var $ leadingLamIds e'
        ars' = ars ++ lamI

    return $ NonDet [orig_e, e']
addCounterfactualBranch'' cfn e = modifyChildrenM (addCounterfactualBranch'' cfn) e

-- Creates Lambda bindings to saturate the type of the given Typed thing,
-- and a list of the bindings so they can be used elsewhere
tyBindings :: Typed t => t -> LHStateM (Expr -> Expr)
tyBindings t = do
    let at = spArgumentTypes t
    fn <- freshNamesN (length at)
    return $ tyBindings' fn at

tyBindings' :: [Name] -> [ArgType] -> Expr -> Expr
tyBindings' _ [] = id
tyBindings' ns (NamedType i:ts) = Lam TypeL i . tyBindings' ns ts
tyBindings' (n:ns) (AnonType t:ts) = Lam TermL (Id n t) . tyBindings' ns ts
tyBindings' [] _ = error "Name list exhausted in tyBindings'"