Yul literal refactoring
- Dominant language
- C++
- Stars
- 25.7k
- Forks
- 6.2k
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 21
Description
The `solidity::yul::LiteralValue` class handles two different cases:
1. Normal literals: Data that enters bytecode (`42`, `"hello"`, `false`) and is bound by `u256`. It may have a representation hint such that when, e.g., IR is produced as compiler output, the originally used representation of the value is reproduced.
2. Literal arguments of builtins: Certain builtins have arguments that _must_ contain a literal, i.e., such values cannot be moved to variables. Furthermore - in case of strings - they can exceed the normal 256 bit limit (`datasize("obj.subobject1.subobject2...")`, `verbatim_1i_1o("bytecode", arg)`). There are also builtins with non-string literal arguments, for example `memoryguard(arg)`. The definition of a builtin is what determines which of its arguments must be literals. There is currently no syntactical difference to regular arguments.
# Motivation
The current architecture conflates the above mentioned cases and concepts. What is described below disentangles these concepts on the side of internal representation of literals in the Yul AST as well as on a syntax level.
# Naming
- Literal arguments: Currently refers to arguments of builtins which must contain a literal and may exceed `u256`. Will be retired as name and replaced by "immediate arguments"
- Immediate arguments: Refers to arguments which must contain a literal.
- Unlimited literals: Currently refers to literals with a value that can exceed `u256`. To be replaced by symbolic literals
- Literal value: Currently a struct containing both the values of normal and unlimited literals. To be reduced to just contain values of normal literals (plus representation hints).
- Symbolic literals: Currently don't exist, meant to be literals which are immediate arguments of builtin functions.
# Changes on Yul AST level
## Current
`FunctionCall` has a vector of `Expression` arguments.

## New
`FunctionCall` has a vector of `FunctionArgument = variant` as arguments.

with
```cpp
struct SymbolicLiteral { std::variant value; };
```
While this can be implemented so that the AST JSON does not change, it is better to let it reflect the actual structure of the AST. Therefore, this - even without syntax change - will be breaking.
# Syntax changes
These are (at least most of) the uses of unlimited string literal arguments:
| Function | Example | Purpose |
|----------|---------|---------|
| `linkersymbol` | `linkersymbol("sym")` | Linker symbol references |
| `datasize` | `datasize("obj")` | Object size queries |
| `dataoffset` | `dataoffset("obj")` | Object offset queries |
| `setimmutable` | `setimmutable(offset, "var", val)` | Immutable variable assignment (2nd arg) |
| `loadimmutable` | `loadimmutable("var")` | Immutable variable loading |
| `eofcreate` | `eofcreate("subobj", ...)` | EOF contract creation (1st arg) |
| `returncontract` | `returncontract("subobj", ...)` | EOF contract return (1st arg) |
| `verbatim_*` | `verbatim_1i_1o("bytecode", x)` | Raw bytecode injection (1st arg) |
We could separate them from the normal argument list:
```js
datasize("obj") => datasize<"obj">()
memoryguard(0x20) => memoryguard<0x20>()
// keep string identifier
setimmutable(offset, "var", val) => setimmutable<"var">(offset, val)
// reference identifier directly?
setimmutable(offset, "var", val) => setimmutable(offset, val)
// conflated with verbatim refactor
verbatim_1i_1o("bytecode", x) => verbatim<"bytecode", 1, 1>(x)
```
Gives opportunity to save the symbolic / unlimited literal arguments separately in the `FunctionCall` struct, as we are no longer bound by the order of arguments in the function call definition:
```cpp
// current
struct FunctionCall {
FunctionName functionName;
std::vector arguments;
}
// proposal
struct FunctionCall {
FunctionName functionName;
std::vector arguments;
}
// with syntax change
struct FunctionCall {
FunctionName functionName;
std::vector arguments;
std::vector symbolicArguments;
}
```
Contributor guide
Research direction
Start by tracing the Yul AST structures named in the issue, especially LiteralValue and FunctionCall, and compare how current builtin arguments are represented. Review the listed syntax examples and decide on the AST and syntax direction; done means normal and symbolic literals are separated consistently and the affected AST representation and parsing behavior are covered.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, solidity
- Domain
- compilers
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 20/100