Repository navigation
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #8728 +/- ##
==========================================
+ Coverage 81.95% 82.00% +0.04%
==========================================
Files 456 456
Lines 65612 65659 +47
==========================================
+ Hits 53775 53845 +70
+ Misses 11837 11814 -23
🚀 New features to boost your workflow:
|
36bd95a to
b71e398
Compare
rescript
@rescript/belt
@rescript/darwin-arm64
@rescript/linux-arm64
@rescript/linux-x64
@rescript/runtime
@rescript/win32-x64
commit: |
|
Developer playground preview: https://rescript-lang.github.io/rescript/dev-playground/?version=pr-8728 |
e2110a3 to
17bf46b
Compare
`pval_prim` is the primitive string as written, with its location. The frontend still checks each FFI external, but `Typedecl` gets its spec through `Primitive.resolve_external`, so the parsetree no longer depends on `External_ffi_types`. Review: Why does the frontend resolve each external at all? It reports the errors and warnings where they were reported before, and decides whether a relative `@module` keeps other modules from inlining the external. The type checker's second resolution runs without warnings. Review: How do `@inline` constants work without `Prim_inline_const`? The built-in PPX turns them into an external with the internal primitive `#rescript-inline` that keeps the `@inline(<literal>)` attribute. Review: Is the global `Primitive.resolve_external` safe with parallel requests? `Ast_external` sets it once during module initialization, like the `Oprint` hooks, and it is only read afterwards. Area: frontend typechecker syntax gentype tests Output: unchanged, generated JS of the test suites is byte-identical Formatting: unchanged Formats: ast cmt PPX: unchanged, an external's primitive string reaches v0 as before Editor: unchanged Errors: none, the new fixture follows the warning_<NN> naming convention Tests: +tests/syntax_tests/data/ast-mapping/Externals.res +tests/build_tests/super_errors/fixtures/warning_101_external_arg_attribute.res ~tests/ounit_tests/ounit_ast_mapper0_tests.ml ~tests/ounit_tests/ounit_string_literal_tests.ml PR: #8728 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018oLiCQjGFSE2qwfYqCMF9e Signed-off-by: Christoph Knittel <ck@cca.io>
17bf46b to
0527e03
Compare
Changelog🐛 Bug fix
🏠 Internal
Semantic Diff5 commits · Full page ✨ Resolve an external's FFI attributes in the type checker9d69d5d · cknitt + Claude · Oct 16, 2026, 19:28 UTC · new
AST modified (externals after the built-in PPX) type t
@send external on: (t, @as("click") _, unit => unit) => unit = "addEventListener"
@inline let answer = 42
…
structure_item
Pstr_primitive
value_description "on"
+ attribute "send"
+ []
core_type
Ptyp_arrow
- arity = 2
+ arity = 3
Nolabel
core_type
Ptyp_constr "t"
[]
Nolabel
+ Parg_fixed
+ value
+ Pfixed_string (source="click", semantic="click")
+ Nolabel
core_type
Ptyp_arrow
…
core_type
Ptyp_constr "unit"
[]
"addEventListener"
- <ffi spec>
structure_item
Pstr_primitive
value_description "answer"
+ attribute "inline"
+ structure_item
+ Pstr_eval
+ expression
+ Pexp_constant PConst_int (42,None)
core_type
Ptyp_constr "int"
[]
- <inline const>
+ "#rescript-inline"Typedtree modified (externals) type t
@send external on: (t, @as("click") _, unit => unit) => unit = "addEventListener"
@inline let answer = 42
…
structure_item
Tstr_primitive
value_description on/1002
core_type
Ttyp_arrow
Nolabel
core_type
Ttyp_constr "t/1001"
[]
Nolabel
core_type
Ttyp_arrow
…
core_type
Ttyp_constr "unit/5"
[]
"addEventListener"
- <ffi spec>
structure_item
Tstr_primitive
value_description answer/1003
core_type
Ttyp_constr "int/1"
[]
- <inline const>
+ "#rescript-inline"Review
✨ Check an external's unused attributes where it is resolved548eab4 · cknitt + Claude · Nov 02, 2026, 19:49 UTC · new
Diagnostic modified (unused attribute on a relative @module("./local") external relative: (@as("v") int) => unit = "relative"
Warning number 101
A.res:1:40-42
Unused attribute: @as
-
-Warning number 101
-A.res:1:40-42
-Unused attribute: @asReview
✨ Read a value's primitive from its kind, not copies of it3cd7c05 · cknitt + Claude · Nov 02, 2026, 19:51 UTC · new
✨ Inline
|
02f4919 to
d7f09dd
Compare
d7f09dd to
e642cee
Compare
Changelog🐛 Bug fix
🏠 Internal
Semantic Diff1 commit · Full page ✏️ Resolve an external's FFI attributes in the type checker85671c0 · cknitt + Claude · Nov 10, 2026, 19:24 UTC · edited
AST modified (externals after the built-in PPX) type t
@send external on: (t, @as("click") _, unit => unit) => unit = "addEventListener"
@inline let answer = 42
…
structure_item
Pstr_primitive
value_description "on"
+ attribute "send"
+ []
core_type
Ptyp_arrow
- arity = 2
+ arity = 3
Nolabel
core_type
Ptyp_constr "t"
[]
Nolabel
+ Parg_fixed
+ value
+ Pfixed_string (source="click", semantic="click")
+ Nolabel
core_type
Ptyp_arrow
…
core_type
Ptyp_constr "unit"
[]
"addEventListener"
- <ffi spec>
structure_item
Pstr_primitive
value_description "answer"
+ attribute "inline"
+ structure_item
+ Pstr_eval
+ expression
+ Pexp_constant PConst_int (42,None)
core_type
Ptyp_constr "int"
[]
- <inline const>
+ "#rescript-inline"AST modified (an external with a relative path after the built-in PPX) @module("./local") external relative: int => int = "relative"
structure_item
- Pstr_include
- module_expr
- Pmod_constraint
- module_expr
- Pmod_structure
- structure_item
- Pstr_primitive
- value_description "relative"
- attribute "module"
- …
- core_type
- …
- "relative"
- module_type
- Pmty_signature
- signature_item
- Psig_value
- value_description "relative"
- core_type
- …
+ Pstr_primitive
+ value_description "relative"
+ attribute "module"
+ …
+ core_type
+ …
+ "relative"Typedtree modified (an external with a relative path) @module("./local") external relative: int => int = "relative"
structure_item
Tstr_include
module_expr
- Tmod_constraint
module_expr
Tmod_structure
structure_item
Tstr_primitive
value_description relative/1001
…
"relative"
- module_type
- Tmty_signature
- signature_item
- Tsig_value
- value_description relative/1002
- …Typedtree modified (externals) type t
@send external on: (t, @as("click") _, unit => unit) => unit = "addEventListener"
@inline let answer = 42
…
structure_item
Tstr_primitive
value_description on/1002
core_type
Ttyp_arrow
…
"addEventListener"
- <ffi spec>
structure_item
Tstr_primitive
value_description answer/1003
core_type
Ttyp_constr "int/1"
[]
- <inline const>
+ "#rescript-inline"Diagnostic modified (unused attribute on a relative @module("./local") external relative: @as("v") int => unit = "relative"
Warning number 101
A.res:1:39-41
Unused attribute: @as
-
-Warning number 101
-A.res:1:39-41
-Unused attribute: @asDiagnostic removed (unused attribute under a @warning("-101") @module("./local")
external silenced: @as("w") int => unit = "silenced"
JS modified ( @inline let big = 10n
let use = () => big
function use() {
return 10n;
}
-let big = 10n;
-exports.big = big;
exports.use = use;Diagnostic removed ( @inline let big = 10n
Review
|
|
| Benchmark | Base | Head | Change |
|---|---|---|---|
packages/@rescript/runtime/Stdlib_Symbol.res |
144,086 | 147,406 | +2.30% |
packages/@rescript/runtime/Stdlib_Math.res |
326,831 | 332,053 | +1.60% |
packages/@rescript/runtime/Stdlib_TypedArray.res |
782,659 | 795,150 | +1.60% |
packages/@rescript/runtime/Stdlib_Date.res |
551,382 | 558,996 | +1.38% |
packages/@rescript/runtime/Stdlib_Console.res |
369,767 | 374,811 | +1.36% |
packages/@rescript/runtime/Stdlib_Object.res |
244,951 | 248,099 | +1.29% |
packages/@rescript/runtime/Stdlib_Temporal_PlainDateTime.res |
436,943 | 442,165 | +1.20% |
packages/@rescript/runtime/Stdlib_RegExp.res |
162,817 | 164,733 | +1.18% |
packages/@rescript/runtime/Stdlib_String.res |
504,410 | 510,268 | +1.16% |
packages/@rescript/runtime/Stdlib_Temporal_ZonedDateTime.res |
572,204 | 578,845 | +1.16% |
141 more changes
| Benchmark | Base | Head | Change |
|---|---|---|---|
packages/@rescript/runtime/Stdlib_Promise.res |
351,719 | 355,728 | +1.14% |
packages/@rescript/runtime/Stdlib_Temporal_PlainDate.res |
402,866 | 407,270 | +1.09% |
packages/@rescript/runtime/Pervasives.res |
238,939 | 241,474 | +1.06% |
packages/@rescript/runtime/Stdlib_DataView.res |
230,058 | 232,457 | +1.04% |
packages/@rescript/runtime/JsxEvent.res |
1,088,117 | 1,099,058 | +1.01% |
packages/@rescript/runtime/Stdlib_Temporal_Duration.res |
372,671 | 376,332 | +0.98% |
packages/@rescript/runtime/Stdlib_Set.res |
210,614 | 212,428 | +0.86% |
packages/@rescript/runtime/Stdlib_Float.res |
218,542 | 220,422 | +0.86% |
packages/@rescript/runtime/Stdlib_Intl_Locale.res |
197,097 | 198,775 | +0.85% |
packages/@rescript/runtime/Stdlib_Temporal_PlainTime.res |
319,801 | 322,483 | +0.84% |
packages/@rescript/runtime/Dom_storage2.res |
119,493 | 120,493 | +0.84% |
packages/@rescript/runtime/Stdlib_Temporal_PlainYearMonth.res |
381,509 | 384,680 | +0.83% |
packages/@rescript/runtime/Stdlib_Symbol.resi |
159,849 | 161,151 | +0.81% |
packages/@rescript/runtime/Stdlib_Global.res |
141,326 | 142,375 | +0.74% |
packages/@rescript/runtime/Stdlib_Temporal_Instant.res |
307,209 | 309,434 | +0.72% |
packages/@rescript/runtime/Stdlib_Map.res |
205,383 | 206,852 | +0.72% |
packages/@rescript/runtime/Stdlib_Dict.res |
319,549 | 321,817 | +0.71% |
packages/@rescript/runtime/Stdlib_BigInt.res |
206,822 | 208,189 | +0.66% |
packages/@rescript/runtime/Stdlib_BigInt.resi |
186,312 | 187,493 | +0.63% |
packages/@rescript/runtime/Dom_storage.res |
166,886 | 167,889 | +0.60% |
packages/@rescript/runtime/Stdlib_Math.resi |
330,661 | 332,614 | +0.59% |
packages/@rescript/runtime/Stdlib_Array.res |
1,293,029 | 1,300,460 | +0.57% |
packages/@rescript/runtime/Stdlib_Int.resi |
205,677 | 206,841 | +0.57% |
packages/@rescript/runtime/Stdlib_Temporal_PlainMonthDay.res |
268,033 | 269,502 | +0.55% |
packages/@rescript/runtime/Stdlib_Float.resi |
194,403 | 195,451 | +0.54% |
packages/@rescript/runtime/Stdlib_Intl_NumberFormat.res |
403,719 | 405,882 | +0.54% |
packages/@rescript/runtime/Stdlib_IteratorObject.res |
250,973 | 252,312 | +0.53% |
packages/@rescript/runtime/Stdlib_Temporal_Now.res |
142,907 | 143,667 | +0.53% |
packages/@rescript/runtime/Stdlib_JsError.res |
230,016 | 231,223 | +0.52% |
packages/@rescript/runtime/Stdlib_WeakMap.res |
134,936 | 135,623 | +0.51% |
packages/@rescript/runtime/Stdlib_Dict.resi |
241,018 | 242,214 | +0.50% |
packages/@rescript/runtime/Stdlib_String.resi |
500,049 | 502,499 | +0.49% |
packages/@rescript/runtime/Stdlib_RegExp.resi |
188,001 | 188,909 | +0.48% |
packages/@rescript/runtime/Stdlib_JSON.resi |
236,625 | 237,740 | +0.47% |
packages/@rescript/runtime/Stdlib_WeakSet.res |
122,595 | 123,164 | +0.46% |
packages/@rescript/runtime/Stdlib_Intl_PluralRules.res |
201,180 | 202,107 | +0.46% |
packages/@rescript/runtime/Stdlib_Date.resi |
618,508 | 621,342 | +0.46% |
packages/@rescript/runtime/Stdlib_Promise.resi |
357,176 | 358,790 | +0.45% |
packages/@rescript/runtime/Stdlib_Array.resi |
731,792 | 735,033 | +0.44% |
packages/@rescript/runtime/Stdlib_Intl_ListFormat.res |
169,175 | 169,869 | +0.41% |
packages/@rescript/runtime/Primitive_object_extern.res |
114,242 | 114,710 | +0.41% |
packages/@rescript/belt/src/Belt_Int.res |
165,421 | 166,090 | +0.40% |
packages/@rescript/runtime/Stdlib_JsError.resi |
185,010 | 185,732 | +0.39% |
packages/@rescript/runtime/Stdlib_Console.resi |
414,795 | 416,383 | +0.38% |
packages/@rescript/runtime/Stdlib_Intl_RelativeTimeFormat.res |
179,816 | 180,492 | +0.38% |
packages/@rescript/runtime/Stdlib_Intl_Segmenter.res |
153,921 | 154,488 | +0.37% |
packages/@rescript/belt/src/Belt_Float.res |
164,752 | 165,354 | +0.37% |
packages/@rescript/runtime/Stdlib_Set.resi |
229,595 | 230,415 | +0.36% |
packages/@rescript/runtime/Stdlib_Ordering.res |
152,123 | 152,660 | +0.35% |
packages/@rescript/runtime/Jsx.res |
121,256 | 121,671 | +0.34% |
packages/@rescript/runtime/Stdlib_Int.res |
457,227 | 458,775 | +0.34% |
packages/@rescript/runtime/Stdlib_JSON.res |
297,543 | 298,527 | +0.33% |
packages/@rescript/belt/src/Belt_Int.resi |
149,367 | 149,854 | +0.33% |
packages/@rescript/runtime/Stdlib_ArrayBuffer.res |
107,060 | 107,408 | +0.33% |
packages/@rescript/runtime/Stdlib_DataView.resi |
269,445 | 270,319 | +0.32% |
packages/@rescript/runtime/Stdlib_Nullable.resi |
199,638 | 200,285 | +0.32% |
packages/@rescript/belt/src/Belt_Float.resi |
150,843 | 151,331 | +0.32% |
packages/@rescript/runtime/Stdlib_Global.resi |
155,741 | 156,235 | +0.32% |
packages/@rescript/runtime/Stdlib_Map.resi |
217,639 | 218,329 | +0.32% |
packages/@rescript/runtime/Stdlib_Intl_Collator.res |
178,054 | 178,613 | +0.31% |
packages/@rescript/runtime/Primitive_array_extern.res |
113,857 | 114,212 | +0.31% |
packages/@rescript/runtime/Stdlib_IteratorObject.resi |
235,056 | 235,761 | +0.30% |
packages/@rescript/runtime/Stdlib_Type.res |
201,437 | 202,008 | +0.28% |
packages/@rescript/runtime/Stdlib_Null.resi |
204,322 | 204,899 | +0.28% |
packages/@rescript/runtime/Stdlib_Intl_Segments.res |
116,121 | 116,448 | +0.28% |
packages/@rescript/belt/src/Belt_Array.resi |
649,773 | 651,405 | +0.25% |
packages/@rescript/runtime/Stdlib_Intl_DateTimeFormat.res |
388,726 | 389,663 | +0.24% |
packages/@rescript/runtime/Stdlib_Bool.resi |
116,432 | 116,710 | +0.24% |
packages/@rescript/belt/src/Belt_MutableSetString.resi |
264,513 | 265,127 | +0.23% |
packages/@rescript/belt/src/Belt_MutableSetInt.resi |
264,630 | 265,244 | +0.23% |
packages/@rescript/belt/src/Belt_SetString.resi |
259,453 | 260,023 | +0.22% |
packages/@rescript/belt/src/Belt_SetInt.resi |
259,580 | 260,150 | +0.22% |
packages/@rescript/runtime/Primitive_dict.res |
102,325 | 102,540 | +0.21% |
packages/@rescript/runtime/Stdlib_IterableIterator.resi |
149,129 | 149,441 | +0.21% |
packages/@rescript/runtime/Stdlib_JsExn.resi |
122,404 | 122,657 | +0.21% |
packages/@rescript/runtime/Stdlib_List.resi |
409,309 | 410,155 | +0.21% |
packages/@rescript/belt/src/Belt_MutableMapString.resi |
314,510 | 315,124 | +0.20% |
packages/@rescript/belt/src/Belt_MutableMapInt.resi |
314,628 | 315,242 | +0.20% |
packages/@rescript/runtime/Stdlib_Float32Array.res |
343,118 | 343,786 | +0.19% |
packages/@rescript/runtime/Stdlib_Float64Array.res |
343,118 | 343,786 | +0.19% |
packages/@rescript/runtime/Stdlib_Int8Array.res |
343,000 | 343,667 | +0.19% |
packages/@rescript/runtime/Stdlib_Uint8Array.res |
343,015 | 343,681 | +0.19% |
packages/@rescript/runtime/Stdlib_Int16Array.res |
343,147 | 343,813 | +0.19% |
packages/@rescript/runtime/Stdlib_BigUint64Array.res |
343,254 | 343,920 | +0.19% |
packages/@rescript/runtime/Stdlib_Uint8ClampedArray.res |
343,530 | 344,196 | +0.19% |
packages/@rescript/runtime/Stdlib_Uint16Array.res |
343,154 | 343,819 | +0.19% |
packages/@rescript/runtime/Stdlib_Uint32Array.res |
343,154 | 343,819 | +0.19% |
packages/@rescript/runtime/Stdlib_BigInt64Array.res |
343,180 | 343,845 | +0.19% |
packages/@rescript/runtime/Stdlib_Int32Array.res |
343,149 | 343,813 | +0.19% |
packages/@rescript/belt/src/Belt_MapString.resi |
390,782 | 391,529 | +0.19% |
packages/@rescript/belt/src/Belt_MapInt.resi |
390,825 | 391,572 | +0.19% |
packages/@rescript/runtime/RescriptTools.res |
104,120 | 104,314 | +0.19% |
packages/@rescript/runtime/Stdlib_Generator.res |
200,647 | 201,016 | +0.18% |
packages/@rescript/runtime/Stdlib_AsyncIterableIterator.resi |
159,531 | 159,823 | +0.18% |
packages/@rescript/runtime/Stdlib_Option.resi |
236,075 | 236,505 | +0.18% |
packages/@rescript/runtime/Primitive_exceptions.res |
201,856 | 202,223 | +0.18% |
packages/@rescript/runtime/Stdlib_Generator.resi |
160,178 | 160,469 | +0.18% |
packages/@rescript/belt/src/Belt_List.resi |
758,030 | 759,396 | +0.18% |
packages/@rescript/runtime/Primitive_js_extern.res |
239,398 | 239,819 | +0.18% |
packages/@rescript/runtime/Stdlib_AsyncGenerator.resi |
165,530 | 165,821 | +0.18% |
packages/@rescript/runtime/Primitive_string_extern.res |
97,857 | 98,028 | +0.17% |
packages/@rescript/runtime/Stdlib_AsyncGenerator.res |
211,493 | 211,861 | +0.17% |
packages/@rescript/runtime/Stdlib_ArrayBuffer.resi |
110,780 | 110,969 | +0.17% |
packages/@rescript/runtime/Stdlib_Iterator.res |
235,116 | 235,514 | +0.17% |
packages/@rescript/belt/src/Belt_internalAVLset.resi |
354,491 | 355,076 | +0.17% |
packages/@rescript/belt/src/Belt_HashMapString.resi |
213,316 | 213,665 | +0.16% |
packages/@rescript/belt/src/Belt_HashMapInt.resi |
213,431 | 213,779 | +0.16% |
packages/@rescript/runtime/Stdlib_IterableIterator.res |
178,833 | 179,124 | +0.16% |
packages/@rescript/belt/src/Belt_MutableQueue.resi |
206,944 | 207,278 | +0.16% |
packages/@rescript/belt/src/Belt_Option.resi |
242,980 | 243,369 | +0.16% |
packages/@rescript/belt/src/Belt_MutableMap.resi |
394,956 | 395,585 | +0.16% |
packages/@rescript/belt/src/Belt_HashSetString.resi |
175,179 | 175,454 | +0.16% |
packages/@rescript/belt/src/Belt_HashSetInt.resi |
175,292 | 175,567 | +0.16% |
packages/@rescript/belt/src/Belt_internalAVLtree.resi |
452,018 | 452,722 | +0.16% |
packages/@rescript/belt/src/Belt_MutableSet.resi |
397,063 | 397,677 | +0.15% |
packages/@rescript/runtime/Primitive_object.resi |
116,778 | 116,948 | +0.15% |
packages/@rescript/belt/src/Belt_MapDict.resi |
513,029 | 513,775 | +0.15% |
packages/@rescript/runtime/Stdlib_Iterator.resi |
159,008 | 159,234 | +0.14% |
packages/@rescript/belt/src/Belt_SetDict.resi |
402,431 | 403,001 | +0.14% |
packages/@rescript/belt/src/Belt_Id.resi |
259,758 | 260,124 | +0.14% |
packages/@rescript/belt/src/Belt_Set.resi |
436,953 | 437,567 | +0.14% |
packages/@rescript/runtime/Stdlib_AsyncIterator.resi |
177,546 | 177,790 | +0.14% |
packages/@rescript/runtime/Stdlib_Result.resi |
295,963 | 296,367 | +0.14% |
packages/@rescript/belt/src/Belt_Map.resi |
578,948 | 579,736 | +0.14% |
packages/@rescript/runtime/Stdlib_AsyncIterator.res |
295,706 | 296,104 | +0.13% |
packages/@rescript/runtime/Stdlib_Type.resi |
135,551 | 135,727 | +0.13% |
packages/@rescript/belt/src/Belt_MutableStack.resi |
168,936 | 169,153 | +0.13% |
packages/@rescript/runtime/Stdlib_Intl_NumberFormat_Grouping.res |
159,837 | 160,039 | +0.13% |
packages/@rescript/runtime/Primitive_int_extern.res |
97,539 | 97,660 | +0.12% |
packages/@rescript/runtime/Stdlib_Lazy.resi |
112,105 | 112,244 | +0.12% |
packages/@rescript/belt/src/Belt_HashSet.resi |
222,404 | 222,679 | +0.12% |
packages/@rescript/runtime/Stdlib_Lazy.res |
222,145 | 222,417 | +0.12% |
packages/@rescript/runtime/Primitive_promise.res |
96,689 | 96,807 | +0.12% |
packages/@rescript/runtime/Stdlib_Pair.res |
165,629 | 165,830 | +0.12% |
packages/@rescript/runtime/Stdlib_Bool.res |
146,041 | 146,214 | +0.12% |
packages/@rescript/belt/src/Belt_HashMap.resi |
296,511 | 296,859 | +0.12% |
packages/@rescript/runtime/Stdlib_Intl.res |
173,934 | 174,137 | +0.12% |
packages/@rescript/runtime/Stdlib_Nullable.res |
331,825 | 332,197 | +0.11% |
packages/@rescript/belt/src/Belt_internalBucketsType.resi |
164,493 | 164,675 | +0.11% |
packages/@rescript/belt/src/Belt_Result.resi |
239,810 | 240,056 | +0.10% |
packages/@rescript/belt/src/Belt_SortArray.resi |
253,595 | 253,855 | +0.10% |
Instructions
In total: 9,211,172,800 → 9,214,096,307 (+0.03%).
| Benchmark | Base | Head | Change |
|---|---|---|---|
packages/@rescript/runtime/Stdlib_Math.resi |
21,158,495 | 21,430,032 | +1.28% |
packages/@rescript/runtime/Stdlib_TypedArray.res |
41,079,388 | 41,593,742 | +1.25% |
packages/@rescript/runtime/Stdlib_Date.resi |
34,102,855 | 34,478,711 | +1.10% |
packages/@rescript/runtime/Stdlib_Temporal_ZonedDateTime.res |
31,030,446 | 31,323,533 | +0.94% |
packages/@rescript/runtime/Stdlib_String.resi |
29,334,052 | 29,594,365 | +0.89% |
packages/@rescript/runtime/Stdlib_Temporal_PlainDateTime.res |
25,401,662 | 25,606,474 | +0.81% |
packages/@rescript/runtime/Stdlib_Temporal_PlainDate.res |
23,966,342 | 24,154,560 | +0.79% |
packages/@rescript/runtime/Stdlib_Array.resi |
39,941,655 | 40,253,758 | +0.78% |
packages/@rescript/belt/src/Belt_Array.resi |
33,675,836 | 33,733,263 | +0.17% |
packages/@rescript/runtime/Stdlib_List.resi |
24,502,787 | 24,537,005 | +0.14% |
2 more changes
| Benchmark | Base | Head | Change |
|---|---|---|---|
parse/RedBlackTree.res |
14,933,665 | 14,954,003 | +0.14% |
parse/RedBlackTreeNoComments.res |
14,532,761 | 14,552,058 | +0.13% |
Output size
In total: 25,843,198 → 25,816,622 (-0.10%).
🎉 Improvement: 1 output is more than 1% smaller.
| Benchmark | Base | Head | Change |
|---|---|---|---|
output/tests/*.cmti |
176,053 | 173,922 | -1.21% |
output/runtime/*.cmti |
1,118,869 | 1,115,134 | -0.33% |
output/runtime/*.cmt |
3,044,137 | 3,035,903 | -0.27% |
output/belt/*.cmti |
981,298 | 980,059 | -0.13% |
Pull request 85671c0 compared with base 00e3f40 in this run.
e642cee to
282e6dd
Compare
Why:
The parsetree is what PPXs see and what `-bs-ast` serializes, and
#8624 wants it to describe source only. After the built-in PPX had
read an external's `@send`, `@module`, `@as`… attributes, it replaced
the primitive string with the computed FFI specification
(`Prim_ffi`, `Prim_inline_const`), so `Parsetree` depended on
`External_ffi_types` and carried cases no PPX can observe. Now
`pval_prim` is the string as written, with its location, and
`Typedecl` resolves the attributes, once, through
`Primitive.resolve_external`, a hook the frontend registers since the
resolver lives above `compiler/ml`. The resolver also reports the
external's errors and warnings, inside the declaration's `@warning`
scope. The generated JavaScript is unchanged.
AST modified (externals after the built-in PPX):
Example:
type t
@send external on: (t, @as("click") _, unit => unit) => unit = "addEventListener"
@inline let answer = 42
Before (expanded):
…
structure_item
Pstr_primitive
value_description "on"
core_type
Ptyp_arrow
arity = 2
Nolabel
core_type
Ptyp_constr "t"
[]
Nolabel
core_type
Ptyp_arrow
…
core_type
Ptyp_constr "unit"
[]
"addEventListener"
<ffi spec>
structure_item
Pstr_primitive
value_description "answer"
core_type
Ptyp_constr "int"
[]
<inline const>
After (expanded):
…
structure_item
Pstr_primitive
value_description "on"
attribute "send"
[]
core_type
Ptyp_arrow
arity = 3
Nolabel
core_type
Ptyp_constr "t"
[]
Nolabel
Parg_fixed
value
Pfixed_string (source="click", semantic="click")
Nolabel
core_type
Ptyp_arrow
…
core_type
Ptyp_constr "unit"
[]
"addEventListener"
structure_item
Pstr_primitive
value_description "answer"
attribute "inline"
[
structure_item
Pstr_eval
expression
Pexp_constant PConst_int (42,None)
]
core_type
Ptyp_constr "int"
[]
"#rescript-inline"
AST modified (an external with a relative path after the built-in PPX):
Example:
@module("./local") external relative: int => int = "relative"
Before (expanded):
structure_item
Pstr_include
module_expr
Pmod_constraint
module_expr
Pmod_structure
[
structure_item
Pstr_primitive
value_description "relative"
attribute "module"
…
core_type
…
"relative"
]
module_type
Pmty_signature
[
signature_item
Psig_value
value_description "relative"
core_type
…
]
After (expanded):
structure_item
Pstr_primitive
value_description "relative"
attribute "module"
…
core_type
…
"relative"
Typedtree modified (an external with a relative path):
Example:
@module("./local") external relative: int => int = "relative"
Before (typed): the constraint the built-in PPX wrote
structure_item
Tstr_include
module_expr
Tmod_constraint
module_expr
Tmod_structure
[
structure_item
Tstr_primitive
value_description relative/1001
…
"relative"
]
module_type
Tmty_signature
[
signature_item
Tsig_value
value_description relative/1002
…
]
After (typed): the constraint Typemod adds, implicit
structure_item
Tstr_include
module_expr
module_expr
Tmod_structure
[
structure_item
Tstr_primitive
value_description relative/1001
…
"relative"
]
Typedtree modified (externals):
Example:
type t
@send external on: (t, @as("click") _, unit => unit) => unit = "addEventListener"
@inline let answer = 42
Before (typed):
…
structure_item
Tstr_primitive
value_description on/1002
core_type
Ttyp_arrow
…
"addEventListener"
<ffi spec>
structure_item
Tstr_primitive
value_description answer/1003
core_type
Ttyp_constr "int/1"
[]
<inline const>
After (typed):
…
structure_item
Tstr_primitive
value_description on/1002
core_type
Ttyp_arrow
…
"addEventListener"
structure_item
Tstr_primitive
value_description answer/1003
core_type
Ttyp_constr "int/1"
[]
"#rescript-inline"
Diagnostic modified (unused attribute on a relative @module external):
Example:
@module("./local") external relative: @as("v") int => unit = "relative"
Before (diagnostic):
Warning number 101
A.res:1:39-41
Unused attribute: @as
Warning number 101
A.res:1:39-41
Unused attribute: @as
After (diagnostic):
Warning number 101
A.res:1:39-41
Unused attribute: @as
Diagnostic removed (unused attribute under a @warning that disables it):
Example:
@warning("-101") @module("./local")
external silenced: @as("w") int => unit = "silenced"
Before (diagnostic):
Warning number 101
A.res:2:21-23
Unused attribute: @as
JS modified (@inline let with a bigint):
Example:
@inline let big = 10n
let use = () => big
Before (js):
function use() {
return 10n;
}
let big = 10n;
exports.big = big;
exports.use = use;
After (js):
function use() {
return 10n;
}
exports.use = use;
Diagnostic removed (@inline on a bigint let):
Example:
@inline let big = 10n
Before (diagnostic):
Warning number 53
the @inline attribute cannot appear in this context
Review: How does an external with a relative path stay out of other
modules? Its path is relative to the file that declares it, so other
modules call the value its module exports. In a signature, `Typedecl`
gives it `Val_reg`. In a structure, `Typemod` types it as
`include (struct external … end : {let …})`, the wrapper the built-in
PPX used to write, whose coercion (`Tcoerce_primitive`) emits the
exported function. `Primitive.no_cross_module_inline` reads
relativeness off the resolved spec (`External_ffi_types.is_relative`,
split out of `check_decl`).
Review: Why a hook rather than passing the result along? `-bs-ast` writes
the parsetree after the built-in PPX and a later `bsc` process reads
it back, so the specification has to be recomputable from the tree.
`Ppx_entry`, which every compiler driver links, registers the resolver
at module initialization, as the `Oprint` hooks are.
Review: What do diagnostics look like now? An external's errors and
warning 101 come during type checking, in source order with the type
errors, instead of in the parse step; an earlier type error hides
them until it is fixed. The reprint path and `-bs-ast` alone no longer
report them.
Review: What does this cost? Each external is resolved once, in the
compile step, which reads it as written: on Stdlib_Math.resi the
parse and compile steps allocate 0.6% more words than before this
change (resolving in the frontend as well had made it 4.3%).
Review: What is `"#rescript-inline"`? The primitive of an `@inline`
constant's declaration, which keeps its `@inline(<literal>)`
attribute; the resolver turns it into `Kind_inline_const`. A
structural form is a separate #8624 item.
Review: Do PPXs see a difference? No: they run before the built-in PPX,
and the v0 bridge carries the string as before; its error branch for
already-resolved externals is gone.
Review: What changes under `-bs-no-builtin-ppx`? An external with a plain
name is now resolved as an FFI external by the type checker; before,
it was typed as an intrinsic of that name.
Review: What else goes? `Typedtree.val_prim`, a copy of the primitive
string whose readers only need the value's resolved kind, and
`Primitive.print`: `Outcometree.oval_prim` is the
`Primitive.description` itself.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018oLiCQjGFSE2qwfYqCMF9e
Claude-Session: https://claude.ai/code/session_01Kbf3gLpf7RN1MiRcubPgRn
Claude-Session: https://claude.ai/code/session_01Kbf3gJFAva9685fjvkajub
Claude-Session: https://claude.ai/code/session_01Kbf3gzUzT5G
Signed-off-by: Christoph Knittel <ck@cca.io>
282e6dd to
85671c0
Compare
Why
Parsetreeis the AST that PPXs see, that-bs-astserializes, and that #8624 wants to freeze as "v1". A principle there is that the parsetree describes source code only.Externals broke that. After the built-in PPX had read an
external's FFI attributes (@send,@module,@as,@inline, …), it replaced the primitive string with the computed specification:So
parsetree.mldepended on the compiler's internal FFI representation, the type had two cases no PPX can observe, and the v0 bridge needed an error branch for them.What changes
pval_primis the string as written, with its location:string loc option. The attributes stay where the user put them, and the type checker interprets them, once.For
the tree after the built-in PPX (
bsc -only-parse -dparsetree) changes fromto
How the pieces fit:
Typedecl.transl_value_declcallsPrimitive.resolve_external, a hook that returns the resolved type, the attributes resolution did not consume, the primitive name and its kind (Kind_intrinsic,Kind_external spec,Kind_inline_const c). It types the resolved type, so.cmi/.cmjand the generated JavaScript are what they were.Ast_external.resolve) lives abovecompiler/ml, so it is a hook;Ppx_entry, which every compiler driver links, registers it at module initialization, like theOprinthooks. It runs in the type checker because-bs-astwrites the parsetree after the built-in PPX and a laterbscprocess reads it back: the specification must be recomputable from the tree.Bs_ast_invariant's pass skips FFI externals). It runs inside the declaration's@warningscope, so@warning("-101")on an external now silences its unused-attribute warning. Errors and warning 101 now come in source order with type errors rather than in the parse step; the reprint path and-bs-astalone no longer report them.@modulepaths. Such an external must not be inlined in other modules, since its path is relative to the declaring file. The built-in PPX used to wrap it ininclude (struct external … end : {let …}). Now the type checker does the equivalent: in a signature,Typedeclgives itVal_reg; in a structure,Typemodbuilds that constrainedincludein the typed tree, whose coercion (Tcoerce_primitive) emits the exported function.Primitive.no_cross_module_inlinereads relativeness off the resolved spec (External_ffi_types.is_relative, split out ofcheck_decl), soAst_external_process.resolutionloses itsno_inline_cross_moduleflag. The JavaScript is unchanged, and so are hover, references and definition in the editor.@moduleexternal used to keep its resolved form as avalin the wrapper's interface, which was checked as well, so its unused attributes were reported twice.@inlineconstants becomeexternal answer: int = "#rescript-inline"with the@inline(<literal>)attribute kept;Ast_external_mkhas the helpers that build and read such a declaration, andBs_builtin_ppxshrinks to one case per item instead of one per literal kind. A structural node for them is a separate Stamp parsetree v1: what remains before the AST can be frozen for PPXs #8624 item.@inline letbigint constants are inlined now, like the other literals and like bigint constants in signatures. Before, aletskipped them:@inline let big = 10nstayed an exported binding, with warning 53 saying@inlinecannot appear there.Ast_external_process.handle_attributesisresolve, returning aresolutionrecord (pval_type,spec,pval_attributes) instead of a tuple and a second wrapper.Outcometree.oval_primis the value's resolvedPrimitive.description, so hover and interface output still render the FFI attributes, andPrimitive.printis gone.Printast,Pprintastand the ReScript printer print the string as written.Typedtree.val_primis gone: its two readers, the typed tree printer and gentype, use the value'sval_kind(Val_prim), which has the resolved primitive.Ast_mapper_to0no longer needs an error branch;Ast_mapper_from0gives the string a ghost location, since v0 has none.Ast_attributes.rs_externalsis renamedis_ffi_externaland takes the primitive name.Bs_ast_invariantloses aBs_ffi_warningbranch that could not fire.Under
-bs-no-builtin-ppx, a plain-named external is now resolved as an FFI external by the type checker, where it used to be typed as an intrinsic of that name.Cost
Each external is resolved once, in the compile step, which now reads it as written. Allocated words for the parse and compile steps together, measured locally against master:
Stdlib_Math.resiStdlib_Math.resStdlib_Console.resThe parse step allocates less than on master; the rest is the compile step reading each external's attributes as written and resolving them there.
Tests
tests/syntax_tests/data/ast-mapping/Externals.res: intrinsic,@val,@send, relative@module,@scope,@obj,@asand@variadicexternals print back unchanged after the v0 round trip.ounit_ast_mapper0_tests: an external's primitive string and@sendattribute surviveAst_mapper_to0andAst_mapper_from0.ounit_string_literal_testsbuilds its inline constants withAst_external_mk.inline_const_of_expression.tests/build_tests/super_errors/fixtures/warning_101_external_arg_attribute.res: an@ason a non-_argument of a@sendexternal still reports warning 101, once per external, in source order.tests/build_tests/super_errors/fixtures/warning_101_relative_module_external.res: the same on a relative@moduleexternal is reported once, where it used to be reported twice, and@warning("-101")on an external silences it.tests/tests/src/relative_path_use.res: another module calls relative externals (top level, nested module, functor, declared in a.resiasexternalor aslet) through the exported values, and inlines only the non-relative one.tests/analysis_tests/tests/src/RelativeExternal.res: hover, references and definition on relative externals.tests/tests/src/inline_const.res:@inline letbigint constants, positive and negative, are inlined.🤖 Generated with Claude Code
https://claude.ai/code/session_01Kbf3gzUzT5G