1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
//! The "Lawyer" layer for TypeScript compatibility.
//!
//! This module implements the compatibility layer that sits between the public API
//! and the core structural subtype checking ("Judge" layer). It applies TypeScript-
//! specific business logic, including nuanced rules for `any` propagation.
//!
//! ## Judge vs. Lawyer Architecture (SOLVER.md Section 8)
//!
//! - **Judge (SubtypeChecker):** Implements strict, sound set theory semantics.
//! It knows nothing about TypeScript legacy behavior.
//! - **Lawyer (`AnyPropagationRules` + CompatChecker):** Applies TypeScript-specific
//! rules and delegates to the Judge with appropriate configuration.
//!
//! ## TypeScript Quirks Handled
//!
//! ### A. `any` Propagation (The Black Hole)
//! `any` violates the partial order of sets - it's both a subtype and supertype
//! of everything. The `AnyPropagationRules` struct handles this short-circuit.
//!
//! ### B. Function Variance
//! - **Strict mode (strictFunctionTypes):** Parameters are contravariant (sound)
//! - **Legacy mode:** Parameters are bivariant (unsound but backward-compatible)
//! - **Methods:** Always bivariant regardless of strictFunctionTypes
//!
//! ### C. Freshness (Excess Property Checking)
//! Object literals are "fresh" and trigger excess property checking.
//! Once assigned to a variable, they lose freshness and allow width subtyping.
//! Freshness is tracked on the `TypeId` via `ObjectFlags`, with object literals
//! interning to fresh shapes and widening removing the fresh flag. Sound Mode's
//! binding-level tracking lives in the Checker.
//!
//! ### D. The Void Exception
//! TypeScript allows `() => void` to match `() => T` for any T, because
//! the caller promises to ignore the return value.
//!
//! ### E. Weak Type Detection (TS2559)
//! Types with only optional properties require at least one common property
//! with the source type to prevent accidental assignment mistakes.
//!
//! ### F. Nominality Overrides (The "Brand" Check)
//!
//! TypeScript is primarily structurally typed, but has specific exceptions where
//! nominality is enforced. These are "escape hatches" from structural subtyping
//! that prevent unsound or surprising assignments.
//!
//! #### F.1. Enum Nominality (TS2322)
//! Enum members are nominally typed, not structurally.
//!
//! **Rule**: `EnumA.Member1` is NOT assignable to `EnumB.Member2` even if both
//! have the same underlying value (e.g., both are `0`).
//!
//! **Implementation**:
//! - Enum members are wrapped in `TypeData::Enum(def_id, literal_type)`
//! - The `def_id` provides nominal identity (which enum)
//! - The `literal_type` preserves the value (for assignability checks)
//! - `enum_assignability_override` in `CompatChecker` enforces this rule
//!
//! **Examples**:
//! ```typescript
//! enum E { A = 0, B = 1 }
//! enum F { A = 0, B = 1 }
//!
//! let x: E.A = E.B; // ❌ TS2322: different members
//! let y: E.A = F.A; // ❌ TS2322: different enums
//! let z: E.A = 0; // ✅ OK: numeric enum to number
//! let w: number = E.A; // ✅ OK: numeric enum to number
//! ```
//!
//! #### F.2. Private/Protected Brands (TS2322)
//! Classes with private/protected members behave nominally, not structurally.
//!
//! **Rule**: Two classes with the same private member signature are NOT compatible
//! unless they share the same declaration (or one extends the other).
//!
//! **Rationale**: Private members create a "brand" that distinguishes otherwise
//! structurally identical types. This prevents accidentally mixing objects that
//! happen to have the same shape but represent different concepts.
//!
//! **Implementation**:
//! - `private_brand_assignability_override` in `CompatChecker`
//! - Uses `SymbolId` comparison to verify private members originate from same declaration
//! - Subclasses inherit the parent's private brand (are compatible)
//! - Public members remain structural (do not create brands)
//!
//! **Examples**:
//! ```typescript
//! class A { private x: number = 1; }
//! class B { private x: number = 1; }
//!
//! let a: A = new B(); // ❌ TS2322: separate private declarations
//! let b: B = new A(); // ❌ TS2322: separate private declarations
//!
//! class C extends A {}
//! let c: A = new C(); // ✅ OK: subclass inherits brand
//! ```
//!
//! #### F.3. Constructor Accessibility (TS2673, TS2674)
//! Classes with private/protected constructors cannot be instantiated from
//! invalid scopes.
//!
//! **Rule**:
//! - `private constructor()`: Only accessible within the class declaration
//! - `protected constructor()`: Only accessible within the class or subclasses
//! - `public constructor()` or no modifier: Accessible everywhere (default)
//!
//! **Implementation**:
//! - `constructor_accessibility_override` in `CompatChecker`
//! - Checks constructor symbol flags when assigning class type to constructable
//! - Validates scope (inside class, subclass, or external)
//!
//! **Examples**:
//! ```typescript
//! class A { private constructor() {} }
//! let a = new A(); // ❌ TS2673: private constructor
//! A.staticCreate(); // ✅ OK: inside class
//!
//! class B { protected constructor() {} }
//! class C extends B { constructor() { super(); } }
//! let b = new B(); // ❌ TS2674: protected constructor
//! let c = new C(); // ✅ OK: subclass access
//! ```
//!
//! ### Why These Override The Judge
//!
//! The **Judge** (`SubtypeChecker`) implements sound, structural set theory semantics.
//! It would correctly determine that `class A { private x }` and `class B { private x }`
//! have the same shape and are structurally compatible.
//!
//! The **Lawyer** (`CompatChecker`) steps in and says "Wait, TypeScript says these
//! are incompatible because of the private brand." This is TypeScript-specific
//! legacy behavior that violates soundness principles for practical/ergonomic reasons.
//!
//! **Key Principle**: The Lawyer never makes types MORE compatible. It only
//! makes them LESS compatible by adding restrictions on top of the Judge's
//! structural analysis.
//!
//! The key principle is that `any` should NOT silence structural mismatches.
//! While `any` is TypeScript's escape hatch, we still want to catch real errors
//! even when `any` is involved.
use crateAnyPropagationMode;
/// Rules for `any` propagation in type checking.
///
/// In TypeScript, `any` is both a top type (everything is assignable to `any`)
/// and a bottom type (`any` is assignable to everything). This struct captures
/// whether `any` is allowed to suppress nested structural mismatches by
/// configuring the subtype engine's propagation mode.
/// Summary of TypeScript quirks handled by the Lawyer layer.
///
/// Kept as a test-visible contract for parity regression coverage.
;