In earlier versions of VL such as vl2014, we used a fairly
The most significant of these was the lack of type safety. We often
expected expressions to have certain shapes. For instance, we typically
expected that any hierarchical identifier, like
The ability to create degenerate/nonsense expressions is not necessarily so bad—just don't create nonsense expressions and what's the a problem? But the possibility of these degenerate expressions might exist turned out to have a pervasive impact when writing code to process expressions: VL's many transforms and utilities always had to defend against such malformed expressions.
This defense was generally carried out by adding guards or explicit run-time tests that expressions were sensible. The result was copious error handling code, difficult and tedious proofs about well-formedness (e.g., see vl2014::welltyped), and additional interfacing layers such as the vl2014::hid-tools to hide the problem. These layers became ever more complex as we implemented more of SystemVerilog, e.g., scope expressions and datatype indexing greatly complicated the handling of hierarchical identifiers.
Reflecting on these problems, and considering our improving ability to handle mutual-recursion via macro libraries such as fty and defines, we decided to overhaul the expression representation and replace it with a much more strongly typed, mutually recursive approach.
Our new expression format is much more complex than before. However, it also intrinsically rules out many expressions that were previously allowed, which helps to avoid needing error checking code when processing expressions, and generally makes it easier to write safe expression-processing code.