Skip to content

[v2] fix(semantic): type address literals as address - #2184

Merged
nebasuke merged 5 commits into
mainfrom
bas/v2-address-literal-overload
Oct 7, 2026
Merged

nebasuke merged 5 commits into
mainfrom
bas/v2-address-literal-overload

Conversation

@nebasuke

@nebasuke nebasuke commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Type address literals with address explicitly. This mainly matters in overloading context.

Claude summary

Fixes #2179. solc types a 40-digit hex literal (0xb701…) as non-payable address. Slang gave it
its own literal kind, LiteralKind::Address, and every rule about address had to handle that
kind separately. Two rules didn't:

  • Overloads. The literal had no implicit conversion to address, so when a call had several
    candidates, every overload taking an address rejected it. Slang reported
    no-matching-callable-declaration, or member-not-found when the callee was reached through a
    member (Address.functionDelegateCall(0x142F…, …), Clones.clone(0xeA34…)). A callee with a
    single candidate never compares argument types, so only overloaded calls failed.
  • using L for address did not apply to the literal (0xb701….tag() reported
    member-not-found).

The literal kind is gone. The literal is now typed address, so both cases go through ordinary
address resolution, and so do built-in members, mobile types and the inline-assembly constant
check. The kind also let the value fold as a number: 0x… + 1 was typed as an integer literal and
(0x…) counted as a number in assembly. Now the first is no longer typed as a number, and the
second is rejected, as in solc (7615).

The AST's HexNumberExpression::integer_value / number_value still read the value off the
node's type. Only an address literal, whose type carries no value, is parsed from its source text.

Tests. Three overload tests (bare name, member access through this, address vs
address payable) and a using for test. All four fail on main: the overload tests with the
diagnostics from the issue, the using for test with member-not-found. The existing AST test
for integer_value on a 40-digit literal fails without the source-text fallback. No snapshot
changes.

Sourcify census (all 0.8.x, 2,326,676 contracts, #2181 harness, first commit only): failures
drop from 40,830 to 8,126, no contract that passed before fails, no panics.

Bucket Before After
bind:resolution/member-not-found 38,151 5,960
bind:resolution/no-matching-callable-declaration 629 114

Replacing the literal kind with address changes no contract's outcome on the same corpus.

Most of the remaining no-matching-callable-declaration contracts (75 of 114) are a different
shape: a string literal shorter than N passed to a bytesN overload (DssExecLib's
setValue(_vat, "Line", …)), filed as #2182.

Public API. slang_solidity_v2_semantic: LiteralKind::Address is removed and
types::literals::value_of_hex_number_expression is added. The AST's LiteralType::mobile_type
no longer returns an address.

Downstream. solx reads literal values off the type, so it needs a small change when it bumps
its Slang pin: lower an unfolded hex literal from integer_value(). That change is ready. Built
against this branch, the solx lit suite and cargo test-slang pass, and the Sol MLIR is
byte-identical to main for every lit input plus an inline-assembly address constant.

Not covered: solc rejects a 40-digit literal with an invalid checksum, and Slang still accepts it
(TODO SDR[38]).

Bencher. Two alerts on compute_contracts_abi (merkle_proof +1.7%, pointer_libraries +2.5%
estimated cycles). Instructions and memory accesses are identical to main; only 1 and 3 accesses
moved from L1 to RAM, which the cycle estimate weights 35×. semantic on cooldogs drops 8.5% in
instructions.

@changeset-bot

changeset-bot Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: f585526

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Branchbas/v2-address-literal-overload
Testbedci

⚠️ WARNING: Truncated view!

The full continuous benchmarking report exceeds the maximum length allowed on this platform.

🐰 View full continuous benchmarking report in Bencher

@nebasuke
nebasuke marked this pull request as ready for review September 25, 2026 16:41
@nebasuke
nebasuke requested review from a team as code owners September 25, 2026 16:41
@nebasuke
nebasuke marked this pull request as draft September 25, 2026 17:36
@nebasuke nebasuke changed the title [v2] fix(semantic): let address literals select address overloads [v2] fix(semantic): type address literals as address Sep 25, 2026
@nebasuke
nebasuke force-pushed the bas/v2-address-literal-overload branch from bcfc879 to 86179ad Compare September 25, 2026 18:46
@nebasuke
nebasuke marked this pull request as ready for review September 25, 2026 19:03

@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'm not sure I agree with the proposed changes. With them, we're removing detail from the type system and it's probably unnecessary.

I see in the PR history that you started with the implicit conversion route, which was my initial intuitive solution to the presented problem. The change to find_canonical_type_id to handle literal addresses with using attached functions is sound. We may need to add other similar cases for the other literal kinds. Removing the LiteralKind::Address from Number::from_literal_kind is required as well as it is a bug, but it might have been what tipped you to dropping the literal kind altogether.

Did you find any other strong reasons to remove LiteralKind::Address and type it as Type::Address directly?

@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 took a second look at this and the disadvantages of keeping the literal type are bigger than any advantage of the additional provenance information. We'd need to add the literal case to many places to match the behaviour of Type::Address and there's no real upside.

What I would ask is a bit of coverage improvement, especially surrounding the previously implicit conversion to number from a constant, eg. 0x... + 1 which is no longer typed as a number (not necessarily rejected yet because the diagnostic is not implemented).

Claude also found these adjacent cases, although I'm not sure we should follow solc here (I'd either reject or accept all of these):

uint[0x0000…0003] x;      // accepted
uint[0x0000…0003 + 1] x;  // accepted, but solc rejects
contract C layout at 0xb701…7641 {}  // same, should be rejected

OTOH, a parenthesized address literal can no longer be used from Yul:

address constant K = 0x1111…1111;    // accepted
address constant K = (0xb701…7641);  // now rejected

Which matches solc, but maybe doesn't make sense and it's not consistent with our constants treatment above.

An address literal had no implicit conversion to `address`, so overload
resolution rejected every candidate taking one and reported
`no-matching-callable-declaration`, or `member-not-found` through a member
access. Calls with a single candidate never compare argument types, which
is why only overloaded callees failed. solc 0.8 types the literal as
non-payable `address`, so it converts to `address` only.

Fixes #2179.
…erals

solc types an address literal as `address`, so `using L for address`
applies to it. The directive lookup matched the literal's own type and
reported `member-not-found`. Integer literals stay unattached, as in solc.
With different arities the call count alone ruled out the other candidate,
so the test didn't show the literal choosing between overloads.
solc types a 40-digit hex literal as non-payable `address` and never as a
literal type. Keeping a separate LiteralKind::Address meant every consumer
of `address` had to special-case it; #2179 and the using-for lookup were
two that did not. It also let the value fold as a number, so `0x… + 1`
typed as an integer literal. The AST still reads a hex literal's value off
its type and parses the source text only for an address literal, whose
type carries none.
Typing a 40-digit hex literal as `address` stops it folding as a number in
expressions, but the constant evaluator still reads its digits for array
lengths and storage base slots. The fixtures pin both sides: a bare length
and a parenthesised assembly constant agree with solc, while `0x… + 1` as a
length and `layout at 0x…` are accepted pending the operator (SDR #839)
and base-slot type (SDR #778) checks.
@nebasuke
nebasuke force-pushed the bas/v2-address-literal-overload branch from 86179ad to f585526 Compare October 7, 2026 08:35
@nebasuke

nebasuke commented Oct 7, 2026 •

Copy link
Copy Markdown
Member Author

Thanks!

Added a typing test for 0x… + 1 and fixtures for all four cases. The evaluator reads hex digits like solc's does, so uint[0x…3] stays accepted; uint[0x…3 + 1] and layout at 0x… are rejected in solc by type checks we don't have yet, so they're pinned as expected divergences citing NomicFoundation/slang-diagnostics-research#839 and NomicFoundation/slang-diagnostics-research#778. (0x…) in assembly is rejected because it's typed address, the same rule that lets (1) through, so I'd keep it.

Note that NomicFoundation/slang-diagnostics-research#778 was already closed, so I'm reopening it to be addressed as a follow-up, rather than fixing it in this PR.

@nebasuke
nebasuke added this pull request to the merge queue Oct 7, 2026
Merged via the queue into main with commit f6f07d7 Oct 7, 2026
19 checks passed
@nebasuke
nebasuke deleted the bas/v2-address-literal-overload branch October 7, 2026 09:30
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.

[v2] Address literal argument does not select the address overload

2 participants