fix(eliminate): skip inlining when free vars in Init are moved before substitution site#57
Merged
Conversation
… substitution site Fixes #56. Before this change, a candidate like: let display = var.clone(); ... var moved into struct field ... devlog!(..., display, ...); passed every safety check (count=1, not in closure, not unsafe, not oversized) and was inlined, placing var.clone() after the move, which produces E0382. The new IsFreeVarSafe function in Safe.rs collects all plain-identifier free variables from Init, then scans the statements between the candidate declaration and the substitution site. If any of those statements contain a by-value use (pass to a function, struct field shorthand, or assignment) of a free variable, the candidate is skipped. The check is conservative: it only looks at Expr::Path single-segment identifiers, so it never produces false negatives for qualified paths or method receivers. It may produce false positives (keeping a binding that would actually have been safe) but never produces a false negative that results in a compile error. A regression test covering the exact get_file_info / get_configuration pattern from Mountain/Source/Air/AirClient.rs is included in Inline.rs.
NikolaRHristov
added a commit
that referenced
this pull request
May 25, 2026
…od.rs PR #57 added RunPreserve() to Transform/mod.rs on Current. PR #58 branched before that merge, so its mod.rs is the pre-#57 version and conflicts. Forward-port Current's mod.rs (Run + RunPreserve, no CountReferences calls) onto this branch. Count.rs and Inline.rs already have the correct 3-tuple CountReferences signature and InLoop guard — no changes needed there. After this commit the branch compiles cleanly against Current.
NikolaRHristov
added a commit
that referenced
this pull request
May 25, 2026
…nLoop guard Current's Inline.rs (post-#57) added IsFreeVarSafe/FindSubstSite which #58's branch did not have. #58's branch added the InLoop guard and 3-tuple CountReferences destructure which Current did not have. This commit is the true three-way merge: - Keeps #57's full structure: section banners, FindSubstSite, IsFreeVarSafe, all test sections including free-variable-move-safety tests. - Applies #58's changes: (RefCount, InClosure, InLoop) 3-tuple destructure, `|| InLoop` guard in EliminateBlock, loop-body tests. - Updates FindSubstSite's internal CountReferences call to discard the two new booleans with (Count, _, _) to match #58's Count.rs signature. - Adds #58's loop-body tests alongside #57's free-var tests.
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.
Fixes #56.
Problem
A candidate like:
passed every existing safety check (count=1, not-in-closure, not-unsafe, not-oversized) and was inlined. The result placed
path.clone()inside thedevlog!macro token stream afterpathhad already been moved by value intoFileInfoRequest, producingE0382.Fix
Safe.rs
Adds
IsFreeVarSafe(Init, StmtsBetween). It:FreeIdentCollectorto collect all plain single-segment identifier free variables.MoveDetectorwhich flags a by-value use of that identifier in a call argument, struct field, let RHS, or assignment.MoveDetectordeliberately skips&/&mutreference expressions (borrows, not moves) and method-call receivers (almost always borrowed self).Inline.rs
Adds
FindSubstSite(Stmts, Target)which returns the index of the first statement in StmtsAfter that contains a reference to the candidate. This bounds theStmtsBetweenslice to only the statements that execute between the declaration and the substitution point.EliminateBlocknow callsIsFreeVarSafe(&Candidate.Init, StmtsBetween)after the count check and beforeSubstituteRef. A false result skips the candidate.Tests added
Safe::Tests::FreeVarUnsafeWhenMovedIntoStructField- struct field shorthand moves the varSafe::Tests::FreeVarUnsafeWhenMovedIntoCall- by-value function argumentSafe::Tests::FreeVarSafeWhenVarOnlyBorrowed- reference: not a moveSafe::Tests::FreeVarSafeWhenDifferentVarMoved- unrelated var moved, target is safeSafe::Tests::FreeVarUnsafeWhenMovedIntoRpcRequest- exact AirClient patternInline::Tests::DisplayCloneKeptWhenOriginalMovedFirst- get_file_info regressionInline::Tests::SectionDisplayCloneKeptWhenOriginalMovedFirst- get_configuration regressionInline::Tests::DisplayCloneInlinedWhenOriginalNotMoved- false positive guard