Skip to content

Commit cda4816

Browse files
committed
Speed up record inclusion check.
Speed up record inclusion check. Fixes #6284 Record inclusion check (between implementation and interface) is quadratic. Example: ```res module M : { type t<'a, 'b, 'c> = {x:list<('a, 'b)>, y:int, z:int} } = { type t<'a, 'b, 'c> = {x:list<('a, 'c)>, y:int, z:int} } ``` The algorithm tries to instantiate type parameters. It only reports an error if there is an inconsistency. This requires solving type equations involving many types at once. To improve error message, the first problematic field is reported. So the type equations are checked again and again with size 1, 2, ...n where n is the number of fields. (Plus the type parameters). This is quadratic and is problematic for types of ~1K elements. This PR provides a fast path which just checks if there is an error, without blaming a specific field. The fast path is linear. Only if an error is detected, the quadratic path is take to blame precisely which field is involved.
1 parent 3ad5cb1 commit cda4816

File tree

2 files changed

+33
-3
lines changed

2 files changed

+33
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
- Fix issue where uncurried type internals leak in type error. https://github.com/rescript-lang/rescript-compiler/pull/6264
2727
- Improve error messages for untagged variant definitions https://github.com/rescript-lang/rescript-compiler/pull/6290
28-
28+
- Fix type checking performance issue for large records. https://github.com/rescript-lang/rescript-compiler/issues/6284
2929

3030
# 11.0.0-beta.1
3131

jscomp/ml/includecore.ml

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,35 @@ and compare_variants ~loc env params1 params2 n
237237
end
238238

239239

240+
and compare_records_fast ~loc env params1 params2 n
241+
(labels1 : Types.label_declaration list)
242+
(labels2 : Types.label_declaration list) =
243+
match labels1, labels2 with
244+
[], [] ->
245+
Ctype.equal env true params1 params2
246+
| [], _::_ -> false
247+
| _::_, [] -> false
248+
| ld1::rem1, ld2::rem2 ->
249+
if Ident.name ld1.ld_id <> Ident.name ld2.ld_id
250+
then false
251+
else if ld1.ld_mutable <> ld2.ld_mutable then false else begin
252+
Builtin_attributes.check_deprecated_mutable_inclusion
253+
~def:ld1.ld_loc
254+
~use:ld2.ld_loc
255+
loc
256+
ld1.ld_attributes ld2.ld_attributes
257+
(Ident.name ld1.ld_id);
258+
let field_mismatch = !Builtin_attributes.check_bs_attributes_inclusion
259+
ld1.ld_attributes ld2.ld_attributes
260+
(Ident.name ld1.ld_id) in
261+
match field_mismatch with
262+
| Some _ -> false
263+
| None ->
264+
compare_records_fast ~loc env
265+
(ld1.ld_type::params1) (ld2.ld_type::params2)
266+
(n+1)
267+
rem1 rem2
268+
end
240269
and compare_records ~loc env params1 params2 n
241270
(labels1 : Types.label_declaration list)
242271
(labels2 : Types.label_declaration list) =
@@ -324,8 +353,9 @@ let type_declarations ?(equality = false) ~loc env name decl1 id decl2 =
324353
if equality then mark cstrs2 Env.Positive (Ident.name id) decl2;
325354
compare_variants ~loc env decl1.type_params decl2.type_params 1 cstrs1 cstrs2
326355
| (Type_record(labels1,rep1), Type_record(labels2,rep2)) ->
327-
let err = compare_records ~loc env decl1.type_params decl2.type_params
328-
1 labels1 labels2 in
356+
let ok = compare_records_fast ~loc env decl1.type_params decl2.type_params 1 labels1 labels2 in
357+
let err = if ok then [] else
358+
compare_records ~loc env decl1.type_params decl2.type_params 1 labels1 labels2 in
329359
if err <> [] || rep1 = rep2 then err else
330360
[Record_representation (rep1, rep2)]
331361
| (Type_open, Type_open) -> []

0 commit comments

Comments
 (0)