@@ -59,6 +59,8 @@ use rustc_trait_selection::infer::InferCtxtExt;
5959use rustc_trait_selection:: traits:: ObligationCtxt ;
6060use rustc_trait_selection:: traits:: { self , ObligationCauseCode } ;
6161
62+ use smallvec:: SmallVec ;
63+
6264impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
6365 pub fn check_expr_has_type_or_error (
6466 & self ,
@@ -2318,6 +2320,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23182320 display
23192321 }
23202322
2323+ /// Find the position of a field named `ident` in `base_def`, accounting for unnammed fields.
2324+ /// Return whether such a field has been found. The path to it is stored in `nested_fields`.
2325+ /// `ident` must have been adjusted beforehand.
2326+ fn find_adt_field (
2327+ & self ,
2328+ base_def : ty:: AdtDef < ' tcx > ,
2329+ ident : Ident ,
2330+ nested_fields : & mut SmallVec < [ ( FieldIdx , & ' tcx ty:: FieldDef ) ; 1 ] > ,
2331+ ) -> bool {
2332+ // No way to find a field in an enum.
2333+ if base_def. is_enum ( ) {
2334+ return false ;
2335+ }
2336+
2337+ for ( field_idx, field) in base_def. non_enum_variant ( ) . fields . iter_enumerated ( ) {
2338+ if field. is_unnamed ( ) {
2339+ // We have an unnamed field, recurse into the nested ADT to find `ident`.
2340+ // If we find it there, return immediately, and `nested_fields` will contain the
2341+ // correct path.
2342+ nested_fields. push ( ( field_idx, field) ) ;
2343+
2344+ let field_ty = self . tcx . type_of ( field. did ) . instantiate_identity ( ) ;
2345+ let adt_def = field_ty. ty_adt_def ( ) . expect ( "expect Adt for unnamed field" ) ;
2346+ if self . find_adt_field ( adt_def, ident, & mut * nested_fields) {
2347+ return true ;
2348+ }
2349+
2350+ nested_fields. pop ( ) ;
2351+ } else if field. ident ( self . tcx ) . normalize_to_macros_2_0 ( ) == ident {
2352+ // We found the field we wanted.
2353+ nested_fields. push ( ( field_idx, field) ) ;
2354+ return true ;
2355+ }
2356+ }
2357+
2358+ false
2359+ }
2360+
23212361 // Check field access expressions
23222362 fn check_field (
23232363 & self ,
@@ -2339,44 +2379,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23392379 let body_hir_id = self . tcx . local_def_id_to_hir_id ( self . body_id ) ;
23402380 let ( ident, def_scope) =
23412381 self . tcx . adjust_ident_and_get_scope ( field, base_def. did ( ) , body_hir_id) ;
2342- let mut adt_def = * base_def;
2343- let mut last_ty = None ;
2344- let mut nested_fields = Vec :: new ( ) ;
2345- let mut index = None ;
23462382
23472383 // we don't care to report errors for a struct if the struct itself is tainted
2348- if let Err ( guar) = adt_def . non_enum_variant ( ) . has_errors ( ) {
2384+ if let Err ( guar) = base_def . non_enum_variant ( ) . has_errors ( ) {
23492385 return Ty :: new_error ( self . tcx ( ) , guar) ;
23502386 }
2351- while let Some ( idx) = self . tcx . find_field ( ( adt_def. did ( ) , ident) ) {
2352- let & mut first_idx = index. get_or_insert ( idx) ;
2353- let field = & adt_def. non_enum_variant ( ) . fields [ idx] ;
2354- let field_ty = self . field_ty ( expr. span , field, args) ;
2355- if let Some ( ty) = last_ty {
2356- nested_fields. push ( ( ty, idx) ) ;
2357- }
2358- if field. ident ( self . tcx ) . normalize_to_macros_2_0 ( ) == ident {
2359- // Save the index of all fields regardless of their visibility in case
2360- // of error recovery.
2361- self . write_field_index ( expr. hir_id , first_idx, nested_fields) ;
2362- let adjustments = self . adjust_steps ( & autoderef) ;
2363- if field. vis . is_accessible_from ( def_scope, self . tcx ) {
2364- self . apply_adjustments ( base, adjustments) ;
2365- self . register_predicates ( autoderef. into_obligations ( ) ) ;
23662387
2367- self . tcx . check_stability (
2368- field. did ,
2369- Some ( expr. hir_id ) ,
2370- expr. span ,
2371- None ,
2372- ) ;
2373- return field_ty;
2374- }
2375- private_candidate = Some ( ( adjustments, base_def. did ( ) ) ) ;
2376- break ;
2388+ let mut field_path = SmallVec :: new ( ) ;
2389+ if self . find_adt_field ( * base_def, ident, & mut field_path) {
2390+ let ( first_idx, _) = field_path[ 0 ] ;
2391+ let ( _, last_field) = field_path. last ( ) . unwrap ( ) ;
2392+
2393+ // Save the index of all fields regardless of their visibility in case
2394+ // of error recovery.
2395+ let nested_fields = field_path[ ..]
2396+ . array_windows ( )
2397+ . map ( |[ ( _, outer) , ( inner_idx, _) ] | {
2398+ let outer_ty = self . field_ty ( expr. span , outer, args) ;
2399+ ( outer_ty, * inner_idx)
2400+ } )
2401+ . collect ( ) ;
2402+ self . write_field_index ( expr. hir_id , first_idx, nested_fields) ;
2403+
2404+ let adjustments = self . adjust_steps ( & autoderef) ;
2405+ if last_field. vis . is_accessible_from ( def_scope, self . tcx ) {
2406+ self . apply_adjustments ( base, adjustments) ;
2407+ self . register_predicates ( autoderef. into_obligations ( ) ) ;
2408+
2409+ self . tcx . check_stability (
2410+ last_field. did ,
2411+ Some ( expr. hir_id ) ,
2412+ expr. span ,
2413+ None ,
2414+ ) ;
2415+ return self . field_ty ( expr. span , last_field, args) ;
23772416 }
2378- last_ty = Some ( field_ty) ;
2379- adt_def = field_ty. ty_adt_def ( ) . expect ( "expect Adt for unnamed field" ) ;
2417+
2418+ // The field is not accessible, fall through to error reporting.
2419+ private_candidate = Some ( ( adjustments, base_def. did ( ) ) ) ;
23802420 }
23812421 }
23822422 ty:: Tuple ( tys) => {
0 commit comments