@@ -28,6 +28,7 @@ type error =
2828 | Repeated_parameter
2929 | Duplicate_constructor of string
3030 | Duplicate_label of string * string option
31+ | Object_spread_with_record_field of string
3132 | Recursive_abbrev of string
3233 | Cycle_in_def of string * type_expr
3334 | Definition_mismatch of type_expr * Includecore .type_mismatch list
@@ -623,24 +624,43 @@ let transl_declaration ~type_record_as_object ~untagged_wfc env sdecl id =
623624 else if optional then Record_regular
624625 else Record_regular ),
625626 sdecl )
626- | None ->
627- (* Could not find record type decl for ...t: assume t is an object type and this is syntax ambiguity *)
628- type_record_as_object := true ;
629- let fields =
630- Ext_list. map lbls_ (fun ld ->
631- match ld.pld_name.txt with
632- | "..." -> Parsetree. Oinherit ld.pld_type
633- | _ -> Otag (ld.pld_name, ld.pld_attributes, ld.pld_type))
627+ | None -> (
628+ (* Could not find record type decl for ...t. This happens when the spread
629+ target is not a record type (e.g. an object type). If additional
630+ fields are present in the record, this mixes a record field with an
631+ object-type spread and should be rejected. If only the spread exists,
632+ reinterpret as an object type for backwards compatibility. *)
633+ (* TODO: We really really need to make this "spread that needs to be resolved"
634+ concept 1st class in the AST or similar. This is quite hacky and fragile as
635+ is.*)
636+ let non_spread_field =
637+ List. find_map
638+ (fun ld -> if ld.pld_name.txt <> " ..." then Some ld else None )
639+ lbls_
634640 in
635- let sdecl =
636- {
637- sdecl with
638- ptype_kind = Ptype_abstract ;
639- ptype_manifest =
640- Some (Ast_helper.Typ. object_ ~loc: sdecl.ptype_loc fields Closed );
641- }
642- in
643- (Ttype_abstract , Type_abstract , sdecl))
641+ match non_spread_field with
642+ | Some ld ->
643+ (* Error on the first record field mixed with an object spread. *)
644+ raise
645+ (Error (ld.pld_loc, Object_spread_with_record_field ld.pld_name.txt))
646+ | None ->
647+ (* Only a spread present: treat as object type (syntax ambiguity). *)
648+ type_record_as_object := true ;
649+ let fields =
650+ Ext_list. map lbls_ (fun ld ->
651+ match ld.pld_name.txt with
652+ | "..." -> Parsetree. Oinherit ld.pld_type
653+ | _ -> Otag (ld.pld_name, ld.pld_attributes, ld.pld_type))
654+ in
655+ let sdecl =
656+ {
657+ sdecl with
658+ ptype_kind = Ptype_abstract ;
659+ ptype_manifest =
660+ Some (Ast_helper.Typ. object_ ~loc: sdecl.ptype_loc fields Closed );
661+ }
662+ in
663+ (Ttype_abstract , Type_abstract , sdecl)))
644664 | Ptype_open -> (Ttype_open , Type_open , sdecl)
645665 in
646666 let tman, man =
@@ -2076,6 +2096,12 @@ let report_error ppf = function
20762096 " The field @{<info>%s@} is defined several times in this record. Fields \
20772097 can only be added once to a record."
20782098 s
2099+ | Object_spread_with_record_field field_name ->
2100+ fprintf ppf
2101+ " @[You cannot mix a record field with an object type spread.@\n \
2102+ Remove the record field or change it to an object field (e.g. \" %s\" : \
2103+ ...).@]"
2104+ field_name
20792105 | Invalid_attribute msg -> fprintf ppf " %s" msg
20802106 | Duplicate_label (s , Some record_name ) ->
20812107 fprintf ppf
0 commit comments