Skip to content

Commit 8b72d7a

Browse files
committed
Auto merge of rust-lang#127718 - cjgillot:find_field, r=compiler-errors
find_field does not need to be a query. The current implementation is quadratic in the number of nested fields. r? `@davidtwco` as you reviewed rust-lang#115367 Fixes rust-lang#121755
2 parents d9284af + b494d98 commit 8b72d7a

File tree

4 files changed

+73
-55
lines changed

4 files changed

+73
-55
lines changed

compiler/rustc_hir_analysis/src/collect.rs

-19
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, Ty, TyCtxt, Upcast};
3535
use rustc_middle::{bug, span_bug};
3636
use rustc_span::symbol::{kw, sym, Ident, Symbol};
3737
use rustc_span::{Span, DUMMY_SP};
38-
use rustc_target::abi::FieldIdx;
3938
use rustc_target::spec::abi;
4039
use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName;
4140
use rustc_trait_selection::infer::InferCtxtExt;
@@ -85,7 +84,6 @@ pub fn provide(providers: &mut Providers) {
8584
coroutine_kind,
8685
coroutine_for_closure,
8786
is_type_alias_impl_trait,
88-
find_field,
8987
..*providers
9088
};
9189
}
@@ -914,23 +912,6 @@ fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) {
914912
}
915913
}
916914

917-
fn find_field(tcx: TyCtxt<'_>, (def_id, ident): (DefId, Ident)) -> Option<FieldIdx> {
918-
let adt = tcx.adt_def(def_id);
919-
if adt.is_enum() {
920-
return None;
921-
}
922-
923-
adt.non_enum_variant().fields.iter_enumerated().find_map(|(idx, field)| {
924-
if field.is_unnamed() {
925-
let field_ty = tcx.type_of(field.did).instantiate_identity();
926-
let adt_def = field_ty.ty_adt_def().expect("expect Adt for unnamed field");
927-
tcx.find_field((adt_def.did(), ident)).map(|_| idx)
928-
} else {
929-
(field.ident(tcx).normalize_to_macros_2_0() == ident).then_some(idx)
930-
}
931-
})
932-
}
933-
934915
#[derive(Clone, Copy)]
935916
struct NestedSpan {
936917
span: Span,

compiler/rustc_hir_typeck/src/expr.rs

+72-32
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ use rustc_trait_selection::infer::InferCtxtExt;
5959
use rustc_trait_selection::traits::ObligationCtxt;
6060
use rustc_trait_selection::traits::{self, ObligationCauseCode};
6161

62+
use smallvec::SmallVec;
63+
6264
impl<'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) => {

compiler/rustc_hir_typeck/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// tidy-alphabetical-start
22
#![allow(rustc::diagnostic_outside_of_impl)]
33
#![allow(rustc::untranslatable_diagnostic)]
4+
#![feature(array_windows)]
45
#![feature(box_patterns)]
56
#![feature(control_flow_enum)]
67
#![feature(if_let_guard)]

compiler/rustc_middle/src/query/mod.rs

-4
Original file line numberDiff line numberDiff line change
@@ -2280,10 +2280,6 @@ rustc_queries! {
22802280
desc { "whether the item should be made inlinable across crates" }
22812281
separate_provide_extern
22822282
}
2283-
2284-
query find_field((def_id, ident): (DefId, rustc_span::symbol::Ident)) -> Option<rustc_target::abi::FieldIdx> {
2285-
desc { |tcx| "find the index of maybe nested field `{ident}` in `{}`", tcx.def_path_str(def_id) }
2286-
}
22872283
}
22882284

22892285
rustc_query_append! { define_callbacks! }

0 commit comments

Comments
 (0)