Skip to content

Commit c5cbe8c

Browse files
authored
Merge pull request #2440 from actonlang/fix-ty-file-corruption
Handle corrupted .ty files gracefully
2 parents 5522a0a + b6134ff commit c5cbe8c

1 file changed

Lines changed: 35 additions & 11 deletions

File tree

compiler/actonc/Main.hs

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ import qualified InterfaceFiles
4545
import Control.Applicative
4646
import Control.Concurrent.Async
4747
import Control.Concurrent.MVar
48-
import Control.Exception (throw,catch,displayException,IOException,ErrorCall)
48+
import Control.Exception (throw,catch,displayException,IOException,ErrorCall,try,SomeException)
4949
import Control.Exception (bracketOnError)
5050
import Control.Concurrent (forkIO)
5151
import Control.Monad
@@ -810,17 +810,17 @@ doTask gopts opts paths env t@(ActonTask mn src m) = do
810810
timeStart <- getTime Monotonic
811811

812812
let outFiles = [tyFile, hFile, cFile]
813-
ok <- checkUptoDate gopts opts paths actFile outFiles (importsOf t)
814-
if C.only_build opts || (ok && not (mn == (modName paths) && (forceCompilation opts)))
815-
then do
816-
timeBeforeTy <- getTime Monotonic
817-
(_,nmod) <- InterfaceFiles.readFile tyFile
813+
timeBeforeCheck <- getTime Monotonic
814+
uptoDateResult <- checkUptoDate gopts opts paths actFile outFiles (importsOf t)
815+
timeAfterCheck <- getTime Monotonic
816+
iff (C.timing gopts) $ putStrLn(" Check up-to-date & read .ty file: " ++ fmtTime(timeAfterCheck - timeBeforeCheck))
817+
case uptoDateResult of
818+
Just (ms, nmod) | C.only_build opts || not (mn == (modName paths) && (forceCompilation opts)) -> do
818819
timeEnd <- getTime Monotonic
819-
iff (C.timing gopts) $ putStrLn(" Read .ty file " ++ makeRelative (projPath paths) tyFile ++ ": " ++ fmtTime(timeEnd - timeBeforeTy))
820820
iff (not (quiet gopts opts)) $ putStrLn(" Already up to date, in " ++ fmtTime(timeEnd - timeStart))
821821
let A.NModule te mdoc = nmod
822822
return (Acton.Env.addMod mn te mdoc env)
823-
else do
823+
_ -> do
824824
createDirectoryIfMissing True (getModPath (projTypes paths) mn)
825825
env' <- runRestPasses gopts opts paths env m
826826
`catch` handle gopts opts "Compilation error" generalError src paths mn
@@ -839,7 +839,18 @@ doTask gopts opts paths env t@(ActonTask mn src m) = do
839839
|| (C.norm args) || (C.deact args) || (C.cps args) || (C.llift args) || (C.hgen args) ||(C.cgen args)
840840

841841

842-
checkUptoDate :: C.GlobalOptions -> C.CompileOptions -> Paths -> FilePath -> [FilePath] -> [A.ModName] -> IO Bool
842+
-- | Check if a module is up-to-date and return its type interface if it is.
843+
-- Returns Nothing if the module needs recompilation (not up-to-date or .ty file unreadable).
844+
-- Returns Just (imports, nameInfo) if the module is up-to-date with a valid .ty file.
845+
--
846+
-- The function checks:
847+
-- 1. All output files exist
848+
-- 2. Output files are newer than source files
849+
-- 3. Import dependencies are up-to-date
850+
-- 4. The .ty file (first in outFiles) can be successfully read. Torn writes
851+
-- (actonc was killed while working) or compiler updates can cause .ty files
852+
-- to be unreadable and thus we should consider it out-of-date and recompile
853+
checkUptoDate :: C.GlobalOptions -> C.CompileOptions -> Paths -> FilePath -> [FilePath] -> [A.ModName] -> IO (Maybe ([A.ModName], A.NameInfo))
843854
checkUptoDate gopts opts paths actFile outFiles imps = do
844855
iff (C.verbose gopts) (putStrLn (" Checking " ++ makeRelative (srcDir paths) actFile ++ " is up to date..."))
845856
-- get the path to the actonc binary, i.e. ourself
@@ -853,13 +864,26 @@ checkUptoDate gopts opts paths actFile outFiles imps = do
853864
if not (and outExists)
854865
then do
855866
iff (C.verbose gopts) (putStrLn (" Missing output files: " ++ show outExists ++ " for " ++ show outFiles))
856-
return False
867+
return Nothing
857868
else do
858869
-- get the time of the last modified source file
859870
srcTime <- head <$> sortBy (comparing Down) <$> mapM System.Directory.getModificationTime srcFiles
860871
outTiming <- mapM System.Directory.getModificationTime outFiles
861872
impsOK <- mapM (impOK (head outTiming)) imps
862-
return (all (srcTime <) outTiming && and impsOK)
873+
if not (all (srcTime <) outTiming && and impsOK)
874+
then do
875+
iff (C.verbose gopts) (putStrLn (" Source files are newer than output files"))
876+
return Nothing
877+
else do
878+
-- All timestamps check out, now verify the .ty file is readable
879+
-- The .ty file is always the first in outFiles
880+
let tyFile = head outFiles
881+
tyResult <- (try :: IO a -> IO (Either SomeException a)) $ InterfaceFiles.readFile tyFile
882+
case tyResult of
883+
Left e -> do
884+
iff (C.verbose gopts) (putStrLn (" .ty file is unreadable: " ++ displayException e))
885+
return Nothing
886+
Right contents -> return (Just contents)
863887
where
864888
srcBase = joinPath [takeDirectory actFile, takeBaseName actFile]
865889
srcCFile = srcBase ++ ".c"

0 commit comments

Comments
 (0)