feat(slang): inheritance - #697
hedgar2017 wants to merge 2 commits into
Conversation
7cbb0fa to
586eea6
Compare
Coverage Summary
|
28da541 to
7fb3184
Compare
c237244 to
6d41503
Compare
6d41503 to
96cebda
Compare
ed6c4df to
31ee803
Compare
2629bb2 to
88459fe
Compare
88459fe to
67d6b7c
Compare
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The fallback fixture contains an unobserved base implementation, so its CHECKs do not fully pin the input.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 1
Open (1)
What changed in this PR
Adds Solidity inheritance lowering to the Slang frontend, covering inherited members, virtual dispatch, and constructor chaining.
Changes:
- Lowers inherited state, getters, functions,
super, and qualified calls. - Builds base-constructor chains with correctly scoped arguments.
- Updates MLIR dispatch APIs, Slang revision, and inheritance fixtures.
| File | Description |
|---|---|
solx-slang/src/source_unit.rs |
Enables contracts with bases. |
solx-slang/src/scope/source_unit.rs |
Passes constructor-chain context. |
solx-slang/src/scope/function.rs |
Tracks constructor frames. |
solx-slang/src/scope/contract.rs |
Adds hierarchy-aware resolution. |
solx-slang/src/contract/state_variable.rs |
Initializes inherited state. |
solx-slang/src/contract/object.rs |
Exposes linearized members and layout. |
solx-slang/src/contract/mod.rs |
Emits inherited contract members. |
solx-slang/src/contract/getter/mod.rs |
Updates getter dispatch. |
solx-slang/src/contract/function/statement/revert.rs |
Adapts argument lowering. |
solx-slang/src/contract/function/statement/event.rs |
Adapts event arguments. |
solx-slang/src/contract/function/mod.rs |
Emits inherited functions and constructors. |
solx-slang/src/contract/function/expression/mod.rs |
Handles parenthesized inheritance expressions. |
solx-slang/src/contract/function/expression/member.rs |
Resolves super and qualified members. |
solx-slang/src/contract/function/expression/keyword.rs |
Updates keyword documentation. |
solx-slang/src/contract/function/expression/identifier.rs |
Applies virtual function resolution. |
solx-slang/src/contract/function/expression/call/mod.rs |
Classifies inheritance-related calls. |
solx-slang/src/contract/function/expression/call/arguments.rs |
Simplifies converted arguments. |
solx-slang/src/contract/constructor_chain/mod.rs |
Implements constructor chaining. |
solx-slang/src/contract/constructor_chain/arguments.rs |
Models base-constructor arguments. |
solx-mlir/tests/lit/inheritance_virtual.sol |
Covers virtual and qualified calls. |
solx-mlir/tests/lit/inheritance_synthesized.sol |
Covers synthesized constructors. |
solx-mlir/tests/lit/inheritance_super.sol |
Covers super resolution. |
solx-mlir/tests/lit/inheritance_state.sol |
Covers inherited state. |
solx-mlir/tests/lit/inheritance_getter.sol |
Covers getter overrides. |
solx-mlir/tests/lit/inheritance_fallback.sol |
Covers fallback inheritance. |
solx-mlir/tests/lit/inheritance_constructor.sol |
Covers constructor arguments. |
solx-mlir/src/lib.rs |
Removes the obsolete function-entry export. |
solx-mlir/src/context/function/mod.rs |
Returns entry blocks directly. |
solx-mlir/src/context/function/entry.rs |
Removes the entry wrapper. |
solx-mlir/src/context/function/dispatch.rs |
Adds symbol-only dispatch. |
Cargo.toml |
Pins the required Slang revision. |
Cargo.lock |
Records the updated Slang sources. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
ceb06b0 to
1793fad
Compare
1793fad to
3e3b8da
Compare
959f1ae to
204dfbd
Compare
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Selector dispatch is still precomputed before emission, contrary to the frontend’s required on-demand model.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 1
Open (2)
Resolved since last review (2)
204dfbd to
95dcff6
Compare
95dcff6 to
434b7d6
Compare
| /// The functions the object dispatches and defines: a contract's resolved hierarchy, where an | ||
| /// overridden or getter-shadowed function has given way to its override, listed in declaration | ||
| /// order per contract of its linearisation, as print-init emits them; a library's own. | ||
| pub fn functions(&self) -> Vec<FunctionDefinition> { |
There was a problem hiding this comment.
- "dispatches and defines"? you mean all functions?
- don't say "print-init". nothing in the codegen should depend on mlir textual dumps
There was a problem hiding this comment.
- yes, the comment is outdated
- yes, also leftover slop, everything verified against legacy although semanticTests don't really cover everything
Thanks for noticing!
|
thank you for working on this! This is pre-existing, but what does the |
| Some(enclosing) if enclosing.node_id() == self.object_id => function.compute_selector(), | ||
|
|
||
| let is_constructor = matches!(function.kind(), FunctionKind::Constructor); | ||
| let is_most_derived = is_constructor |
There was a problem hiding this comment.
is_most_derived_constructor instead?
There was a problem hiding this comment.
and combine the:
if is_constructor {
if is_most_derived {
below
abinavpp
left a comment
There was a problem hiding this comment.
thank you! lgmt! mostly nits/questions. i don't see any major concerns with the lowering..
| /// Classifies `call`'s callee into the single kind that emits it. A type conversion is probed | ||
| /// before the callee's shape, its callee may be an elementary type or `payable` keyword as well | ||
| /// as a named type, and its one-argument arity is part of the classification, per the variant's | ||
| /// definition. | ||
| /// before the callee's shape, since its callee may be an elementary type or `payable` keyword | ||
| /// as well as a named type. The lookup of a named function is the callee's shape: a bare name is virtual, | ||
| /// a `super` member resolves after its enclosing contract, and a contract-qualified name names its | ||
| /// declaration. |
There was a problem hiding this comment.
This function just classifies a call depending on the callee/syntax, right? I personally find the comment hard to read. Sounds like the overly technical Claude Code English that I fight every day :P. Maybe we could have the technical stuff next to the relevant code instead and keep the header dumb and simple?
There was a problem hiding this comment.
Also, maybe this whole thing could be part of slang?
| /// The constructors and arguments emitted for the object's creation. | ||
| pub struct Constructor<'context> { | ||
| /// The contracts of the object's linearisation, consumed in call order. | ||
| pub contracts: IntoIter<ContractDefinition>, |
There was a problem hiding this comment.
Oh, one more thing, not sure about it: the chain works by mutating Constructor in a fixed order: next_contract advances an iterator, function_definition sets current and forwarded, base_constructor_call consumes them, and None in arguments means "the synthesized constructor". Could Constructor::new compute all of this up front (for each constructor: who it calls, which argument lists it evaluates, what it forwards) so emission just reads a table?
ggiraldez
left a comment
There was a problem hiding this comment.
I left some minor suggestions and questions, but looks good to me! Can't really evaluate the MLIR part, but I can see Abinav already approved as well.
| if call.is_type_conversion() | ||
| && let ArgumentsDeclaration::PositionalArguments(arguments) = &call.arguments() | ||
| && arguments.len() == 1 | ||
| { | ||
| if call.is_type_conversion() { |
There was a problem hiding this comment.
Should we move the check on the arguments to the AST? Not sure how the removal affects the rest of the backend.
| if is_most_derived { | ||
| scope.state_variable_initializers(); | ||
| } | ||
| scope.base_constructor_call(); |
There was a problem hiding this comment.
AFAIU, this is compatible with the legacy pipeline in solc. I think it would be nice to have it documented with a comment and some kind of marker, so we can easily refer to areas of the backend that conform to legacy vs. via-IR.
| scope.state_variable_initializers(); | ||
| scope.base_constructor_call(); |
There was a problem hiding this comment.
Ditto here re: the legacy vs via-IR compatibility documentation.
| self.contracts() | ||
| .iter() | ||
| .flat_map(|base| base.functions()) | ||
| .filter(|function| resolved.contains(&function.node_id())) | ||
| .collect() |
There was a problem hiding this comment.
I assume this is to get the "declaration order per contract of its linearisation". Curious to know, why is this necessary?
| //! | ||
| //! Constructor emission in Slang's linearisation order. | ||
| //! |
There was a problem hiding this comment.
This was by far the hardest file to understand. I think a brief explanation of how the constructor for each contract is generated would help a lot here. Eg. why do we need to forward parameters, how that can change the declared signature of intermediate contracts, how Constructor mutates as the linearised bases are traversed, etc.. The sections "The value that has to travel" and "The trace" from this Claude walkthrough were the most informative.
| pub struct Constructor<'context> { | ||
| /// The contracts of the object's linearisation, consumed in call order. | ||
| pub contracts: IntoIter<ContractDefinition>, | ||
| /// The argument lists supplied for bases, keyed by the provider's definition id. |
There was a problem hiding this comment.
It's unclear to me what "provider definition id" refers to and what it means when it's None.
| use crate::scope::function::FunctionScope; | ||
|
|
||
| /// The constructors and arguments emitted for the object's creation. | ||
| pub struct Constructor<'context> { |
There was a problem hiding this comment.
On the first pass I didn't realize this object mutated as the constructor was being built. Maybe rename this to ConstructorBuilder or something similar?


Lowers contracts with contract bases in the Slang frontend: inherited state and getters, virtual dispatch to the most-derived override,
superand contract-qualified calls, and the base-constructor chain with each argument list evaluated at the call to its target. The tester's Slang view follows the compilation API of the revision this pins.cargo run-tester-slang: 20182 passed, 85 failed, 35 invalid.