@@ -4034,11 +4034,16 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
40344034 return self . new_invalid_constant ( location) ;
40354035 }
40364036
4037- let mut remaining_fields = field_map. fields . clone ( ) ;
4038- let explicit_arguments_count = arguments. len ( ) ;
4037+ // Emit warning if no fields are being overridden
4038+ if arguments. is_empty ( ) {
4039+ self . problems
4040+ . warning ( Warning :: NoFieldsRecordUpdate { location } ) ;
4041+ }
4042+
4043+ let mut implicit_labelled_arguments = field_map. fields . clone ( ) ;
4044+ let mut update_argument_indices = HashSet :: new ( ) ;
40394045
4040- // Type-check explicit override arguments
4041- let mut typed_overrides = Vec :: new ( ) ;
4046+ let mut final_arguments = base_arguments;
40424047 for argument in arguments {
40434048 if argument. uses_label_shorthand ( ) {
40444049 self . track_feature_usage (
@@ -4050,7 +4055,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
40504055 let label = & argument. label ;
40514056 let typed_value = self . infer_const ( & None , argument. value ) ;
40524057
4053- let Some ( index) = remaining_fields . remove ( label) else {
4058+ let Some ( index) = implicit_labelled_arguments . remove ( label) else {
40544059 if field_map. fields . contains_key ( label) {
40554060 self . problems . error ( Error :: DuplicateArgument {
40564061 location : argument. location ,
@@ -4069,7 +4074,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
40694074 return self . new_invalid_constant ( location) ;
40704075 } ;
40714076
4072- // Type check: the override value must match the field type
4077+ // Record update argument value must match the field type
40734078 if let Some ( expected_type) = field_types. get ( index as usize )
40744079 && let Err ( error) = unify ( expected_type. clone ( ) , typed_value. type_ ( ) )
40754080 {
@@ -4078,49 +4083,57 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
40784083 return self . new_invalid_constant ( location) ;
40794084 }
40804085
4081- typed_overrides. push ( CallArg {
4086+ let _ = update_argument_indices. insert ( index as usize ) ;
4087+
4088+ * final_arguments
4089+ . get_mut ( index as usize )
4090+ . expect ( "Index out of bounds" ) = CallArg {
40824091 label : Some ( label. clone ( ) ) ,
40834092 value : typed_value,
40844093 location : argument. location ,
40854094 implicit : None ,
4086- } ) ;
4087- }
4088-
4089- // Emit warning if no fields are being overridden
4090- if explicit_arguments_count == 0 {
4091- self . problems
4092- . warning ( Warning :: NoFieldsRecordUpdate { location } ) ;
4095+ } ;
40934096 }
40944097
40954098 // Emit warning if all fields are being overriden
4096- if remaining_fields . is_empty ( ) {
4099+ if implicit_labelled_arguments . is_empty ( ) {
40974100 self . problems
40984101 . warning ( Warning :: AllFieldsRecordUpdate { location } ) ;
40994102 }
41004103
4101- // Merge: start with base record arguments, override with explicit
4102- // update arguments
4103- let mut final_arguments = base_arguments;
4104- for override_argument in typed_overrides {
4105- if let Some ( label) = & override_argument. label
4106- && let Some ( & index) = field_map. fields . get ( label)
4107- && ( index as usize ) < final_arguments. len ( )
4108- {
4109- * final_arguments
4110- . get_mut ( index as usize )
4111- . expect ( "Index out of bounds" ) = override_argument;
4104+ // Check that fields implicitly overridden (including unlabelled ones) have compatible types.
4105+ for ( index, field_arg) in final_arguments. iter ( ) . enumerate ( ) {
4106+ // Skip fields that were record update arguments, as they've already been type-checked above
4107+ if update_argument_indices. contains ( & index) {
4108+ continue ;
41124109 }
4113- }
41144110
4115- // Check that implicit fields (fields copied from base) have compatible types
4116- for ( _label, index) in remaining_fields. iter ( ) {
4117- if let Some ( field_arg) = final_arguments. get ( * index as usize )
4118- && let Some ( expected_field_type) = field_types. get ( * index as usize )
4111+ if let Some ( expected_field_type) = field_types. get ( index)
41194112 && let Err ( unify_error) =
41204113 unify ( expected_field_type. clone ( ) , field_arg. value . type_ ( ) )
41214114 {
4122- self . problems
4123- . error ( convert_unify_error ( unify_error, location) ) ;
4115+ let field = field_map
4116+ . fields
4117+ . iter ( )
4118+ . find ( |( _, i) | * * i == index as u32 )
4119+ . map ( |( name, _) | RecordField :: Labelled ( name. clone ( ) ) )
4120+ . unwrap_or_else ( || RecordField :: Unlabelled ( index as u32 ) ) ;
4121+
4122+ self . problems . error ( match unify_error {
4123+ UnifyError :: CouldNotUnify {
4124+ expected, given, ..
4125+ } => Error :: UnsafeRecordUpdate {
4126+ location : record. location ,
4127+ reason : UnsafeRecordUpdateReason :: IncompatibleFieldTypes {
4128+ constructed_variant : expected_type. clone ( ) ,
4129+ record_variant : typed_record_type. clone ( ) ,
4130+ expected_field_type : expected,
4131+ record_field_type : given,
4132+ field,
4133+ } ,
4134+ } ,
4135+ _ => convert_unify_error ( unify_error, location) ,
4136+ } ) ;
41244137 return self . new_invalid_constant ( location) ;
41254138 }
41264139 }
0 commit comments