Skip to content

fix(hy-check): preserve generic struct type arguments across shape and loan checks - #779

Closed
aazminu wants to merge 1 commit into
limitlessprojects:mainfrom
aazminu:fix/generic-struct-type-arg-erasure-772
Closed

aazminu wants to merge 1 commit into
limitlessprojects:mainfrom
aazminu:fix/generic-struct-type-arg-erasure-772

Conversation

@aazminu

@aazminu aazminu commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #772

Checker::named_shape_depth previously stripped generic type arguments from instantiated struct types (box[[]int] -> TypeShape::Struct(Some("box"))), and downstream shape, sendability, loan, freeze, and nil/zero-value checks looked up raw un-substituted field types in self.structs.get(base) where t resolved to TypeShape::Unknown:

  • Tier-0 alias freeze (HYB-E3003) & non-owned move (HYB-E3004): shape_may_share_at and field_shape returned false / Unknown on box[[]int], allowing aliases of generic structs holding slices/maps to mutate shared storage after move into a task and allowing return move b.v on non-owned parameters.
  • const deep immutability (HYB-E3006): shape_is_proven_pure_value and field_shape failed to classify const cb box[[]int] copies and field projections as const-backed reference aliases.
  • Task spawn / channel send / use-after-move (HYB-E3001 / HYB-E3002 / HYB-E3003): shape_may_share, shape_carries_handle, shape_holds_closure, and shape_holds_unmovable_leaf ignored substituted type arguments on generic structs.
  • Map-slot loan (HYB-E3102): &b.v["a"] on box[map[string]svc] did not resolve b.v as TypeShape::Map(...).
  • Nil safety N1 (HYB-E4001) & N3 (HYB-E4002): check_composite_nils, place_shape, and no_zero_value_reason did not substitute type parameters on generic struct fields.

Changes

  • compiler/crates/hy-check/src/shape.rs: Keep the full instantiated name in TypeShape::Struct(Some(name.to_string())) and substitute generic type arguments via struct_fields_for_sname / fn_subst_for_state in shape_may_share_at, shape_carries_handle_at, struct_has_mutex_field, field_shape, call_shape, ty_of_expr, no_zero_value_reason, and call_result_tys_with_args.
  • compiler/crates/hy-check/src/sendability.rs: Expose struct_fields_for_sname and fn_subst_for_state and use substituted field types in shape_holds_closure, shape_holds_unmovable_leaf, shape_is_const_sendable, check_composite_nils, spawn_param_shapes, and shape_may_be_elem.
  • compiler/crates/hy-check/src/loans.rs, freeze.rs, emit_guard.rs: Use struct_fields_for_sname in place_target_ty, shape_is_proven_pure_value, incomparable_rec, and struct literal constant-overflow checks, and normalize struct receiver / generic function names when querying self.fns and self.method_decls.
  • compiler/crates/hy-check/tests/generic_struct_soundness_issue_772.rs: Add regression tests covering HYB-E3001, HYB-E3002, HYB-E3003, HYB-E3004, HYB-E3006, HYB-E3102, HYB-E4001, HYB-E4002, and pure-value box[int] negative controls.

Verification

  • cargo test -p hy-check -p hy-emit -p hy-syntax -p hy-diag

@limitlessprojects limitlessprojects added triage:duplicate Overlapping change-set with another open PR triage:new New arrival (<= 3 days old), awaiting recon triage:tests Includes test/corpus coverage labels Oct 19, 2026
@github-actions github-actions Bot added the bot:stale-base Shepherd: behind main beyond threshold label Oct 20, 2026
@github-actions

Copy link
Copy Markdown

Shepherd (Phase A — propose only): this PR's changed-file set overlaps >=0.6 Jaccard with #106, which carries the larger take. Proposing close as superseded.

This is a proposal, not a close — nothing auto-closes while this comment is fresh. If this PR has unique content (a different rule, extra tests, a different fix), say so or push, and the label comes off. Cluster signal is a file-set heuristic; the verdict stays human.

@github-actions github-actions Bot added the bot:dup-of:#106 Shepherd W4: superseded by cluster survivor label Oct 20, 2026
@sonson0910

Copy link
Copy Markdown
Contributor

Prior-Art & First-Reporter Notice (Issue #105 / Companion PR #106, Filed 98 Hours Earlier)

For maintainer triage reference (@limitlessprojects — corroborating Shepherd's Jaccard overlap flag with #106):

The generic struct type-argument stripping bug in TypeShape::Struct across shape.rs, loans.rs, freeze.rs, sendability.rs, and lib.rs was already reported and fixed 98 hours (4 days) earlier by @sonson0910:

Artifact Author Created (UTC) Coverage
Issue #105 @sonson0910 2026-10-15T01:49:18Z Reports TypeShape::Struct dropping generic type arguments (box[map[string]int], lru_cache[string, int]) across shape.rs, sendability.rs, freeze.rs, loans.rs, and closures.rs.
PR #106 @sonson0910 2026-10-15T01:52:46Z Preserves and substitutes generic struct type arguments across shape.rs, sendability.rs, freeze.rs, loans.rs, closures.rs, emit_guard.rs, and lib.rs (synced to latest main, behind_by = 0).
Issue #772 / PR #779 @aazminu 2026-10-19T03:38:03Z / 03:51:10Z (+98h later) Duplicate report and fix for generic struct type arguments in shape.rs and loans.rs.

…d loan checks

`named_shape_depth` previously stripped generic type arguments via
`base_named_type_str` (`box[[]int]` -> `TypeShape::Struct(Some("box"))`),
and `shape_may_share_at`, `shape_carries_handle_at`, `field_shape`,
`call_shape`, `ty_of_expr`, `no_zero_value_reason`, `place_target_ty`,
`check_composite_nils`, and `shape_holds_*` looked up raw un-substituted
field types in `self.structs.get("box")` where `T` resolved to
`TypeShape::Unknown`.

Preserve the instantiated generic struct name in `TypeShape::Struct(Some(name))`
and substitute declared struct/function/method type parameters through
`struct_fields_for_sname` and `fn_subst_for_state`, while normalizing
receiver base names when looking up method declarations.

Fixes limitlessprojects#772
@aazminu
aazminu force-pushed the fix/generic-struct-type-arg-erasure-772 branch from 17b976b to 783da43 Compare October 24, 2026 06:34
@limitlessprojects

Copy link
Copy Markdown
Owner

Queue-reset sweep: this repo now runs a rolling conveyor, and open PRs
outside the active review window are being closed as not planned to keep the
queue honest — a stale queue wastes every contributor's time, including yours.

This is not a verdict on the work. If this change still matters:

  1. rebase against current main (main has landed a lot since this PR opened),
  2. confirm the diff still applies and the affected verdicts still hold,
  3. reopen — the triage board picks reopened PRs up as fresh arrivals.

Wildcard holds (verdict window 2026-11-01) and Pareto land candidates are
unaffected. Thanks for the effort — high-volume repos only stay reviewable
with a queue that actually drains.

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

Labels

bot:dup-of:#106 Shepherd W4: superseded by cluster survivor bot:stale-base Shepherd: behind main beyond threshold triage:duplicate Overlapping change-set with another open PR triage:new New arrival (<= 3 days old), awaiting recon triage:tests Includes test/corpus coverage

Projects

None yet

3 participants