Repository navigation
Conversation
…les it The last line of `bun check --aot` and of `bun build --compile --aot` counted every file whose types the build does not verify. That took in files that the build does not compile: a file that nothing imports as code, a file that is imported for a type, and a file that only an `import()` that never runs names. `count_files` now asks `files.is_compiled` before it counts a demoted file, as its loop does for every other count. The list of demoted files and the roles that trust reads stay as they are.
|
Updated 11:42 AM PT - Nov 9th, 2026
✅ @robobun, your commit 4f7093316515102737b12692341320485dcf599d passed in 🧪 To try this PR locally: bunx bun-pr 48379That installs a local version of the PR into your bun-48379 --bun |
|
Status: ready for review. The diff is green in CI.
How I reproduced it on main 2596ac5, with the canary build of that commit: With this branch neither line has those words. With |
|
No actionable comments were generated in the recent review. WalkthroughThe AOT report now counts demoted files only when they are compiled. New tests cover file counts for nested TypeScript configurations, different import types, and files without type checks. The documentation clarifies which TypeScript files contribute to the compiled-without-types count. ChangesAOT file-count reporting
Possibly related PRs
Suggested reviewers: Merge Risk:
|
|
This review needs no change. I checked the two points that it raises.
|
Problem
bun check --aotandbun build --compile --aotcounts files that the build does not compile. Abun initproject withweb/tsconfig.json={}and three files that nothing imports printsNo type errors in 1 file, 3 files compiled without types.count_files(src/aot/report/file_counts.rs:83) counted each entry ofRoleDecision.demoted, compiled or not.Fix
count_filesasksfiles.is_compiledbefore it counts a demoted file, as its loop does. The list and the roles do not change.import type, animport()that never runs, or no code reaches.test/aot/summary-line.test.ts(5 new cases), 1 unit test, 3 IR files. All fail on main. Also ran thebun_aotunit and IR suites.Background
// @ts-nocheck, or whosetsconfig.jsondiffers inallowJs,libor eight other options. Its types become guesses.tsconfig.jsonwithoutincludenames each file below it. The build reads those files and compiles only what the program imports.decide_roles, which makes the list.Queries::file_trustreads the demoted role of files that are not compiled.Downsides
Notes
Origin. An automated fuzz report in the team chat. There is no GitHub issue. Reproduced on main 2596ac5 with the canary build of that commit.
Who sees it. The
tsconfig.jsonthatbun initwrites has"allowJs": trueand noinclude. A folder whose owntsconfig.jsondoes not extend it and does not setallowJsneeds another program. This includes a package of a monorepo. Two packages of 20 files each printNo type errors in 1 file, 40 files compiled without types. The emitted program is the same with and without those folders.History. The filter dates from #45631, when the list held only
// @ts-nocheckfiles. #47942 added thetsconfig.jsoncause to the list and did not touch the filter.What main prints, and what this branch prints (
bun check --aot --no-pretty --explain=statistics, debug builds of the same tree):SourceFilesDemotedtsconfig.json1 fileweb/tsconfig.jsonwith a DOMlib, nothing imported1 file{}1 fileimport type1 file2 files, 1 file compiled without types1 file// @ts-nocheck: one value import, oneimport type4 files, 1 file compiled without types.tsfile of the folder as{ type: "text" }2 files3 files, 1 file compiled without typesNo other line of
--explain=statisticschanges in these 9 projects (time lines left out).What the words mean. With nothing of the folder imported, the
--aot-outprogram is the same with and withoutweb/tsconfig.json(0 of 2,584 bytes differ), and main prints3 files compiled without types. With one file of a folder imported for a value,web/tsconfig.json={}against{"extends":"../tsconfig.json"}changes 125 of 99,384 bytes of the program. So the words now show when compiled code lost its types, and only then.The text rows. A file embedded as text is a data module. Its tree role is
Inferred, sofind_unverified_filesnever lists it. What main counts there is the copy of the same path that thetsconfig.jsonnames and that nothing imports as code. When notsconfig.jsonnames the file, main prints the right line for a text import. Sois_compiledalone decides, and the fix needs no test of the kind of file.Why the list stays.
find_unverified_files(src/sema/driver/compile/roles.rs) lists each file of the program whose types the build does not verify, compiled or not.decide_rolessets the role of each toInferred.Queries::file_trust(src/sema/check/aot/trust.rs) reads that role for the file that declares a type. So a type from a file that is only read stays a guess. The new IR testfiles-that-are-not-compiled.tshas aCHECKline forflags=[IsExpected]on such a type. The line does not hold when the innertsconfig.jsonmakes the same program.Docs.
docs/compiler/aot.mdxsaid that the last line counts the files of such a folder. It now says that the line counts the TypeScript files that the build compiles there. A.jsfile of the folder is compiled and never counted: withweb/a.tsandweb/j.jsboth imported, the line says3 files, 1 file compiled without types.Measurements (without = main 2596ac5, with = this branch):
RoleDecision.demoted: 0 rounds with nothing unverified, 3 for the repro, 40 for 2 x 20 files.cargo build -p bun_aot --lib --release:-O3, one codegen unit, no LTO), withllvm-size,llvm-nm,llvm-objdump: text 5,066,837 -> 5,066,805 bytes, data unchanged.count_filesis inlined intoBuild::check_and_lower::{closure#0}: 5,301 -> 5,269 bytes, 1,221 -> 1,226 instructions.count_filescall (gdb hit counts on__rust_alloc,__rust_alloc_zeroed,__rust_realloc,__rust_dealloc, debug builds): 3 alloc, 6 realloc, 3 dealloc for the application project and 3, 1, 3 for the runtime project, without and with.--aot-outprogram, debug builds without and with: 0 bytes differ for 6 inputs. The// @ts-nocheckand the nestedtsconfig.jsonvariant of a type-only file still differ from the plain one in 3 bytes each, as on main.STATISTIC: SourceFilesDemotedline change, and 0 unit assertions.Suites.
test/aot/summary-line.test.ts: 14 of 14 pass. The 5 new cases fail on the main release build and on a debug build of main.bun_aotunit tests (cargo test -p bun_aot --lib): 3290 of 3290 pass. The new one fails without the source change (2 where 1 is asserted).cargo test -p bun_aot --test ir_tests): 2602 of 2603 files pass.lower/modules/names-that-can-only-be-paths/loads.cjsfails with and without the change on my machine, because it lists thenode_modulesof the checkout. Without the source change, the 2 new files fail (SourceFilesDemoted is 3andis 2), and so doesprograms/modules/file-another-tsconfig-imports-nothing.tswith its 2 newSTATISTIClines (is 3).Self-review. The first version of this branch tested the kind that the loop gave the file (
SourceFilesInferred), to leave out a.tsfile that is embedded as text. The review showed that such a file is never on the list, so the test decided nothing. The fix is now the oneis_compiledtest, and the unit test of that state is gone. The review also asked for the twoSTATISTIClines infile-another-tsconfig-imports-nothing.ts, for the word "TypeScript" in the docs clause, and for the first two lines below. Not taken: a new docs sentence on how to make the build check such a folder again (make bothtsconfig.jsonfiles agree on the ten options).Not changed here.
--explainattributes nothing to a file that is compiled without types.DemotionCausehas one variant for// @ts-nocheckand for atsconfig.jsonthat differs.roles.rs:137: a type-only file of a package that names theaotcondition, under its owntsconfig.json, is still demoted, becauseis_aot_sourceexists for compiled files only. This is a trust question and needs its own tests.RoleDecision.demotedstill lists files that are not compiled. No other reader counts them.// @ts-nocheckalso gives the words.[human-review] gate passed · iteration 0 · 7 files touched
fails on main (without fix)
passes on PR (with fix)
diff hotspot
gate history · 1 passed · 0 rejected · iteration 0
evidence per changed file