Skip to content

Commit

Permalink
Merge pull request #2226 from ucsd-progsys/fd/ghc-api-tests
Browse files Browse the repository at this point in the history
Add a GHC API test and move more things to GHC.API.Extra
  • Loading branch information
facundominguez authored Sep 23, 2023
2 parents e641476 + 8912b71 commit 79b339c
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 41 deletions.
69 changes: 69 additions & 0 deletions liquidhaskell-boot/ghc-api-tests/GhcApiTests.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@

import Control.Monad
import Liquid.GHC.API
( ApiComment(ApiBlockComment)
, apiCommentsParsedSource
)
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.Runners.AntXML

import qualified GHC.Data.EnumSet as EnumSet
import qualified GHC.Data.FastString as GHC
import qualified GHC.Data.StringBuffer as GHC
import qualified GHC.Parser as GHC
import qualified GHC.Parser.Lexer as GHC
import qualified GHC.Types.SrcLoc as GHC

main :: IO ()
main =
defaultMainWithIngredients (antXMLRunner:defaultIngredients) testTree

testTree :: TestTree
testTree =
testGroup "GHC API" [
testCase "apiComments" testApiComments
]

-- Tests that Liquid.GHC.API.Extra.apiComments can retrieve the comments in
-- the right order from an AST
testApiComments :: IO ()
testApiComments = do
let str = unlines
[ "{-@ LIQUID \"--ple\" @-}"
, "module A where"
, "import B"
, ""
, "{-@ i :: { v:Int | v>=0 } @-}"
, "i :: Int"
, "i = 4"
, ""
, "{-@ infixr ++ @-}"
, ""
, "{-@ abs :: Int -> { v:Int | v >= 0 } @-}"
, "abs :: Int -> Int"
, "abs x = z"
, " where"
, " {-@ { v: Int | z >= 0 } @-}"
, " z = if x < 0 then -x else x"
]
lhsMod <- parseMod str "A.hs"
let comments = map GHC.unLoc (apiCommentsParsedSource lhsMod)
expected = map ApiBlockComment
[ "{-@ LIQUID \"--ple\" @-}"
, "{-@ i :: { v:Int | v>=0 } @-}"
, "{-@ infixr ++ @-}"
, "{-@ abs :: Int -> { v:Int | v >= 0 } @-}"
, "{-@ { v: Int | z >= 0 } @-}"
]
when (expected /= comments) $
fail $ unlines $ "Unexpected comments:" : map show comments
where
parseMod str filepath = do
let location = GHC.mkRealSrcLoc (GHC.mkFastString filepath) 1 1
buffer = GHC.stringToStringBuffer str
popts = GHC.mkParserOpts EnumSet.empty EnumSet.empty False True True True
parseState = GHC.initParserState popts buffer location
case GHC.unP GHC.parseModule parseState of
GHC.POk _ result -> return result
_ -> fail "Unexpected parser error"
13 changes: 13 additions & 0 deletions liquidhaskell-boot/liquidhaskell-boot.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,19 @@ library
if flag(devel)
ghc-options: -Wall -Werror

test-suite ghc-api-tests
type: exitcode-stdio-1.0
main-is: GhcApiTests.hs
hs-source-dirs: ghc-api-tests
build-depends: base
, ghc
, liquidhaskell-boot
, tasty
, tasty-ant-xml
, tasty-hunit
default-language: Haskell2010
ghc-options: -W

test-suite liquidhaskell-parser
type: exitcode-stdio-1.0
main-is: Parser.hs
Expand Down
1 change: 0 additions & 1 deletion liquidhaskell-boot/src-ghc/Liquid/GHC/API.hs
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,6 @@ import GHC.Driver.Phases as Ghc (Phase(StopLn))
import GHC.Driver.Pipeline as Ghc (compileFile)
import GHC.Driver.Session as Ghc
( WarnReason(NoReason)
, defaultDynFlags
, getDynFlags
, gopt_set
, updOptLevel
Expand Down
63 changes: 60 additions & 3 deletions liquidhaskell-boot/src-ghc/Liquid/GHC/API/Extra.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,22 @@ module Liquid.GHC.API.Extra (
module StableModule
, ApiComment(..)
, apiComments
, apiCommentsParsedSource
, dataConSig
, desugarModuleIO
, fsToUnitId
, getDependenciesModuleNames
, isPatErrorAlt
, lookupModSummary
, modInfoLookupNameIO
, moduleInfoTc
, moduleUnitId
, parseModuleIO
, qualifiedNameFS
, relevantModules
, renderWithStyle
, showPprQualified
, showSDocQualified
, thisPackage
, tyConRealArity
, typecheckModuleIO
Expand All @@ -31,8 +36,10 @@ import Data.Generics (extQ)
import Data.Foldable (asum)
import Data.List (foldl', sortOn)
import qualified Data.Set as S
import GHC.Core as Ghc
import GHC.Core.Coercion as Ghc
import GHC.Core.DataCon as Ghc
import GHC.Core.Make (pAT_ERROR_ID)
import GHC.Core.Type as Ghc hiding (typeKind , isPredTy, extendCvSubst, linear)
import GHC.Data.FastString as Ghc
import qualified GHC.Data.EnumSet as EnumSet
Expand All @@ -41,8 +48,10 @@ import GHC.Driver.Env
import GHC.Driver.Main
import GHC.Driver.Session as Ghc
import GHC.Tc.Types
import GHC.Types.Name (isSystemName, nameModule_maybe, occNameFS)
import GHC.Types.SrcLoc as Ghc
import GHC.Types.TypeEnv
import GHC.Types.Unique (getUnique)
import GHC.Types.Unique.FM

import GHC.Unit.Module.Deps as Ghc (Dependencies(dep_mods))
Expand Down Expand Up @@ -163,12 +172,15 @@ desugarModuleIO hscEnv originalModSum typechecked = do
data ApiComment
= ApiLineComment String
| ApiBlockComment String
deriving Show
deriving (Eq, Show)

-- | Extract top-level comments from a module.
apiComments :: ParsedModule -> [Ghc.Located ApiComment]
apiComments pm =
let hs = unLoc (pm_parsed_source pm)
apiComments pm = apiCommentsParsedSource (pm_parsed_source pm)

apiCommentsParsedSource :: Located HsModule -> [Ghc.Located ApiComment]
apiCommentsParsedSource ps =
let hs = unLoc ps
go :: forall a. Data a => a -> [LEpaComment]
go = gmapQr (++) [] go `extQ` (id @[LEpaComment])
in Data.List.sortOn (spanToLineColumn . getLoc) $
Expand Down Expand Up @@ -214,3 +226,48 @@ moduleInfoTc hscEnv ms tcGblEnv = do
let hsc_env_tmp = hscEnv { hsc_dflags = ms_hspp_opts ms }
details <- md_types <$> liftIO (makeSimpleDetails hsc_env_tmp tcGblEnv)
pure ModuleInfoLH { minflh_type_env = details }

-- | Tells if a case alternative calls to patError
isPatErrorAlt :: CoreAlt -> Bool
isPatErrorAlt (Alt _ _ exprCoreBndr) = hasPatErrorCall exprCoreBndr
where
hasPatErrorCall :: CoreExpr -> Bool
-- auto generated undefined case: (\_ -> (patError @levity @type "error message")) void
-- Type arguments are erased before calling isUndefined
hasPatErrorCall (App (Var x) _) = x == pAT_ERROR_ID
-- another auto generated undefined case:
-- let lqanf_... = patError "error message") in case lqanf_... of {}
hasPatErrorCall (Let (NonRec x e) (Case (Var v) _ _ []))
| x == v = hasPatErrorCall e
hasPatErrorCall (Let _ e) = hasPatErrorCall e
-- otherwise
hasPatErrorCall _ = False


qualifiedNameFS :: Name -> FastString
qualifiedNameFS n = concatFS [modFS, occFS, uniqFS]
where
modFS = case nameModule_maybe n of
Nothing -> fsLit ""
Just m -> concatFS [moduleNameFS (moduleName m), fsLit "."]

occFS = occNameFS (getOccName n)
uniqFS
| isSystemName n
= concatFS [fsLit "_", fsLit (showPprQualified (getUnique n))]
| otherwise
= fsLit ""

-- Variants of Outputable functions which now require DynFlags!
showPprQualified :: Outputable a => a -> String
showPprQualified = showSDocQualified . ppr

showSDocQualified :: Ghc.SDoc -> String
showSDocQualified = Ghc.renderWithContext ctx
where
style = Ghc.mkUserStyle myQualify Ghc.AllTheWay
ctx = Ghc.defaultSDocContext { sdocStyle = style }

myQualify :: Ghc.PrintUnqualified
myQualify = Ghc.neverQualify { Ghc.queryQualifyName = Ghc.alwaysQualifyNames }
-- { Ghc.queryQualifyName = \_ _ -> Ghc.NameNotInScope1 }
21 changes: 3 additions & 18 deletions liquidhaskell-boot/src/Language/Haskell/Liquid/GHC/Misc.hs
Original file line number Diff line number Diff line change
Expand Up @@ -189,15 +189,12 @@ pprDoc = sDocDoc . ppr

-- Overriding Outputable functions because they now require DynFlags!
showPpr :: Outputable a => a -> String
showPpr = showSDoc . ppr
showPpr = Ghc.showPprQualified

-- FIXME: somewhere we depend on this printing out all GHC entities with
-- fully-qualified names...
showSDoc :: Ghc.SDoc -> String
showSDoc = Ghc.renderWithContext ctx
where
style = Ghc.mkUserStyle myQualify Ghc.AllTheWay
ctx = Ghc.defaultSDocContext { sdocStyle = style }
showSDoc = Ghc.showSDocQualified

myQualify :: Ghc.PrintUnqualified
myQualify = Ghc.neverQualify { Ghc.queryQualifyName = Ghc.alwaysQualifyNames }
Expand Down Expand Up @@ -486,19 +483,7 @@ exportedVarSymbol x = notracepp msg . symbol . getName $ x
msg = "exportedVarSymbol: " ++ showPpr x

qualifiedNameSymbol :: Name -> Symbol
qualifiedNameSymbol n = symbol $ concatFS [modFS, occFS, uniqFS]
where
_msg = showSDoc (ppr n) -- getOccString n
modFS = case nameModule_maybe n of
Nothing -> fsLit ""
Just m -> concatFS [moduleNameFS (moduleName m), fsLit "."]

occFS = occNameFS (getOccName n)
uniqFS
| isSystemName n
= concatFS [fsLit "_", fsLit (showPpr (getUnique n))]
| otherwise
= fsLit ""
qualifiedNameSymbol = symbol . Ghc.qualifiedNameFS

instance Symbolic FastString where
symbol = symbol . fastStringText
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ instance Simplify C.CoreExpr where
= -- Misc.traceShow ("To simplify allowTC case") $
sub (M.singleton x (simplify allowTC e)) (simplify allowTC ee)
simplify allowTC (C.Case e x t alts)
= C.Case (simplify allowTC e) x t (filter (not . isUndefined) (simplify allowTC <$> alts))
= C.Case (simplify allowTC e) x t (filter (not . isPatErrorAlt) (simplify allowTC <$> alts))
simplify allowTC (C.Cast e c)
= C.Cast (simplify allowTC e) c
simplify allowTC (C.Tick _ e)
Expand All @@ -646,24 +646,6 @@ instance Simplify C.CoreExpr where
inline _ (C.Coercion c) = C.Coercion c
inline _ (C.Type t) = C.Type t

isUndefined :: CoreAlt -> Bool
isUndefined (Alt _ _ exprCoreBndr) = isUndefinedExpr exprCoreBndr
where
isUndefinedExpr :: C.CoreExpr -> Bool
-- auto generated undefined case: (\_ -> (patError @levity @type "error message")) void
-- Type arguments are erased before calling isUndefined
isUndefinedExpr (C.App (C.Var x) _)
| show x `elem` perrors = True
-- another auto generated undefined case:
-- let lqanf_... = patError "error message") in case lqanf_... of {}
isUndefinedExpr (C.Let (C.NonRec x e) (C.Case (C.Var v) _ _ []))
| x == v = isUndefinedExpr e
isUndefinedExpr (C.Let _ e) = isUndefinedExpr e
-- otherwise
isUndefinedExpr _ = False

perrors = ["Control.Exception.Base.patError"]


instance Simplify C.CoreBind where
simplify allowTC (C.NonRec x e) = C.NonRec x (simplify allowTC e)
Expand Down

0 comments on commit 79b339c

Please sign in to comment.