Skip to content

Compile event-loop metrics, predicate-narrowed catch bindings and other wrapper-reachable gaps - #1433

Merged
ctate merged 4 commits into
opt/variadic-generic-wrappersfrom
opt/trace-reachable-blockers
Oct 20, 2026
Merged

ctate merged 4 commits into
opt/variadic-generic-wrappersfrom
opt/trace-reachable-blockers

Conversation

@cramforce

Copy link
Copy Markdown
Contributor

Stacked on #1413 (variadic generic wrappers). Once wrapper bodies compile, the code behind them becomes reachable, and with it a set of independent gaps. This PR closes the general ones.

Summary

  • performance.eventLoopUtilization() (no-argument, one-argument and two-argument difference forms) and monitorEventLoopDelay() (an IntervalHistogram with enable, disable, reset, percentile, and the min, max, mean, stddev, count and exceeds readings). These work under the shipped fallback declarations and under a project's own @types/node.
    • The histogram is a numeric handle into a new runtime unit, scr_perf_loop.c. The unit samples on an unref'd interval at the requested resolution and records each interval in nanoseconds, like Node's ELDHistogram. Values land in HdrHistogram-shaped buckets (three significant figures), so the readings are the bucket values Node reports. An empty histogram reads min as 2^63−1 and mean/stddev as NaN, like Node. percentile validates Node's range and throws ERR_OUT_OF_RANGE.
    • Utilization follows Node's algorithm: zeros before the loop starts, and the one- and two-argument difference forms. Active time is the main thread's CPU time since the loop started, which differs from Node's poll-phase accounting (documented as a limitation). The emitted main marks the loop's start only in programs that read the metric.
    • Linking: the unit links only for programs that use these APIs, under a new runtime feature, perfLoop.
  • Catch bindings narrowed by a user type predicate or an assertion (if (isErrorLike(e)) e.message, TypeError & { code: unknown }) read their members through the checked-dynamic value, as JavaScript reads them. Before, these reads were SC1063.
  • ?? with a function default of a different signature (opts.tags ?? (() => ({}))). The result is the callback's own function type, and the default adapts into it. Before, the checker's merged signature caused SC1090 and would have lost the callback's result shape.
  • Optional calls on interface-typed receivers (tracer.scope().active()?.addTags(tags), s?.m(x)) dispatch like the non-optional protocol call. Before, these were SC1090.
  • any in TypeScript function types: a parameter typed any (optional or not) of a function declaration or a spelled function type is the checked-dynamic slot, as an any binding already is. A generic instantiated at any (for example a type argument inferred from an untyped forwarding call such as tracer.trace(name, () => fn.apply(this, args))) takes unknown for that parameter or result.
  • 'key' in err on Error receivers for keys other than code, message and name. The answer is true when the static class declares the key, otherwise a runtime class test over the subclasses that declare it.
  • Object.entries over a generic record parameter: keyof T extends never ? unknown : T[keyof T] resolves through the binding. Fields of different scalar types map to their union.

Forms not covered keep their refusals:

  • Pick<SomeClass, "method"> record members still don't accept class instances. Mapping them to the class would refuse object-literal values that compile today.
  • Overriding a method whose base returns any (or otherwise differs in its native ABI).

Evidence

  • New corpus programs (Node vs native, byte-identical, plain and sanitized lanes): 6390 (predicate-narrowed catch bindings), 6391 (?? function defaults), 6392 (optional protocol calls), 6393 (any parameters), 6394 (in on Error subclasses), 6395 (generic Object.entries), 6396 (generic method at any), 6397 (event-loop metrics).
  • @types/node fixtures event-loop-metrics.ts and timeout-type.ts, run by tests/harness/project-config.test.ts.
  • Zero-cost gate against Specialize variadic generic wrappers to the wrapped function's signature #1413 (scripts/zero-cost-gate.mjs, bench + self-hosted compiler + all corpus entries, sharded over three sandboxes):
    • IR, LLVM and release executables are identical for every pre-existing program. The 8 new corpus programs are refused by the baseline.
    • Compile time: bench geomean −0.07% (95% CI −0.76% to +0.68%), self-hosted compiler +0.57%.
    • An intermediate run caught three issues, each fixed and re-checked:
      • The new runtime unit added a file symbol to programs that link dynAsync. It is now its own perfLoop feature, linked only where used.
      • The T[keyof T] union widened a literal-key read in 2550-generics-keyof-pick.
      • The any parameter rule changed two object literals of any-taking lambdas and methods (checked-callback-options, 5197); it now covers only function declarations and spelled function types.
  • pnpm manifest, pnpm node-compat, pnpm node-compat:check, lint, format, pnpm test:ts7 --baselines-only, diagnostics, frontend unit tests, and the self-hosted IR build all pass. The compatibility diff marks performance.eventLoopUtilization, monitorEventLoopDelay and four Histogram methods partial, with the new tests as evidence.

🤖 Generated with Claude Code

…er wrapper-reachable gaps

Once generic wrappers compile (#1413), the code behind them is reachable.
This closes the general gaps found there:

- performance.eventLoopUtilization() and monitorEventLoopDelay()'s
  IntervalHistogram (enable, disable, reset, percentile and the min, max,
  mean, stddev, count and exceeds readings), under the fallback
  declarations and @types/node. A new runtime unit, scr_perf_loop.c, links
  only for programs that read them (the perfLoop feature): an unref'd
  sampling interval records into HdrHistogram-shaped buckets, and
  utilization measures active time as the main thread's CPU time since
  the loop started.
- Catch bindings narrowed by a user type predicate or an assertion read
  their members through the checked-dynamic value.
- `??` with a function default of a different signature takes the
  optional callback's own function type and adapts the default.
- Optional calls on interface-typed receivers dispatch like the plain
  protocol call.
- `any` parameters of function declarations and spelled function types
  are checked-dynamic slots, and a generic instantiated at `any` takes
  `unknown` there.
- `'key' in err` on Error receivers answers from the declaring subclasses.
- Object.entries over a generic record resolves
  `keyof T extends never ? unknown : T[keyof T]` through the binding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
scriptc Ready Ready Preview, v0 Oct 20, 2026 7:08am UTC

Comment thread packages/compiler/src/frontend/lowering/lower-calls.ts Outdated
Comment thread packages/compiler/src/frontend/lowering/lower-exprs.ts Outdated
Comment thread packages/runtime/src/scr_perf_loop.c Outdated
Comment thread packages/compiler/src/ir/runtime-features.ts Outdated
Inside a generic body Object.entries(t) over T extends Record<string,
unknown> types its values 'keyof T extends never ? unknown : T[keyof T]'.
An instance whose T maps to the checked-dynamic value now reads both
branches as unknown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… 'in' keys, event-loop metrics in libraries

- monitorEventLoopDelay is lowered from the builtin call dispatch, by module and member, so aliased named imports, namespace members and destructured requires all reach it. Shorthand and possibly undefined resolutions take Node's default.
- The runtime validates the resolution as Node does (an integer of at least 1) and throws a catchable RangeError with ERR_OUT_OF_RANGE.
- `'stack' in err` and Object.prototype's keys are true for every Error. The declared-member fold applies only to keys some program class declares and that built-in errors don't carry in runtime slots; other keys stay fenced.
- Library mode's async_free gate refuses the event-loop metrics, which library links leave out. They join the perf_hooks fence family.
@ctate
ctate merged commit f31dbf8 into opt/variadic-generic-wrappers Oct 20, 2026
63 checks passed
@ctate
ctate deleted the opt/trace-reachable-blockers branch October 20, 2026 07:16
cramforce added a commit that referenced this pull request Oct 20, 2026
Brings main in through the already-resolved stack merge (f4c6441: main
merged into #1413 and #1433). Main split lower-exprs.ts into
expressions/*: the review follow-up's Error 'in' keys (own 'stack',
Object.prototype keys, BUILTIN_ERROR_SLOT_KEYS gating the declared-member
fold) move to expressions/relational.ts, and the perfLoop feature keeps
main's rule table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cramforce added a commit that referenced this pull request Oct 20, 2026
…ance-error-expando

Brings main into the stack (thrown-value fixes, the lower-exprs split,
the checker helper removal). This branch's lower-exprs.ts hunks move to
expressions/relational.ts (`in` on errors), expressions/property-access.ts
(stdlib members of a structurally narrowed catch binding) and
expressions/narrowing.ts (assertions on an `any` catch binding); the
`this` blame, the `any` catch-binding read and the setPrototypeOf
feature test stay in lower-exprs.ts. The Error member read keeps both
this branch's intersection members and #1433's subclass-owned `code`.
Corpus 6407 and 6408 renumber to 6412 and 6413 (main has its own 6407
and 6408), and 6414 adds the thrown-value round trip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cramforce added a commit that referenced this pull request Oct 20, 2026
…expando

Brings in #1433's review follow-up (now merged into #1413). In `key in
err`, own `stack` and Object.prototype keys answer true, keys built-in
errors keep outside their class fields stay fenced, and every other key
keeps this branch's answer: a declaring subclass or a runtime-set own
property.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — 069545a1 Deployed Oct 20, 2026 by vercel[bot]
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.

2 participants