Skip to content

feat(slang): inheritance - #697

Open
hedgar2017 wants to merge 2 commits into
mainfrom
az-slang-inheritance
Open

hedgar2017 wants to merge 2 commits into
mainfrom
az-slang-inheritance

Conversation

@hedgar2017

@hedgar2017 hedgar2017 commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Lowers contracts with contract bases in the Slang frontend: inherited state and getters, virtual dispatch to the most-derived override, super and 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.

@hedgar2017
hedgar2017 force-pushed the az-slang-inheritance branch 2 times, most recently from 7cbb0fa to 586eea6 Compare September 8, 2026 07:23
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

Coverage Summary

Crate Line Coverage Function Coverage
solx 🟢 83.6% 🔴 20.0%
solx-benchmark-converter 🔴 0.0% 🔴 0.0%
solx-codegen-evm 🔴 24.7% 🔴 11.5%
solx-compiler-downloader 🔴 0.0% 🔴 0.0%
solx-core 🔴 39.2% 🔴 46.9%
solx-dev 🔴 2.4% 🔴 3.0%
solx-evm-assembly 🔴 0.0% 🔴 0.0%
solx-mlir 🟡 50.3% 🔴 46.3%
solx-slang 🔴 22.6% 🔴 31.0%
solx-solc-test-adapter 🔴 1.7% 🔴 2.1%
solx-standard-json 🔴 42.6% 🔴 47.7%
solx-tester 🔴 36.1% 🔴 34.3%
solx-utils 🔴 32.3% 🔴 35.1%
solx-yul 🔴 0.0% 🔴 0.0%
Total 🔴 10.9% 🔴 13.1%

Codecov Report | HTML Report | Workflow Run

@hedgar2017
hedgar2017 force-pushed the az-slang-inheritance branch 6 times, most recently from 28da541 to 7fb3184 Compare September 9, 2026 22:15
@abinavpp
abinavpp force-pushed the app-slang-inline-assembly branch 2 times, most recently from c237244 to 6d41503 Compare September 16, 2026 13:18
@hedgar2017
hedgar2017 force-pushed the app-slang-inline-assembly branch from 6d41503 to 96cebda Compare September 16, 2026 16:03
@abinavpp
abinavpp force-pushed the app-slang-inline-assembly branch 3 times, most recently from ed6c4df to 31ee803 Compare September 17, 2026 12:29
Base automatically changed from app-slang-inline-assembly to main September 17, 2026 19:59
@hedgar2017
hedgar2017 force-pushed the az-slang-inheritance branch 9 times, most recently from 2629bb2 to 88459fe Compare September 19, 2026 13:46
@hedgar2017 hedgar2017 self-assigned this Sep 19, 2026
@hedgar2017
hedgar2017 marked this pull request as ready for review September 19, 2026 13:47
@hedgar2017
hedgar2017 requested review from a team and a balanced review from Copilot September 19, 2026 13:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 Low severity

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.

Comment thread solx-mlir/tests/lit/inheritance_fallback.sol Outdated
@hedgar2017
hedgar2017 force-pushed the az-slang-inheritance branch 3 times, most recently from ceb06b0 to 1793fad Compare September 19, 2026 15:03
@hedgar2017
hedgar2017 requested a balanced review from Copilot September 19, 2026 15:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Constructor-chain construction introduces a prohibited hierarchy pre-pass and threaded cache.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 Medium severity · 1 Low severity

Open (2)

Comment thread solx-slang/src/contract/mod.rs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 Medium severity · 1 Low severity

Open (2)
Resolved since last review (2)

Comment thread solx-slang/src/scope/contract.rs Outdated
Comment thread solx-slang/src/contract/constructor.rs Outdated
Comment on lines +93 to +96
/// 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> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • "dispatches and defines"? you mean all functions?
  • don't say "print-init". nothing in the codegen should depend on mlir textual dumps

@hedgar2017 hedgar2017 Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • yes, the comment is outdated
  • yes, also leftover slop, everything verified against legacy although semanticTests don't really cover everything

Thanks for noticing!

@abinavpp

Copy link
Copy Markdown
Contributor

thank you for working on this!

This is pre-existing, but what does the Object enum in solx-slang/src/contract/object.rs buy us over slang's own definitions? Object::functions(), state_variables() and storage_layout() are now thin wrappers over linearised_functions(), linearised_state_variables() and compute_abi(), and the library cases are the plain node.functions() / node.state_variables(). If I read functions() right, the extra walk over contracts() is only there to emit in linearisation/declaration order rather than slang's name order

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_most_derived_constructor instead?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and combine the:

 if is_constructor {
                if is_most_derived {

below

@abinavpp abinavpp left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you! lgmt! mostly nits/questions. i don't see any major concerns with the lowering..

Comment on lines 253 to +257
/// 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ggiraldez left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines -245 to +264
if call.is_type_conversion()
&& let ArgumentsDeclaration::PositionalArguments(arguments) = &call.arguments()
&& arguments.len() == 1
{
if call.is_type_conversion() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we move the check on the arguments to the AST? Not sure how the removal affects the rest of the backend.

Comment on lines +106 to +109
if is_most_derived {
scope.state_variable_initializers();
}
scope.base_constructor_call();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +118 to +119
scope.state_variable_initializers();
scope.base_constructor_call();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto here re: the legacy vs via-IR compatibility documentation.

Comment on lines +104 to +108
self.contracts()
.iter()
.flat_map(|base| base.functions())
.filter(|function| resolved.contains(&function.node_id()))
.collect()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this is to get the "declaration order per contract of its linearisation". Curious to know, why is this necessary?

Comment on lines +1 to +3
//!
//! Constructor emission in Slang's linearisation order.
//!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants