Skip to content

Commit 50ec424

Browse files
committed
fix compiler crash when spreading the whole record
1 parent f1d0698 commit 50ec424

File tree

4 files changed

+44
-18
lines changed

4 files changed

+44
-18
lines changed

compiler/ml/typecore.ml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -530,8 +530,10 @@ let rec build_as_type env p =
530530
row_fixed = false;
531531
row_closed = false;
532532
})
533-
| Tpat_record (lpl, _, _rest) ->
534-
let lbl = snd4 (List.hd lpl) in
533+
| Tpat_record ([], _, _rest) ->
534+
(* Rest-only record patterns already carry the source record type. *)
535+
p.pat_type
536+
| Tpat_record (((_, lbl, _, _) :: _ as lpl), _, _rest) ->
535537
if lbl.lbl_private = Private then p.pat_type
536538
else
537539
let ty = newvar () in

compiler/ml/typedtree.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ and pattern_desc =
9898
(** { l1=P1; ...; ln=Pn } (flag = Closed)
9999
{ l1=P1; ...; ln=Pn; _} (flag = Open)
100100
101-
Invariant: n > 0
101+
Invariant: n > 0 unless this is a rest-only record pattern
102102
*)
103103
| Tpat_array of pattern list (** [| P1; ...; Pn |] *)
104104
| Tpat_or of pattern * pattern * row_desc option

tests/tests/src/record_rest_test.mjs

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,32 @@ Mocha.describe("Record_rest_test", () => {
113113
version: "3.5",
114114
debug: false
115115
}));
116+
Mocha.test("rest-only record patterns can also bind the whole alias", () => {
117+
let whole = {
118+
name: "wholeAlias",
119+
version: "3.6",
120+
debug: true
121+
};
122+
let rest = ((({...__rest}) => __rest))(whole);
123+
Test_utils.eq("File \"record_rest_test.res\", line 170, characters 7-14", whole, {
124+
name: "wholeAlias",
125+
version: "3.6",
126+
debug: true
127+
});
128+
Test_utils.eq("File \"record_rest_test.res\", line 171, characters 7-14", rest, {
129+
name: "wholeAlias",
130+
version: "3.6",
131+
debug: true
132+
});
133+
});
116134
Mocha.test("optional overlap keeps the remaining fields in the rest object", () => {
117135
let onClick = () => {};
118136
let rest = extractClassName({
119137
className: "btn",
120138
style: "bold",
121139
onClick: onClick
122140
});
123-
Test_utils.eq("File \"record_rest_test.res\", line 171, characters 7-14", rest, {
141+
Test_utils.eq("File \"record_rest_test.res\", line 177, characters 7-14", rest, {
124142
style: "bold",
125143
onClick: onClick
126144
});
@@ -130,18 +148,18 @@ Mocha.describe("Record_rest_test", () => {
130148
id: "1",
131149
value: 42
132150
});
133-
Test_utils.eq("File \"record_rest_test.res\", line 176, characters 7-14", "1", "1");
134-
Test_utils.eq("File \"record_rest_test.res\", line 177, characters 7-14", intRest, {
151+
Test_utils.eq("File \"record_rest_test.res\", line 182, characters 7-14", "1", "1");
152+
Test_utils.eq("File \"record_rest_test.res\", line 183, characters 7-14", intRest, {
135153
value: 42
136154
});
137-
Test_utils.eq("File \"record_rest_test.res\", line 178, characters 7-14", ((({id: __unused0, ...__rest}) => __rest))({
155+
Test_utils.eq("File \"record_rest_test.res\", line 184, characters 7-14", ((({id: __unused0, ...__rest}) => __rest))({
138156
id: "2",
139157
value: "hello"
140158
}), {
141159
value: "hello"
142160
});
143161
});
144-
Mocha.test("tuple nested record rest is initialized", () => Test_utils.eq("File \"record_rest_test.res\", line 183, characters 6-13", getTupleRest([
162+
Mocha.test("tuple nested record rest is initialized", () => Test_utils.eq("File \"record_rest_test.res\", line 189, characters 6-13", getTupleRest([
145163
{
146164
name: "tuple",
147165
version: "4.0",
@@ -153,7 +171,7 @@ Mocha.describe("Record_rest_test", () => {
153171
debug: false
154172
}));
155173
Mocha.test("variant payload rest works through the or-pattern path", () => {
156-
Test_utils.eq("File \"record_rest_test.res\", line 191, characters 6-13", getWrappedRest({
174+
Test_utils.eq("File \"record_rest_test.res\", line 197, characters 6-13", getWrappedRest({
157175
TAG: "Wrap",
158176
_0: {
159177
name: "wrapped",
@@ -164,7 +182,7 @@ Mocha.describe("Record_rest_test", () => {
164182
version: "5.0",
165183
debug: true
166184
});
167-
Test_utils.eq("File \"record_rest_test.res\", line 196, characters 6-13", getWrappedRest({
185+
Test_utils.eq("File \"record_rest_test.res\", line 202, characters 6-13", getWrappedRest({
168186
TAG: "Mirror",
169187
_0: {
170188
name: "mirror",
@@ -177,7 +195,7 @@ Mocha.describe("Record_rest_test", () => {
177195
});
178196
});
179197
Mocha.test("inline record variant rest removes the runtime tag field", () => {
180-
Test_utils.eq("File \"record_rest_test.res\", line 204, characters 6-13", getInlineWrappedRest({
198+
Test_utils.eq("File \"record_rest_test.res\", line 210, characters 6-13", getInlineWrappedRest({
181199
TAG: "InlineWrap",
182200
name: "inline",
183201
version: "7.0",
@@ -186,7 +204,7 @@ Mocha.describe("Record_rest_test", () => {
186204
version: "7.0",
187205
debug: true
188206
});
189-
Test_utils.eq("File \"record_rest_test.res\", line 209, characters 6-13", getInlineWrappedRest({
207+
Test_utils.eq("File \"record_rest_test.res\", line 215, characters 6-13", getInlineWrappedRest({
190208
TAG: "InlineMirror",
191209
name: "inlineMirror",
192210
version: "8.0",
@@ -197,7 +215,7 @@ Mocha.describe("Record_rest_test", () => {
197215
});
198216
});
199217
Mocha.test("inline record variant rest excludes fields renamed with @as", () => {
200-
Test_utils.eq("File \"record_rest_test.res\", line 217, characters 6-13", getRenamedInlineWrappedRest({
218+
Test_utils.eq("File \"record_rest_test.res\", line 223, characters 6-13", getRenamedInlineWrappedRest({
201219
TAG: "RenamedInlineWrap",
202220
"user-name": "inlineRenamed",
203221
version: "8.5",
@@ -206,7 +224,7 @@ Mocha.describe("Record_rest_test", () => {
206224
version: "8.5",
207225
debug: true
208226
});
209-
Test_utils.eq("File \"record_rest_test.res\", line 224, characters 6-13", getRenamedInlineWrappedRest({
227+
Test_utils.eq("File \"record_rest_test.res\", line 230, characters 6-13", getRenamedInlineWrappedRest({
210228
TAG: "RenamedInlineMirror",
211229
"user-name": "inlineRenamed2",
212230
version: "8.6",
@@ -217,7 +235,7 @@ Mocha.describe("Record_rest_test", () => {
217235
});
218236
});
219237
Mocha.test("inline record variant rest removes a custom runtime tag field", () => {
220-
Test_utils.eq("File \"record_rest_test.res\", line 234, characters 6-13", getCustomTaggedInlineWrappedRest({
238+
Test_utils.eq("File \"record_rest_test.res\", line 240, characters 6-13", getCustomTaggedInlineWrappedRest({
221239
kind: "CustomInlineWrap",
222240
name: "customInline",
223241
version: "9.0",
@@ -226,7 +244,7 @@ Mocha.describe("Record_rest_test", () => {
226244
version: "9.0",
227245
debug: true
228246
});
229-
Test_utils.eq("File \"record_rest_test.res\", line 241, characters 6-13", getCustomTaggedInlineWrappedRest({
247+
Test_utils.eq("File \"record_rest_test.res\", line 247, characters 6-13", getCustomTaggedInlineWrappedRest({
230248
kind: "CustomInlineMirror",
231249
name: "customInlineMirror",
232250
version: "10.0",
@@ -237,7 +255,7 @@ Mocha.describe("Record_rest_test", () => {
237255
});
238256
});
239257
Mocha.test("inline record rest works with a non-identifier custom tag name", () => {
240-
Test_utils.eq("File \"record_rest_test.res\", line 251, characters 6-13", getDashedTaggedInlineWrappedRest({
258+
Test_utils.eq("File \"record_rest_test.res\", line 257, characters 6-13", getDashedTaggedInlineWrappedRest({
241259
"custom-tag": "DashedInlineWrap",
242260
name: "dashedInline",
243261
version: "11.0",
@@ -246,7 +264,7 @@ Mocha.describe("Record_rest_test", () => {
246264
version: "11.0",
247265
debug: true
248266
});
249-
Test_utils.eq("File \"record_rest_test.res\", line 258, characters 6-13", getDashedTaggedInlineWrappedRest({
267+
Test_utils.eq("File \"record_rest_test.res\", line 264, characters 6-13", getDashedTaggedInlineWrappedRest({
250268
"custom-tag": "DashedInlineMirror",
251269
name: "dashedInlineMirror",
252270
version: "12.0",

tests/tests/src/record_rest_test.res

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,12 @@ describe(__MODULE__, () => {
165165
)
166166
})
167167

168+
test("rest-only record patterns can also bind the whole alias", () => {
169+
let {...config as rest} as whole = ({name: "wholeAlias", version: "3.6", debug: true}: config)
170+
eq(__LOC__, whole, {name: "wholeAlias", version: "3.6", debug: true})
171+
eq(__LOC__, rest, {name: "wholeAlias", version: "3.6", debug: true})
172+
})
173+
168174
test("optional overlap keeps the remaining fields in the rest object", () => {
169175
let onClick = () => ()
170176
let rest = extractClassName({className: "btn", style: "bold", onClick})

0 commit comments

Comments
 (0)