Fix accessor vtable dispatch through base-typed references#289
Merged
Conversation
Closes the "much bigger gap" flagged alongside super-accessor-crash-fix (PR #278): overriding a get/set accessor pair in a subclass and accessing it through a BASE-TYPED reference (an ordinary upcast, not `super`) always resolved statically to the base's own accessor instead of dispatching virtually to the override - a real correctness gap (this is standard OOP virtual dispatch, not an edge case), unlike ordinary methods which already dispatch through the vtable correctly. Root cause: an accessor's backing get/set functions are ALREADY registered as ordinary MethodInfo entries in ClassInfo::methods (same list ordinary methods use) with a real vtable slot assigned via registerClassMethodMember/getVirtualTable - accessors are virtual by default exactly like methods. But ClassAccessorAccess never checked isVirtual at all: it unconditionally built ThisAccessorOp with a hardcoded FlatSymbolRefAttr naming THIS class's own get/set symbol, completely bypassing the vtable that was already being correctly built. Fix: mirror ClassMethodAccess's own isVirtual/isStorageType check (isStorageType is true for `super.<accessor>`'s raw-struct thisValue, so super access is naturally excluded from virtual dispatch, same as methods) and route through the vtable via VirtualSymbolRefOp (the same op static virtual method access already uses) feeding into the existing "indirect" (function-value) ThisIndirectAccessorOp, instead of the hardcoded-symbol ThisAccessorOp. Checked before the isDynamicImport branch, matching ClassMethodAccess's ordering - the vtable's own construction already resolves dynamic-import-owned slots, so virtual dispatch through it is correct regardless of module boundaries. One regression found and fixed during verification: getThisRefOfClass only narrows `thisValue` for the isSuperClass case, so an ordinary access whose static type is still a wider union (e.g. `T | null` narrowed only at the type-info level, never cast at the value level - hit by 00iterator_bug.ts's spread-operator-generated `.length` access) reached the new vtable lookup with an un-narrowed union type, which mlirGenPropertyAccessExpression's PropertyRefOp rejects (unlike the old AnyType-typed ThisAccessorOp it replaces). Fixed by explicitly casting to classInfo->classType before the vtable lookup. New same-file regression test 00class_accessor_virtual.ts (compile + jit tiers). Also closed the historical NOTE in import_class_accessor.ts documenting this exact gap as deliberately untested - now asserts virtual dispatch through a base-typed reference works, cross-module, across all 3 tiers (plain, -shared compile, -jit -shared). Full suite: 810/810 green. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.
Summary
super-accessor-crash-fix(Fix cross-module super-accessor crash and add class coverage #278): overriding a get/set accessor pair in a subclass and accessing it through a BASE-TYPED reference (an ordinary upcast, notsuper) always resolved statically to the base's own accessor instead of dispatching virtually to the override - a real correctness gap (standard OOP virtual dispatch), unlike ordinary methods which already dispatch through the vtable correctly.MethodInfoentries inClassInfo::methods(same list ordinary methods use) with a real vtable slot assigned viaregisterClassMethodMember/getVirtualTable- accessors are virtual by default exactly like methods. ButClassAccessorAccessnever checkedisVirtualat all: it unconditionally builtThisAccessorOpwith a hardcodedFlatSymbolRefAttrnaming THIS class's own get/set symbol, completely bypassing the vtable that was already being correctly built.ClassMethodAccess's ownisVirtual/isStorageTypecheck (isStorageTypeis true forsuper.<accessor>'s raw-structthisValue, so super access is naturally excluded from virtual dispatch, same as methods) and route through the vtable viaVirtualSymbolRefOp(the same op static virtual method access already uses) feeding into the existing "indirect" (function-value)ThisIndirectAccessorOp, instead of the hardcoded-symbolThisAccessorOp. Checked before theisDynamicImportbranch, matchingClassMethodAccess's ordering.getThisRefOfClassonly narrowsthisValuefor theisSuperClasscase, so an ordinary access whose static type is still a wider union (e.g.T | null, narrowed only at the type-info level, never cast at the value level - hit by00iterator_bug.ts's spread-operator-generated.lengthaccess) reached the new vtable lookup with an un-narrowed union type, whichmlirGenPropertyAccessExpression'sPropertyRefOprejects (unlike the oldAnyType-typedThisAccessorOpit replaces). Fixed by explicitly casting toclassInfo->classTypebefore the vtable lookup.Test plan
00class_accessor_virtual.ts(compile + jit tiers)import_class_accessor.tsdocumenting this exact gap as deliberately untested - now asserts virtual dispatch through a base-typed reference works, cross-module, across all 3 tiers (plain,-sharedcompile,-jit -shared)ctest -C Debug -j8— 810/810 green (808 previously green + 2 new; caught and fixed a00iterator_bug.tsregression mid-development, see commit message)🤖 Generated with Claude Code