sbv-14.8: SBVTestSuite/TestSuite/CodeGeneration/Boundaries.hs
-----------------------------------------------------------------------------
-- |
-- Module : TestSuite.CodeGeneration.Boundaries
-- Copyright : (c) Levent Erkok
-- License : BSD3
-- Maintainer: erkokl@gmail.com
-- Stability : experimental
--
-- Executable contracts for deliberately unsupported C-backend features.
-----------------------------------------------------------------------------
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -Wall -Werror #-}
module TestSuite.CodeGeneration.Boundaries (tests) where
import Control.Exception (ErrorCall, displayException, try)
import Control.Monad (void)
import Data.List (isInfixOf)
import System.Directory (listDirectory)
import System.IO.Temp (withSystemTempDirectory)
import Test.Tasty.HUnit (assertBool, assertEqual)
import Data.SBV.Internals (AlgRealPoly(..))
import Data.SBV.Control (SMTOption(..))
import qualified Data.SBV.List as SL
import Data.SBV.Tools.CodeGen
import Utils.SBVTestFramework
-- | An abstract solver sort deliberately lacking a C representation.
data Opaque
-- | Recursive references hidden inside a by-value tuple cannot have a finite C layout.
data NestedRecursion = EndRecursion | NestedRecursion (Word8, NestedRecursion)
mkSymbolic [''Opaque, ''NestedRecursion]
-- | Every rejected feature is checked through both public entry points;
-- library failures must not leave even an earlier, valid component behind.
tests :: TestTree
tests = testGroup "CodeGeneration.Boundaries"
[ rejects "uninterpreted sort" "uninterpreted sorts" $ do
value <- cgInput "value" :: SBVCodeGen (SBV Opaque)
cgReturn value
, rejects "nested recursive ADT" "Recursive ADT references nested inside composite fields" $ do
value <- cgInput "value" :: SBVCodeGen (SBV NestedRecursion)
cgReturn value
, rejects "solver option" "SMT solver options have no executable C semantics" $ do
setOption (ProduceAssertions True)
cgReturn sTrue
, rejects "solver option through cgSym" "SMT solver options have no executable C semantics" $ do
cgSym $ setOption (ProduceAssertions True)
cgReturn sTrue
, rejects "nested uninterpreted sort" "uninterpreted sorts" $ do
value <- cgInput "value" :: SBVCodeGen (SList (Maybe Opaque))
cgReturn value
, rejects "finite quantifier" "quantified" $ do
value <- cgInput "value" :: SBVCodeGen SBool
cgReturn (quantifiedBool (\(Forall b) -> b .|| value))
, rejects "infinite quantifier" "quantified" $ do
value <- cgInput "value" :: SBVCodeGen SInteger
cgReturn (quantifiedBool (\(Forall n) -> n .>= value))
, rejects "quantifier in private function" "quantified" $ do
value <- cgInput "value" :: SBVCodeGen SInteger
cgReturn (smtFunction "quantifiedFunction" (\v -> quantifiedBool (\(Forall n) -> n .>= v)) value)
, rejects "quantifier in array lambda" "quantified" $
cgReturn (lambdaArray (\v -> quantifiedBool (\(Forall n) -> n .>= v)) :: SArray Integer Bool)
, rejects "special relation" "special relations" $
cgReturn (isPartialOrder "order" (uncurry ((.<=) :: SInteger -> SInteger -> SBool)))
, rejects "soft constraint" "Soft constraints" $ do
value <- cgInput "value" :: SBVCodeGen SBool
softConstrain value
cgReturn value
, rejects "SMT-only constraint attribute" "Constraint attributes: :weight" $ do
value <- cgInput "value" :: SBVCodeGen SBool
constrainWithAttribute [(":weight", "2")] value
cgReturn value
, rejects "minimize" "Optimization objectives require a solver" $ do
value <- cgInput "value" :: SBVCodeGen SInteger
cgSym $ minimize "minimum" value
cgReturn value
, rejects "maximize" "Optimization objectives require a solver" $ do
value <- cgInput "value" :: SBVCodeGen SWord8
cgSym $ maximize "maximum" value
cgReturn value
, rejects "implicit higher-order capture" "Defined functions with implicit captures" $ do
offset <- cgInput "offset" :: SBVCodeGen SInteger
values <- cgInput "values" :: SBVCodeGen (SList Integer)
cgReturn (SL.map (+ offset) values)
, rejects "implicit managed higher-order capture" "Defined functions with implicit captures" $ do
offset <- cgInput "offset" :: SBVCodeGen SString
values <- cgInput "values" :: SBVCodeGen (SList String)
cgReturn (SL.map (.== offset) values)
, rejects "penalized assertion" "Optimization objectives require a solver" $ do
value <- cgInput "value" :: SBVCodeGen SBool
cgSym $ assertWithPenalty "penalty" value DefaultPenalty
cgReturn value
, rejects "algebraic real literal" "Algebraic SReal literals" $ cgReturn (literal algebraic)
, rejects "nested algebraic real literal" "Algebraic SReal literals" $ cgReturn (literal [algebraic])
, rejects "mapped algebraic real literal" "Algebraic SReal literals" $ cgSRealType CgDouble >> cgReturn (literal algebraic)
, rejects "inexact real literal" "Inexact SReal literals" $ cgReturn (literal (AlgRational False (1 / 3)))
, rejects "exact transcendental" "exact GMP-rational SReal representation cannot represent" $ do
value <- cgInput "value" :: SBVCodeGen SReal
cgReturn (sin value)
, rejects "private exact transcendental" "exact GMP-rational SReal representation cannot represent" $ do
value <- cgInput "value" :: SBVCodeGen SReal
cgReturn (smtFunction "exactSine" sin value)
, rejects "sparse infinite-domain equality" "cannot enumerate key domain" $ do
value <- cgInput "value" :: SBVCodeGen SWord8
let base = constArray 0 :: SArray Integer Word8
cgReturn (writeArray base 1 value .== writeArray base 2 value)
]
-- | The positive square root of two, represented without an approximation.
algebraic :: AlgReal
algebraic = AlgPolyRoot (2, AlgRealPoly [(1, 2), (-2, 0)]) Nothing
-- | Check an intentional diagnostic, not just any exception, and ensure that
-- validation completes before standalone or library output is written.
rejects :: String -> String -> SBVCodeGen () -> TestTree
rejects testName diagnostic program = testGroup testName
[ testCase mode $ withSystemTempDirectory "sbv-c-boundary" $ \dir -> do
let prepare body = cgGenerateDriver False >> cgOverwriteFiles True >> body
action | library = void $ compileToCLib (Just dir) "rejectedLibrary"
[("validComponent", prepare (cgReturn sTrue)), ("rejectedComponent", prepare program)]
| True = compileToC (Just dir) "rejectedComponent" (prepare program)
result <- try action :: IO (Either ErrorCall ())
case result of
Left exception -> do
let message = displayException exception
assertBool message (diagnostic `isInfixOf` message)
assertBool message (not (any (`isInfixOf` message) ["Unexpected:", "Missing assignment", "Impossible happened"]))
Right () -> assertFailure "Expected an explicit unsupported-feature diagnostic"
assertEqual "Rejected generation must not write any files" [] =<< listDirectory dir
| (mode, library) <- [("standalone", False), ("library", True)]
]