Skip to content

Commit

Permalink
Lowering field access for anonymous adts
Browse files Browse the repository at this point in the history
  • Loading branch information
frank-king committed Jan 5, 2024
1 parent 128ea96 commit 3dce559
Show file tree
Hide file tree
Showing 17 changed files with 388 additions and 68 deletions.
27 changes: 11 additions & 16 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1369,33 +1369,28 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// // ^_____________________|
// }
// ```
TyKind::AnonStruct(def_node_id, fields) | TyKind::AnonUnion(def_node_id, fields) => {
let (def_kind, item_kind): (DefKind, fn(_, _) -> _) = match t.kind {
TyKind::AnonStruct(..) => (DefKind::Struct, hir::ItemKind::Struct),
TyKind::AnonUnion(..) => (DefKind::Union, hir::ItemKind::Union),
_ => unreachable!(),
};
let def_id = self.create_def(
self.current_hir_id_owner.def_id,
*def_node_id,
kw::Empty,
def_kind,
t.span,
);
TyKind::AnonStruct(node_id, fields) | TyKind::AnonUnion(node_id, fields) => {
// Here its `def_id` is created in `build_reduced_graph`.
let def_id = self.local_def_id(*node_id);
debug!(?def_id);
let owner_id = hir::OwnerId { def_id };
self.with_hir_id_owner(*def_node_id, |this| {
self.with_hir_id_owner(*node_id, |this| {
let fields = this.arena.alloc_from_iter(
fields.iter().enumerate().map(|f| this.lower_field_def(f)),
);
let span = t.span;
let variant_data = hir::VariantData::Struct(fields, false);
let variant_data = hir::VariantData::Struct { fields, recovered: false };
// FIXME: capture the generics from the outer adt.
let generics = hir::Generics::empty();
let kind = match t.kind {
TyKind::AnonStruct(..) => hir::ItemKind::Struct(variant_data, generics),
TyKind::AnonUnion(..) => hir::ItemKind::Union(variant_data, generics),
_ => unreachable!(),
};
hir::OwnerNode::Item(this.arena.alloc(hir::Item {
ident: Ident::new(kw::Empty, span),
owner_id,
kind: item_kind(variant_data, generics),
kind,
span: this.lower_span(span),
vis_span: this.lower_span(span.shrink_to_lo()),
}))
Expand Down
26 changes: 20 additions & 6 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use rustc_middle::ty::util::{Discr, IntTypeExt};
use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, ToPredicate, Ty, TyCtxt};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::Span;
use rustc_target::abi::FieldIdx;
use rustc_target::spec::abi;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName;
Expand Down Expand Up @@ -81,6 +82,7 @@ pub fn provide(providers: &mut Providers) {
coroutine_kind,
collect_mod_item_types,
is_type_alias_impl_trait,
find_field,
..*providers
};
}
Expand Down Expand Up @@ -867,15 +869,27 @@ fn check_field_uniqueness(
// Abort due to errors (there must be an error if an unnamed field
// has any type kind other than an anonymous adt or a named adt)
_ => {
debug_assert!(tcx.sess.has_errors().is_some());
tcx.sess.abort_if_errors()
debug_assert!(tcx.dcx().has_errors().is_some());
tcx.dcx().abort_if_errors()
}
}
return;
}
check(field.ident, field.span.into());
}

fn find_field(tcx: TyCtxt<'_>, (def_id, ident): (DefId, Ident)) -> Option<FieldIdx> {
tcx.adt_def(def_id).non_enum_variant().fields.iter_enumerated().find_map(|(idx, field)| {
if field.is_unnamed() {
let field_ty = tcx.type_of(field.did).instantiate_identity();
let adt_def = field_ty.ty_adt_def().expect("expect Adt for unnamed field");
tcx.find_field((adt_def.did(), ident)).map(|_| idx)
} else {
(field.ident(tcx).normalize_to_macros_2_0() == ident).then_some(idx)
}
})
}

fn convert_variant(
tcx: TyCtxt<'_>,
variant_did: Option<LocalDefId>,
Expand All @@ -900,14 +914,14 @@ fn convert_variant(
let ident = ident.normalize_to_macros_2_0();
match (field_decl, seen_fields.get(&ident).copied()) {
(NotNested(span), Some(NotNested(prev_span))) => {
tcx.sess.emit_err(errors::FieldAlreadyDeclared::NotNested {
tcx.dcx().emit_err(errors::FieldAlreadyDeclared::NotNested {
field_name,
span,
prev_span,
});
}
(NotNested(span), Some(Nested(prev))) => {
tcx.sess.emit_err(errors::FieldAlreadyDeclared::PreviousNested {
tcx.dcx().emit_err(errors::FieldAlreadyDeclared::PreviousNested {
field_name,
span,
prev_span: prev.span,
Expand All @@ -918,15 +932,15 @@ fn convert_variant(
Nested(NestedSpan { span, nested_field_span }),
Some(NotNested(prev_span)),
) => {
tcx.sess.emit_err(errors::FieldAlreadyDeclared::CurrentNested {
tcx.dcx().emit_err(errors::FieldAlreadyDeclared::CurrentNested {
field_name,
span,
nested_field_span,
prev_span,
});
}
(Nested(NestedSpan { span, nested_field_span }), Some(Nested(prev))) => {
tcx.sess.emit_err(errors::FieldAlreadyDeclared::BothNested {
tcx.dcx().emit_err(errors::FieldAlreadyDeclared::BothNested {
field_name,
span,
nested_field_span,
Expand Down
54 changes: 37 additions & 17 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1708,7 +1708,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let ident = tcx.adjust_ident(field.ident, variant.def_id);
let field_type = if let Some((i, v_field)) = remaining_fields.remove(&ident) {
seen_fields.insert(ident, field.span);
self.write_field_index(field.hir_id, i);
// FIXME: handle nested fields
self.write_field_index(field.hir_id, i, Vec::new());

// We don't look at stability attributes on
// struct-like enums (yet...), but it's definitely not
Expand Down Expand Up @@ -2352,24 +2353,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let body_hir_id = self.tcx.local_def_id_to_hir_id(self.body_id);
let (ident, def_scope) =
self.tcx.adjust_ident_and_get_scope(field, base_def.did(), body_hir_id);
let fields = &base_def.non_enum_variant().fields;
if let Some((index, field)) = fields
.iter_enumerated()
.find(|(_, f)| f.ident(self.tcx).normalize_to_macros_2_0() == ident)
{
let mut adt_def = *base_def;
let mut last_ty = None;
let mut nested_fields = Vec::new();
let mut index = None;
while let Some(idx) = self.tcx.find_field((adt_def.did(), ident)) {
let &mut first_idx = index.get_or_insert(idx);
let field = &adt_def.non_enum_variant().fields[idx];
let field_ty = self.field_ty(expr.span, field, args);
// Save the index of all fields regardless of their visibility in case
// of error recovery.
self.write_field_index(expr.hir_id, index);
let adjustments = self.adjust_steps(&autoderef);
if field.vis.is_accessible_from(def_scope, self.tcx) {
self.apply_adjustments(base, adjustments);
self.register_predicates(autoderef.into_obligations());
if let Some(ty) = last_ty {
nested_fields.push((ty, idx));
}
if field.ident(self.tcx).normalize_to_macros_2_0() == ident {
// Save the index of all fields regardless of their visibility in case
// of error recovery.
self.write_field_index(expr.hir_id, first_idx, nested_fields);
let adjustments = self.adjust_steps(&autoderef);
if field.vis.is_accessible_from(def_scope, self.tcx) {
self.apply_adjustments(base, adjustments);
self.register_predicates(autoderef.into_obligations());

self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span, None);
return field_ty;
self.tcx.check_stability(
field.did,
Some(expr.hir_id),
expr.span,
None,
);
return field_ty;
}
private_candidate = Some((adjustments, base_def.did()));
break;
}
private_candidate = Some((adjustments, base_def.did()));
last_ty = Some(field_ty);
adt_def = field_ty.ty_adt_def().expect("expect Adt for unnamed field");
}
}
ty::Tuple(tys) => {
Expand All @@ -2380,7 +2396,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.apply_adjustments(base, adjustments);
self.register_predicates(autoderef.into_obligations());

self.write_field_index(expr.hir_id, FieldIdx::from_usize(index));
self.write_field_index(
expr.hir_id,
FieldIdx::from_usize(index),
Vec::new(),
);
return field_ty;
}
}
Expand Down
10 changes: 9 additions & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

pub fn write_field_index(&self, hir_id: hir::HirId, index: FieldIdx) {
pub fn write_field_index(
&self,
hir_id: hir::HirId,
index: FieldIdx,
nested_fields: Vec<(Ty<'tcx>, FieldIdx)>,
) {
self.typeck_results.borrow_mut().field_indices_mut().insert(hir_id, index);
if !nested_fields.is_empty() {
self.typeck_results.borrow_mut().nested_fields_mut().insert(hir_id, nested_fields);
}
}

#[instrument(level = "debug", skip(self))]
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1390,7 +1390,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
field_map
.get(&ident)
.map(|(i, f)| {
self.write_field_index(field.hir_id, *i);
// FIXME: handle nested fields
self.write_field_index(field.hir_id, *i, Vec::new());
self.tcx.check_stability(f.did, Some(pat.hir_id), span, None);
self.field_ty(span, f, args)
})
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_hir_typeck/src/writeback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,11 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
{
self.typeck_results.field_indices_mut().insert(hir_id, index);
}
if let Some(nested_fields) =
self.fcx.typeck_results.borrow_mut().nested_fields_mut().remove(hir_id)
{
self.typeck_results.nested_fields_mut().insert(hir_id, nested_fields);
}
}

#[instrument(skip(self, span), level = "debug")]
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/query/erase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ trivial! {
Option<rustc_span::def_id::DefId>,
Option<rustc_span::def_id::LocalDefId>,
Option<rustc_span::Span>,
Option<rustc_target::abi::FieldIdx>,
Option<rustc_target::spec::PanicStrategy>,
Option<usize>,
Result<(), rustc_errors::ErrorGuaranteed>,
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2200,6 +2200,10 @@ rustc_queries! {
desc { "whether the item should be made inlinable across crates" }
separate_provide_extern
}

query find_field((def_id, ident): (DefId, rustc_span::symbol::Ident)) -> Option<rustc_target::abi::FieldIdx> {
desc { |tcx| "find the index of maybe nested field `{ident}` in `{}`", tcx.def_path_str(def_id) }
}
}

rustc_query_append! { define_callbacks! }
Expand Down
19 changes: 19 additions & 0 deletions compiler/rustc_middle/src/ty/typeck_results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ pub struct TypeckResults<'tcx> {
/// belongs, but it may not exist if it's a tuple field (`tuple.0`).
field_indices: ItemLocalMap<FieldIdx>,

/// Resolved types and indices for the nested fields' accesses of `obj.field` (expanded
/// to `obj._(1)._(2).field` in THIR). This map only stores the intermediate type
/// of `obj._(1)` and index of `_(1)._(2)`, and the type of `_(1)._(2)`, and the index of
/// `_(2).field`.
nested_fields: ItemLocalMap<Vec<(Ty<'tcx>, FieldIdx)>>,

/// Stores the types for various nodes in the AST. Note that this table
/// is not guaranteed to be populated outside inference. See
/// typeck::check::fn_ctxt for details.
Expand Down Expand Up @@ -214,6 +220,7 @@ impl<'tcx> TypeckResults<'tcx> {
hir_owner,
type_dependent_defs: Default::default(),
field_indices: Default::default(),
nested_fields: Default::default(),
user_provided_types: Default::default(),
user_provided_sigs: Default::default(),
node_types: Default::default(),
Expand Down Expand Up @@ -285,6 +292,18 @@ impl<'tcx> TypeckResults<'tcx> {
self.field_indices().get(id).cloned()
}

pub fn nested_fields(&self) -> LocalTableInContext<'_, Vec<(Ty<'tcx>, FieldIdx)>> {
LocalTableInContext { hir_owner: self.hir_owner, data: &self.nested_fields }
}

pub fn nested_fields_mut(&mut self) -> LocalTableInContextMut<'_, Vec<(Ty<'tcx>, FieldIdx)>> {
LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.nested_fields }
}

pub fn nested_field_tys_and_indices(&self, id: hir::HirId) -> &[(Ty<'tcx>, FieldIdx)] {
self.nested_fields().get(id).map_or(&[], Vec::as_slice)
}

pub fn user_provided_types(&self) -> LocalTableInContext<'_, CanonicalUserType<'tcx>> {
LocalTableInContext { hir_owner: self.hir_owner, data: &self.user_provided_types }
}
Expand Down
20 changes: 15 additions & 5 deletions compiler/rustc_mir_build/src/thir/cx/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -730,11 +730,21 @@ impl<'tcx> Cx<'tcx> {
});
ExprKind::Loop { body }
}
hir::ExprKind::Field(source, ..) => ExprKind::Field {
lhs: self.mirror_expr(source),
variant_index: FIRST_VARIANT,
name: self.typeck_results.field_index(expr.hir_id),
},
hir::ExprKind::Field(source, ..) => {
let mut kind = ExprKind::Field {
lhs: self.mirror_expr(source),
variant_index: FIRST_VARIANT,
name: self.typeck_results.field_index(expr.hir_id),
};
let nested_field_tys_and_indices =
self.typeck_results.nested_field_tys_and_indices(expr.hir_id);
for &(ty, idx) in nested_field_tys_and_indices {
let expr = Expr { temp_lifetime, ty, span: source.span, kind };
let lhs = self.thir.exprs.push(expr);
kind = ExprKind::Field { lhs, variant_index: FIRST_VARIANT, name: idx };
}
kind
}
hir::ExprKind::Cast(source, cast_ty) => {
// Check for a user-given type annotation on this `cast`
let user_provided_types = self.typeck_results.user_provided_types();
Expand Down
Loading

0 comments on commit 3dce559

Please sign in to comment.