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
static this leaks the instance type: typeof this.instanceMethod is "function" in a static body (node: undefined) #9404
This class has no computed member. Confirmed on 757beace0 (current main, i.e. with #9386 merged), byte-compared against node --experimental-strip-types.
#9386 fixed the computed-member case by gating the computed-member lowering routes on a new FnCtx::is_static_class_this. It deliberately left the deeper cause alone, and that cause is not computed-member-specific:
static_type_of(Expr::This) (crates/perry-hir/src/type_analysis/predicates.rs:689) returns Named(class_stack.last()) in a static body — the same answer it gives in an instance body — and refine_type_from_init writes that into local_types. receiver_class_name(Expr::This) (same file, ~366) has the identical shape.
So a static body's this is typed as an instance of the class everywhere that answer is consumed.
Two known consequences
The example above: instance members resolve on the class object.
const t = this makes the receiver a LocalGet, so is_static_class_this no longer applies and it reaches the computed-member route through the metadata-only guarded_declared_class_get_candidate fallback — carrying the wrong type from local_types.
Note also that the computed-member gate is not under receiver_class_is_proven, contrary to the rule stated three lines above it in that file.
Making static_type_of / receiver_class_name honest about static this changes every consumer of that answer inside static bodies — static field reads, static-method dispatch, private brand checks. That is materially larger than a change whose job was to unblock the cc --help parity gate, which is why #9386 scoped it out deliberately rather than by oversight.
Not currently reachable from claude-code
A tokenizer scan of the cc bundle (brace-matching through strings/templates/regex/comments) found 412 static methods, 0 containing an alias-of-this that is then member-accessed. The scanner was validated by appending known instances to a copy of the bundle and finding 2/2. So the alias half is theoretical for cc today; the typeof this.instanceMethod half is a plain spec divergence reachable by any user.
Fixed by PR #9465, landed via #9472. Both defects — the static-this codegen predicates and the constructor-side prototype walk — are covered; test_static_this_is_not_an_instance_9404.ts. Verified on main 0a1c137d90 with a fresh build: fixture byte-identical to node --experimental-strip-types (stdout, stderr, and exit code).
Residual (the call form this.m() resolving the instance vtable method) is documented in the fixture's comment and was deliberately not forced into this fix.
Activity
fix: static this is the constructor; class name/toString/inspect repo…
proggeramlug commentedon Sep 2, 2026
Fixed by PR #9465, landed via #9472. Both defects — the static-
thiscodegen predicates and the constructor-side prototype walk — are covered;test_static_this_is_not_an_instance_9404.ts. Verified on main0a1c137d90with a fresh build: fixture byte-identical tonode --experimental-strip-types(stdout, stderr, and exit code).Residual (the call form
this.m()resolving the instance vtable method) is documented in the fixture's comment and was deliberately not forced into this fix.fix(decorators): instance-member decorators receive Class.prototype; …