Formally prove the correctness of Sqrt.sol and Cbrt.sol#511
Open
duncancmt wants to merge 549 commits intodcmt/cbrt512from
Open
Formally prove the correctness of Sqrt.sol and Cbrt.sol#511duncancmt wants to merge 549 commits intodcmt/cbrt512from
duncancmt wants to merge 549 commits intodcmt/cbrt512from
Conversation
2 tasks
🛡️ Immunefi PR ReviewsWe noticed that your project isn't set up for automatic code reviews. If you'd like this PR reviewed by the Immunefi team, you can request it manually using the link below: Once submitted, we'll take care of assigning a reviewer and follow up here. |
66c27b4 to
9e9f388
Compare
… norm reservation - _inline_single_call now constant-folds the condition of a leave-bearing switch/if-else before rejecting it, dropping the dead branch when constant - _model_name_reserved check is now per-model: skip_norm models only check base reserved names, not norm helper names they won't collide with - Bonus: also fixes allows_constant_switch_with_dead_leave_branch (32 remaining) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When `leave` is inside a bare block, the inner `_parse_assignment_loop` returns with `inner_leave=True`. The outer loop must skip remaining tokens in the enclosing function body before breaking, otherwise `parse_function` fails on unexpected tokens after the bare block. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…, 3/5)
Add constant-folding awareness to the reference scanning phase:
1. `if 0 { ... }` bodies are skipped (constant-false dead code)
2. `switch N case M { ... }` only records the matching case branch
3. Sibling function bodies exclude locally-shadowed names from the
external name set, preventing false transitive dependencies
Also adds `_block_has_leave_at_depth1` helper (unused for now but
needed for future leave-awareness work).
NOTE: 2 of 5 WS2 tests remain unfixed (leave-based dead code). The
`_scope_references_any` correctly detects dead references after `leave`,
but `find_function`'s disambiguation logic needs additional changes to
handle the case where NEITHER candidate references the known names.
This requires a design decision about fallback behavior that needs
further analysis.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ha-renaming (WS-K) Five fixes for helper function scoping during inlining: 1. Scope-aware collect_all_functions: skip non-function brace blocks (object/code wrappers) so functions in different object blocks never collide. Duplicate names within the same scope now raise ParseError. 2. Scope chain helper collection: walk up enclosing block scopes from outermost to innermost, with inner names overriding outer ones. Also collect nested helpers from inside the target function body. 3. find_exact_function: add search_nested parameter (used by prepare_translation) to find functions inside other function bodies. 4. Alpha-rename callee locals during inlining: when a callee declares a local variable whose name appears in the argument expressions, generate a fresh name to avoid substitution clobbering. 5. _find_function_body_range helper for extracting the token range of a function's body contents. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ng (WS-J/WS-I partial)
Two independent fixes:
1. Set ssa_count[clean]=1 after emitting zero-init assignment for return
variables, so subsequent assignments get SSA-suffixed names instead
of overwriting the zero-init binding.
2. Extend constant-folding in _inline_single_call to handle:
- `if 0 { leave }` (no else_body): skip the dead block entirely
- `if 1 { ... leave }` (no else_body): process then-body and
return immediately, skipping dead code after the leave site
- Non-leave constant if/switch: eliminate dead branches before
processing bodies, preventing spurious "conditional memory write"
errors on dead code paths
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…y writes (WS-I) When `if` or `switch` conditions are constant at parse time: - Constant-false `if`: parse the body (tolerating memory writes) but discard it entirely as dead code - Constant-true `if`: flatten the body into the outer scope as straight-line code - Constant-discriminant `switch`: parse all branches, flatten only the matching branch, discard dead branches This prevents spurious "conditional memory write" errors when mstore appears in provably-dead branches, and correctly folds constant-true conditionals so their assignments become straight-line. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…, switch leave 1. find_exact_function: prefer top-level match before searching nested scopes, fixing regression where an unrelated nested homonym caused ambiguity (introduced by 9ffbc2b) 2. collect_all_functions: check both `functions` and `rejected` dicts for duplicate names, so a rejected-then-valid or valid-then-rejected duplicate pair is caught (pre-existing bug) 3. Nested rejected helpers shadow valid outer helpers: remove the outer name from helper_table when an inner scope rejects the same name, preventing silent fallback to the wrong definition (pre-existing bug) 4. Constant-false leave-bearing switch inlining: process the else-body (case-0 branch) as straight-line code instead of rewriting into a new ParsedIfBlock whose condition semantics are inverted, which caused the non-leave constant-fold path to skip the live branch entirely (pre-existing bug, exposed by commit 0bca759's incomplete constant-fold) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ambiguation Two code-quality improvements, zero behavioral changes: 1. _TokenReader base class: _ReferenceScopeParser and YulParser had 7 byte-for-byte identical methods (_at_end, _peek, _peek_kind, _pop, _expect, _expect_ident, _parse_expr). Extract them into a shared base class so a future expression-syntax change only needs one edit. 2. _disambiguate_by_references: extract the dense 3-level conditional block from find_function into a named helper with a docstring that explains the priority ordering for leaf selection (exclude_known=True), wrapper selection (exclude_known=False), and the partial-parse fallback. Add inline comments on each branch. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…olding Duplicated decision logic for constant-folding if/switch conditions existed independently in _inline_single_call (Site B) and yul_function_to_model (Site C). Extract the shared classification into a single _IfFoldDecision enum + _classify_if_fold function, and refactor both sites to use it. Also adds the previously-missing constant-true handling in yul_function_to_model's non-leave path (Site C), which now flattens then-body as straight-line assignments instead of emitting a ConditionalBlock with a constant-true condition. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The THEN_LIVE case in Site C's non-leave path was flattening body
assignments into the outer scope using the outer var_map/subst, which
let block-local variables escape their scope. This caused three bugs:
1. Block-local variables (e.g. `let usr$tmp` inside `if 1 { ... }`)
leaked into the outer scope, silently producing wrong models
2. Block-local pointer variables used by subsequent mstore went
undetected instead of raising "non-constant address"
3. Block-local binders with names matching generated model functions
could cause naming collisions in Lean emission
Fix by processing the body in branch-local copies of var_map/subst/
const_locals (same pattern as _process_conditional_branch), then
emitting only assignments to variables that existed in the outer scope.
Also add an out-of-scope variable check in _process_assignment_into:
after substitution and renaming, any Var not in emitted_ssa_names is
a reference to a variable from a closed scope.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the branch-copy-plus-filter approach with _lower_live_branch,
a dedicated helper that models two scopes during lowering:
- Outer mutable state (var_map, SSA counters, emitted names, constant
facts): outer writes go through the normal SSA machinery immediately,
so reassigned variables get fresh binders (x_2, not a dropped x).
- Block-local overlay: names introduced by `let` inside the branch
are parked in var_map/subst for visibility to later statements in
the same block, then cleaned up at block exit.
Scope survival is decided by source-level binding identity: a target
already in var_map or subst is an outer write; an unknown target is a
block-local declaration.
Fixes three bugs from the prior approach:
1. Outer writes to SSA-renamed variables were dropped because the
branch-local base name didn't match the outer SSA name.
2. Constant facts from outer writes inside the branch were lost,
breaking downstream mstore address resolution.
3. ELSE_LIVE in the leave path used the same unsafe straight-line
pattern; now shares _lower_live_branch with THEN_LIVE.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The fundamental problem: _lower_live_branch inferred declaration-vs-
assignment from name visibility (s.target in outer_known), which breaks
when a `let` inside a taken branch shadows an outer binding with the
same Yul name.
Add `is_declaration: bool` to PlainAssignment and set it in the parser's
_parse_let method. Propagate through alpha-rename, inline_calls body
processing, and block substitution.
_lower_live_branch is now driven by is_declaration:
- Declaration (True): always block-local. The processed expression
goes into subst for visibility to later statements in the block,
then is removed at block exit. Shadowed outer subst entries are
saved and restored.
- Reassignment (False): writes the outer binding via normal SSA.
Parser constant-fold flattening (Site A) also gains block scoping via
_flatten_scoped_block: when flattening constant-true if or constant-
switch live branches, declarations are substituted away and only
reassignments survive to the outer scope.
Fixes:
- `let x` inside constant-true block no longer overwrites outer x
- Block-local real vars available to later outer writes in same block
- Constant-switch with shadowing let now respects block scope
- switch-with-leave shadowing test now passes (bonus)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
is_declaration marks when a name enters the local scope, but the binding's lifetime extends to block exit. A later `x := 2` after `let x := 1` in the same block must update the local binding, not escape to the outer scope. Both _flatten_scoped_block (parser constant-fold) and _lower_live_branch (model lowerer) now maintain a block_locals set that accumulates names from declarations. Subsequent reassignments to names in that set update the local binding instead of emitting to outer scope / outer SSA. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add `map_stmt` as the canonical structural NStmt mapper in norm_walk.py,
replacing three near-identical hand-rolled dispatch chains (_freshen_stmt,
_subst_stmt, translator.rw_stmt). Add `for_each_model_expr` and
`map_model_expr` to model_helpers.py, replacing four duplicated Expr
traversals.
Move dead-branch elimination out of norm_constprop into norm_simplify as
the single canonical implementation. Unify the two uniquification
strategies in restricted_names.py to a consistent `_{n}` suffix pattern.
Remove dead code: unused imports (for_each_expr, validate_ident,
Protocol), unused defaults (FunctionModel param/return names),
unreachable branches, no-op exception handlers, redundant double
validation, unnecessary variable aliases, and the SymbolIdAllocator
Protocol. Privatize functions that had no external callers.
Add assert_never to non-exhaustive dispatch chains in restricted_eval.py
and selection.py. Fix SymbolTable.declare leaked loop variable and remove
unused span parameter from SymbolTable.lookup. Fix misleading docstrings
in model_ir.py, restricted_ir.py, and norm_inline.py.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ions Replace the monolithic ParseError with a TranslationError base class and phase-specific subclasses (ParseError, ResolutionError, SelectionError, LoweringError, ValidationError, EmissionError) so integrating code can catch errors by pipeline phase. Dissolve norm_optimize_shared.py: move const_value, const_truthy, and simplify_ite into norm_walk.py; inline sequential_block into its only consumer (norm_constprop.py). Replace the custom FrozenMap class with types.MappingProxyType. Remove the duplicate _try_const helper from norm_inline (use const_value from norm_walk). Cache _SyntaxFunctionInfo.key/top_level_key as fields instead of recomputing on every access. Short-circuit expr_contains to return on first match. Rename _is_known_effect_call to _is_recognized_expr_stmt for accuracy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add shared walker modules for restricted IR (restricted_walk.py) and syntax AST (yul_walk.py), then refactor consumers to use them. Simplify max_symbol_id via for_each_stmt_expr, replace direct _FactEnv field mutation with update_from, and add EVM-correct div/mod identity folds (div-by-zero yields 0, mod(x,x) yields 0). Remove unused simp arguments in Sqrt512Proof to fix Lean linter warnings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract generic map_expr, for_each_expr, and expr_contains into expr_walk.py using structural field dispatch (hasattr + dataclasses.replace). All three IR walker modules re-export from the shared implementation. Remove mload from recognized expression-statement patterns in the function classifier so bare mload(x) statements are flagged. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.