@@ -455,36 +455,61 @@ impl<'interner> TypeChecker<'interner> {
455
455
456
456
fn check_member_access ( & mut self , access : expr:: HirMemberAccess , expr_id : ExprId ) -> Type {
457
457
let lhs_type = self . check_expression ( & access. lhs ) . follow_bindings ( ) ;
458
+ let span = self . interner . expr_span ( & expr_id) ;
459
+
460
+ match self . check_field_access ( & lhs_type, & access. rhs . 0 . contents , span) {
461
+ Some ( ( element_type, index) ) => {
462
+ self . interner . set_field_index ( expr_id, index) ;
463
+ element_type
464
+ }
465
+ None => Type :: Error ,
466
+ }
467
+ }
468
+
469
+ /// This will verify that an expression in the form `lhs.rhs_name` has the given field and will push
470
+ /// a type error if it does not. If there is no error, the type of the struct/tuple field is returned
471
+ /// along with the index of the field in question.
472
+ ///
473
+ /// This function is abstracted from check_member_access so that it can be shared between
474
+ /// there and the HirLValue::MemberAccess case of check_lvalue.
475
+ pub ( super ) fn check_field_access (
476
+ & mut self ,
477
+ lhs_type : & Type ,
478
+ field_name : & str ,
479
+ span : Span ,
480
+ ) -> Option < ( Type , usize ) > {
481
+ let lhs_type = lhs_type. follow_bindings ( ) ;
458
482
459
483
if let Type :: Struct ( s, args) = & lhs_type {
460
484
let s = s. borrow ( ) ;
461
- if let Some ( ( field, index) ) = s. get_field ( & access. rhs . 0 . contents , args) {
462
- self . interner . set_field_index ( expr_id, index) ;
463
- return field;
485
+ if let Some ( ( field, index) ) = s. get_field ( field_name, args) {
486
+ return Some ( ( field, index) ) ;
464
487
}
465
488
} else if let Type :: Tuple ( elements) = & lhs_type {
466
- if let Ok ( index) = access. rhs . 0 . contents . parse :: < usize > ( ) {
467
- if index < elements. len ( ) {
468
- self . interner . set_field_index ( expr_id, index) ;
469
- return elements[ index] . clone ( ) ;
489
+ if let Ok ( index) = field_name. parse :: < usize > ( ) {
490
+ let length = elements. len ( ) ;
491
+ if index < length {
492
+ return Some ( ( elements[ index] . clone ( ) , index) ) ;
493
+ } else {
494
+ self . errors . push ( TypeCheckError :: Unstructured {
495
+ msg : format ! ( "Index {index} is out of bounds for this tuple {lhs_type} of length {length}" ) ,
496
+ span,
497
+ } ) ;
498
+ return None ;
470
499
}
471
500
}
472
501
}
473
502
474
503
// If we get here the type has no field named 'access.rhs'.
475
504
// Now we specialize the error message based on whether we know the object type in question yet.
476
505
if let Type :: TypeVariable ( ..) = & lhs_type {
477
- self . errors . push ( TypeCheckError :: TypeAnnotationsNeeded {
478
- span : self . interner . expr_span ( & access. lhs ) ,
479
- } ) ;
506
+ self . errors . push ( TypeCheckError :: TypeAnnotationsNeeded { span } ) ;
480
507
} else if lhs_type != Type :: Error {
481
- self . errors . push ( TypeCheckError :: Unstructured {
482
- msg : format ! ( "Type {lhs_type} has no member named {}" , access. rhs) ,
483
- span : self . interner . expr_span ( & access. lhs ) ,
484
- } ) ;
508
+ let msg = format ! ( "Type {lhs_type} has no member named {field_name}" ) ;
509
+ self . errors . push ( TypeCheckError :: Unstructured { msg, span } ) ;
485
510
}
486
511
487
- Type :: Error
512
+ None
488
513
}
489
514
490
515
fn comparator_operand_type_rules (
0 commit comments