Skip to content

fix(hy-check): preserve generic struct type args in TypeShape::Struct across all soundness passes (Fixes #775) - #792

Open
huynhnhatquandongnai-source wants to merge 1 commit into
limitlessprojects:mainfrom
huynhnhatquandongnai-source:fix-issue-775
Open

huynhnhatquandongnai-source wants to merge 1 commit into
limitlessprojects:mainfrom
huynhnhatquandongnai-source:fix-issue-775

Conversation

@huynhnhatquandongnai-source

Copy link
Copy Markdown

Fixes #775

Summary

In compiler/crates/hy-check/src/shape.rs, Checker::named_shape_depth previously stripped generic type arguments via base_named_type_str(name) and returned TypeShape::Struct(Some(base.to_string())), while field lookups across sendability.rs, shape.rs, loans.rs, closures.rs, emit_guard.rs, and freeze.rs queried self.structs.get(...) without substituting the struct's declared type parameters (struct_type_params).

As a result, any generic struct field typed as a type parameter T resolved to TypeShape::Unknown, causing multiple Tier-0 soundness checks (and completeness checks across emit_guard.rs, closures.rs, and freeze.rs) to silently bypass:

  1. O5.2 Shallow-copy alias tracking (shape_may_share_at): b1 := box[[]int]{...}; b2 := b1; task worker(move b1); b2.v[0] = 42 was accepted without HYB-E3003 and caused a concurrent data race.
  2. O5.1 / O5.2 / O5.4 / T22 Field projection (field_shape & ty_of_expr): s := b.v resolved to TypeShape::Unknown, dropping may_share, non_owned_origin, and immutable (const) propagation (HYB-E3003 / HYB-E3004 / HYB-E3006).
  3. O4 / A5 Composite move & const-sendability checks (shape_holds_closure, shape_holds_unmovable_leaf, shape_is_const_sendable): box[fn()], box[sync.Mutex], and const box[[]int] bypassed HYB-E3004 / HYB-E3001.
  4. O12 / A6 & O2 Borrow-in-field and Emit-Guard invariants (field_borrow_kind, emit_guard.rs, closures.rs, freeze.rs): Generic struct instantiations carrying pointers or borrow handles (box[*int], box[serde_json.view]), incomparable generic structs (box[[]int] == box[[]int]), and constant overflow on generic struct literals (box[int8]{v: 200}) are now properly checked (HYB-E3103, HYB-E6003, HYB-E6002).

Changes

  • sendability.rs: Add Checker::struct_fields_for_sname (with alias resolution and struct_type_params substitution) and update shape_holds_closure, shape_holds_unmovable_leaf, shape_is_const_sendable, spawn_param_shapes, shape_may_be_elem, and check_composite_nils.
  • shape.rs: Preserve full generic instantiation name in named_shape_depth (TypeShape::Struct(Some(name.to_string()))), preserve index type args in call_shape("new"), and use struct_fields_for_sname / struct_fields_for_type in shape_may_share_at, shape_carries_handle_at, struct_has_mutex_field, field_shape, ty_of_expr, and no_zero_value_reason.
  • loans.rs & closures.rs: Make Checker::field_borrow_kind recurse into instantiated TypeShape::Struct(Some(sname)) fields with cycle protection, normalize receiver base names in method lookups, and substitute generic fields in place_target_ty.
  • emit_guard.rs & freeze.rs: Check generic struct type arguments and A6 stored-borrow invariants in check_type_expr_with_state (HYB-E3103), use struct_fields_for_sname in Expr::StructLit and incomparable_rec, and normalize base_named_type_str in shape_is_proven_pure_value.
  • tests/generic_struct_type_shape_issue_775.rs: Add regression test suite covering HYB-E3003, HYB-E3004, HYB-E3006, HYB-E3001, HYB-E3103, and positive controls (Verdict::Accept).

Author / Payout Attribution:

  • GitHub: @huynhnhatquandongnai-source
  • Wallet (USDC / USDT — BEP20 / EVM): 0xb9f3765aeb1974c27a193463f655a1a57b3aec94

…Fixes limitlessprojects#775)

Preserve full generic instantiation names in named_shape_depth and substitute struct type parameters via struct_fields_for_sname / struct_fields_for_type across shape_may_share, field_shape, shape_holds_closure, shape_holds_unmovable_leaf, shape_is_const_sendable, field_borrow_kind, ty_of_expr, and place_target_ty.

Fixes limitlessprojects#775

Author: @huynhnhatquandongnai-source

Wallet (USDC/USDT BEP20/EVM): 0xb9f3765aeb1974c27a193463f655a1a57b3aec94
@sonson0910

sonson0910 commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Duplicate / Copied PR & Regression Notice — Reported & Fixed 99 Hours Earlier in #105 / PR #106

For maintainer triage: this PR (#792, opened 2026-10-19T05:09:20Z) is a duplicate of our earlier issue and pull request opened more than 4 days (99 hours 16 minutes) earlier, and was opened 80 minutes after we posted a prior-art link to PR #106 on #775:

  1. Original First-Reporter Issue: #105 — opened 2026-10-15T01:51:53Z (99 hours 17 minutes before #792).
  2. Original Companion Pull Request: PR #106 (fix/generic-struct-and-set-soundness) — opened 2026-10-15T01:52:46Z (99 hours 16 minutes before #792).
  3. Prior-Art Comment Posted on #775: #775 (comment) — posted 2026-10-19T03:48:53Z (80 minutes before #792 was opened).

1. Regression Introduced by #792 (tests/const_overflow.rs::type_alias_struct_literal_overflow_is_rejected Fails)

In compiler/crates/hy-check/src/emit_guard.rs:413-419, #792 deleted the while let Some(TypeExpr::Named { name: target, .. }) = self.type_aliases.get(&sname) { sname = target.clone(); } loop before calling self.struct_fields_for_sname(&sname). Because struct_fields_for_sname only looks up self.structs.get(sname), any struct literal constructed through a type alias (type MyLimit = Limit; MyLimit{limit: 300}) returns None and breaks existing test tests/const_overflow.rs::type_alias_struct_literal_overflow_is_rejected:

---- type_alias_struct_literal_overflow_is_rejected stdout ----
thread 'type_alias_struct_literal_overflow_is_rejected' panicked at crates/hy-check/tests/const_overflow.rs:624:5:
expected HYB-E6002 for aliased struct literal limit: 300, got []

2. Direct Code Overlap & Strict Superset Comparison (PR #106 vs #792)

  • Core mechanism in #792 was already implemented in PR #106 on Oct 15:
    • Preserving the full instantiated generic struct name in TypeShape::Struct(Some(name.to_string())) in shape.rs:231 instead of stripping [T1, T2].
    • Substituting Decl::Struct.type_params across field types via struct_fields_for_name / parse_generic_type_args / substitute_type_params in sendability.rs and shape.rs.
    • Recursive struct-field borrow inspection (shape_field_borrow_kind / emit_struct_field_borrow_diag) and generic receiver base-name stripping across loans.rs, closures.rs, emit_guard.rs (while preserving self.type_aliases resolution so const_overflow passes 94/94), and freeze.rs.
  • What PR #106 additionally covers that #792 omits:
    • Prelude set[T] sendability and share-edge tracking (HYB-E3001 / HYB-E3003),
    • Generic function call type-argument inference and return-shape substitution (resolve_hy_fn_for_call),
    • Multi-parameter generic struct instantiation (Pair[K, V]), chained type aliases to generic structs (type Alias = Box[map[string]int]), and nested generic struct fields (Outer[Inner[[]int]]),
    • Rebased cleanly on current main with 100% of unit and integration tests passing (including tests/const_overflow.rs 94/94 ok and 27 dedicated generic-struct/set regression tests).

Recommend closing #792 and #775 as duplicates of #105 / PR #106.

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

Labels

None yet

Projects

None yet

2 participants