Commit 84edbae5 by Zachary Snow

preliminary scoped errors with approximate source location

- scoped traversals can now raise errors which contain the path of the
  current scope and an approximate source location based on preceding
  trace comments, if available
- initially, this new error messaging has only been added for the
  illegal size cast checks in the TypeOf and Cast conversions
- error suite tests can provide a verbose mode expected source location
parent f061e882
...@@ -5,6 +5,11 @@ ...@@ -5,6 +5,11 @@
* Added support for excluding the conversion of unbased unsized literals (e.g., * Added support for excluding the conversion of unbased unsized literals (e.g.,
`'1`, `'x`) via `--exclude UnbasedUniszed` `'1`, `'x`) via `--exclude UnbasedUniszed`
### Other Enhancements
* Certain errors raised during conversion now also provide hierarchical and
approximate source location information to help locate the error
## v0.0.9 ## v0.0.9
### Breaking Changes ### Breaking Changes
......
...@@ -115,7 +115,8 @@ convertCastM (Number size) _ _ ...@@ -115,7 +115,8 @@ convertCastM (Number size) _ _
where where
maybeInt = numberToInteger size maybeInt = numberToInteger size
Just int = maybeInt Just int = maybeInt
illegal s = error $ "size cast width " ++ show size ++ " is not " ++ s illegal = scopedErrorM . msg
msg s = "size cast width " ++ show size ++ " is not " ++ s
convertCastM (Number size) (Number value) signed = convertCastM (Number size) (Number value) signed =
return $ Number $ return $ Number $
numberCast signed (fromIntegral size') value numberCast signed (fromIntegral size') value
......
...@@ -57,6 +57,8 @@ module Convert.Scoper ...@@ -57,6 +57,8 @@ module Convert.Scoper
, withinProcedureM , withinProcedureM
, procedureLoc , procedureLoc
, procedureLocM , procedureLocM
, scopedError
, scopedErrorM
, isLoopVar , isLoopVar
, isLoopVarM , isLoopVarM
, loopVarDepth , loopVarDepth
...@@ -68,7 +70,7 @@ module Convert.Scoper ...@@ -68,7 +70,7 @@ module Convert.Scoper
) where ) where
import Control.Monad.State.Strict import Control.Monad.State.Strict
import Data.List (findIndices, partition) import Data.List (findIndices, intercalate, isPrefixOf, partition)
import Data.Maybe (isNothing) import Data.Maybe (isNothing)
import qualified Data.Map.Strict as Map import qualified Data.Map.Strict as Map
...@@ -105,6 +107,7 @@ data Scopes a = Scopes ...@@ -105,6 +107,7 @@ data Scopes a = Scopes
, sProcedureLoc :: [Access] , sProcedureLoc :: [Access]
, sInjectedItems :: [(Bool, ModuleItem)] , sInjectedItems :: [(Bool, ModuleItem)]
, sInjectedDecls :: [Decl] , sInjectedDecls :: [Decl]
, sLatestTrace :: String
} deriving Show } deriving Show
extractMapping :: Scopes a -> Map.Map Identifier a extractMapping :: Scopes a -> Map.Map Identifier a
...@@ -353,6 +356,26 @@ procedureLocM = gets procedureLoc ...@@ -353,6 +356,26 @@ procedureLocM = gets procedureLoc
procedureLoc :: Scopes a -> [Access] procedureLoc :: Scopes a -> [Access]
procedureLoc = sProcedureLoc procedureLoc = sProcedureLoc
debugLocation :: Scopes a -> String
debugLocation s =
hierarchy ++
if null latestTrace
then " (use -v to get approximate source location)"
else ", near " ++ latestTrace
where
hierarchy = intercalate "." $ map tierToStr $ sCurrent s
latestTrace = sLatestTrace s
tierToStr :: Tier -> String
tierToStr (Tier "" _) = "<unnamed_block>"
tierToStr (Tier x "") = x
tierToStr (Tier x y) = x ++ '[' : y ++ "]"
scopedErrorM :: Monad m => String -> ScoperT a m x
scopedErrorM msg = get >>= flip scopedError msg
scopedError :: Scopes a -> String -> x
scopedError scopes = error . (++ ", within scope " ++ debugLocation scopes)
isLoopVar :: Scopes a -> Identifier -> Bool isLoopVar :: Scopes a -> Identifier -> Bool
isLoopVar scopes x = any matches $ sCurrent scopes isLoopVar scopes x = any matches $ sCurrent scopes
where matches = (== x) . tierIndex where matches = (== x) . tierIndex
...@@ -411,7 +434,10 @@ runScoperT :: Monad m => ScoperT a m x -> m (x, Scopes a) ...@@ -411,7 +434,10 @@ runScoperT :: Monad m => ScoperT a m x -> m (x, Scopes a)
runScoperT = flip runStateT initialState runScoperT = flip runStateT initialState
initialState :: Scopes a initialState :: Scopes a
initialState = Scopes [] Map.empty [] [] [] initialState = Scopes [] Map.empty [] [] [] ""
tracePrefix :: String
tracePrefix = "Trace: "
scopeModuleItem scopeModuleItem
:: forall a m. Monad m :: forall a m. Monad m
...@@ -420,7 +446,7 @@ scopeModuleItem ...@@ -420,7 +446,7 @@ scopeModuleItem
-> MapperM (ScoperT a m) GenItem -> MapperM (ScoperT a m) GenItem
-> MapperM (ScoperT a m) Stmt -> MapperM (ScoperT a m) Stmt
-> MapperM (ScoperT a m) ModuleItem -> MapperM (ScoperT a m) ModuleItem
scopeModuleItem declMapper moduleItemMapper genItemMapper stmtMapper = scopeModuleItem declMapperRaw moduleItemMapper genItemMapper stmtMapperRaw =
wrappedModuleItemMapper wrappedModuleItemMapper
where where
fullStmtMapper :: Stmt -> ScoperT a m Stmt fullStmtMapper :: Stmt -> ScoperT a m Stmt
...@@ -438,6 +464,21 @@ scopeModuleItem declMapper moduleItemMapper genItemMapper stmtMapper = ...@@ -438,6 +464,21 @@ scopeModuleItem declMapper moduleItemMapper genItemMapper stmtMapper =
then traverseSinglyNestedStmtsM fullStmtMapper stmt' then traverseSinglyNestedStmtsM fullStmtMapper stmt'
else fullStmtMapper $ Block Seq "" injected [stmt'] else fullStmtMapper $ Block Seq "" injected [stmt']
declMapper :: Decl -> ScoperT a m Decl
declMapper decl@(CommentDecl c) =
consumeComment c >> return decl
declMapper decl = declMapperRaw decl
stmtMapper :: Stmt -> ScoperT a m Stmt
stmtMapper stmt@(CommentStmt c) =
consumeComment c >> return stmt
stmtMapper stmt = stmtMapperRaw stmt
consumeComment :: String -> ScoperT a m ()
consumeComment c =
when (tracePrefix `isPrefixOf` c) $
modify' $ \s -> s { sLatestTrace = drop (length tracePrefix) c }
-- converts a decl and adds decls injected during conversion -- converts a decl and adds decls injected during conversion
declMapper' :: Decl -> ScoperT a m [Decl] declMapper' :: Decl -> ScoperT a m [Decl]
declMapper' decl = do declMapper' decl = do
......
...@@ -130,7 +130,8 @@ traverseExprM other = ...@@ -130,7 +130,8 @@ traverseExprM other =
elaborateSizeCast :: Expr -> Expr -> ST Expr elaborateSizeCast :: Expr -> Expr -> ST Expr
elaborateSizeCast (Number size) _ | Just 0 == numberToInteger size = elaborateSizeCast (Number size) _ | Just 0 == numberToInteger size =
-- special case because zero-width ranges cannot be represented -- special case because zero-width ranges cannot be represented
error $ "size cast width " ++ show size ++ " is not a positive integer" scopedErrorM $ "size cast width " ++ show size
++ " is not a positive integer"
elaborateSizeCast size value = do elaborateSizeCast size value = do
t <- typeof value t <- typeof value
force <- isStringParam value force <- isStringParam value
......
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
runErrorTest() { runErrorTest() {
extractFlag pattern $1.sv extractFlag pattern $1.sv
pattern="${flag:-.}" pattern="${flag:-.}"
extractFlag location $1.sv
location="${flag//./\.}"
location="${location:-.}"
runAndCapture $1.sv runAndCapture $1.sv
assertFalse "regular conversion should have failed" $result assertFalse "regular conversion should have failed" $result
...@@ -15,6 +18,7 @@ runErrorTest() { ...@@ -15,6 +18,7 @@ runErrorTest() {
assertNull "verbose stdout should be empty" "$stdout" assertNull "verbose stdout should be empty" "$stdout"
assertNotNull "verbose stderr should not be empty" "$stderr" assertNotNull "verbose stderr should not be empty" "$stderr"
assertMatch "verbose error message" "$stderr" "$pattern" assertMatch "verbose error message" "$stderr" "$pattern"
assertMatch "verbose location" "$stderr" "$location[^0-9]"
} }
addTest() { addTest() {
......
// pattern: size cast width 1'sb1 is not a positive integer // pattern: size cast width 1'sb1 is not a positive integer
// location: size_cast_neg_lit_1.sv:4:13
module top; module top;
initial $display((1'sb1)'(2)); initial $display((1'sb1)'(2));
endmodule endmodule
// pattern: size cast width 2'sb11 is not a positive integer // pattern: size cast width 2'sb11 is not a positive integer
// location: size_cast_neg_lit_2.sv:4:13
module top; module top;
initial $display((2'sb11)'(2)); initial $display((2'sb11)'(2));
endmodule endmodule
// pattern: size cast width 1'sb1 is not a positive integer // pattern: size cast width 1'sb1 is not a positive integer
// location: size_cast_neg_var_1.sv:5:13
module top; module top;
wire x = 0; wire x = 0;
initial $display((1'sb1)'(x)); initial $display((1'sb1)'(x));
......
// pattern: size cast width 2'sb11 is not a positive integer // pattern: size cast width 2'sb11 is not a positive integer
// location: size_cast_neg_var_2.sv:5:13
module top; module top;
wire x = 0; wire x = 0;
initial $display((2'sb11)'(x)); initial $display((2'sb11)'(x));
......
// pattern: size cast width 1'bx is not an integer // pattern: size cast width 1'bx is not an integer
// location: size_cast_x_lit.sv:4:13
module top; module top;
initial $display((1'bx)'(2)); initial $display((1'bx)'(2));
endmodule endmodule
// pattern: size cast width 1'bx is not an integer // pattern: size cast width 1'bx is not an integer
// location: size_cast_x_var.sv:5:13
module top; module top;
wire x = 0; wire x = 0;
initial $display((1'bx)'(x)); initial $display((1'bx)'(x));
......
// pattern: size cast width 0 is not a positive integer // pattern: size cast width 0 is not a positive integer
// location: size_cast_zero_lit.sv:4:13
module top; module top;
initial $display((0)'(2)); initial $display((0)'(2));
endmodule endmodule
// pattern: size cast width 0 is not a positive integer // pattern: size cast width 0 is not a positive integer
// location: size_cast_zero_var.sv:5:13
module top; module top;
wire x = 0; wire x = 0;
initial $display((0)'(x)); initial $display((0)'(x));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment