Skip to content

Commit

Permalink
Make hir ProjectionKind more precise
Browse files Browse the repository at this point in the history
This commit also categorizing access as Field, Index, or Subslice.

Ideas are taken from `mir::ProjectionElem`.

Proposed changes: https://github.com/rust-lang/project-rfc-2229/blob/master/hir-place-target.md

Closes: rust-lang/project-rfc-2229#1,
rust-lang/project-rfc-2229#2

Co-authored-by: Aman Arora <me@aman-arora.com>
Co-authored-by: Chris Pardy <chrispardy36@gmail.com>
Co-authored-by: Dhruv Jauhar <dhruvjhr@gmail.com>
  • Loading branch information
3 people committed Jul 7, 2020
1 parent 70f9d23 commit f2754af
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 12 deletions.
3 changes: 3 additions & 0 deletions src/librustc_typeck/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::PatKind;
use rustc_index::vec::Idx;
use rustc_infer::infer::InferCtxt;
use rustc_middle::ty::{self, adjustment, TyCtxt};
use rustc_target::abi::VariantIdx;

use crate::mem_categorization as mc;
use rustc_span::Span;
Expand Down Expand Up @@ -396,6 +398,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
&*with_expr,
with_place.clone(),
with_field.ty(self.tcx(), substs),
mc::ProjectionKind::Field(f_index as u32, VariantIdx::new(0)),
);
self.delegate_consume(&field_place);
}
Expand Down
183 changes: 171 additions & 12 deletions src/librustc_typeck/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,13 @@ use rustc_middle::ty::{self, Ty, TyCtxt};

use rustc_data_structures::fx::FxIndexMap;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def_id::LocalDefId;
use rustc_hir::PatKind;
use rustc_index::vec::Idx;
use rustc_infer::infer::InferCtxt;
use rustc_span::Span;
use rustc_target::abi::VariantIdx;
use rustc_trait_selection::infer::InferCtxtExt;

#[derive(Clone, Debug)]
Expand All @@ -77,7 +79,22 @@ pub enum PlaceBase {
pub enum ProjectionKind {
/// A dereference of a pointer, reference or `Box<T>` of the given type
Deref,
/// An index or a field

/// `B.F` where `B` is the base expression and `F` is
/// the field. The field is identified by which variant
/// it appears in along with a field index. The variant
/// is used for enums.
Field(u32, VariantIdx),

/// Some index like `B[x]`, where `B` is the base
/// expression. We don't preserve the index `x` because
/// we won't need it.
Index,

/// A subslice covering a range of values like `B[x..y]`.
Subslice,

/// Other
Other,
}

Expand Down Expand Up @@ -406,7 +423,20 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
hir::ExprKind::Field(ref base, _) => {
let base = self.cat_expr(&base)?;
debug!("cat_expr(cat_field): id={} expr={:?} base={:?}", expr.hir_id, expr, base);
Ok(self.cat_projection(expr, base, expr_ty))

let field_idx = self
.tables
.field_indices()
.get(expr.hir_id)
.cloned()
.expect("Field index not found");

Ok(self.cat_projection(
expr,
base,
expr_ty,
ProjectionKind::Field(field_idx as u32, VariantIdx::new(0)),
))
}

hir::ExprKind::Index(ref base, _) => {
Expand All @@ -419,7 +449,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
self.cat_overloaded_place(expr, base)
} else {
let base = self.cat_expr(&base)?;
Ok(self.cat_projection(expr, base, expr_ty))
Ok(self.cat_projection(expr, base, expr_ty, ProjectionKind::Index))
}
}

Expand Down Expand Up @@ -533,9 +563,10 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
node: &N,
base_place: PlaceWithHirId<'tcx>,
ty: Ty<'tcx>,
kind: ProjectionKind,
) -> PlaceWithHirId<'tcx> {
let mut projections = base_place.place.projections;
projections.push(Projection { kind: ProjectionKind::Other, ty: ty });
projections.push(Projection { kind: kind, ty: ty });
let ret = PlaceWithHirId::new(
node.hir_id(),
base_place.place.base_ty,
Expand Down Expand Up @@ -609,6 +640,62 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
self.cat_pattern_(place, pat, &mut op)
}

fn variant_index_for_adt(
&self,
qpath: &hir::QPath<'_>,
hir_id: hir::HirId,
span: Span,
) -> Option<VariantIdx> {
let res = self.tables.qpath_res(qpath, hir_id);
let ty = self.tables.node_type(hir_id);
let adt_def = match ty.kind {
ty::Adt(adt_def, _) => adt_def,
_ => {
self.tcx()
.sess
.delay_span_bug(span, "struct or tuple struct pattern not applied to an ADT");

return None;
}
};

match res {
Res::Def(DefKind::Variant, variant_id) => {
Some(adt_def.variant_index_with_id(variant_id))
}
Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => {
Some(adt_def.variant_index_with_ctor_id(variant_ctor_id))
}
Res::Def(DefKind::Ctor(CtorOf::Struct, ..), _)
| Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _)
| Res::SelfCtor(..)
| Res::SelfTy(..) => {
// Structs and Unions have only have one variant.
Some(VariantIdx::new(0))
}
_ => bug!("expected ADT path, found={:?}", res),
}
}

fn total_fields_in_adt_variant(
&self,
hir_id: hir::HirId,
variant_index: VariantIdx,
span: Span,
) -> Option<usize> {
let ty = self.tables.node_type(hir_id);
match ty.kind {
ty::Adt(adt_def, _) => Some(adt_def.variants[variant_index].fields.len()),
_ => {
self.tcx()
.sess
.delay_span_bug(span, "struct or tuple struct pattern not applied to an ADT");

None
}
}
}

// FIXME(#19596) This is a workaround, but there should be a better way to do this
fn cat_pattern_<F>(
&self,
Expand Down Expand Up @@ -679,20 +766,82 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
op(&place_with_id, pat);

match pat.kind {
PatKind::TupleStruct(_, ref subpats, _) | PatKind::Tuple(ref subpats, _) => {
// S(p1, ..., pN) or (p1, ..., pN)
PatKind::Tuple(ref subpats, _) => {
// (p1, ..., pN)
for subpat in subpats.iter() {
let subpat_ty = self.pat_ty_adjusted(&subpat)?;
let sub_place = self.cat_projection(pat, place_with_id.clone(), subpat_ty);
let sub_place = self.cat_projection(
pat,
place_with_id.clone(),
subpat_ty,
ProjectionKind::Other,
);
self.cat_pattern_(sub_place, &subpat, op)?;
}
}

PatKind::Struct(_, field_pats, _) => {
PatKind::TupleStruct(ref qpath, ref subpats, dots_pos) => {
// S(p1, ..., pN)
let variant_index = if let Some(variant_index) =
self.variant_index_for_adt(qpath, pat.hir_id, pat.span)
{
variant_index
} else {
return Err(());
};

// We can unwrap safely here
// Any ADT related errors would've been catched when reading the `variant_index`
let total_fields =
self.total_fields_in_adt_variant(pat.hir_id, variant_index, pat.span).unwrap();

for (i, subpat) in subpats.iter().enumerate() {
let subpat_ty = self.pat_ty_adjusted(&subpat)?;
let projection_kind = if let Some(dots_pos) = dots_pos {
if i < dots_pos {
ProjectionKind::Field(i as u32, variant_index)
} else if dots_pos == i {
ProjectionKind::Other
} else {
let field_index = total_fields - subpats.len() + i;
ProjectionKind::Field(field_index as u32, variant_index)
}
} else {
ProjectionKind::Field(i as u32, variant_index)
};

let sub_place =
self.cat_projection(pat, place_with_id.clone(), subpat_ty, projection_kind);
self.cat_pattern_(sub_place, &subpat, op)?;
}
}

PatKind::Struct(ref qpath, field_pats, _) => {
// S { f1: p1, ..., fN: pN }

let variant_index = if let Some(variant_index) =
self.variant_index_for_adt(qpath, pat.hir_id, pat.span)
{
variant_index
} else {
return Err(());
};

for fp in field_pats {
let field_ty = self.pat_ty_adjusted(&fp.pat)?;
let field_place = self.cat_projection(pat, place_with_id.clone(), field_ty);
let field_index = self
.tables
.field_indices()
.get(fp.hir_id)
.cloned()
.expect("no index for a field");

let field_place = self.cat_projection(
pat,
place_with_id.clone(),
field_ty,
ProjectionKind::Field(field_index as u32, variant_index),
);
self.cat_pattern_(field_place, &fp.pat, op)?;
}
}
Expand Down Expand Up @@ -723,13 +872,23 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
return Err(());
}
};
let elt_place = self.cat_projection(pat, place_with_id.clone(), element_ty);
let elt_place = self.cat_projection(
pat,
place_with_id.clone(),
element_ty,
ProjectionKind::Subslice,
);
for before_pat in before {
self.cat_pattern_(elt_place.clone(), &before_pat, op)?;
}
if let Some(ref slice_pat) = *slice {
let slice_pat_ty = self.pat_ty_adjusted(&slice_pat)?;
let slice_place = self.cat_projection(pat, place_with_id, slice_pat_ty);
let slice_place = self.cat_projection(
pat,
place_with_id,
slice_pat_ty,
ProjectionKind::Subslice,
);
self.cat_pattern_(slice_place, &slice_pat, op)?;
}
for after_pat in after {
Expand Down

0 comments on commit f2754af

Please sign in to comment.