diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,47 @@
+## v0.0.12
+
+### Breaking Changes
+
+* Removed deprecated CLI flags `-d`/`-e`/`-i`, which have been aliased to
+  `-D`/`-E`/`-I` with a warning since late 2019
+
+### New Features
+
+* `unique`, `unique0`, and `priority` case statements now produce corresponding
+  `parallel_case` and `full_case` statement attributes
+* Added support for attributes in unary, binary, and ternary expressions
+* Added support for streaming concatenations within ternary expressions
+* Added support for shadowing interface names with local typenames
+* Added support for passing through `wait` statements
+
+### Bug Fixes
+
+* Fixed signed unsized literals with a leading 1 bit (e.g., `'sb1`, `'sh8f`)
+  incorrectly sign-extending in size and type casts
+* Fixed conflicting genvar names when inlining interfaces and modules that use
+  them; all genvars are now given a design-wide unique name
+* Fixed unconverted structs within explicit type casts
+* Fixed byte order of strings in size casts
+* Fixed unconverted multidimensional struct fields within dimension queries
+* Fixed non-typenames (e.g., from packages or subsequent declarations)
+  improperly shadowing the names of `struct` pattern fields
+* Fixed shadowing of interface array indices passed to port connections
+* Fixed failure to resolve typenames suffixed with dimensions in contexts
+  permitting both types and expressions, e.g., `$bits(T[W-1:0])`
+* Fixed an issue that prevented parsing tasks and functions with `inout` ports
+* Fixed errant constant folding of shadowed non-trivial localparams
+* Fixed conversion of function calls with no arguments passed to other functions
+* Fixed certain non-ANSI style port declarations being incorrectly reported as
+  incompatible
+
+### Other Enhancements
+
+* `always_comb` and `always_latch` now reliably execute at time zero
+* Added error checking for unresolved typenames
+* Added constant folding for `||` and `&&`
+* `input reg` module ports are now converted to `input wire`
+* `x | |y` and `x & &y` are now output as `x | (|y)` and `x & (&y)`
+
 ## v0.0.11
 
 ### New Features
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 BSD 3-Clause License
 
-Copyright 2019-2023 Zachary Snow
+Copyright 2019-2024 Zachary Snow
 Copyright 2011-2015 Tom Hawkins
 
 All rights reserved.
diff --git a/NOTICE b/NOTICE
--- a/NOTICE
+++ b/NOTICE
@@ -1,5 +1,5 @@
 ================================================================================
-Dependency: alex-3.2.7.1
+Dependency: alex-3.4.0.1
 ================================================================================
 
   Copyright (c) 1995-2011, Chris Dornan and Simon Marlow
@@ -34,7 +34,7 @@
   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 ================================================================================
-Dependency: array-0.5.4.0
+Dependency: array-0.5.6.0
 ================================================================================
 
   This library (libraries/base) is derived from code from several
@@ -123,7 +123,7 @@
 
 
 ================================================================================
-Dependency: base-4.14.3.0
+Dependency: base-4.18.2.0
 ================================================================================
 
   This library (libraries/base) is derived from code from several
@@ -211,7 +211,7 @@
   -----------------------------------------------------------------------------
 
 ================================================================================
-Dependency: binary-0.8.8.0
+Dependency: binary-0.8.9.1
 ================================================================================
 
   Copyright (c) Lennart Kolmodin
@@ -246,13 +246,14 @@
   POSSIBILITY OF SUCH DAMAGE.
 
 ================================================================================
-Dependency: bytestring-0.10.12.0
+Dependency: bytestring-0.11.5.3
 ================================================================================
 
   Copyright (c) Don Stewart 2005-2009
             (c) Duncan Coutts 2006-2015
             (c) David Roundy 2003-2005
             (c) Simon Meier 2010-2011
+            (c) Koz Ross 2021
 
   All rights reserved.
 
@@ -281,10 +282,10 @@
   SUCH DAMAGE.
 
 ================================================================================
-Dependency: cmdargs-0.10.21
+Dependency: cmdargs-0.10.22
 ================================================================================
 
-  Copyright Neil Mitchell 2009-2021.
+  Copyright Neil Mitchell 2009-2023.
   All rights reserved.
 
   Redistribution and use in source and binary forms, with or without
@@ -316,7 +317,7 @@
   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 ================================================================================
-Dependency: containers-0.6.5.1
+Dependency: containers-0.6.7
 ================================================================================
 
   The Glasgow Haskell Compiler License
@@ -352,7 +353,7 @@
   DAMAGE.
 
 ================================================================================
-Dependency: deepseq-1.4.4.0
+Dependency: deepseq-1.4.8.1
 ================================================================================
 
   This library (deepseq) is derived from code from the GHC project which
@@ -396,7 +397,7 @@
   -----------------------------------------------------------------------------
 
 ================================================================================
-Dependency: directory-1.3.6.0
+Dependency: directory-1.3.8.1
 ================================================================================
 
   This library (libraries/base) is derived from code from two
@@ -464,13 +465,49 @@
   -----------------------------------------------------------------------------
 
 ================================================================================
-Dependency: filepath-1.4.2.1
+Dependency: exceptions-0.10.7
 ================================================================================
 
-  Copyright Neil Mitchell 2005-2018.
+  Copyright 2013-2015 Edward Kmett
+  Copyright 2012 Google Inc.
+
   All rights reserved.
 
   Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+
+  3. Neither the name of the author nor the names of his contributors
+     may be used to endorse or promote products derived from this software
+     without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
+  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  POSSIBILITY OF SUCH DAMAGE.
+
+================================================================================
+Dependency: filepath-1.4.200.1
+================================================================================
+
+  Copyright Neil Mitchell 2005-2020.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are
   met:
 
@@ -499,9 +536,45 @@
   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 ================================================================================
-Dependency: ghc-prim-0.6.1
+Dependency: ghc-bignum-1.3
 ================================================================================
 
+  The Glasgow Haskell Compiler License
+
+  Copyright 2020, The University Court of the University of Glasgow.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+  - Redistributions of source code must retain the above copyright notice,
+  this list of conditions and the following disclaimer.
+
+  - Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+  - Neither name of the University nor the names of its contributors may be
+  used to endorse or promote products derived from this software without
+  specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY COURT OF THE UNIVERSITY OF
+  GLASGOW AND THE CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  UNIVERSITY COURT OF THE UNIVERSITY OF GLASGOW OR THE CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+  DAMAGE.
+
+================================================================================
+Dependency: ghc-prim-0.10.0
+================================================================================
+
   This library (libraries/ghc-prim) is derived from code from several
   sources:
 
@@ -566,7 +639,7 @@
 
 
 ================================================================================
-Dependency: githash-0.1.6.2
+Dependency: githash-0.1.7.0
 ================================================================================
 
   Copyright (c) 2018, Michael Snoyman, 2015, Adam C. Foltzer
@@ -598,7 +671,7 @@
   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 ================================================================================
-Dependency: happy-1.20.0
+Dependency: happy-1.20.1.1
 ================================================================================
 
   The Happy License
@@ -633,7 +706,7 @@
   IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 ================================================================================
-Dependency: hashable-1.3.0.0
+Dependency: hashable-1.4.4.0
 ================================================================================
 
   Copyright Milan Straka 2010
@@ -668,42 +741,7 @@
   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 ================================================================================
-Dependency: integer-gmp-1.0.3.0
-================================================================================
-
-  Copyright (c) 2014, Herbert Valerio Riedel
-
-  All rights reserved.
-
-  Redistribution and use in source and binary forms, with or without
-  modification, are permitted provided that the following conditions are met:
-
-      * Redistributions of source code must retain the above copyright
-        notice, this list of conditions and the following disclaimer.
-
-      * Redistributions in binary form must reproduce the above
-        copyright notice, this list of conditions and the following
-        disclaimer in the documentation and/or other materials provided
-        with the distribution.
-
-      * Neither the name of Herbert Valerio Riedel nor the names of other
-        contributors may be used to endorse or promote products derived
-        from this software without specific prior written permission.
-
-  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-================================================================================
-Dependency: mtl-2.2.2
+Dependency: mtl-2.3.1
 ================================================================================
 
   The Glasgow Haskell Compiler License
@@ -739,6 +777,41 @@
   DAMAGE.
 
 ================================================================================
+Dependency: os-string-2.0.2.1
+================================================================================
+
+  Copyright Neil Mitchell 2005-2020.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+
+      * Neither the name of Neil Mitchell nor the names of other
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+================================================================================
 Dependency: pretty-1.1.3.6
 ================================================================================
 
@@ -783,7 +856,7 @@
   -----------------------------------------------------------------------------
 
 ================================================================================
-Dependency: primitive-0.7.3.0
+Dependency: primitive-0.8.0.0
 ================================================================================
 
   Copyright (c) 2008-2009, Roman Leshchinskiy
@@ -818,7 +891,7 @@
 
 
 ================================================================================
-Dependency: process-1.6.13.2
+Dependency: process-1.6.17.0
 ================================================================================
 
   This library (libraries/process) is derived from code from two
@@ -886,10 +959,46 @@
   -----------------------------------------------------------------------------
 
 ================================================================================
-Dependency: template-haskell-2.16.0.0
+Dependency: stm-2.5.1.0
 ================================================================================
 
+  The Glasgow Haskell Compiler License
 
+  Copyright 2004, The University Court of the University of Glasgow.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+  - Redistributions of source code must retain the above copyright notice,
+  this list of conditions and the following disclaimer.
+
+  - Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+  - Neither name of the University nor the names of its contributors may be
+  used to endorse or promote products derived from this software without
+  specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY COURT OF THE UNIVERSITY OF
+  GLASGOW AND THE CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  UNIVERSITY COURT OF THE UNIVERSITY OF GLASGOW OR THE CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+  DAMAGE.
+
+================================================================================
+Dependency: template-haskell-2.20.0.0
+================================================================================
+
+
   The Glasgow Haskell Compiler License
 
   Copyright 2002-2007, The University Court of the University of Glasgow.
@@ -924,7 +1033,7 @@
 
 
 ================================================================================
-Dependency: text-1.2.4.1
+Dependency: text-2.0.2
 ================================================================================
 
   Copyright (c) 2008-2009, Tom Harper
@@ -955,7 +1064,7 @@
   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 ================================================================================
-Dependency: th-compat-0.1.3
+Dependency: th-compat-0.1.5
 ================================================================================
 
   Copyright (c) 2020, Ryan Scott
@@ -990,10 +1099,10 @@
   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 ================================================================================
-Dependency: time-1.9.3
+Dependency: time-1.12.2
 ================================================================================
 
-  TimeLib is Copyright (c) Ashley Yakeley, 2004-2014. All rights reserved.
+  TimeLib is Copyright (c) Ashley Yakeley and contributors, 2004-2022. All rights reserved.
   Certain sections are Copyright 2004, The University Court of the University of Glasgow. All rights reserved.
 
   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
@@ -1005,7 +1114,7 @@
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 ================================================================================
-Dependency: transformers-0.5.6.2
+Dependency: transformers-0.6.1.0
 ================================================================================
 
   The Glasgow Haskell Compiler License
@@ -1041,7 +1150,7 @@
   DAMAGE.
 
 ================================================================================
-Dependency: unix-2.7.2.2
+Dependency: unix-2.8.4.0
 ================================================================================
 
   The Glasgow Haskell Compiler License
@@ -1077,10 +1186,13 @@
   DAMAGE.
 
 ================================================================================
-Dependency: vector-0.12.3.1
+Dependency: vector-0.13.1.0
 ================================================================================
 
   Copyright (c) 2008-2012, Roman Leshchinskiy
+                2020-2022, Alexey Kuleshevich
+                2020-2022, Aleksey Khudyakov
+                2020-2022, Andrew Lelechenko
   All rights reserved.
 
   Redistribution and use in source and binary forms, with or without
@@ -1110,4 +1222,40 @@
   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
   DAMAGE.
 
+================================================================================
+Dependency: vector-stream-0.1.0.1
+================================================================================
+
+  Copyright (c) 2008-2012, Roman Leshchinskiy
+                2020-2022, Alexey Kuleshevich
+                2020-2022, Aleksey Khudyakov
+                2020-2022, Andrew Lelechenko
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+  - Redistributions of source code must retain the above copyright notice,
+  this list of conditions and the following disclaimer.
+
+  - Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+  - Neither name of the University nor the names of its contributors may be
+  used to endorse or promote products derived from this software without
+  specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY COURT OF THE UNIVERSITY OF
+  GLASGOW AND THE CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  UNIVERSITY COURT OF THE UNIVERSITY OF GLASGOW OR THE CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+  DAMAGE.
 
diff --git a/src/Convert.hs b/src/Convert.hs
--- a/src/Convert.hs
+++ b/src/Convert.hs
@@ -27,6 +27,7 @@
 import qualified Convert.Foreach
 import qualified Convert.FuncRet
 import qualified Convert.FuncRoutine
+import qualified Convert.GenvarName
 import qualified Convert.HierConst
 import qualified Convert.ImplicitNet
 import qualified Convert.Inside
@@ -114,8 +115,10 @@
     , Convert.DoWhile.convert
     , Convert.Foreach.convert
     , Convert.FuncRoutine.convert
+    , Convert.GenvarName.convert
     , selectExclude Job.Assert Convert.Assertion.convert
     , selectExclude Job.Always Convert.AlwaysKW.convert
+    , Convert.Interface.disambiguate
     , Convert.Package.convert
     , Convert.StructConst.convert
     , Convert.PortDecl.convert
diff --git a/src/Convert/AlwaysKW.hs b/src/Convert/AlwaysKW.hs
--- a/src/Convert/AlwaysKW.hs
+++ b/src/Convert/AlwaysKW.hs
@@ -5,16 +5,18 @@
  -
  - `always_comb` and `always_latch` become `always @*`, or produce an explicit
  - sensitivity list if they need to pick up sensitivities from the functions
- - they call. `always_ff` simply becomes `always`.
- -
- - TODO: `always_comb` blocks do not run at time zero
+ - they call. These blocks are triggered at time zero by adding a no-op
+ - statement reading from `_sv2v_0`, which is injected and updated in an
+ - `initial` block. `always_ff` simply becomes `always`.
  -}
 
 module Convert.AlwaysKW (convert) where
 
+import Control.Monad (when, zipWithM, (>=>))
 import Control.Monad.State.Strict
 import Control.Monad.Writer.Strict
 import Data.Maybe (fromMaybe, mapMaybe)
+import Data.Monoid (Any(Any), getAny)
 
 import Convert.Scoper
 import Convert.Traverse
@@ -24,9 +26,15 @@
 convert = map $ traverseDescriptions traverseDescription
 
 traverseDescription :: Description -> Description
-traverseDescription description@Part{} =
-    evalState (evalScoperT $ scopePart op description) mempty
-    where op = traverseModuleItem >=> scoper
+traverseDescription (Part att ext kw lif name pts items) =
+    Part att ext kw lif name pts $
+    if getAny anys && not (elem triggerDecl items')
+        then triggerDecl : items' ++ [triggerFire]
+        else items'
+    where
+        op = traverseModuleItem >=> scoper
+        (items', (anys, _)) = flip runState mempty $ evalScoperT $
+            insertElem triggerIdent Var >> scopeModuleItems op name items
 traverseDescription description = description
 
 type SC = ScoperT Kind (State (Any, [Expr]))
@@ -182,12 +190,13 @@
 -- custom traversal which converts SystemVerilog `always` keywords and tracks
 -- information about task and functions
 traverseModuleItem :: ModuleItem -> SC ModuleItem
-traverseModuleItem (AlwaysC AlwaysLatch stmt) = do
-    e <- fmap toEvent $ findNonLocals $ Initial stmt
-    return $ AlwaysC Always $ Timing (Event e) stmt
+traverseModuleItem (AlwaysC AlwaysLatch stmt) =
+    traverseModuleItem $ AlwaysC AlwaysComb stmt
 traverseModuleItem (AlwaysC AlwaysComb stmt) = do
-    e <- fmap toEvent $ findNonLocals $ Initial stmt
-    return $ AlwaysC Always $ Timing (Event e) stmt
+    push (Any True, [])
+    e <- fmap toEvent $ findNonLocals $ Initial stmt'
+    return $ AlwaysC Always $ Timing (Event e) stmt'
+    where stmt' = addTriggerStmt stmt
 traverseModuleItem (AlwaysC AlwaysFF stmt) =
     return $ AlwaysC Always stmt
 traverseModuleItem item@(MIPackageItem (Function _ _ x decls _)) = do
@@ -220,8 +229,28 @@
 findNonLocals :: ModuleItem -> SC (Bool, [Expr])
 findNonLocals item = do
     scopes <- get
+    prev <- lift get
     lift $ put mempty
     _ <- scoper item
     (anys, exprs) <- lift get
+    lift $ put prev
     let nonLocals = mapMaybe (longestStaticPrefix scopes) exprs
     return (getAny anys, nonLocals)
+
+triggerIdent :: Identifier
+triggerIdent = "_sv2v_0"
+
+triggerDecl :: ModuleItem
+triggerDecl = MIPackageItem $ Decl $ Variable Local t triggerIdent [] Nil
+    where t = IntegerVector TReg Unspecified []
+
+triggerFire :: ModuleItem
+triggerFire = Initial $ Asgn AsgnOpEq Nothing (LHSIdent triggerIdent) (RawNum 0)
+
+triggerStmt :: Stmt
+triggerStmt = If NoCheck (Ident triggerIdent) Null Null
+
+addTriggerStmt :: Stmt -> Stmt
+addTriggerStmt (Block Seq name decls stmts) =
+    Block Seq name decls $ triggerStmt : stmts
+addTriggerStmt stmt = Block Seq "" [] [triggerStmt, stmt]
diff --git a/src/Convert/Cast.hs b/src/Convert/Cast.hs
--- a/src/Convert/Cast.hs
+++ b/src/Convert/Cast.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE PatternSynonyms #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  -
@@ -28,9 +27,11 @@
 
 module Convert.Cast (convert) where
 
+import Control.Monad (when)
 import Control.Monad.Writer.Strict
 import Data.List (isPrefixOf)
 import Data.Maybe (isJust)
+import Data.Monoid (Any(Any), getAny)
 
 import Convert.ExprUtils
 import Convert.Scoper
diff --git a/src/Convert/EmptyArgs.hs b/src/Convert/EmptyArgs.hs
--- a/src/Convert/EmptyArgs.hs
+++ b/src/Convert/EmptyArgs.hs
@@ -64,7 +64,8 @@
 traverseExpr :: Expr -> SC Expr
 traverseExpr (Call func (Args args [])) = do
     details <- lookupElemM $ Dot func dummyIdent
-    let args' = if details /= Nothing
+    args' <- mapM traverseExpr $
+                if details /= Nothing
                     then RawNum 0 : args
                     else args
     return $ Call func (Args args' [])
diff --git a/src/Convert/ExprAsgn.hs b/src/Convert/ExprAsgn.hs
--- a/src/Convert/ExprAsgn.hs
+++ b/src/Convert/ExprAsgn.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE PatternSynonyms #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  -
diff --git a/src/Convert/ExprUtils.hs b/src/Convert/ExprUtils.hs
--- a/src/Convert/ExprUtils.hs
+++ b/src/Convert/ExprUtils.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE PatternSynonyms #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  -
@@ -14,6 +13,8 @@
     , endianCondRange
     , dimensionsSize
     , stringToNumber
+    , simplifyRange
+    , simplifyDimensions
     ) where
 
 import Data.Bits ((.&.), (.|.), shiftL, shiftR)
@@ -27,15 +28,15 @@
 
 simplifyStep :: Expr -> Expr
 
-simplifyStep (UniOp LogNot (Number n)) =
+simplifyStep (UniOpA LogNot a (Number n)) =
     case numberToInteger n of
         Just 0 -> bool True
         Just _ -> bool False
-        Nothing -> UniOp LogNot $ Number n
+        Nothing -> UniOpA LogNot a $ Number n
 simplifyStep (UniOp LogNot (BinOp Eq a b)) = BinOp Ne a b
 simplifyStep (UniOp LogNot (BinOp Ne a b)) = BinOp Eq a b
 
-simplifyStep (UniOp UniSub (UniOp UniSub e)) = e
+simplifyStep (UniOpA UniSub _ (UniOpA UniSub _ e)) = e
 simplifyStep (UniOp UniSub (BinOp Sub e1 e2)) = BinOp Sub e2 e1
 
 simplifyStep (Concat [Number (Decimal size _ value)]) =
@@ -47,11 +48,11 @@
 simplifyStep (Concat es) = Concat $ flattenConcat es
 simplifyStep (Repeat (Dec 0) _) = Concat []
 simplifyStep (Repeat (Dec 1) es) = Concat es
-simplifyStep (Mux (Number n) e1 e2) =
+simplifyStep (MuxA a (Number n) e1 e2) =
     case numberToInteger n of
         Just 0 -> e2
         Just _ -> e1
-        Nothing -> Mux (Number n) e1 e2
+        Nothing -> MuxA a (Number n) e1 e2
 
 simplifyStep (Call (Ident "$clog2") (Args [SizDec k] [])) =
     simplifyStep $ Call (Ident "$clog2") (Args [RawNum k] [])
@@ -159,6 +160,9 @@
 simplifyBinOp op (ConvertedUU sz v k) (Number n) | isCmpOp op =
     simplifyBinOp op (uuExtend sz v k) (Number n)
 
+simplifyBinOp op (Number n) e | op == LogAnd || op == LogOr =
+    simplifyLogAndOr op n e
+
 simplifyBinOp op e1 e2 =
     case (e1, e2) of
         (Dec    x, Dec    y) -> constantFold orig op   x    y
@@ -284,9 +288,7 @@
 
 -- convert a string to big integer
 stringToInteger :: String -> Integer
-stringToInteger [] = 0
-stringToInteger (x : xs) =
-    fromIntegral (ord x) + (256 :: Integer) * stringToInteger xs
+stringToInteger = foldl ((+) . (256 *)) 0 . map (fromIntegral . ord)
 
 -- cast string to number at least as big as the width of the given number
 sizeStringAs :: String -> Number -> Expr
@@ -319,3 +321,28 @@
 pattern ConvertedUU sz v k <- Repeat
     (RawNum sz)
     [Number (Based 1 True Binary v k)]
+
+simplifyRange :: Range -> Range
+simplifyRange (e1, e2) = (simplify e1, simplify e2)
+
+simplifyDimensions :: [Range] -> [Range]
+simplifyDimensions = map simplifyRange
+
+-- TODO: extend this to other logical binary operators
+simplifyLogAndOr :: BinOp -> Number -> Expr -> Expr
+simplifyLogAndOr op n1 (Number n2) =
+    case (numberToInteger n1, numberToInteger n2) of
+        (Just v, _) | (v /= 0) == isOr -> bool isOr
+        (_, Just v) | (v /= 0) == isOr -> bool isOr
+        (Nothing, _) -> boolUnknown
+        (_, Nothing) -> boolUnknown
+        _ -> bool $ not isOr
+    where
+        isOr = op == LogOr
+        boolUnknown = Number $ Based 1 False Binary 0 1
+simplifyLogAndOr op n e =
+    case numberToInteger n of
+        Just v | (v /= 0) == isOr -> bool isOr
+        Just _ -> UniOp LogNot $ UniOp LogNot e
+        Nothing -> BinOp op (Number n) e
+    where isOr = op == LogOr
diff --git a/src/Convert/GenvarName.hs b/src/Convert/GenvarName.hs
new file mode 100644
--- /dev/null
+++ b/src/Convert/GenvarName.hs
@@ -0,0 +1,127 @@
+{- sv2v
+ - Author: Zachary Snow <zach@zachjs.com>
+ -
+ - Assign unique names to `genvar`s to avoid conflicts within explicitly-scoped
+ - variables when inlining interface arrays.
+ -}
+
+module Convert.GenvarName (convert) where
+
+import Control.Monad (when)
+import Control.Monad.State.Strict
+import Control.Monad.Writer.Strict
+import Data.Functor ((<&>))
+import Data.List (isPrefixOf)
+import qualified Data.Map.Strict as Map
+import qualified Data.Set as Set
+
+import Convert.Scoper (replaceInExpr)
+import Convert.Traverse
+import Language.SystemVerilog.AST
+
+convert :: [AST] -> [AST]
+convert files = evalState
+    (mapM (traverseDescriptionsM traverseDescription) files)
+    (collectFiles files, mempty)
+
+type IdentSet = Set.Set Identifier
+type IdentMap = Map.Map Identifier Identifier
+type SC = State (IdentSet, IdentMap)
+
+-- get all of the seemingly sv2v-generated genvar names already present anywhere
+-- in the sources so we can avoid generating new ones that conflict with them
+collectFiles :: [AST] -> IdentSet
+collectFiles = execWriter . mapM (collectDescriptionsM collectDescription)
+
+collectDescription :: Description -> Writer IdentSet ()
+collectDescription = collectModuleItemsM collectModuleItem
+
+collectModuleItem :: ModuleItem -> Writer IdentSet ()
+collectModuleItem (Genvar ident) =
+    when (isGeneratedName ident) $ tell (Set.singleton ident)
+collectModuleItem _ = return ()
+
+traverseDescription :: Description -> SC Description
+traverseDescription (Part att ext kw lif name ports items) =
+    mapM traverseModuleItem items <&> Part att ext kw lif name ports
+traverseDescription description = return description
+
+traverseModuleItem :: ModuleItem -> SC ModuleItem
+traverseModuleItem (Genvar ident) =
+    renameGenvar ident <&> Genvar
+traverseModuleItem (Generate genItems) =
+    mapM traverseGenItem genItems <&> Generate
+traverseModuleItem (MIAttr attr item) =
+    traverseModuleItem item <&> MIAttr attr
+traverseModuleItem item = return item
+
+traverseGenItem :: GenItem -> SC GenItem
+traverseGenItem (GenFor start@(index, _) cond incr item)
+    | not (isGeneratedName index) = do
+    index' <- gets $ (Map.! index) . snd
+    item' <- traverseGenItem item
+    return $ if index == index'
+        then GenFor start cond incr item'
+        else renameInLoop start cond incr index' item'
+traverseGenItem (GenBlock blk items) = do
+    priorMapping <- gets snd
+    items' <- mapM traverseGenItem items
+    -- keep all assigned names, but prefer names from the outer scope
+    modify' $ (, priorMapping) . fst
+    return $ GenBlock blk items'
+traverseGenItem (GenModuleItem item) =
+    traverseModuleItem item <&> GenModuleItem
+traverseGenItem item =
+    traverseSinglyNestedGenItemsM traverseGenItem item
+
+-- rename all usages of the genvar in the initialization, guard, and
+-- incrementation of a generate for loop
+renameInLoop :: (Identifier, Expr) -> Expr -> (Identifier, AsgnOp, Expr)
+    -> Identifier -> GenItem -> GenItem
+renameInLoop (index, start) cond (dest, op, next) index' =
+    GenFor (index', start') cond' (dest', op, next') . prependGenItem decl
+    where
+        expr = Ident index'
+        replacements = Map.singleton index expr
+        start' = replaceInExpr replacements start
+        cond' = replaceInExpr replacements cond
+        next' = replaceInExpr replacements next
+        dest' = if dest == index then index' else dest
+        decl = GenModuleItem $ MIPackageItem $ Decl $
+                Param Localparam UnknownType index expr
+
+-- add an item to the beginning of the given generate block
+prependGenItem :: GenItem -> GenItem -> GenItem
+prependGenItem item block = GenBlock blk $ item : items
+    where GenBlock blk items = block
+
+prefixIntf :: Identifier
+prefixIntf = "_arr_"
+prefixUniq :: Identifier
+prefixUniq = "_gv_"
+
+isGeneratedName :: Identifier -> Bool
+isGeneratedName ident =
+    isPrefixOf prefixIntf ident ||
+    isPrefixOf prefixUniq ident
+
+-- generate and record a unique name for the given genvar
+renameGenvar :: Identifier -> SC Identifier
+renameGenvar ident | isGeneratedName ident = return ident
+renameGenvar ident = do
+    idents <- gets fst
+    let ident' = uniqueGenvarName idents prefix 1
+    modify' $ (<>) (Set.singleton ident', Map.singleton ident ident')
+    return ident'
+    where prefix = prefixUniq ++ ident ++ "_"
+
+-- increment the counter until it produces a unique identifier
+uniqueGenvarName :: IdentSet -> Identifier -> Int -> Identifier
+uniqueGenvarName idents prefix = step
+    where
+        step :: Int -> Identifier
+        step counter =
+            if Set.member candidate idents
+                then step $ counter + 1
+                else candidate
+            where candidate = prefix ++ show counter
diff --git a/src/Convert/ImplicitNet.hs b/src/Convert/ImplicitNet.hs
--- a/src/Convert/ImplicitNet.hs
+++ b/src/Convert/ImplicitNet.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE PatternSynonyms #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  -
diff --git a/src/Convert/Inside.hs b/src/Convert/Inside.hs
--- a/src/Convert/Inside.hs
+++ b/src/Convert/Inside.hs
@@ -21,6 +21,7 @@
 
 import Control.Monad.Writer
 import Data.Maybe (fromMaybe)
+import Data.Monoid (Any(Any), getAny)
 
 convert :: [AST] -> [AST]
 convert = map $ traverseDescriptions $ traverseModuleItems convertModuleItem
@@ -43,8 +44,8 @@
             BinOp LogAnd
                 (BinOp Le lo expr)
                 (BinOp Ge hi expr)
-        toCheck pattern =
-            BinOp WEq expr pattern
+        toCheck pat =
+            BinOp WEq expr pat
 convertExpr other = other
 
 convertStmt :: Stmt -> Stmt
diff --git a/src/Convert/Interface.hs b/src/Convert/Interface.hs
--- a/src/Convert/Interface.hs
+++ b/src/Convert/Interface.hs
@@ -1,14 +1,15 @@
-{-# LANGUAGE PatternSynonyms #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  -
  - Conversion for interfaces
  -}
 
-module Convert.Interface (convert) where
+module Convert.Interface (convert, disambiguate) where
 
 import Data.List (intercalate, (\\))
 import Data.Maybe (isJust, isNothing, mapMaybe)
+import Data.Monoid (Any(Any), getAny)
+import Control.Monad (when, (>=>))
 import Control.Monad.Writer.Strict
 import Text.Read (readMaybe)
 import qualified Data.Map.Strict as Map
@@ -38,11 +39,6 @@
             (map . convertDescription tops)
             files
     where
-        -- we can only collect/map non-extern interfaces and modules
-        collectPart :: Description -> Writer PartInfos ()
-        collectPart (Part _ False kw _ name ports items) =
-            tell $ Map.singleton name $ PartInfo kw ports items
-        collectPart _ = return ()
         -- multidimensional instances need to be flattened before this
         -- conversion can proceed
         needsFlattening =
@@ -55,6 +51,67 @@
         checkItem (Instance _ _ _ rs _) = when (length rs > 1) $ tell $ Any True
         checkItem _ = return ()
 
+-- we can only collect/map non-extern interfaces and modules
+collectPart :: Description -> Writer PartInfos ()
+collectPart (Part _ False kw _ name ports items) =
+    tell $ Map.singleton name $ PartInfo kw ports items
+collectPart _ = return ()
+
+-- disambiguate typenames from interface names
+disambiguate :: [AST] -> [AST]
+disambiguate = traverseFiles
+    (collectDescriptionsM collectPart)
+    (map . disambiguateDescription)
+
+-- disambiguate any typenames within a description
+disambiguateDescription :: PartInfos -> Description -> Description
+disambiguateDescription parts (Part att ext kw lif name ports items) =
+    Part att ext kw lif name ports $ map traverseModuleItem items
+    where
+        typeNames = getTypeNames items
+
+        traverseModuleItem :: ModuleItem -> ModuleItem
+        traverseModuleItem (MIAttr attr item) =
+            MIAttr attr $ traverseModuleItem item
+        traverseModuleItem (MIPackageItem (Decl (Variable d t x a e))) =
+            MIPackageItem $ Decl $ Variable d (traverseType t) x a e
+        traverseModuleItem other = other
+
+        traverseType :: Type -> Type
+        traverseType (Alias interfaceName rs) =
+            if isInterface interfaceName && not (elem interfaceName typeNames)
+                then InterfaceT interfaceName "" rs
+                else Alias interfaceName rs
+        traverseType orig@(InterfaceT interfaceName _ _) =
+            if null interfaceName || isInterface interfaceName
+                then orig
+                else error $ "declaration type " ++ show orig ++ " appears to "
+                    ++ "refer to an interface that isn't defined"
+        traverseType other = other
+
+        isInterface :: Identifier -> Bool
+        isInterface partName =
+            fmap pKind (Map.lookup partName parts) == Just Interface
+
+disambiguateDescription _ other = other
+
+-- get all of the typenames declared anywhere in the top-level module items
+getTypeNames :: [ModuleItem] -> [Identifier]
+getTypeNames (MIAttr _ item : rest) = getTypeNames $ item : rest
+getTypeNames (Generate genItems : rest) =
+    getTypeNames $ genModuleItems genItems ++ rest
+getTypeNames (MIPackageItem (Decl (ParamType _ name _)) : rest) =
+    name : getTypeNames rest
+getTypeNames (_ : rest) = getTypeNames rest
+getTypeNames [] = []
+
+-- get the top-level (i.e., un-scoped) module items within a generate block
+genModuleItems :: [GenItem] -> [ModuleItem]
+genModuleItems (GenModuleItem item : rest) =
+    item : genModuleItems rest
+genModuleItems (_ : rest) = genModuleItems rest
+genModuleItems [] = []
+
 topInterfaceError :: String -> String -> a
 topInterfaceError name issue = error $
     "Specified top module " ++ name ++ " " ++ issue ++ ". Please " ++
@@ -167,7 +224,10 @@
                 then Nothing
                 else Just (portName, modportBinding)
             where
-                modportBinding = (substitutions, replaceBit modportE)
+                modportBinding =
+                    ( substitutions
+                    , scopeExprWithScopes modports $ replaceBit modportE
+                    )
                 substitutions =
                     genSubstitutions modports base instanceE modportE
                 maybeInfo =
@@ -307,7 +367,6 @@
                     else if elem x (pPorts partInfo) then
                         tell [(x, info)] >> return decl
                     else
-                        -- TODO: This does not handle shadowed typenames.
                         scopedErrorM $
                             "Modport not in port list: " ++ show t ++ " " ++ x
                             ++ ". Is this an interface missing a port list?"
@@ -317,22 +376,9 @@
                 checkDecl decl = return decl
 
         extractModportInfo :: Type -> Maybe (Identifier, Identifier)
-        extractModportInfo (InterfaceT "" "" _) = Just ("", "")
         extractModportInfo (InterfaceT interfaceName modportName _) =
-            if isInterface interfaceName
-                then Just (interfaceName, modportName)
-                else Nothing
-        extractModportInfo (Alias interfaceName _) =
-            if isInterface interfaceName
-                then Just (interfaceName, "")
-                else Nothing
+            Just (interfaceName, modportName)
         extractModportInfo _ = Nothing
-
-        isInterface :: Identifier -> Bool
-        isInterface partName =
-            case Map.lookup partName parts of
-                Nothing -> False
-                Just info -> pKind info == Interface
 
 convertDescription _ _ other = other
 
diff --git a/src/Convert/Jump.hs b/src/Convert/Jump.hs
--- a/src/Convert/Jump.hs
+++ b/src/Convert/Jump.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE PatternSynonyms #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  -
@@ -12,6 +11,7 @@
 
 import Control.Monad.State.Strict
 import Control.Monad.Writer.Strict
+import Data.Monoid (Any(Any), getAny)
 
 import Convert.Traverse
 import Language.SystemVerilog.AST
diff --git a/src/Convert/Logic.hs b/src/Convert/Logic.hs
--- a/src/Convert/Logic.hs
+++ b/src/Convert/Logic.hs
@@ -25,6 +25,7 @@
 
 module Convert.Logic (convert) where
 
+import Control.Monad (when)
 import Control.Monad.Writer.Strict
 import qualified Data.Map.Strict as Map
 import qualified Data.Set as Set
@@ -168,8 +169,16 @@
             let t' = Implicit sg rs
             insertElem accesses t'
             return $ Net d TWire DefaultStrength t' x a e
-rewriteDeclM _ decl@(Variable _ t x _ _) =
-    insertElem x t >> return decl
+rewriteDeclM locations decl@(Variable d t x a e) = do
+    inProcedure <- withinProcedureM
+    case (d, t, inProcedure) of
+        -- Reinterpret `input reg` module ports as `input logic`. We still don't
+        -- treat `logic` and `reg` as the same keyword, as specifying `reg`
+        -- explicitly is typically expected to flow downstream.
+        (Input, IntegerVector TReg sg rs, False) ->
+            rewriteDeclM locations $ Variable Input t' x a e
+            where t' = IntegerVector TLogic sg rs
+        _ -> insertElem x t >> return decl
 rewriteDeclM _ (Net d n s (IntegerVector _ sg rs) x a e) =
     insertElem x t >> return (Net d n s t x a e)
     where t = Implicit sg rs
diff --git a/src/Convert/MultiplePacked.hs b/src/Convert/MultiplePacked.hs
--- a/src/Convert/MultiplePacked.hs
+++ b/src/Convert/MultiplePacked.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE TupleSections #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  -
@@ -51,21 +50,22 @@
 -- collects and converts declarations with multiple packed dimensions
 traverseDeclM :: Decl -> Scoper TypeInfo Decl
 traverseDeclM (Variable dir t ident a e) = do
-    t' <- traverseTypeM t a ident
+    recordTypeM t a ident
     traverseDeclExprsM traverseExprM $ Variable dir t' ident a e
+    where t' = flattenType t
 traverseDeclM net@Net{} =
     traverseNetAsVarM traverseDeclM net
 traverseDeclM (Param s t ident e) = do
-    t' <- traverseTypeM t [] ident
+    recordTypeM t [] ident
     traverseDeclExprsM traverseExprM $ Param s t' ident e
+    where t' = flattenType t
 traverseDeclM other = traverseDeclExprsM traverseExprM other
 
--- write down the given declaration and then flatten it
-traverseTypeM :: Type -> [Range] -> Identifier -> Scoper TypeInfo Type
-traverseTypeM t a ident = do
+-- write down the given declaration
+recordTypeM :: Type -> [Range] -> Identifier -> Scoper TypeInfo ()
+recordTypeM t a ident = do
     tScoped <- scopeType t
     insertElem ident (tScoped, a)
-    return $ flattenType t
 
 -- flatten the innermost dimension of the given type, and any types it contains
 flattenType :: Type -> Type
@@ -133,8 +133,21 @@
     traverseStmtExprsM traverseExprM
 
 traverseExprM :: Expr -> Scoper TypeInfo Expr
-traverseExprM = traverseNestedExprsM convertExprM
+traverseExprM =
+    embedScopes convertExpr >=>
+    traverseExprTypesM traverseTypeM >=>
+    traverseSinglyNestedExprsM traverseExprM
 
+traverseTypeM :: Type -> Scoper TypeInfo Type
+traverseTypeM typ =
+    traverseTypeExprsM traverseExprM >=>
+    traverseSinglyNestedTypesM traverseTypeM $
+    case typ of
+        Struct{} -> typ'
+        Union {} -> typ'
+        _ -> typ
+    where typ' = traverseSinglyNestedTypes flattenType typ
+
 traverseGenItemM :: GenItem -> Scoper TypeInfo GenItem
 traverseGenItemM = traverseGenItemExprsM traverseExprM
 
@@ -148,19 +161,9 @@
         traverseLHSSingleM :: LHS -> Scoper TypeInfo LHS
         traverseLHSSingleM lhs = do
             let expr = lhsToExpr lhs
-            expr' <- convertExprM expr
+            expr' <- embedScopes convertExpr expr
             let Just lhs' = exprToLHS expr'
             return lhs'
-
-convertExprM :: Expr -> Scoper TypeInfo Expr
-convertExprM =
-    traverseExprTypesM convertTypeM >=>
-    embedScopes convertExpr
-
-convertTypeM :: Type -> Scoper TypeInfo Type
-convertTypeM =
-    traverseNestedTypesM $ traverseTypeExprsM $
-    traverseNestedExprsM convertExprM
 
 convertExpr :: Scopes TypeInfo -> Expr -> Expr
 convertExpr scopes =
diff --git a/src/Convert/Package.hs b/src/Convert/Package.hs
--- a/src/Convert/Package.hs
+++ b/src/Convert/Package.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE TupleSections #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  -
@@ -24,8 +23,10 @@
     , prefixItems
     ) where
 
+import Control.Monad (when, (>=>))
 import Control.Monad.State.Strict
 import Control.Monad.Writer.Strict
+import Data.Functor ((<&>))
 import Data.List (insert, intercalate, isPrefixOf)
 import Data.Maybe (mapMaybe)
 import qualified Data.Map.Strict as Map
@@ -262,6 +263,9 @@
                             ++ " conflicts with prior import of "
                             ++ otherPkg ++ "::" ++ ident
 
+pattern PatternTag :: Char
+pattern PatternTag = '*'
+
 -- main logic responsible for translating packages, resolving imports and
 -- exports, and rewriting identifiers referring to package declarations
 processItems :: Identifier -> Identifier -> [ModuleItem]
@@ -367,7 +371,10 @@
                 Variable d t x a e -> declHelp x $ \x' -> Variable d t x' a e
                 Net  d n s t x a e -> declHelp x $ \x' -> Net  d n s t x' a e
                 Param    p t x   e -> declHelp x $ \x' -> Param    p t x'   e
-                ParamType  p x   t -> declHelp x $ \x' -> ParamType  p x'   t
+                ParamType  p x   t ->
+                    -- make this typename available for use in patterns
+                    prefixIdent (PatternTag : x) >>
+                    declHelp x (\x' -> ParamType p x' t)
                 CommentDecl c -> return $ CommentDecl c
             where declHelp x f = prefixIdent x >>= return . f
 
@@ -410,10 +417,25 @@
             x' <- resolvePSIdent' p x
             return $ Ident x'
         traverseExprM (Ident x) = resolveIdent x >>= return . Ident
+        traverseExprM (Pattern items) = do
+            keys' <- mapM keyMapper keys
+            vals' <- mapM traverseExprM vals
+            return $ Pattern $ zip keys' vals'
+            where (keys, vals) = unzip items
         traverseExprM other =
             traverseSinglyNestedExprsM traverseExprM other
             >>= traverseExprTypesM traverseTypeM
 
+        -- equivalent to the special case in traverseExprIdentsM
+        keyMapper (Left t) =
+            traverseTypeM t <&> Left
+        keyMapper (Right (Ident x)) = do
+            tagged' <- traverseExprM $ Ident $ PatternTag : x
+            let Ident x' = tagged'
+            return $ Right $ Ident $ filter (/= PatternTag) x'
+        keyMapper (Right e) =
+            traverseExprM e <&> Right
+
         traverseLHSM :: LHS -> Scope LHS
         traverseLHSM (LHSIdent x) = resolveIdent x >>= return . LHSIdent
         traverseLHSM other = traverseSinglyNestedLHSsM traverseLHSM other
@@ -784,14 +806,29 @@
 
 -- visits all identifiers in an expression
 traverseExprIdentsM :: Monad m => MapperM m Identifier -> MapperM m Expr
-traverseExprIdentsM identMapper = fullMapper
+traverseExprIdentsM identMapper = exprMapper
     where
-        fullMapper = exprMapper >=> traverseSinglyNestedExprsM fullMapper
-        exprMapper (Call (Ident x) args) =
-            identMapper x >>= \x' -> return $ Call (Ident x') args
         exprMapper (Ident x) = identMapper x >>= return . Ident
-        exprMapper other = return other
+        exprMapper (Pattern items) = do
+            keys' <- mapM keyMapper keys
+            vals' <- mapM exprMapper vals
+            return $ Pattern $ zip keys' vals'
+            where (keys, vals) = unzip items
+        exprMapper other =
+            traverseExprTypesM (traverseTypeIdentsM identMapper) other >>=
+            traverseSinglyNestedExprsM exprMapper
 
+        -- Don't reorder or inject a pattern key unless it refers to a known
+        -- typename. TODO: This prevents referring to expressions in packages in
+        -- pattern keys, though this probably isn't common.
+        keyMapper (Left t) =
+            traverseTypeIdentsM identMapper t <&> Left
+        keyMapper (Right (Ident x)) =
+            identMapper (PatternTag : x)
+                <&> filter (/= PatternTag) <&> Ident <&> Right
+        keyMapper (Right e) =
+            exprMapper e <&> Right
+
 -- visits all identifiers in a type
 traverseTypeIdentsM :: Monad m => MapperM m Identifier -> MapperM m Type
 traverseTypeIdentsM identMapper = fullMapper
@@ -814,14 +851,9 @@
 
 -- visits all identifiers in a statement
 traverseStmtIdentsM :: Monad m => MapperM m Identifier -> MapperM m Stmt
-traverseStmtIdentsM identMapper = fullMapper
-    where
-        fullMapper = stmtMapper
-            >=> traverseStmtExprsM (traverseExprIdentsM identMapper)
-            >=> traverseStmtLHSsM  (traverseLHSIdentsM  identMapper)
-        stmtMapper (Subroutine (Ident x) args) =
-            identMapper x >>= \x' -> return $ Subroutine (Ident x') args
-        stmtMapper other = return other
+traverseStmtIdentsM identMapper =
+    traverseStmtExprsM (traverseExprIdentsM identMapper) >=>
+    traverseStmtLHSsM  (traverseLHSIdentsM  identMapper)
 
 -- visits all identifiers in a declaration
 traverseDeclIdentsM :: Monad m => MapperM m Identifier -> MapperM m Decl
@@ -838,7 +870,7 @@
 piNames (Decl (Variable _ _ ident _ _)) = [ident]
 piNames (Decl (Net  _ _ _ _ ident _ _)) = [ident]
 piNames (Decl (Param    _ _ ident   _)) = [ident]
-piNames (Decl (ParamType  _ ident   _)) = [ident]
+piNames (Decl (ParamType  _ ident   _)) = [ident, PatternTag : ident]
 piNames (Decl (CommentDecl          _)) = []
 piNames item@DPIImport{} = [show item]
 piNames item@DPIExport{} = [show item]
diff --git a/src/Convert/ParamNoDefault.hs b/src/Convert/ParamNoDefault.hs
--- a/src/Convert/ParamNoDefault.hs
+++ b/src/Convert/ParamNoDefault.hs
@@ -14,6 +14,7 @@
 
 module Convert.ParamNoDefault (convert) where
 
+import Control.Monad (when)
 import Control.Monad.Writer.Strict
 import Data.List (intercalate)
 import qualified Data.Map.Strict as Map
diff --git a/src/Convert/ParamType.hs b/src/Convert/ParamType.hs
--- a/src/Convert/ParamType.hs
+++ b/src/Convert/ParamType.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE PatternSynonyms #-}
-{-# LANGUAGE TupleSections #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  -
@@ -12,6 +10,7 @@
 import Data.Either (isRight, lefts)
 import qualified Data.Map.Strict as Map
 import qualified Data.Set as Set
+import Data.Monoid (Any(Any), getAny)
 
 import Convert.Traverse
 import Language.SystemVerilog.AST
diff --git a/src/Convert/PortDecl.hs b/src/Convert/PortDecl.hs
--- a/src/Convert/PortDecl.hs
+++ b/src/Convert/PortDecl.hs
@@ -18,6 +18,7 @@
 import Data.List (intercalate, (\\))
 import Data.Maybe (mapMaybe)
 
+import Convert.ExprUtils (simplifyDimensions)
 import Convert.Traverse
 import Language.SystemVerilog.AST
 
@@ -34,9 +35,9 @@
 
 convertPackageItem :: PackageItem -> PackageItem
 convertPackageItem (Function l t x decls stmts) =
-   Function l t x (convertTFDecls decls) stmts
+    Function l t x (convertTFDecls decls) stmts
 convertPackageItem (Task l x decls stmts) =
-   Task l x (convertTFDecls decls) stmts
+    Task l x (convertTFDecls decls) stmts
 convertPackageItem other = other
 
 convertPorts :: Identifier -> [Identifier] -> [ModuleItem] -> [ModuleItem]
@@ -102,9 +103,9 @@
 combineDecls portDecl dataDecl
     | eP /= Nil =
         mismatch "invalid initialization at port declaration"
-    | aP /= aD =
+    | simplifyDimensions aP /= simplifyDimensions aD =
         mismatch "different unpacked dimensions"
-    | rsP /= rsD =
+    | simplifyDimensions rsP /= simplifyDimensions rsD =
         mismatch "different packed dimensions"
     | otherwise =
         base (tf rsD) ident aD Nil
diff --git a/src/Convert/ResolveBindings.hs b/src/Convert/ResolveBindings.hs
--- a/src/Convert/ResolveBindings.hs
+++ b/src/Convert/ResolveBindings.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE TupleSections #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  -
diff --git a/src/Convert/Scoper.hs b/src/Convert/Scoper.hs
--- a/src/Convert/Scoper.hs
+++ b/src/Convert/Scoper.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE TupleSections #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE FlexibleInstances #-}
 {- sv2v
@@ -39,6 +38,8 @@
     , replaceInExpr
     , scopeExpr
     , scopeType
+    , scopeExprWithScopes
+    , scopeTypeWithScopes
     , insertElem
     , removeElem
     , injectItem
@@ -69,6 +70,7 @@
     , LookupResult
     ) where
 
+import Control.Monad (join, when)
 import Control.Monad.State.Strict
 import Data.List (findIndices, intercalate, isPrefixOf, partition)
 import Data.Maybe (isNothing)
@@ -209,6 +211,12 @@
 
 {-# INLINABLE scopeExpr #-}
 {-# INLINABLE scopeType #-}
+
+scopeExprWithScopes :: Scopes a -> Expr -> Expr
+scopeExprWithScopes scopes = flip evalState scopes . scopeExpr
+
+scopeTypeWithScopes :: Scopes a -> Type -> Type
+scopeTypeWithScopes scopes = flip evalState scopes . scopeType
 
 class ScopePath k where
     toTiers :: Scopes a -> k -> [Tier]
diff --git a/src/Convert/Simplify.hs b/src/Convert/Simplify.hs
--- a/src/Convert/Simplify.hs
+++ b/src/Convert/Simplify.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE PatternSynonyms #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  -
@@ -17,8 +16,6 @@
 
 module Convert.Simplify (convert) where
 
-import Control.Monad (when)
-
 import Convert.ExprUtils
 import Convert.Scoper
 import Convert.Traverse
@@ -53,13 +50,14 @@
 insertExpr :: Identifier -> Expr -> Scoper Expr ()
 insertExpr ident expr = do
     expr' <- substituteExprM expr
-    case expr' of
+    insertElem ident $ case expr' of
         Cast (Left (SimpleVector sg l r)) (Number n) ->
-            insertElem ident $ Number $ numberCast signed size n
+            Number $ numberCast signed size n
             where
                 signed = sg == Signed
                 size = fromIntegral $ abs $ l - r + 1
-        _ -> when (isSimpleExpr expr') $ insertElem ident expr'
+        _ | isSimpleExpr expr' -> expr'
+        _ -> Nil
 
 isSimpleExpr :: Expr -> Bool
 isSimpleExpr Number{}  = True
@@ -84,7 +82,7 @@
 traverseGenItemM = traverseGenItemExprsM traverseExprM
 
 traverseStmtM :: Stmt -> Scoper Expr Stmt
-traverseStmtM stmt = traverseStmtExprsM traverseExprM stmt
+traverseStmtM = traverseStmtExprsM traverseExprM
 
 traverseExprM :: Expr -> Scoper Expr Expr
 traverseExprM = embedScopes convertExpr
@@ -114,27 +112,27 @@
         e' = convertExpr info $ substitute info e
         val = Call (Ident "$clog2") (Args [e'] [])
         val' = simplifyStep val
-convertExpr info (Mux cc aa bb) =
+convertExpr info (MuxA a cc aa bb) =
     if before == after
-        then simplifyStep $ Mux cc' aa' bb'
-        else simplifyStep $ Mux after aa' bb'
+        then simplifyStep $ MuxA a cc' aa' bb'
+        else simplifyStep $ MuxA a after aa' bb'
     where
         before = substitute info cc'
         after = convertExpr info before
         aa' = convertExpr info aa
         bb' = convertExpr info bb
         cc' = convertExpr info cc
-convertExpr info (BinOp op e1 e2) =
-    case simplifyStep $ BinOp op e1'Sub e2'Sub of
+convertExpr info (BinOpA op a e1 e2) =
+    case simplifyStep $ BinOpA op a e1'Sub e2'Sub of
         Number n -> Number n
-        _ -> simplifyStep $ BinOp op e1' e2'
+        _ -> simplifyStep $ BinOpA op a e1' e2'
     where
         e1' = convertExpr info e1
         e2' = convertExpr info e2
         e1'Sub = substituteIdent info e1'
         e2'Sub = substituteIdent info e2'
-convertExpr info (UniOp op expr) =
-    simplifyStep $ UniOp op $ convertExpr info expr
+convertExpr info (UniOpA op a expr) =
+    simplifyStep $ UniOpA op a $ convertExpr info expr
 convertExpr info (Repeat expr exprs) =
     simplifyStep $ Repeat
         (convertExpr info expr)
diff --git a/src/Convert/Stream.hs b/src/Convert/Stream.hs
--- a/src/Convert/Stream.hs
+++ b/src/Convert/Stream.hs
@@ -47,7 +47,7 @@
 traverseModuleItemM (Assign opt lhs (Stream StreamL chunk exprs)) =
     injectItem (MIPackageItem func) >> return (Assign opt lhs expr')
     where
-        fnName = streamerFuncName $ shortHash lhs
+        fnName = streamerFuncName $ shortHash (lhs, chunk, exprs)
         t = TypeOf $ lhsToExpr lhs
         arg = Concat exprs
         func = streamerFunc fnName chunk (TypeOf arg) t
@@ -56,12 +56,25 @@
     traverseModuleItemM $
         Assign opt (LHSConcat lhss)
         (Stream StreamL chunk [expr])
+traverseModuleItemM (Assign opt lhs (Mux cond expr@Stream{} other)) = do
+    (lhs', expr') <- indirectAssign lhs expr
+    return $ Assign opt lhs' $ Mux cond expr' other
+traverseModuleItemM (Assign opt lhs (Mux cond other expr@Stream{})) = do
+    (lhs', expr') <- indirectAssign lhs expr
+    return $ Assign opt lhs' $ Mux cond other expr'
 traverseModuleItemM (Assign opt lhs expr) =
     traverseExprM expr' >>= return . Assign opt lhs'
     where Asgn AsgnOpEq Nothing lhs' expr' =
             traverseAsgn (lhs, expr) (Asgn AsgnOpEq Nothing)
 traverseModuleItemM item = return item
 
+indirectAssign :: LHS -> Expr -> Scoper () (LHS, Expr)
+indirectAssign lhs expr = do
+    let item = Assign AssignOptionNone lhs expr
+    item' <- traverseModuleItemM item
+    let Assign AssignOptionNone lhs' expr' = item'
+    return (lhs', expr')
+
 traverseStmtM :: Stmt -> Scoper () Stmt
 traverseStmtM (Subroutine fn (Args args [])) = do
     args' <- traverseCall fn args
@@ -190,6 +203,12 @@
         lhs = LHSConcat lhss
         lhsSize = sizeof $ lhsToExpr lhs
         exprSize = sizeof expr
+traverseAsgn (lhs, Mux cond e1@Stream{} e2) constructor =
+    traverseAsgn (lhs, e1) constructor'
+    where constructor' lhs' e1' = constructor lhs' $ Mux cond e1' e2
+traverseAsgn (lhs, Mux cond e1 e2@Stream{}) constructor =
+    traverseAsgn (lhs, e2) constructor'
+    where constructor' lhs' e2' = constructor lhs' $ Mux cond e1 e2'
 traverseAsgn (lhs, expr) constructor =
     constructor lhs expr
 
diff --git a/src/Convert/StringParam.hs b/src/Convert/StringParam.hs
--- a/src/Convert/StringParam.hs
+++ b/src/Convert/StringParam.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE PatternSynonyms #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  -
@@ -12,6 +11,7 @@
 
 module Convert.StringParam (convert) where
 
+import Control.Monad (when)
 import Control.Monad.Writer.Strict
 import Data.Maybe (mapMaybe)
 import qualified Data.Set as Set
@@ -53,7 +53,7 @@
 
 -- utility pattern for candidate string parameter items
 pattern StringParam :: Identifier -> String -> ModuleItem
-pattern StringParam x s =
+pattern StringParam x s <-
     MIPackageItem (Decl (Param Parameter UnknownType x (String s)))
 
 -- write down which parameters may be variable-length strings
diff --git a/src/Convert/Struct.hs b/src/Convert/Struct.hs
--- a/src/Convert/Struct.hs
+++ b/src/Convert/Struct.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE PatternSynonyms #-}
-{-# LANGUAGE TupleSections #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  -
@@ -198,8 +196,8 @@
 -- try expression conversion by looking at the *outermost* type first
 convertExpr :: Scopes a -> Type -> Expr -> Expr
 convertExpr _ _ Nil = Nil
-convertExpr scopes t (Mux c e1 e2) =
-    Mux c e1' e2'
+convertExpr scopes t (MuxA a c e1 e2) =
+    MuxA a c e1' e2'
     where
         e1' = convertExpr scopes t e1
         e2' = convertExpr scopes t e2
@@ -319,8 +317,8 @@
         t' = dropInnerTypeRange t
         isUnsizedNumber :: Expr -> Bool
         isUnsizedNumber (Number n) = not $ numberIsSized n
-        isUnsizedNumber (UniOp _ e) = isUnsizedNumber e
-        isUnsizedNumber (BinOp _ e1 e2) =
+        isUnsizedNumber (UniOpA _ _ e) = isUnsizedNumber e
+        isUnsizedNumber (BinOpA _ _ e1 e2) =
             isUnsizedNumber e1 || isUnsizedNumber e2
         isUnsizedNumber _ = False
 
@@ -497,8 +495,11 @@
         (retType, _) = fallbackType scopes e
         args' = convertCall scopes e args
 convertSubExpr scopes (Cast (Left t) e) =
-    (t, Cast (Left t) e')
-    where (_, e') = convertSubExpr scopes e
+    (t', Cast (Left t') e')
+    where
+        e' = convertExpr scopes t $ snd $ convertSubExpr scopes e
+        t' = convertType t
+
 convertSubExpr scopes (Pattern items) =
     if all (== Right Nil) $ map fst items'
         then (UnknownType, Concat $ map snd items')
@@ -507,8 +508,8 @@
         items' = map mapItem items
         mapItem (x, e) = (x, e')
             where (_, e') = convertSubExpr scopes e
-convertSubExpr scopes (Mux a b c) =
-    (t, Mux a' b' c')
+convertSubExpr scopes (MuxA r a b c) =
+    (t, MuxA r a' b' c')
     where
         (_, a') = convertSubExpr scopes a
         (t, b') = convertSubExpr scopes b
diff --git a/src/Convert/StructConst.hs b/src/Convert/StructConst.hs
--- a/src/Convert/StructConst.hs
+++ b/src/Convert/StructConst.hs
@@ -9,6 +9,7 @@
 
 module Convert.StructConst (convert) where
 
+import Control.Monad (join, mplus, when)
 import Control.Monad.State.Strict
 import Data.Maybe (fromMaybe)
 import Data.Tuple (swap)
diff --git a/src/Convert/Traverse.hs b/src/Convert/Traverse.hs
--- a/src/Convert/Traverse.hs
+++ b/src/Convert/Traverse.hs
@@ -112,6 +112,7 @@
 
 import Data.Bitraversable (bimapM)
 import Data.Functor.Identity (Identity, runIdentity)
+import Control.Monad ((>=>))
 import Control.Monad.Writer.Strict
 import Language.SystemVerilog.AST
 
@@ -256,6 +257,7 @@
         cs (Subroutine expr exprs) = return $ Subroutine expr exprs
         cs (Trigger blocks x) = return $ Trigger blocks x
         cs stmt@Force{} = return stmt
+        cs (Wait e stmt) = fullMapper stmt >>= return . Wait e
         cs (Assertion a) =
             traverseAssertionStmtsM fullMapper a >>= return . Assertion
         cs (Continue) = return Continue
@@ -448,17 +450,17 @@
             pes <- mapM exprMapper $ map snd p
             let p' = zip (map fst p) pes
             return $ Call e' (Args l' p')
-        em (UniOp      o e) =
-            exprMapper e >>= return . UniOp o
-        em (BinOp      o e1 e2) = do
+        em (UniOpA   o a e) =
+            exprMapper e >>= return . UniOpA o a
+        em (BinOpA   o a e1 e2) = do
             e1' <- exprMapper e1
             e2' <- exprMapper e2
-            return $ BinOp o e1' e2'
-        em (Mux        e1 e2 e3) = do
+            return $ BinOpA o a e1' e2'
+        em (MuxA    a e1 e2 e3) = do
             e1' <- exprMapper e1
             e2' <- exprMapper e2
             e3' <- exprMapper e3
-            return $ Mux e1' e2' e3'
+            return $ MuxA a e1' e2' e3'
         em (Cast tore e) = do
             tore' <- typeOrExprMapper tore
             e' <- exprMapper e
@@ -710,6 +712,8 @@
         l' <- lhsMapper l
         e' <- exprMapper e
         return $ Force kw l' e'
+    flatStmtMapper (Wait e stmt) =
+        exprMapper e >>= \e' -> return $ Wait e' stmt
     flatStmtMapper (Assertion a) =
         traverseAssertionExprsM exprMapper a >>= return . Assertion
     flatStmtMapper (Continue) = return Continue
diff --git a/src/Convert/TypeOf.hs b/src/Convert/TypeOf.hs
--- a/src/Convert/TypeOf.hs
+++ b/src/Convert/TypeOf.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE PatternSynonyms #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  -
@@ -255,9 +254,9 @@
                 Just typ -> typ
                 Nothing -> TypeOf orig
 typeof (Cast (Left t) _) = traverseTypeM t
-typeof (UniOp op expr) = typeofUniOp op expr
-typeof (BinOp op a b) = typeofBinOp op a b
-typeof (Mux   _       a b) = largerSizeType a b
+typeof (UniOpA op _  expr) = typeofUniOp op expr
+typeof (BinOpA op _   a b) = typeofBinOp op a b
+typeof (MuxA      _ _ a b) = largerSizeType a b
 typeof (Concat      exprs) = return $ typeOfSize Unsigned $ concatSize exprs
 typeof (Stream _ _  exprs) = return $ typeOfSize Unsigned $ concatSize exprs
 typeof (Repeat reps exprs) = return $ typeOfSize Unsigned size
@@ -378,7 +377,7 @@
 -- returns the size of an expression, with the short-circuiting
 sizeof :: Expr -> Expr
 sizeof (Number n) = RawNum $ numberBitLength n
-sizeof (Mux _ a b) = largerSizeOf a b
+sizeof (MuxA _ _ a b) = largerSizeOf a b
 sizeof expr = DimsFn FnBits $ Left $ TypeOf expr
 
 -- returns the maximum size of the two given expressions
@@ -446,8 +445,8 @@
 makeExplicit :: Expr -> Expr
 makeExplicit (Number n) =
     Number $ numberCast (numberIsSigned n) (fromIntegral $ numberBitLength n) n
-makeExplicit (BinOp op e1 e2) =
-    BinOp op (makeExplicit e1) (makeExplicit e2)
-makeExplicit (UniOp op e) =
-    UniOp op $ makeExplicit e
+makeExplicit (BinOpA op a e1 e2) =
+    BinOpA op a (makeExplicit e1) (makeExplicit e2)
+makeExplicit (UniOpA op a e) =
+    UniOpA op a $ makeExplicit e
 makeExplicit other = other
diff --git a/src/Convert/Typedef.hs b/src/Convert/Typedef.hs
--- a/src/Convert/Typedef.hs
+++ b/src/Convert/Typedef.hs
@@ -1,7 +1,7 @@
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  -
- - Conversion for `typedef`
+ - Conversion for `typedef` and `localparam type`
  -
  - Aliased types can appear in all data declarations, including modules, blocks,
  - and function parameters. They are also found in type cast expressions.
@@ -20,22 +20,44 @@
     where scoper = scopeModuleItem
             traverseDeclM traverseModuleItemM traverseGenItemM traverseStmtM
 
-traverseTypeOrExprM :: TypeOrExpr -> Scoper Type TypeOrExpr
-traverseTypeOrExprM (Left (TypeOf (Ident x))) = do
-    details <- lookupElemM x
-    return $ case details of
-        Nothing -> Left $ TypeOf $ Ident x
-        Just (_, _, UnknownType) -> Left $ TypeOf $ Ident x
-        Just (_, _, typ) -> Left typ
-traverseTypeOrExprM (Right (Ident x)) = do
-    details <- lookupElemM x
-    return $ case details of
-        Nothing -> Right $ Ident x
-        Just (_, _, UnknownType) -> Right $ Ident x
-        Just (_, _, typ) -> Left typ
-traverseTypeOrExprM other = return other
+type SC = Scoper IdentKind
 
-traverseExprM :: Expr -> Scoper Type Expr
+data IdentKind
+    = Type Type -- resolved typename
+    | Pending -- unresolved type parameter
+    | NonType String -- anything else
+
+traverseTypeOrExprM :: TypeOrExpr -> SC TypeOrExpr
+traverseTypeOrExprM tore
+    | Left (TypeOf expr) <- tore = possibleTypeName tore expr
+    | Right expr <- tore = possibleTypeName tore expr
+    | otherwise = return tore
+
+possibleTypeName :: TypeOrExpr -> Expr -> SC TypeOrExpr
+possibleTypeName orig expr
+    | Just (x, rs1) <- maybeTypeName = do
+        details <- lookupElemM x
+        return $ case details of
+            Just (_, _, Type typ) ->
+                Left $ tf $ rs1 ++ rs2
+                where (tf, rs2) = typeRanges typ
+            Just (_, _, Pending) ->
+                Left $ Alias x rs1
+            _ -> orig
+    | otherwise = return orig
+    where maybeTypeName = exprToTypeName [] expr
+
+-- aliases in type-or-expr contexts are parsed as expressions
+exprToTypeName :: [Range] -> Expr -> Maybe (Identifier, [Range])
+exprToTypeName rs (Ident x) = Just (x, rs)
+exprToTypeName rs (Bit expr idx) =
+    exprToTypeName (r : rs) expr
+    where r = (RawNum 0, BinOp Sub idx (RawNum 1))
+exprToTypeName rs (Range expr NonIndexed r) = do
+    exprToTypeName (r : rs) expr
+exprToTypeName _ _ = Nothing
+
+traverseExprM :: Expr -> SC Expr
 traverseExprM (Cast v e) = do
     v' <- traverseTypeOrExprM v
     traverseExprM' $ Cast v' e
@@ -51,68 +73,82 @@
     traverseExprM' $ Pattern $ zip names exprs
 traverseExprM other = traverseExprM' other
 
-traverseExprM' :: Expr -> Scoper Type Expr
+traverseExprM' :: Expr -> SC Expr
 traverseExprM' =
     traverseSinglyNestedExprsM traverseExprM
     >=> traverseExprTypesM traverseTypeM
 
-traverseModuleItemM :: ModuleItem -> Scoper Type ModuleItem
+traverseModuleItemM :: ModuleItem -> SC ModuleItem
 traverseModuleItemM (Instance m params x rs p) = do
     let mapParam (i, v) = traverseTypeOrExprM v >>= \v' -> return (i, v')
     params' <- mapM mapParam params
     traverseModuleItemM' $ Instance m params' x rs p
 traverseModuleItemM item = traverseModuleItemM' item
 
-traverseModuleItemM' :: ModuleItem -> Scoper Type ModuleItem
+traverseModuleItemM' :: ModuleItem -> SC ModuleItem
 traverseModuleItemM' =
     traverseNodesM traverseExprM return traverseTypeM traverseLHSM return
     where traverseLHSM = traverseLHSExprsM traverseExprM
 
-traverseGenItemM :: GenItem -> Scoper Type GenItem
+traverseGenItemM :: GenItem -> SC GenItem
 traverseGenItemM = traverseGenItemExprsM traverseExprM
 
-traverseDeclM :: Decl -> Scoper Type Decl
+traverseDeclM :: Decl -> SC Decl
 traverseDeclM decl = do
     decl' <- traverseDeclNodesM traverseTypeM traverseExprM decl
     case decl' of
-        Variable{} -> return decl'
-        Net{} -> return decl'
+        Variable _ _ x _ _ -> insertElem x (NonType "var") >> return decl'
+        Net  _ _ _ _ x _ _ -> insertElem x (NonType "net") >> return decl'
         Param s (UnpackedType t rs1) x e -> do
-            insertElem x UnknownType
+            insertElem x (NonType $ show s)
             let (tf, rs2) = typeRanges t
             let t' = tf $ rs1 ++ rs2
             return $ Param s t' x e
-        Param _ _ x _ ->
-            insertElem x UnknownType >> return decl'
+        Param s _ x _ ->
+            insertElem x (NonType $ show s) >> return decl'
         ParamType Localparam x t -> do
-            traverseTypeM t >>= scopeType >>= insertElem x
+            traverseTypeM t >>= scopeType >>= insertElem x . Type
             return $ case t of
                 Enum{} -> ParamType Localparam tmpX t
                 _ -> CommentDecl $ "removed localparam type " ++ x
             where tmpX = "_sv2v_keep_enum_for_params"
-        ParamType{} -> return decl'
+        ParamType Parameter x _ ->
+            insertElem x Pending >> return decl'
         CommentDecl{} -> return decl'
 
-traverseStmtM :: Stmt -> Scoper Type Stmt
+traverseStmtM :: Stmt -> SC Stmt
 traverseStmtM = traverseStmtExprsM traverseExprM
 
-traverseTypeM :: Type -> Scoper Type Type
+traverseTypeM :: Type -> SC Type
 traverseTypeM (Alias st rs1) = do
     details <- lookupElemM st
     rs1' <- mapM traverseRangeM rs1
-    return $ case details of
-        Nothing -> Alias st rs1'
-        Just (_, _, UnknownType) -> Alias st rs1'
-        Just (_, _, typ) -> tf $ rs1' ++ rs2
+    case details of
+        Just (_, _, Type typ) ->
+            return $ tf $ rs1' ++ rs2
             where (tf, rs2) = typeRanges typ
+        Just (_, _, Pending) ->
+            return $ Alias st rs1'
+        Just (_, _, NonType kind) ->
+            scopedErrorM $ "expected typename, but found " ++ kind
+                ++ " identifier " ++ show st
+        Nothing ->
+            scopedErrorM $ "couldn't resolve typename " ++ show st
 traverseTypeM (TypedefRef expr) = do
     details <- lookupElemM expr
-    return $ case details of
-        Nothing -> TypedefRef expr
-        Just (_, _, typ) -> typ
+    case details of
+        Just (_, _, Type typ) -> return typ
+        Just (_, _, Pending) ->
+            error "TypdefRef invariant violated! Please file an issue."
+        Just (_, _, NonType kind) ->
+            scopedErrorM $ "expected interface-based typename, but found "
+                ++ kind ++ " " ++ show expr
+        -- This can occur when the interface conversion is delayed due to
+        -- multi-dimension instances.
+        Nothing -> return $ TypedefRef expr
 traverseTypeM other =
     traverseSinglyNestedTypesM traverseTypeM other
     >>= traverseTypeExprsM traverseExprM
 
-traverseRangeM :: Range -> Scoper Type Range
+traverseRangeM :: Range -> SC Range
 traverseRangeM = mapBothM traverseExprM
diff --git a/src/Convert/UnbasedUnsized.hs b/src/Convert/UnbasedUnsized.hs
--- a/src/Convert/UnbasedUnsized.hs
+++ b/src/Convert/UnbasedUnsized.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE PatternSynonyms #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  -
@@ -21,6 +20,7 @@
 import Data.Either (isLeft)
 import Data.Maybe (isNothing, mapMaybe)
 import qualified Data.Map.Strict as Map
+import Data.Monoid (Any(Any), getAny)
 
 import Convert.Package (inject, prefixItems)
 import Convert.Traverse
@@ -218,48 +218,48 @@
     where pnArgs' = map (convertExpr SelfDetermined) pnArgs
 convertExpr _ (Repeat count exprs) =
     Repeat count $ map (convertExpr SelfDetermined) exprs
-convertExpr SelfDetermined (Mux cond e1@UU{} e2@UU{}) =
-    Mux
+convertExpr SelfDetermined (MuxA a cond e1@UU{} e2@UU{}) =
+    MuxA a
     (convertExpr SelfDetermined cond)
     (convertExpr SelfDetermined e1)
     (convertExpr SelfDetermined e2)
-convertExpr SelfDetermined (Mux cond e1 e2) =
-    Mux
+convertExpr SelfDetermined (MuxA a cond e1 e2) =
+    MuxA a
     (convertExpr SelfDetermined cond)
     (convertExpr (ContextDetermined e2) e1)
     (convertExpr (ContextDetermined e1) e2)
-convertExpr (ContextDetermined expr) (Mux cond e1 e2) =
-    Mux
+convertExpr (ContextDetermined expr) (MuxA a cond e1 e2) =
+    MuxA a
     (convertExpr SelfDetermined cond)
     (convertExpr context e1)
     (convertExpr context e2)
     where context = ContextDetermined expr
-convertExpr SelfDetermined (BinOp op e1 e2) =
+convertExpr SelfDetermined (BinOpA op a e1 e2) =
     if isPeerSizedBinOp op || isParentSizedBinOp op
-        then BinOp op
+        then BinOpA op a
             (convertExpr (ContextDetermined e2) e1)
             (convertExpr (ContextDetermined e1) e2)
-        else BinOp op
+        else BinOpA op a
             (convertExpr SelfDetermined e1)
             (convertExpr SelfDetermined e2)
-convertExpr (ContextDetermined expr) (BinOp op e1 e2) =
+convertExpr (ContextDetermined expr) (BinOpA op a e1 e2) =
     if isPeerSizedBinOp op then
-        BinOp op
+        BinOpA op a
             (convertExpr (ContextDetermined e2) e1)
             (convertExpr (ContextDetermined e1) e2)
     else if isParentSizedBinOp op then
-        BinOp op
+        BinOpA op a
             (convertExpr context e1)
             (convertExpr context e2)
     else
-        BinOp op
+        BinOpA op a
             (convertExpr SelfDetermined e1)
             (convertExpr SelfDetermined e2)
     where context = ContextDetermined expr
-convertExpr context (UniOp op expr) =
+convertExpr context (UniOpA op a expr) =
     if isSizedUniOp op
-        then UniOp op (convertExpr context expr)
-        else UniOp op (convertExpr SelfDetermined expr)
+        then UniOpA op a (convertExpr context expr)
+        else UniOpA op a (convertExpr SelfDetermined expr)
 convertExpr SelfDetermined (UU bit) =
     literalFor bit
 convertExpr (ContextDetermined expr) (UU bit) =
diff --git a/src/Convert/Unique.hs b/src/Convert/Unique.hs
--- a/src/Convert/Unique.hs
+++ b/src/Convert/Unique.hs
@@ -3,9 +3,9 @@
  -
  - Conversion for `unique`, `unique0`, and `priority` (verification checks)
  -
- - This conversion simply drops these keywords, as they are only used for
- - optimization and verification. There may be ways to communicate these
- - attributes to certain downstream toolchains.
+ - For `case`, these verification checks are replaced with equivalent
+ - `full_case` and `parallel_case` attributes. For `if`, they are simply
+ - dropped.
  -}
 
 module Convert.Unique (convert) where
@@ -21,6 +21,19 @@
 convertStmt :: Stmt -> Stmt
 convertStmt (If _ cc s1 s2) =
     If NoCheck cc s1 s2
-convertStmt (Case _ kw expr cases) =
-    Case NoCheck kw expr cases
+convertStmt (Case Priority kw expr cases) =
+    StmtAttr caseAttr caseStmt
+    where
+        caseAttr = Attr [("full_case", Nil)]
+        caseStmt = Case NoCheck kw expr cases
+convertStmt (Case Unique kw expr cases) =
+    StmtAttr caseAttr caseStmt
+    where
+        caseAttr = Attr [("full_case", Nil), ("parallel_case", Nil)]
+        caseStmt = Case NoCheck kw expr cases
+convertStmt (Case Unique0 kw expr cases) =
+    StmtAttr caseAttr caseStmt
+    where
+        caseAttr = Attr [("parallel_case", Nil)]
+        caseStmt = Case NoCheck kw expr cases
 convertStmt other = other
diff --git a/src/Convert/UnnamedGenBlock.hs b/src/Convert/UnnamedGenBlock.hs
--- a/src/Convert/UnnamedGenBlock.hs
+++ b/src/Convert/UnnamedGenBlock.hs
@@ -10,6 +10,7 @@
 
 module Convert.UnnamedGenBlock (convert) where
 
+import Control.Monad (when)
 import Control.Monad.State.Strict
 import Data.List (isPrefixOf)
 
diff --git a/src/Convert/UnpackedArray.hs b/src/Convert/UnpackedArray.hs
--- a/src/Convert/UnpackedArray.hs
+++ b/src/Convert/UnpackedArray.hs
@@ -11,6 +11,7 @@
 
 module Convert.UnpackedArray (convert) where
 
+import Control.Monad (when, (>=>))
 import Control.Monad.State.Strict
 import qualified Data.Map.Strict as Map
 
@@ -120,7 +121,7 @@
     unbit (Range e _ _) = (e', n)
         where (e', n) = unbit e
     unbit e = (e, 0)
-    split (Mux _ a b) = Just (a, b)
+    split (MuxA _ _ a b) = Just (a, b)
     split _ = Nothing
 
 instance Key LHS where
diff --git a/src/Convert/Wildcard.hs b/src/Convert/Wildcard.hs
--- a/src/Convert/Wildcard.hs
+++ b/src/Convert/Wildcard.hs
@@ -76,10 +76,10 @@
         BinOp BitAnd couldMatch $
         BinOp BitOr  noExtraXZs $
         Number (Based 1 False Binary 0 1)
-    else if numberToInteger pattern /= Nothing then
+    else if numberToInteger pat /= Nothing then
         BinOp Eq l r
     else
-        BinOp Eq (BinOp BitOr l mask) pattern'
+        BinOp Eq (BinOp BitOr l mask) pat'
     where
         lxl = BinOp BitXor l l
         rxr = BinOp BitXor r r
@@ -92,10 +92,10 @@
         lxlxrxr = BinOp BitXor lxl rxr
         -- For wildcard patterns we can find, use masking
         maybePattern = lookupPattern scopes r
-        Just pattern = maybePattern
-        Based size signed base vals knds = pattern
+        Just pat = maybePattern
+        Based size signed base vals knds = pat
         mask = Number $ Based size signed base knds 0
-        pattern' = Number $ Based size signed base (vals .|. knds) 0
+        pat' = Number $ Based size signed base (vals .|. knds) 0
 convertExpr scopes (BinOp WNe l r) =
     UniOp LogNot $
     convertExpr scopes $
diff --git a/src/Job.hs b/src/Job.hs
--- a/src/Job.hs
+++ b/src/Job.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE TemplateHaskell #-}
 {- sv2v
@@ -12,12 +13,13 @@
 import Data.Char (toLower)
 import Data.List (isPrefixOf, isSuffixOf)
 import Data.Version (showVersion)
-import GitHash (giDescribe, tGitInfoCwdTry)
+#if MIN_VERSION_githash(0,1,5)
+import GitHash (giTag, tGitInfoCwdTry)
+#endif
 import qualified Paths_sv2v (version)
 import System.IO (stderr, hPutStr)
 import System.Console.CmdArgs
 import System.Directory (doesDirectoryExist)
-import System.Environment (getArgs, withArgs)
 import System.Exit (exitFailure)
 
 data Exclude
@@ -54,7 +56,11 @@
     } deriving (Typeable, Data)
 
 version :: String
-version = either (const backup) giDescribe $$tGitInfoCwdTry
+#if MIN_VERSION_githash(0,1,5)
+version = either (const backup) giTag $$tGitInfoCwdTry
+#else
+version = backup
+#endif
     where backup = showVersion Paths_sv2v.version
 
 defaultJob :: Job
@@ -100,7 +106,7 @@
     &= summary ("sv2v " ++ version)
     &= details [ "sv2v converts SystemVerilog to Verilog."
                , "More info: https://github.com/zachjs/sv2v"
-               , "(C) 2019-2023 Zachary Snow, 2011-2015 Tom Hawkins" ]
+               , "(C) 2019-2024 Zachary Snow, 2011-2015 Tom Hawkins" ]
     &= helpArg [explicit, name "help", help "Display this help message"]
     &= versionArg [explicit, name "version"]
     &= verbosityArgs [ignore] [ignore]
@@ -125,32 +131,9 @@
 matches :: String -> String -> Bool
 matches = isPrefixOf . map toLower
 
-type DeprecationPhase = [String] -> IO [String]
-
-flagRename :: String -> String -> DeprecationPhase
-flagRename before after strs = do
-    let strs' = map rename strs
-    if strs == strs'
-        then return strs
-        else do
-            hPutStr stderr $ "Deprecation warning: " ++ before ++
-                " has been renamed to " ++ after ++ "\n"
-            return strs'
-    where
-        rename :: String -> String
-        rename arg =
-            if before == take (length before) arg
-                then after ++ drop (length before) arg
-                else arg
-
 readJob :: IO Job
-readJob = do
-    strs <- getArgs
-    strs' <- return strs
-        >>= flagRename "-i" "-I"
-        >>= flagRename "-d" "-D"
-        >>= flagRename "-e" "-E"
-    withArgs strs' $ cmdArgs defaultJob
+readJob =
+    cmdArgs defaultJob
         >>= setWrite . setSuccinct
 
 setWrite :: Job -> IO Job
diff --git a/src/Language/SystemVerilog/AST/Attr.hs b/src/Language/SystemVerilog/AST/Attr.hs
--- a/src/Language/SystemVerilog/AST/Attr.hs
+++ b/src/Language/SystemVerilog/AST/Attr.hs
@@ -8,6 +8,7 @@
 module Language.SystemVerilog.AST.Attr
     ( Attr (..)
     , AttrSpec
+    , showsAttrs
     ) where
 
 import Text.Printf (printf)
@@ -24,6 +25,9 @@
 
 instance Show Attr where
     show (Attr specs) = printf "(* %s *)" $ commas $ map showSpec specs
+
+showsAttrs :: [Attr] -> ShowS
+showsAttrs = foldr (\a f -> shows a . showChar ' ' . f) id
 
 showSpec :: AttrSpec -> String
 showSpec (x, e) = x ++ showAssignment e
diff --git a/src/Language/SystemVerilog/AST/Attr.hs-boot b/src/Language/SystemVerilog/AST/Attr.hs-boot
new file mode 100644
--- /dev/null
+++ b/src/Language/SystemVerilog/AST/Attr.hs-boot
@@ -0,0 +1,9 @@
+module Language.SystemVerilog.AST.Attr
+    ( Attr
+    , showsAttrs
+    ) where
+
+data Attr
+instance Eq Attr
+
+showsAttrs :: [Attr] -> ShowS
diff --git a/src/Language/SystemVerilog/AST/Decl.hs b/src/Language/SystemVerilog/AST/Decl.hs
--- a/src/Language/SystemVerilog/AST/Decl.hs
+++ b/src/Language/SystemVerilog/AST/Decl.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE PatternSynonyms #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  - Initial Verilog AST Author: Tom Hawkins <tomahawkins@gmail.com>
diff --git a/src/Language/SystemVerilog/AST/Description.hs b/src/Language/SystemVerilog/AST/Description.hs
--- a/src/Language/SystemVerilog/AST/Description.hs
+++ b/src/Language/SystemVerilog/AST/Description.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE PatternSynonyms #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  - Initial Verilog AST Author: Tom Hawkins <tomahawkins@gmail.com>
diff --git a/src/Language/SystemVerilog/AST/Expr.hs b/src/Language/SystemVerilog/AST/Expr.hs
--- a/src/Language/SystemVerilog/AST/Expr.hs
+++ b/src/Language/SystemVerilog/AST/Expr.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE PatternSynonyms #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  - Initial Verilog AST Author: Tom Hawkins <tomahawkins@gmail.com>
@@ -20,6 +19,9 @@
     , ParamBinding
     , showParams
     , pattern RawNum
+    , pattern UniOp
+    , pattern BinOp
+    , pattern Mux
     ) where
 
 import Data.List (intercalate)
@@ -28,6 +30,7 @@
 import Language.SystemVerilog.AST.Number (Number(..))
 import Language.SystemVerilog.AST.Op
 import Language.SystemVerilog.AST.ShowHelp
+import {-# SOURCE #-} Language.SystemVerilog.AST.Attr
 import {-# SOURCE #-} Language.SystemVerilog.AST.Type
 
 type Range = (Expr, Expr)
@@ -37,6 +40,13 @@
 pattern RawNum :: Integer -> Expr
 pattern RawNum n = Number (Decimal (-32) True n)
 
+pattern UniOp :: UniOp -> Expr -> Expr
+pattern UniOp op e = UniOpA op [] e
+pattern BinOp :: BinOp -> Expr -> Expr -> Expr
+pattern BinOp op l r = BinOpA op [] l r
+pattern Mux :: Expr -> Expr -> Expr -> Expr
+pattern Mux c t f = MuxA [] c t f
+
 data Expr
     = String  String
     | Real    String
@@ -51,9 +61,9 @@
     | Concat  [Expr]
     | Stream  StreamOp Expr [Expr]
     | Call    Expr Args
-    | UniOp   UniOp Expr
-    | BinOp   BinOp Expr Expr
-    | Mux     Expr Expr Expr
+    | UniOpA  UniOp [Attr] Expr
+    | BinOpA  BinOp [Attr] Expr Expr
+    | MuxA    [Attr] Expr Expr Expr
     | Cast    TypeOrExpr Expr
     | DimsFn  DimsFn TypeOrExpr
     | DimFn   DimFn  TypeOrExpr Expr
@@ -94,34 +104,41 @@
                 where tStr = if null (show t) then "default" else show t
     show (MinTypMax a b c) = printf "(%s : %s : %s)" (show a) (show b) (show c)
     show (ExprAsgn l r) = printf "(%s = %s)" (show l) (show r)
-    show e@UniOp{} = showsPrec 0 e ""
-    show e@BinOp{} = showsPrec 0 e ""
-    show e@Dot  {} = showsPrec 0 e ""
-    show e@Mux  {} = showsPrec 0 e ""
-    show e@Call {} = showsPrec 0 e ""
+    show e@UniOpA{} = showsPrec 0 e ""
+    show e@BinOpA{} = showsPrec 0 e ""
+    show e@Dot   {} = showsPrec 0 e ""
+    show e@MuxA  {} = showsPrec 0 e ""
+    show e@Call  {} = showsPrec 0 e ""
 
-    showsPrec _ (UniOp   o e  ) =
+    showsPrec _ (UniOpA o a e  ) =
         shows o .
+        (if null a then id else showChar ' ') .
+        showsAttrs a .
         showUniOpPrec e
-    showsPrec _ (BinOp   o a b) =
-        showBinOpPrec a .
+    showsPrec _ (BinOpA o a l r) =
+        showBinOpPrec l .
         showChar ' ' .
         shows o .
         showChar ' ' .
-        showBinOpPrec b
-    showsPrec _ (Dot     e n  ) =
+        showsAttrs a .
+        case (o, r) of
+            (BitAnd, UniOp RedAnd _) -> showExprWrapped r
+            (BitOr , UniOp RedOr  _) -> showExprWrapped r
+            _ -> showBinOpPrec r
+    showsPrec _ (Dot      e n  ) =
         shows e .
         showChar '.' .
         showString n
-    showsPrec _ (Mux     c a b) =
+    showsPrec _ (MuxA   a c t f) =
         showChar '(' .
         shows c .
         showString " ? " .
-        shows a .
+        showsAttrs a .
+        shows t .
         showString " : " .
-        shows b .
+        shows f .
         showChar ')'
-    showsPrec _ (Call    e l  ) =
+    showsPrec _ (Call     e l  ) =
         shows e .
         shows l
     showsPrec _ e = \s -> show e ++ s
@@ -188,13 +205,16 @@
 showRange (h, l) = '[' : show h ++ ':' : show l ++ "]"
 
 showUniOpPrec :: Expr -> ShowS
-showUniOpPrec e@UniOp{} = (showParen True . shows) e
-showUniOpPrec e@BinOp{} = (showParen True . shows) e
+showUniOpPrec e@UniOp{} = showExprWrapped e
+showUniOpPrec e@BinOp{} = showExprWrapped e
 showUniOpPrec e = shows e
 
 showBinOpPrec :: Expr -> ShowS
-showBinOpPrec e@BinOp{} = (showParen True . shows) e
+showBinOpPrec e@BinOp{} = showExprWrapped e
 showBinOpPrec e = shows e
+
+showExprWrapped :: Expr -> ShowS
+showExprWrapped = showParen True . shows
 
 type ParamBinding = (Identifier, TypeOrExpr)
 
diff --git a/src/Language/SystemVerilog/AST/ModuleItem.hs b/src/Language/SystemVerilog/AST/ModuleItem.hs
--- a/src/Language/SystemVerilog/AST/ModuleItem.hs
+++ b/src/Language/SystemVerilog/AST/ModuleItem.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE PatternSynonyms #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  - Initial Verilog AST Author: Tom Hawkins <tomahawkins@gmail.com>
diff --git a/src/Language/SystemVerilog/AST/Number.hs b/src/Language/SystemVerilog/AST/Number.hs
--- a/src/Language/SystemVerilog/AST/Number.hs
+++ b/src/Language/SystemVerilog/AST/Number.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE TupleSections #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  -
@@ -154,12 +153,17 @@
 
         -- high-order X or Z is extended up to the size of the literal
         leadDigit = head digits
-        numDigits = length digits
+        numDigits = length digits + if isSignedUnsizedWithLeading1 then 1 else 0
         leadDigitIsXZ = elem leadDigit xzDigits
         digitsExtended =
             if leadDigitIsXZ
                 then replicate (sizeDigits - numDigits) leadDigit ++ digits
                 else digits
+        isSignedUnsizedWithLeading1 =
+            maybeBase /= Nothing &&
+            not leadDigitIsXZ &&
+            signed &&
+            digitToInt leadDigit >= div (baseSize base) 2
 
         -- determine the number of digits needed based on the size
         sizeDigits = ((abs size) `div` bitsPerDigit) + sizeExtraDigit
diff --git a/src/Language/SystemVerilog/AST/Stmt.hs b/src/Language/SystemVerilog/AST/Stmt.hs
--- a/src/Language/SystemVerilog/AST/Stmt.hs
+++ b/src/Language/SystemVerilog/AST/Stmt.hs
@@ -53,6 +53,7 @@
     | Trigger Bool Identifier
     | Assertion Assertion
     | Force Bool LHS Expr
+    | Wait Expr Stmt
     | Continue
     | Break
     | Null
@@ -106,6 +107,7 @@
                 (True , False) -> "release"
                 (False, True ) -> "assign"
                 (False, False) -> "deassign"
+    show (Wait      e s) = printf "wait (%s)%s" (show e) (showShortBranch s)
     show (Continue     ) = "continue;"
     show (Break        ) = "break;"
     show (Null         ) = ";"
diff --git a/src/Language/SystemVerilog/AST/Type.hs b/src/Language/SystemVerilog/AST/Type.hs
--- a/src/Language/SystemVerilog/AST/Type.hs
+++ b/src/Language/SystemVerilog/AST/Type.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE PatternSynonyms #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  - Initial Verilog AST Author: Tom Hawkins <tomahawkins@gmail.com>
@@ -62,7 +61,8 @@
     show (IntegerVector kw sg rs) = printf "%s%s%s" (show kw) (showPadBefore sg) (showRanges rs)
     show (IntegerAtom   kw sg   ) = printf "%s%s"   (show kw) (showPadBefore sg)
     show (NonInteger    kw      ) = printf "%s"     (show kw)
-    show (InterfaceT    "" "" rs) = printf "interface%s" ( showRanges rs)
+    show (InterfaceT    "" "" rs) = printf "interface%s"   (showRanges rs)
+    show (InterfaceT    xx "" rs) = printf "%s%s"    xx    (showRanges rs)
     show (InterfaceT    xx yy rs) = printf "%s.%s%s" xx yy (showRanges rs)
     show (Enum t vals r) = printf "enum %s{%s}%s" tStr (commas $ map showVal vals) (showRanges r)
         where
diff --git a/src/Language/SystemVerilog/Parser.hs b/src/Language/SystemVerilog/Parser.hs
--- a/src/Language/SystemVerilog/Parser.hs
+++ b/src/Language/SystemVerilog/Parser.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE TupleSections #-}
 {- sv2v
  - Author: Zachary Snow <zach@zachjs.com>
  -}
@@ -7,6 +6,8 @@
     , Config(..)
     ) where
 
+import Control.Monad (when)
+import Control.Monad.IO.Class (liftIO)
 import Control.Monad.Except
 import Data.List (elemIndex)
 import Data.Maybe (catMaybes)
diff --git a/src/Language/SystemVerilog/Parser/Parse.y b/src/Language/SystemVerilog/Parser/Parse.y
--- a/src/Language/SystemVerilog/Parser/Parse.y
+++ b/src/Language/SystemVerilog/Parser/Parse.y
@@ -13,9 +13,9 @@
  -}
 {
 {-# LANGUAGE BlockArguments #-}
-{-# LANGUAGE TupleSections #-}
 module Language.SystemVerilog.Parser.Parse (parse) where
 
+import Control.Monad (when)
 import Control.Monad.Except
 import Control.Monad.State.Strict
 import Data.Maybe (catMaybes, fromMaybe)
@@ -1150,6 +1150,7 @@
   | "deassign" LHS          ";"                { Force False $2 Nil }
   | "force"    LHS "=" Expr ";"                { Force True  $2  $4 }
   | "release"  LHS          ";"                { Force True  $2 Nil }
+  | "wait" "(" Expr ")" Stmt                   { Wait $3 $5 }
 
 OptDelayOrEvent :: { Maybe Timing }
   : DelayOrEvent { Just $1 }
@@ -1344,7 +1345,7 @@
   | Expr "[" Expr "]"           { Bit   $1 $3 }
   | "{" Expr Concat "}"         { Repeat $2 $3 }
   | Concat                      { Concat $1 }
-  | Expr "?" Expr ":" Expr      { Mux $1 $3 $5 }
+  | Expr "?" AttributeInstances Expr ":" Expr { MuxA $3 $1 $4 $6 }
   | Expr "." Identifier         { Dot $1 $3 }
   | "'" "{" PatternItems "}"    { Pattern $3 }
   | Expr "'" "{" PatternItems "}"{ Cast (Right $1) (Pattern $4) }
@@ -1358,47 +1359,47 @@
   | Identifier               "::" Identifier { PSIdent $1    $3 }
   | Identifier ParamBindings "::" Identifier { CSIdent $1 $2 $4 }
   -- binary expressions
-  | Expr "||"  Expr { BinOp LogOr  $1 $3 }
-  | Expr "&&"  Expr { BinOp LogAnd $1 $3 }
-  | Expr "->"  Expr { BinOp LogImp $1 $3 }
-  | Expr "<->" Expr { BinOp LogEq  $1 $3 }
-  | Expr "|"   Expr { BinOp BitOr  $1 $3 }
-  | Expr "^"   Expr { BinOp BitXor $1 $3 }
-  | Expr "&"   Expr { BinOp BitAnd $1 $3 }
-  | Expr "~^"  Expr { BinOp BitXnor $1 $3 }
-  | Expr "^~"  Expr { BinOp BitXnor $1 $3 }
-  | Expr "+"   Expr { BinOp Add $1 $3 }
-  | Expr "-"   Expr { BinOp Sub $1 $3 }
-  | Expr "*"   Expr { BinOp Mul $1 $3 }
-  | Expr "/"   Expr { BinOp Div $1 $3 }
-  | Expr "%"   Expr { BinOp Mod $1 $3 }
-  | Expr "**"  Expr { BinOp Pow $1 $3 }
-  | Expr "=="  Expr { BinOp Eq $1 $3 }
-  | Expr "!="  Expr { BinOp Ne $1 $3 }
-  | Expr "<"   Expr { BinOp Lt $1 $3 }
-  | Expr "<="  Expr { BinOp Le $1 $3 }
-  | Expr ">"   Expr { BinOp Gt $1 $3 }
-  | Expr ">="  Expr { BinOp Ge $1 $3 }
-  | Expr "===" Expr { BinOp TEq $1 $3 }
-  | Expr "!==" Expr { BinOp TNe $1 $3 }
-  | Expr "==?" Expr { BinOp WEq $1 $3 }
-  | Expr "!=?" Expr { BinOp WNe $1 $3 }
-  | Expr "<<"  Expr { BinOp ShiftL $1 $3 }
-  | Expr ">>"  Expr { BinOp ShiftR $1 $3 }
-  | Expr "<<<" Expr { BinOp ShiftAL $1 $3 }
-  | Expr ">>>" Expr { BinOp ShiftAR $1 $3 }
+  | Expr "||"  AttributeInstances Expr { BinOpA LogOr  $3 $1 $4 }
+  | Expr "&&"  AttributeInstances Expr { BinOpA LogAnd $3 $1 $4 }
+  | Expr "->"  AttributeInstances Expr { BinOpA LogImp $3 $1 $4 }
+  | Expr "<->" AttributeInstances Expr { BinOpA LogEq  $3 $1 $4 }
+  | Expr "|"   AttributeInstances Expr { BinOpA BitOr  $3 $1 $4 }
+  | Expr "^"   AttributeInstances Expr { BinOpA BitXor $3 $1 $4 }
+  | Expr "&"   AttributeInstances Expr { BinOpA BitAnd $3 $1 $4 }
+  | Expr "~^"  AttributeInstances Expr { BinOpA BitXnor $3 $1 $4 }
+  | Expr "^~"  AttributeInstances Expr { BinOpA BitXnor $3 $1 $4 }
+  | Expr "+"   AttributeInstances Expr { BinOpA Add $3 $1 $4 }
+  | Expr "-"   AttributeInstances Expr { BinOpA Sub $3 $1 $4 }
+  | Expr "*"   AttributeInstances Expr { BinOpA Mul $3 $1 $4 }
+  | Expr "/"   AttributeInstances Expr { BinOpA Div $3 $1 $4 }
+  | Expr "%"   AttributeInstances Expr { BinOpA Mod $3 $1 $4 }
+  | Expr "**"  AttributeInstances Expr { BinOpA Pow $3 $1 $4 }
+  | Expr "=="  AttributeInstances Expr { BinOpA Eq $3 $1 $4 }
+  | Expr "!="  AttributeInstances Expr { BinOpA Ne $3 $1 $4 }
+  | Expr "<"   AttributeInstances Expr { BinOpA Lt $3 $1 $4 }
+  | Expr "<="  AttributeInstances Expr { BinOpA Le $3 $1 $4 }
+  | Expr ">"   AttributeInstances Expr { BinOpA Gt $3 $1 $4 }
+  | Expr ">="  AttributeInstances Expr { BinOpA Ge $3 $1 $4 }
+  | Expr "===" AttributeInstances Expr { BinOpA TEq $3 $1 $4 }
+  | Expr "!==" AttributeInstances Expr { BinOpA TNe $3 $1 $4 }
+  | Expr "==?" AttributeInstances Expr { BinOpA WEq $3 $1 $4 }
+  | Expr "!=?" AttributeInstances Expr { BinOpA WNe $3 $1 $4 }
+  | Expr "<<"  AttributeInstances Expr { BinOpA ShiftL $3 $1 $4 }
+  | Expr ">>"  AttributeInstances Expr { BinOpA ShiftR $3 $1 $4 }
+  | Expr "<<<" AttributeInstances Expr { BinOpA ShiftAL $3 $1 $4 }
+  | Expr ">>>" AttributeInstances Expr { BinOpA ShiftAR $3 $1 $4 }
   -- unary expressions
-  | "!"  Expr                 { UniOp LogNot $2 }
-  | "~"  Expr                 { UniOp BitNot $2 }
-  | "+"  Expr %prec REDUCE_OP { UniOp UniAdd $2 }
-  | "-"  Expr %prec REDUCE_OP { UniOp UniSub $2 }
-  | "&"  Expr %prec REDUCE_OP { UniOp RedAnd  $2 }
-  | "~&" Expr %prec REDUCE_OP { UniOp RedNand $2 }
-  | "|"  Expr %prec REDUCE_OP { UniOp RedOr   $2 }
-  | "~|" Expr %prec REDUCE_OP { UniOp RedNor  $2 }
-  | "^"  Expr %prec REDUCE_OP { UniOp RedXor  $2 }
-  | "~^" Expr %prec REDUCE_OP { UniOp RedXnor $2 }
-  | "^~" Expr %prec REDUCE_OP { UniOp RedXnor $2 }
+  | "!"  AttributeInstances Expr                 { UniOpA LogNot $2 $3 }
+  | "~"  AttributeInstances Expr                 { UniOpA BitNot $2 $3 }
+  | "+"  AttributeInstances Expr %prec REDUCE_OP { UniOpA UniAdd $2 $3 }
+  | "-"  AttributeInstances Expr %prec REDUCE_OP { UniOpA UniSub $2 $3 }
+  | "&"  AttributeInstances Expr %prec REDUCE_OP { UniOpA RedAnd  $2 $3 }
+  | "~&" AttributeInstances Expr %prec REDUCE_OP { UniOpA RedNand $2 $3 }
+  | "|"  AttributeInstances Expr %prec REDUCE_OP { UniOpA RedOr   $2 $3 }
+  | "~|" AttributeInstances Expr %prec REDUCE_OP { UniOpA RedNor  $2 $3 }
+  | "^"  AttributeInstances Expr %prec REDUCE_OP { UniOpA RedXor  $2 $3 }
+  | "~^" AttributeInstances Expr %prec REDUCE_OP { UniOpA RedXnor $2 $3 }
+  | "^~" AttributeInstances Expr %prec REDUCE_OP { UniOpA RedXnor $2 $3 }
   -- assignments within expressions
   | "(" Expr "="        Expr ")" {% makeExprAsgn (tokenPosition $3, AsgnOpEq) $2 $4 }
   | "(" Expr AsgnBinOpP Expr ")" {% makeExprAsgn $3                           $2 $4 }
@@ -1604,9 +1605,8 @@
     else return (a1 ++ a2, b1 ++ b2)
 
 makeInput :: Decl -> Decl
-makeInput (Variable Local t x a e) = Variable Input t x a e
-makeInput (Variable Input t x a e) = Variable Input t x a e
-makeInput (Variable Output t x a e) = Variable Output t x a e
+makeInput (Variable d t x a e) = Variable d' t x a e
+  where d' = if d == Local then Input else d
 makeInput (CommentDecl c) = CommentDecl c
 makeInput other =
   error $ "unexpected non-var or non-port function decl: " ++ (show other)
diff --git a/src/Language/SystemVerilog/Parser/Preprocess.hs b/src/Language/SystemVerilog/Parser/Preprocess.hs
--- a/src/Language/SystemVerilog/Parser/Preprocess.hs
+++ b/src/Language/SystemVerilog/Parser/Preprocess.hs
@@ -14,6 +14,7 @@
     , Contents
     ) where
 
+import Control.Monad (when)
 import Control.Monad.Except
 import Control.Monad.State.Strict
 import Data.Char (ord)
@@ -961,19 +962,19 @@
 -- search for a pattern in the input and remove remove characters up to and
 -- including the first occurrence of the pattern
 removeThrough :: String -> PPS ()
-removeThrough pattern = do
+removeThrough pat = do
     str <- getInput
-    case findIndex (isPrefixOf pattern) (tails str) of
+    case findIndex (isPrefixOf pat) (tails str) of
         Nothing ->
-            if pattern == "\n"
+            if pat == "\n"
                 then setInput ""
                 else lexicalError $ "Reached EOF while looking for: "
-                        ++ show pattern
+                        ++ show pat
         Just patternIdx -> do
-            let chars = patternIdx + length pattern
+            let chars = patternIdx + length pat
             let (dropped, rest) = splitAt chars str
             advancePositions dropped
-            when (pattern == "\n") $ do
+            when (pat == "\n") $ do
                 pos <- getPosition
                 pushChar '\n' pos
             setInput rest
diff --git a/src/Language/SystemVerilog/Parser/Tokens.hs b/src/Language/SystemVerilog/Parser/Tokens.hs
--- a/src/Language/SystemVerilog/Parser/Tokens.hs
+++ b/src/Language/SystemVerilog/Parser/Tokens.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE PatternSynonyms #-}
 {- sv2v
  - Author: Tom Hawkins <tomahawkins@gmail.com>
  - Modified by: Zachary Snow <zach@zachjs.com>
diff --git a/src/sv2v.hs b/src/sv2v.hs
--- a/src/sv2v.hs
+++ b/src/sv2v.hs
@@ -10,37 +10,44 @@
 
 import Control.Monad (when, zipWithM_)
 import Control.Monad.Except (runExceptT)
+import Data.List (nub)
 
 import Convert (convert)
 import Job (readJob, Job(..), Write(..))
 import Language.SystemVerilog.AST
 import Language.SystemVerilog.Parser (parseFiles, Config(..))
 
-isInterface :: Description -> Bool
-isInterface (Part _ _ Interface _ _ _ _ ) = True
-isInterface _ = False
-
-isPackage :: Description -> Bool
-isPackage Package{} = True
-isPackage _ = False
-
 isComment :: Description -> Bool
 isComment (PackageItem (Decl CommentDecl{})) = True
 isComment _ = False
 
+droppedKind :: Description -> Identifier
+droppedKind description =
+    case description of
+        Part _ _ Interface _ _ _ _ -> "interface"
+        Package{} -> "package"
+        Class{}   -> "class"
+        PackageItem Function{}     -> "function"
+        PackageItem Task    {}     -> "task"
+        PackageItem (Decl Param{}) -> "localparam"
+        _ -> ""
+
 emptyWarnings :: AST -> AST -> IO ()
 emptyWarnings before after =
-    if all isComment before || not (all isComment after) then
+    if all isComment before || not (all isComment after) || null kinds then
         return ()
-    else if any isInterface before then
-        hPutStrLn stderr $ "Warning: Source includes an interface but output is"
-            ++ " empty because there is no top-level module which has no ports"
-            ++ " which are interfaces."
-    else if any isPackage before then
-        hPutStrLn stderr $ "Warning: Source includes packages but no modules."
-            ++ " Please convert packages alongside the modules that use them."
+    else if elem "interface" kinds then
+        hPutStrLn stderr $ "Warning: Source includes an interface but the"
+            ++ " output is empty because there are no modules without any"
+            ++ " interface ports. Please convert interfaces alongside the"
+            ++ " modules that instantiate them."
     else
-        return ()
+        hPutStrLn stderr $ "Warning: Source includes a " ++ kind ++ " but no"
+            ++ " modules. Such elements are elaborated into the modules that"
+            ++ " use them. Please convert all sources in one invocation."
+    where
+        kinds = nub $ filter (not . null) $ map droppedKind before
+        kind = head kinds
 
 rewritePath :: FilePath -> IO FilePath
 rewritePath path = do
diff --git a/sv2v.cabal b/sv2v.cabal
--- a/sv2v.cabal
+++ b/sv2v.cabal
@@ -1,6 +1,6 @@
 cabal-version:   2.4
 name:            sv2v
-version:         0.0.11
+version:         0.0.12
 license:         BSD-3-Clause
 license-file:    LICENSE NOTICE
 maintainer:      Zachary Snow <zach@zachjs.com>
@@ -24,7 +24,7 @@
 
 executable sv2v
     main-is:            sv2v.hs
-    build-tool-depends: alex:alex >=3.2.5 && <4, happy:happy >=1.20 && <2
+    build-tool-depends: alex:alex >=3.2 && <4, happy:happy >=1.19 && <2
     hs-source-dirs:     src
     other-modules:
         Language.SystemVerilog
@@ -66,6 +66,7 @@
         Convert.Foreach
         Convert.FuncRet
         Convert.FuncRoutine
+        Convert.GenvarName
         Convert.HierConst
         Convert.ImplicitNet
         Convert.Inside
@@ -105,18 +106,19 @@
 
     autogen-modules:    Paths_sv2v
     default-language:   Haskell2010
+    default-extensions: PatternSynonyms TupleSections
     ghc-options:
         -O3 -threaded -rtsopts "-with-rtsopts=-N -A32m"
-        -funbox-strict-fields -Wall
+        -funbox-strict-fields -Wall -Wno-incomplete-uni-patterns
 
     build-depends:
-        array <0.6,
-        base <4.15,
-        cmdargs <0.11,
-        containers <0.7,
-        directory <1.4,
-        filepath <1.5,
-        githash <0.2,
-        hashable <1.4,
-        mtl <2.3,
-        vector <0.13
+        array >=0.5.6.0 && <0.6,
+        base >=4.18.2.0 && <4.19,
+        cmdargs >=0.10.22 && <0.11,
+        containers >=0.6.7 && <0.7,
+        directory >=1.3.8.1 && <1.4,
+        filepath >=1.4.200.1 && <1.5,
+        githash >=0.1.7.0 && <0.2,
+        hashable >=1.4.4.0 && <1.5,
+        mtl >=2.3.1 && <2.4,
+        vector >=0.13.1.0 && <0.14
