You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Kotlin scope functions (run, let, apply, also, with) bind to unrelated Java methods #155
Kotlin's scope functions are standard-library calls, but ripwire resolves calls by name. Once Kotlin
and Java share one call graph, x.run { … } binds to a same-named Java run() whenever the Kotlin
side defines no run of its own. apply, let, also and with do the same. Removing these
false edges is what makes --callers, --impact and the ranking trustworthy in mixed Java/Kotlin
codebases — Android above all.
The evidence
On square/retrofit, --callers=run goes from 2 on main to 5 with the Kotlin support PR
(#126). Two of the three new rows are .run { User(name) } lambdas in Kotlin serialization tests —
false edges. The third is a real block.run() on a Runnable, which the fix must keep.
The kit records why the obvious levers cannot fix this on their own: arity, receivers,
own-language-first, and the external-name veto are each examined and ruled out, with three
reproduction trees and their recorded output.
Size
Medium. One extracted per-call-site fact carried through the reference cache record (with parser
and cache version bumps, their mirrors and pins), a small sorted name table in src/externalnames.h, a candidate filter beside keepOwnJvmLanguageCandidates, a new section in test/kotlincheck.sh, and measurements on retrofit, nowinandroid and ktor.
prompts/help-wanted/kotlin-scope-functions.md is a self-contained prompt for a coding agent. It
covers the retrofit rows, how Kotlin calls are captured and resolved, the three reproductions, the
design space (the trailing-lambda fact, the name table, the rule, where refused sites go, and what
stays out of scope), the non-negotiables, red-first acceptance arms, the known traps, and what the
fix PR's description must contain. Like every prompt in prompts/, it ends by writing a plan and
stopping — a maintainer agrees the plan before any code is written.
Thanks @llvm-x86. It's yours, and after #423 we're glad to have you on another one. The plan is right in substance: the trailing-lambda fact, a name table and a site-keyed rule, with name-only vetoes rejected. Go ahead, with the updates below. The kit was written on 09-11, and main has moved since.
Your three questions
Is the plan right? Yes, with the placement change in "Where the rule goes" below.
Cache-version bump? Yes, it's needed. The record changes shape, so kCacheVersion + kIngestCacheVersionMirror + kMinRefRecordBytes move together, and the extraction change moves kParserVer + kIngestParserVerMirror.
Wait for a free slot? No. Use placeholder values above everything live and say so in a comment. We renumber at landing, as we did for feat(situ,test-gate): start the caller walk from the changed symbols, not the changed files #423. Train 34, now in CI, takes main to kParserVer 226 and kCacheVersion 40, and train 35 has reserved values after that. So start at kParserVer 228 and kCacheVersion 41, and put the qschemetrip re-pin in its own commit so it's cheap to redo.
What changed on main since the kit
kMinRefRecordBytes is now 60 (3×u32 + 12×u8 + 9×str), not 39. Your fact is one more u8, written and read beside memberCall / forwarded in writeRef/readRef, with RawRef → Reference copied in ingest_model.h.
externalnames.h now has a builtin-method family: kPythonBuiltinMethodNames, kJsBuiltinMethodNames, kRubyBuiltinMethodNames, kCppStdMemberNames and kRustStdMemberNames. Each table is generated by one command written in its provenance comment, and each has a sortedness static_assert. Give the Kotlin table the same treatment: cite the Kotlin stdlib docs page and version for the five scope functions, so the list can be regenerated and diffed, not hand-tuned.
RawRef::memberCall (FE-A) now records "written on a receiver" for Go/JS/TS/Rust. Kotlin isn't in it. You don't need it for this fix, but it's the natural home for the receiver gap you'll disclose (tree C).
Where the rule goes: fit it into an existing family, not a new standalone rule docs/ARCHITECTURE.md (a seeds → hops → walks section lands with train 34) and CONTRIBUTING's feature-family map both push toward one mechanism per family, with languages differing by data. On main, three neighbours already cover "a call by name that cannot reach the in-tree target":
FE-A FalseEdgeRules (graph.h, gate test/falseedgecheck.sh). It removes candidates the call cannot reach by the language's own rules. When nothing survives, the site is external= with proof (one C external census row), and unresolved= without proof. It never declines and never retargets. Its STATED FLOORS header lists Kotlin as untouched today.
BuiltinMethodGate (Python/JS/TS/Ruby): it declines a builtin-named member call unless the caller's file gives evidence for the target.
StdMemberGate (C++/Rust): one row per language. Its comment says why Java/Kotlin get no name row, because in-tree types implement add/size on purpose. That is the same reason your plan rejects a name-only veto.
Your rule is FE-A-shaped. The trailing lambda is a call-site proof that this is the stdlib scope function, not a guess from the name. So please implement it as a Kotlin arm of FalseEdgeRules:
Then update FE-A's STATED FLOORS header and the #kotlin-extraction paragraph of docs/ARCHITECTURE.md with the floors you leave: receiver typing, SAM conversion, and parenthesised lambdas if you exclude them. If FE-A's structure fights you, say so in the plan reply before you write code. Keeping it beside keepOwnJvmLanguageCandidates is acceptable, but then the PR should say why it isn't an FE-A arm.
One more thing to keep in mind: #59/#163 is the same false-edge class for TypeScript. If the table hangs off a per-language row (the StdMemberRow pattern) keyed on a call-site fact, TypeScript can reuse it later as data, not as a second rule.
Base on main after train 34 merges (a few days; it touches src/model.h, src/ingest_cache.h and src/quality.h). Starting now on current main is fine too; just merge main before you open the PR.
The acceptance arms and measurements in the kit stand as written, red on a named main commit, plus test/falseedgecheck.sh if you take the FE-A route. Looking forward to the plan's numbers on retrofit, nowinandroid and ktor.
Activity
joyful-ii-V-I commentedon Sep 16, 2026
Related, same failure class in TypeScript: #59 (built-in member calls binding unrelated functions) and its first step #163.
llvm-x86 commentedon Oct 24, 2026
I want to take this issue. I need a go-ahead on the plan first, as the kit asks.
I reproduced the false edge on current main.
--callers=runon the kit's tree gives two callers.build(kt/Use.kt:3, via=name) is false.kickis true.Plan:
RawRef,Referenceand the cache record.kCacheVersion,kParserVerand their mirrors. Re-pintest/qschemetrip.hashwith a RE-PIN LOG entry.run,let,apply,alsoandwithinsrc/externalnames.h.keepOwnJvmLanguageCandidatesinsrc/graph.h. A trailing-lambda call to one of these names does not bind to a Java method. Refused sites get a disposition under the fix(resolver): a declined call no longer reads as "no caller exists" — declined=, declined_calls=, and every call accounted for #136 conservation line.test/kotlincheck.sh.Questions:
I will write no code until a maintainer agrees.
joyful-ii-V-I commentedon Oct 24, 2026
Thanks @llvm-x86. It's yours, and after #423 we're glad to have you on another one. The plan is right in substance: the trailing-lambda fact, a name table and a site-keyed rule, with name-only vetoes rejected. Go ahead, with the updates below. The kit was written on 09-11, and main has moved since.
Your three questions
kCacheVersion+kIngestCacheVersionMirror+kMinRefRecordBytesmove together, and the extraction change moveskParserVer+kIngestParserVerMirror.kParserVer226 andkCacheVersion40, and train 35 has reserved values after that. So start atkParserVer228 andkCacheVersion41, and put theqschemetripre-pin in its own commit so it's cheap to redo.What changed on main since the kit
kMinRefRecordBytesis now 60 (3×u32 + 12×u8 + 9×str), not 39. Your fact is one moreu8, written and read besidememberCall/forwardedinwriteRef/readRef, withRawRef→Referencecopied iningest_model.h.externalnames.hnow has a builtin-method family:kPythonBuiltinMethodNames,kJsBuiltinMethodNames,kRubyBuiltinMethodNames,kCppStdMemberNamesandkRustStdMemberNames. Each table is generated by one command written in its provenance comment, and each has a sortednessstatic_assert. Give the Kotlin table the same treatment: cite the Kotlin stdlib docs page and version for the five scope functions, so the list can be regenerated and diffed, not hand-tuned.RawRef::memberCall(FE-A) now records "written on a receiver" for Go/JS/TS/Rust. Kotlin isn't in it. You don't need it for this fix, but it's the natural home for the receiver gap you'll disclose (tree C).Where the rule goes: fit it into an existing family, not a new standalone rule
docs/ARCHITECTURE.md(a seeds → hops → walks section lands with train 34) and CONTRIBUTING's feature-family map both push toward one mechanism per family, with languages differing by data. On main, three neighbours already cover "a call by name that cannot reach the in-tree target":FalseEdgeRules(graph.h, gatetest/falseedgecheck.sh). It removes candidates the call cannot reach by the language's own rules. When nothing survives, the site isexternal=with proof (oneC externalcensus row), andunresolved=without proof. It never declines and never retargets. Its STATED FLOORS header lists Kotlin as untouched today.BuiltinMethodGate(Python/JS/TS/Ruby): it declines a builtin-named member call unless the caller's file gives evidence for the target.StdMemberGate(C++/Rust): one row per language. Its comment says why Java/Kotlin get no name row, because in-tree types implementadd/sizeon purpose. That is the same reason your plan rejects a name-only veto.Your rule is FE-A-shaped. The trailing lambda is a call-site proof that this is the stdlib scope function, not a guess from the name. So please implement it as a Kotlin arm of
FalseEdgeRules:go);external=with proof, which also satisfies fix(resolver): a declined call no longer reads as "no caller exists" — declined=, declined_calls=, and every call accounted for #136's conservation line with no new disposition.Then update FE-A's STATED FLOORS header and the
#kotlin-extractionparagraph ofdocs/ARCHITECTURE.mdwith the floors you leave: receiver typing, SAM conversion, and parenthesised lambdas if you exclude them. If FE-A's structure fights you, say so in the plan reply before you write code. Keeping it besidekeepOwnJvmLanguageCandidatesis acceptable, but then the PR should say why it isn't an FE-A arm.One more thing to keep in mind: #59/#163 is the same false-edge class for TypeScript. If the table hangs off a per-language row (the
StdMemberRowpattern) keyed on a call-site fact, TypeScript can reuse it later as data, not as a second rule.Two coordination notes
test/kotlincheck.shis also being edited in feat(resolvers): order every multi-definition name answer with one tower; disclose splits in --lsp (RIPWIRE-RUBY C) #432 (a--lego=Markertie-break fix). Take the next free § when you open the PR, and expect a small rebase.src/model.h,src/ingest_cache.handsrc/quality.h). Starting now on current main is fine too; just merge main before you open the PR.The acceptance arms and measurements in the kit stand as written, red on a named main commit, plus
test/falseedgecheck.shif you take the FE-A route. Looking forward to the plan's numbers on retrofit, nowinandroid and ktor.fix(kotlin): a trailing-lambda scope function never binds a Java meth…
test(qschemetrip): re-pin for the redhat-et#155 placeholder versions
test(qschemetrip): re-pin for the redhat-et#155 placeholder versions
fix(kotlin): a trailing-lambda scope function never binds a Java meth…
test(qschemetrip): re-pin for the redhat-et#155 placeholder versions
test(qschemetrip): re-pin for the redhat-et#155 placeholder versions
fix(kotlin): keep a Kotlin receiver's proof for trailing-lambda scope…
refactor(kotlin): reuse isStrictlySortedTable and firstChildOfKind (r…
chore(docs): drop the regenerated COMMANDS.md and showcase capture (r…
Merge PR redhat-et#438 (@llvm-x86, Fixes redhat-et#155): Kotlin scope…
Rust: re-parse a file with its precise-capturing use<..> bounds blanked
Merge lane/queue-hop (Python queue hand-off hops) into integration/tr…