Skip to content

feat(slang): emit every contract in a source unit#460

Open
hedgar2017 wants to merge 1 commit into
mainfrom
feat-slang-multi-contract
Open

feat(slang): emit every contract in a source unit#460
hedgar2017 wants to merge 1 commit into
mainfrom
feat-slang-multi-contract

Conversation

@hedgar2017

Copy link
Copy Markdown
Contributor

The Slang frontend was processing only the first contract per source unit, silently dropping the rest of the file. Tests targeting a non-first contract failed with selector not found because the contract was never registered, and any future cross-contract lowering would have nothing to point at on the right-hand side of new B() or B(addr).method().

AstEmitter::emit now takes a single ContractDefinition and is invoked once per concrete contract in the source unit, with a fresh solx_mlir::Context per contract. Each contract gets its own deploy + runtime MLIR module pair through the existing finalize_module path and lands in output.contracts[file_id][contract_name] independently. Abstract contracts are skipped — their unimplemented members would crash the emitter, and derived concrete contracts continue to emit on their own.

Test delta on tests/solidity/simple -O M3B3 is zero (1903 passed, 4 failed, 305 invalid both with and without this change). Every multi-contract test in that suite also depends on contract creation, external calls, or inheritance — none of which are wired in the Slang pipeline yet. This change is a prerequisite for those: without it, the second contract's MLIR is never produced, so even after lowering new B() and b.method() the call sites would have nothing to bind to. solx-solidity semantic tests are unaffected — they exercise the solc-based frontend, not Slang.

Direct verification with a synthetic two-contract source confirms both contracts now appear in output.contracts[file_id] where previously only the first did.

@hedgar2017 hedgar2017 added the ci:slang Trigger slang unit tests on PR label May 28, 2026
@hedgar2017 hedgar2017 force-pushed the feat-slang-multi-contract branch from 5a6eff0 to 7fe873f Compare May 28, 2026 15:45
The Slang frontend was processing only the first contract per source
unit, silently dropping the rest of the file. Tests targeting a
non-first contract failed with `selector not found` because the
contract was never registered, and any future cross-contract lowering
would have nothing to point at on the right-hand side of `new B()` or
`B(addr).method()`.

`AstEmitter::emit` now takes a single `ContractDefinition` and is
invoked once per concrete contract, with a fresh `solx_mlir::Context`
per contract. Each contract gets its own deploy + runtime MLIR module
pair through the existing `finalize_module` path and lands in
`output.contracts[file_id][contract_name]` independently. Abstract
contracts are skipped — their unimplemented members would crash the
emitter, and derived concrete contracts continue to emit on their own.
@hedgar2017 hedgar2017 force-pushed the feat-slang-multi-contract branch from 7fe873f to f579275 Compare June 1, 2026 05:30
@hedgar2017 hedgar2017 marked this pull request as ready for review June 1, 2026 05:30
@hedgar2017 hedgar2017 requested a review from Copilot June 1, 2026 05:30
@hedgar2017 hedgar2017 self-assigned this Jun 1, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a Slang-frontend correctness gap where only the first contract in each Solidity source unit was lowered, by iterating over all concrete contracts and emitting each into output.contracts[file_id][contract_name] independently (with a fresh solx_mlir::Context per contract).

Changes:

  • Iterate through source_unit.contracts() and emit each non-abstract contract instead of only the first one.
  • Refactor AstEmitter::emit to operate on a single ContractDefinition (instead of a SourceUnit) and return (contract_name, method_identifiers) directly.
  • Create/insert per-contract standard-json Contract outputs (MLIR stages + method identifiers) under the correct file/contract keys.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
solx-slang/src/slang/mod.rs Emit and register outputs for each concrete contract in a source unit rather than only the first.
solx-slang/src/ast/mod.rs Change the emitter API to lower exactly one contract definition at a time.

Comment on lines +180 to +206
let runtime_code_identifier = format!(
"{contract_name}{}",
solx_codegen_evm::DEPLOYED_OBJECT_SUFFIX
);
let capture_sol_dialect = input_json.settings.output_selection.check_selection(
file_identifier,
Some(contract_name.as_str()),
solx_standard_json::InputSelector::MLIR,
);
let mlir_stages =
context.finalize_module(&runtime_code_identifier, capture_sol_dialect)?;

output
.contracts
.entry(file_identifier.to_string())
.or_default()
.insert(
contract_name,
solx_standard_json::output::contract::Contract {
mlir: Some(mlir_stages),
evm: Some(solx_standard_json::output::contract::evm::EVM {
method_identifiers: Some(method_identifiers),
..Default::default()
}),
..Default::default()
},
);

@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.

can we add a lit test with 2 contracts?

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

Labels

ci:slang Trigger slang unit tests on PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants