Skip to content

cargo-test (full): aliased native-class import loses native methods (aliased_native_class_import, 2 tests) #8222

Closed

Description

@proggeramlug

Found by the first tier=full CI run in weeks (run 31935729773; the suite was dark behind tag-gating since ~July 4). Full cargo test --release --workspace fails 2/2463:

  • crates/perry/tests/aliased_native_class_import.rs::aliased_net_socket_keeps_native_methods
  • crates/perry/tests/aliased_native_class_import.rs::aliased_matches_unaliased_for_native_class

import { Socket as Sk } from "net"; new Sk() resolves s.connect/s.write/s.destroy/s.end as undefined instead of function — an aliased import of a native base class loses its native-method dispatch (the CLAUDE.md "native base-class subclassing keyed on a literal name" weak area, aliased-import shape).

Per-PR cargo-test is --lib --bins only, so this can only go red in the sweep/full tiers (main-gate/full-suite-gate), where it now does. Repro: cargo test -p perry --test aliased_native_class_import.

Activity

  1. proggeramlug commented on Aug 17, 2026

    @proggeramlug
    ContributorAuthor

    The alias is a red herring — the unaliased form is equally broken

    aliased_matches_unaliased_for_native_class fails on its second assertion, not the first:

    assertion `left == right` failed
      left: "undefined undefined undefined\n"
     right: "function function function\n"
    

    assert_eq!(aliased, unaliased) passes — both spellings agree, and both are wrong. So this is not an aliased-import bug and not the "native base-class keyed on a literal name" weak area; chasing the alias-resolution path will find nothing.

    Minimal repro, no cargo test needed:

    import { Socket } from "net";          // note: NOT aliased
    const s: any = new Socket();
    console.log(typeof s, typeof s.connect, JSON.stringify(Object.keys(s)));
    node : object function []
    perry: object undefined []
    

    Object.keys(s) is [] even when it works — Socket methods are never own properties. They resolve through the property-get path keyed on the binding's ("net","Socket") native-class type (perry-codegen/src/expr/property_get_names.rs → is_net_socket_method_name). There is deliberately no "Socket" arm in lower_builtin_new. So the failure is that the binding loses its native-class type, not that construction builds the wrong object.

    Bisected

    git bisect run over the verified window, judging on connect: function vs undefined, with a coherent rebuild (perry + perry-runtime-static + perry-stdlib-static + perry-ext-net in ONE cargo invocation) at every step:

    690882d6d  #8018  GOOD  connect: function
    a11093a16  #8042  skip  (build failed)
    9c1a672ce  #8043  skip  (build failed)
    3381e1b89  #8033  BAD   connect: undefined
    

    Prime suspect: #8033 fix(codegen): require runtime evidence for local binding types. Its own diff says "Declared TypeScript types are not runtime evidence." That is exactly this mechanism — the native-class binding type is inferred, not runtime-proven, so it stopped being trusted and the property-get arm went dark.

    Caveat: #8042 (async-hooks ALS) and #8043 (nextjs lazy path modules) are skipped, not eliminated — their builds failed so the bisect could not judge them. They are implausible by subject matter, but unproven.

    Likely repair, given #8033 shipped scripts/local_binding_type_audit.py + a 549-entry allowlist: admit native-class construction as legitimate evidence rather than revert. new <NativeClass>() from a resolved native-module import is runtime evidence of the receiver's identity.

    Watch for a wider blast radius

    If a native-class binding type is no longer trusted, net.Socket is unlikely to be the only casualty — the same property-get shape backs other native classes. Worth checking net.Server, tls.Socket, http.ClientRequest in the same run rather than fixing Socket alone.

    Verified window

    The last observed-passing run is 08-12 c4b2c1c8e (aliased_net_socket_keeps_native_methods ... ok). The 08-13 run was cancelled and never ran the test, so "no FAILED line" there does not mean passing — grep for a positive ... ok, not for the absence of FAILED.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions