@@ -59,6 +59,8 @@ use rustc_trait_selection::infer::InferCtxtExt;
59
59
use rustc_trait_selection:: traits:: ObligationCtxt ;
60
60
use rustc_trait_selection:: traits:: { self , ObligationCauseCode } ;
61
61
62
+ use smallvec:: SmallVec ;
63
+
62
64
impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
63
65
pub fn check_expr_has_type_or_error (
64
66
& self ,
@@ -2318,6 +2320,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2318
2320
display
2319
2321
}
2320
2322
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
+
2321
2361
// Check field access expressions
2322
2362
fn check_field (
2323
2363
& self ,
@@ -2339,44 +2379,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2339
2379
let body_hir_id = self . tcx . local_def_id_to_hir_id ( self . body_id ) ;
2340
2380
let ( ident, def_scope) =
2341
2381
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 ;
2346
2382
2347
2383
// 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 ( ) {
2349
2385
return Ty :: new_error ( self . tcx ( ) , guar) ;
2350
2386
}
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 ( ) ) ;
2366
2387
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) ;
2377
2416
}
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 ( ) ) ) ;
2380
2420
}
2381
2421
}
2382
2422
ty:: Tuple ( tys) => {
0 commit comments