|
| 1 | +# `llvm_unreachable("not implemented")` audit |
| 2 | + |
| 3 | +Status: **2 confirmed crashes fixed (this pass), ~120 markers still uninvestigated** — |
| 4 | +written as a roadmap for continuing this audit, not a claim that the sweep is |
| 5 | +complete. Triggered by a user request to review every "not implemented" marker |
| 6 | +in the codebase and see which ones can be implemented. |
| 7 | + |
| 8 | +## 1. Scope and method |
| 9 | + |
| 10 | +A repo-wide search for `not implemented`/`llvm_unreachable` across |
| 11 | +`lib/TypeScript/*.cpp` and `include/TypeScript/{MLIRLogic,LowerToLLVM}/*.h` |
| 12 | +turns up **~130 raw matches** (some are two-line sites: an `LLVM_DEBUG` print |
| 13 | +immediately followed by the `llvm_unreachable`, counted here as one site). |
| 14 | +Grep command used: |
| 15 | + |
| 16 | +``` |
| 17 | +grep -rn "not implemented\|Not implemented\|NOT IMPLEMENTED\|not yet implemented\|NotImplemented" \ |
| 18 | + --include=*.cpp --include=*.h lib include tslang |
| 19 | +``` |
| 20 | + |
| 21 | +These markers span three very different situations that look identical in a |
| 22 | +grep, and need to be told apart before deciding what "implement" even means |
| 23 | +for each: |
| 24 | + |
| 25 | +1. **Legitimate diagnostics.** A handful are `emitError(...)` calls for |
| 26 | + genuinely invalid source (e.g. `MLIRGenClasses.cpp:1919`, "Abstract method |
| 27 | + 'X' is not implemented in 'Y'" — a normal missing-override error; |
| 28 | + `MLIRGenVariables.cpp:285/324`, array binding pattern spread/type |
| 29 | + mismatches). These already produce clean compiler errors. Not gaps. |
| 30 | +2. **Generic exhaustiveness fallbacks.** The majority (~90+ of the raw |
| 31 | + matches) are `.Default([&](auto type) { llvm_unreachable("not |
| 32 | + implemented"); })` at the bottom of an MLIR `TypeSwitch` chain, mostly in |
| 33 | + low-level LLVM-lowering, RTTI, and cast-helper code |
| 34 | + (`LowerToLLVM.cpp`, `CastLogicHelper.h`, `LLVMRTTIHelperVC*.h`, |
| 35 | + `MLIRRTTIHelperVC*.h`, `MLIRTypeHelper.h`'s `funcRef` family, etc.). These |
| 36 | + fire only if a `mlir::Type` value reaches that specific conversion/lowering |
| 37 | + stage with a kind the author never handled there. Some are real gaps; many |
| 38 | + are defensive guards for states the type system already rules out earlier |
| 39 | + in the pipeline (see §3 for a proven example) — **reachability is unknown |
| 40 | + without testing each one**, and that is genuinely the expensive part. |
| 41 | +3. **Named, specific gaps.** ~40 markers carry a message naming the exact |
| 42 | + scenario ("SpreadAssignment not implemented for type: X", "TypeOf NOT |
| 43 | + IMPLEMENTED for Type: X", "not implemented (index)", "not implemented |
| 44 | + (ElementAccessExpression)", …). These are much cheaper to turn into a |
| 45 | + reproduction: the message plus its surrounding `if`/`TypeSwitch` branches |
| 46 | + usually tells you exactly what source-level construct is missing. |
| 47 | + |
| 48 | +Of these, only **6 sites were actually tested** this pass (2 named markers |
| 49 | +confirmed reachable and fixed, 1 generic fallback confirmed dead, 3 more |
| 50 | +named markers read but not yet reproduced). Everything else in §5 is an |
| 51 | +inventory, not a verdict. |
| 52 | + |
| 53 | +## 2. Reproduction recipe used |
| 54 | + |
| 55 | +For each candidate: read the surrounding code to infer what TS source |
| 56 | +pattern would make execution reach that branch, write a minimal `.ts` file |
| 57 | +exercising it, and run it through the actual compiler: |
| 58 | + |
| 59 | +``` |
| 60 | +test-runner.exe <path-to-repro.ts> |
| 61 | +``` |
| 62 | + |
| 63 | +(no `-jit`/`-shared` needed for a single-file reachability check). A crash |
| 64 | +looks like: |
| 65 | + |
| 66 | +``` |
| 67 | +not implemented |
| 68 | +UNREACHABLE executed at I:\...\MLIRGenImpl.h:5797! |
| 69 | +``` |
| 70 | + |
| 71 | +If it crashes, the marker is real and reachable. If it compiles/runs, either |
| 72 | +the type is resolved away earlier in the pipeline (like §3), or the guess |
| 73 | +about the trigger was wrong and needs another attempt. |
| 74 | + |
| 75 | +## 3. Confirmed dead code (proof-of-concept for triaging fallbacks) |
| 76 | + |
| 77 | +`LowerToLLVM.cpp:6267`: |
| 78 | + |
| 79 | +```cpp |
| 80 | +converter.addConversion([&](mlir_ts::IntersectionType type) { |
| 81 | + llvm_unreachable("type usage (IntersectionType) is not implemented"); |
| 82 | + return mlir::Type(); |
| 83 | +}); |
| 84 | +``` |
| 85 | + |
| 86 | +Intersection types (`A & B`) are exercised by two existing, currently-passing |
| 87 | +tests (`00intersection_type.ts`, `00intersection_type_generic.ts`, |
| 88 | +`test-compile-00-intersection-type[-generic]` / `test-jit-...` in |
| 89 | +`test/tester/CMakeLists.txt`), so an `IntersectionType` MLIR value clearly |
| 90 | +*can* exist. It just never survives to LLVM type conversion — it must be |
| 91 | +resolved into its concrete merged/flattened type earlier in MLIRGen. This |
| 92 | +one marker is confirmed unreachable for any currently-expressible source |
| 93 | +program. **This is the template for triaging the remaining ~90 generic |
| 94 | +fallbacks**: find an existing test that plausibly produces the type in |
| 95 | +question, confirm it passes, and if so the fallback is very likely dead for |
| 96 | +today's feature set (not proof for all future features, but proof for now). |
| 97 | + |
| 98 | +## 4. Fixed this pass |
| 99 | + |
| 100 | +Both fixes are in `lib/TypeScript/MLIRGenImpl.h`, both converted a hard |
| 101 | +`llvm_unreachable` crash into an `emitError` + graceful failure, matching the |
| 102 | +pattern already established at `MLIRGenVariables.cpp:285` (Array Binding |
| 103 | +Pattern spread) — nothing new was invented, this is the existing |
| 104 | +"fail loud with a message, don't crash" convention applied to two spots that |
| 105 | +hadn't gotten it yet. |
| 106 | + |
| 107 | +### 4.1 `obj[dynamicKey]` — non-constant index on a tuple/object-literal value |
| 108 | + |
| 109 | +`mlirGenElementAccessTuple` (`MLIRGenImpl.h`, was line 5797): |
| 110 | + |
| 111 | +```ts |
| 112 | +function main() { |
| 113 | + const obj = { a: 1, b: 2 }; |
| 114 | + let key = "a"; |
| 115 | + print(obj[key]); // key is a runtime variable, not a literal |
| 116 | +} |
| 117 | +``` |
| 118 | + |
| 119 | +crashed with `UNREACHABLE executed at MLIRGenImpl.h:5797`. Root cause: |
| 120 | +tuples/object-literals in this compiler lower to a **fixed-layout struct** |
| 121 | +(each field resolved to a specific byte offset at compile time), not a |
| 122 | +dynamic hash map. When the index expression is a compile-time constant, the |
| 123 | +existing code resolves it to a field the normal way; the crash was the `else` |
| 124 | +branch, hit whenever the index is a genuine runtime value. This is not |
| 125 | +missing code so much as **a real limitation of the current object |
| 126 | +representation** — properly "implementing" `obj[runtimeKey]` in general would |
| 127 | +need a dynamic property-bag runtime representation (a different data |
| 128 | +structure entirely, not a small patch). Converted the crash to: |
| 129 | + |
| 130 | +```cpp |
| 131 | +emitError(location) << "Element access with a non-constant index is not supported on this type; " |
| 132 | + "only array types and constant keys (obj[\"literal\"]) can be indexed"; |
| 133 | +return ValueOrLogicalResult(mlir::failure()); |
| 134 | +``` |
| 135 | +
|
| 136 | +### 4.2 Spreading a non-struct-like value into an object literal |
| 137 | +
|
| 138 | +The `SpreadAssignment` `TypeSwitch` inside object-literal codegen |
| 139 | +(`MLIRGenImpl.h`, was line 7863) only handles spreading a |
| 140 | +`TupleType`/`ConstTupleType`/`InterfaceType`/`ClassType`/`ObjectType` into |
| 141 | +`{...expr}`. Anything else hit the `Default` branch: |
| 142 | +
|
| 143 | +```ts |
| 144 | +function main() { |
| 145 | + const arr = [1, 2, 3]; |
| 146 | + const obj = { ...arr }; // crash |
| 147 | +} |
| 148 | +function main2() { |
| 149 | + let x: {a: number} | number[] = { a: 1 }; |
| 150 | + const obj = { ...x }; // crash, same site |
| 151 | +} |
| 152 | +``` |
| 153 | + |
| 154 | +Unlike §4.1, this genuinely **is** a missing feature (array spread would need |
| 155 | +to synthesize numeric-string-keyed fields `"0"`, `"1"`, …; union spread would |
| 156 | +need a runtime type-tag dispatch) rather than an architectural wall — it just |
| 157 | +wasn't scoped/implemented for this pass. Converted to: |
| 158 | + |
| 159 | +```cpp |
| 160 | +emitError(location) << "Spread in an object literal is not supported for type: " << to_print(type); |
| 161 | +return mlir::failure(); |
| 162 | +``` |
| 163 | +
|
| 164 | +Both verified individually (clean diagnostic, no crash) and via the full |
| 165 | +suite (`ctest -C Debug -j8`: 829/829, no regressions — these `Default` |
| 166 | +branches were never reached by any existing passing test). |
| 167 | +
|
| 168 | +## 5. Inventory of remaining markers (untested this pass) |
| 169 | +
|
| 170 | +Grouped by file. "Shape" is a guess from reading the surrounding code, not a |
| 171 | +verified verdict — see §2 for how to actually check one. |
| 172 | +
|
| 173 | +### 5.1 Named/specific (cheapest to investigate next — read the message + local branch, write a 5-line repro) |
| 174 | +
|
| 175 | +| Site | Message | Shape (unverified guess) | |
| 176 | +|---|---|---| |
| 177 | +| `MLIRGenAccessCall.cpp:1159` | not implemented (ElementAccessExpression) | `boxedObj[computedNonStringExpr]` — cousin of the fixed §4.1 site, one level up in the dispatch (only reached before deciding it's a tuple) | |
| 178 | +| `MLIRGenAccessCall.cpp:1219` | not implemented (ElementAccessExpression) | `enumValue[computedExpr]` with a non-constant index | |
| 179 | +| `MLIRGenAccessCall.cpp:1524` | not implemented | unread this pass | |
| 180 | +| `MLIRGenCast.cpp:1321-1322` | TypeOf NOT IMPLEMENTED for Type | inside a generated `__unbox<T>` helper (generic type-parameter unboxing from `any`); `.Default` for a type kind not in its explicit list (Tuple/Array/Enum/Union/Optional are plausible candidates) | |
| 181 | +| `MLIRGenCast.cpp:1498-1499` | TypeOf NOT IMPLEMENTED for Type | second, near-identical site — check if it's reachable via a different call path than 1321 | |
| 182 | +| `MLIRGenImpl.h:5330` | not implemented | unread | |
| 183 | +| `MLIRGenImpl.h:6732` | not implemented | unread | |
| 184 | +| `MLIRGenImpl.h:7314` | not implemented | unread | |
| 185 | +| `MLIRGenImpl.h:7418` | not implemented | unread | |
| 186 | +| `MLIRGenImpl.h:8164` | not implemented | unread | |
| 187 | +| `MLIRGenImpl.h:8382` / `:8400` / `:8426` | not implemented | unread, three sites close together — likely related | |
| 188 | +| `MLIRGenImpl.h:9342` | not implemented | unread | |
| 189 | +| `MLIRGenInterfaces.cpp:475` | not implemented yet | unread | |
| 190 | +| `MLIRGenInterfaces.cpp:932` / `:954` | not implemented | unread | |
| 191 | +| `MLIRGenTypes.cpp:183` | not implemented type declaration | unread | |
| 192 | +| `MLIRGenTypes.cpp:1474` / `:1567` / `:1876` / `:1910` / `:2001` / `:2204` / `:2210` / `:2661` / `:3401` | not implemented | unread, largest single-file cluster after MLIRGenImpl.h | |
| 193 | +| `LLVMCodeHelper.h:452` | array literal is not implemented(1) | likely the LLVM-lowering-side twin of the (confirmed-dead) `MLIRGenImpl.h:7875` "object literal is not implemented(1)" `else` branch — check the same way (is there any other `SyntaxKind` an array-literal element list can produce?) | |
| 194 | +| `UnaryBinLogicalOrHelper.h:42-43` | "Not implemented operator for type 1: 'X'" (`emitError`) then `llvm_unreachable` | **worth a quick look on its own**: this one already calls `emitError` (like §4's fix) but *still* falls through to `llvm_unreachable` right after — likely a copy-paste of the crash-then-message pattern that never got the `return` that would make the error actually graceful. If so, this is a one-line fix (drop the `llvm_unreachable`, return failure), no new investigation needed beyond confirming what emits it currently crashes instead of erroring cleanly. | |
| 195 | +
|
| 196 | +### 5.2 Generic `TypeSwitch::Default` exhaustiveness fallbacks (likely mostly dead, per §3's precedent — verify by checking whether an existing passing test already produces the relevant `mlir::Type` at that pipeline stage) |
| 197 | +
|
| 198 | +`MLIRGenClasses.cpp:603,635,1802,2269,2306` · `MLIRGenExpressions.cpp:530,552,988` · |
| 199 | +`MLIRGenGenerics.cpp:424,542,911,1342` · `MLIRGenImpl.h:3203,3477,3803,4402,4518,6379,7080,7094` · |
| 200 | +`MLIRGenInterfaces.cpp:656` · `CastLogicHelper.h:338,344,353,359,460,487,1002` (four of these say |
| 201 | +"must be processed at MLIR pass" — suggests a *specific*, documented reason |
| 202 | +they should be unreachable at the LLVM-lowering stage, worth reading before |
| 203 | +assuming they're arbitrary) · `CodeLogicHelper.h:241` · `OptionalLogicHelper.h:143,213` · |
| 204 | +`UndefLogicHelper.h:74,107` · `MLIRCodeLogic.h:1218,1660,1680,1722` · |
| 205 | +`MLIRPrinter.h:302-303,534` (type-name printing — a `Default` here would show |
| 206 | +up immediately as a printer test failure, so likely easy to check against |
| 207 | +`unittests/MLIRGen/TypeToString.cpp`'s existing coverage) · `MLIRTypeIterator.h:403-404` · |
| 208 | +`Win32ExceptionPass.cpp:584`. |
| 209 | +
|
| 210 | +### 5.3 RTTI type-switch fallbacks (Windows/Linux variants — the Linux ones can't be exercised from this Windows dev box without a Linux/WSL build) |
| 211 | +
|
| 212 | +`LLVMRTTIHelperVCWin32.h:141,156,169` · `LLVMRTTIHelperVCLinux.h:113,128,142` · |
| 213 | +`MLIRRTTIHelperVC.h:108` · `MLIRRTTIHelperVCWin32.h:216,226,241` · |
| 214 | +`MLIRRTTIHelperVCLinux.h:146,161,182,202,217,230,399`. |
| 215 | +
|
| 216 | +### 5.4 `MLIRTypeHelper.h`'s `funcRef` family — interesting because directly unit-testable |
| 217 | +
|
| 218 | +`getReturnTypeFromFuncRef` (:732-733), `getParamFromFuncRef` (:755-756), |
| 219 | +`getFirstParamFromFuncRef` (:779-780), `getParamsFromFuncRef` (:805-806), |
| 220 | +`getParamsTupleTypeFromFuncRef` (:840, `llvm_unreachable` already commented |
| 221 | +out at :843 — someone deliberately silenced this one, worth understanding |
| 222 | +why before re-enabling), `getVarArgFromFuncRef` (:863-864), plus :410, :420, |
| 223 | +:899, :2108, :2256-2257, :2290, :2307, :2685, :2709. Unlike most of §5.2, |
| 224 | +these are **pure functions taking an `mlir::Type` and returning a piece of |
| 225 | +it** — exactly the shape `unittests/MLIRGen/TypeHelper.cpp` (added this |
| 226 | +session, see `declaration-printer-unit-tests`-style memory entries) already |
| 227 | +tests other `MLIRTypeHelper.h` functions with. Reachability here is testable |
| 228 | +*without* writing a `.ts` repro at all: construct the "wrong" `mlir::Type` |
| 229 | +input directly in a unit test and see what real callers expect the sensible |
| 230 | +behavior to be, the same way the existing `canWideTypeWithoutDataLoss` tests |
| 231 | +work. |
| 232 | +
|
| 233 | +## 6. Suggested next steps, in cost order |
| 234 | +
|
| 235 | +1. `UnaryBinLogicalOrHelper.h:42-43` — likely a one-line fix, already has the |
| 236 | + error message, just needs the crash removed. |
| 237 | +2. The rest of §5.1 (named/specific) — cheap repro-and-check, same recipe as |
| 238 | + §4. |
| 239 | +3. §5.4 (`funcRef` family) — extend `unittests/MLIRGen/TypeHelper.cpp` with |
| 240 | + direct calls instead of writing `.ts` repros; fast to iterate. |
| 241 | +4. §5.2 (generic fallbacks) — triage a handful against existing passing |
| 242 | + tests using §3's method before assuming any individual one is live. |
| 243 | +5. §5.3 (RTTI) — lowest priority from this (Windows) machine; the Linux |
| 244 | + variants need a WSL/Linux build to exercise at all, and even the Windows |
| 245 | + ones are deep in a code path (RTTI/exception typeinfo generation) that's |
| 246 | + hard to reach without a specific class-hierarchy-plus-exception scenario. |
| 247 | +
|
| 248 | +## 7. Non-goals / out of scope |
| 249 | +
|
| 250 | +- This document does not claim every remaining marker is a "real bug" — |
| 251 | + §3 demonstrates at least some meaningful fraction are dead code, and the |
| 252 | + true ratio across all ~120 is unknown. |
| 253 | +- Not attempting §4.2's actual missing feature (array/union spread into an |
| 254 | + object literal) in this pass — only converting its crash into a clean |
| 255 | + error. Implementing real array-spread semantics (numeric-string-keyed |
| 256 | + field synthesis) is a separate, scoped follow-up if ever prioritized. |
| 257 | +- Not attempting a dynamic-property-bag runtime representation for §4.1 — |
| 258 | + that is a different object model entirely, out of scope for a |
| 259 | + crash-to-error pass. |
0 commit comments