Skip to content

Commit

Permalink
Rollup merge of rust-lang#114022 - oli-obk:tait_ice_alias_field_proje…
Browse files Browse the repository at this point in the history
…ction, r=cjgillot

Perform OpaqueCast field projection on HIR, too.

fixes rust-lang#105819

This is necessary for closure captures in 2021 edition, as they capture individual fields, not the full mentioned variables. So it may try to capture a field of an opaque (because the hidden type is known to be something with a field).

See rust-lang#99806 for when and why we added OpaqueCast to MIR.
  • Loading branch information
matthiaskrgr authored Aug 3, 2023
2 parents defed62 + e390dc9 commit 2ddd80a
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 62 deletions.
6 changes: 2 additions & 4 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,14 +498,13 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {

/// Checks that the types internal to the `place` match up with
/// what would be expected.
#[instrument(level = "debug", skip(self, location), ret)]
fn sanitize_place(
&mut self,
place: &Place<'tcx>,
location: Location,
context: PlaceContext,
) -> PlaceTy<'tcx> {
debug!("sanitize_place: {:?}", place);

let mut place_ty = PlaceTy::from_ty(self.body().local_decls[place.local].ty);

for elem in place.projection.iter() {
Expand Down Expand Up @@ -608,7 +607,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
}
}

#[instrument(skip(self), level = "debug")]
#[instrument(skip(self, location), ret, level = "debug")]
fn sanitize_projection(
&mut self,
base: PlaceTy<'tcx>,
Expand All @@ -617,7 +616,6 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
location: Location,
context: PlaceContext,
) -> PlaceTy<'tcx> {
debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, place);
let tcx = self.tcx();
let base_ty = base.ty;
match pi {
Expand Down
84 changes: 40 additions & 44 deletions compiler/rustc_hir_typeck/src/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,14 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
}

/// Like `pat_ty`, but ignores implicit `&` patterns.
#[instrument(level = "debug", skip(self), ret)]
fn pat_ty_unadjusted(&self, pat: &hir::Pat<'_>) -> McResult<Ty<'tcx>> {
let base_ty = self.node_ty(pat.hir_id)?;
debug!("pat_ty(pat={:?}) base_ty={:?}", pat, base_ty);
trace!(?base_ty);

// This code detects whether we are looking at a `ref x`,
// and if so, figures out what the type *being borrowed* is.
let ret_ty = match pat.kind {
match pat.kind {
PatKind::Binding(..) => {
let bm = *self
.typeck_results
Expand All @@ -217,21 +218,18 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
// but what we want here is the type of the underlying value being borrowed.
// So peel off one-level, turning the &T into T.
match base_ty.builtin_deref(false) {
Some(t) => t.ty,
Some(t) => Ok(t.ty),
None => {
debug!("By-ref binding of non-derefable type {:?}", base_ty);
return Err(());
debug!("By-ref binding of non-derefable type");
Err(())
}
}
} else {
base_ty
Ok(base_ty)
}
}
_ => base_ty,
};
debug!("pat_ty(pat={:?}) ret_ty={:?}", pat, ret_ty);

Ok(ret_ty)
_ => Ok(base_ty),
}
}

pub(crate) fn cat_expr(&self, expr: &hir::Expr<'_>) -> McResult<PlaceWithHirId<'tcx>> {
Expand Down Expand Up @@ -299,13 +297,11 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
}
}

#[instrument(level = "debug", skip(self))]
#[instrument(level = "debug", skip(self), ret)]
pub(crate) fn cat_expr_unadjusted(
&self,
expr: &hir::Expr<'_>,
) -> McResult<PlaceWithHirId<'tcx>> {
debug!("cat_expr: id={} expr={:?}", expr.hir_id, expr);

let expr_ty = self.expr_ty(expr)?;
match expr.kind {
hir::ExprKind::Unary(hir::UnOp::Deref, ref e_base) => {
Expand All @@ -319,7 +315,7 @@ 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);
debug!(?base);

let field_idx = self
.typeck_results
Expand Down Expand Up @@ -389,7 +385,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
}
}

#[instrument(level = "debug", skip(self, span))]
#[instrument(level = "debug", skip(self, span), ret)]
pub(crate) fn cat_res(
&self,
hir_id: hir::HirId,
Expand Down Expand Up @@ -430,6 +426,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
/// Note: the actual upvar access contains invisible derefs of closure
/// environment and upvar reference as appropriate. Only regionck cares
/// about these dereferences, so we let it compute them as needed.
#[instrument(level = "debug", skip(self), ret)]
fn cat_upvar(&self, hir_id: hir::HirId, var_id: hir::HirId) -> McResult<PlaceWithHirId<'tcx>> {
let closure_expr_def_id = self.body_owner;

Expand All @@ -439,41 +436,44 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
};
let var_ty = self.node_ty(var_id)?;

let ret = PlaceWithHirId::new(hir_id, var_ty, PlaceBase::Upvar(upvar_id), Vec::new());

debug!("cat_upvar ret={:?}", ret);
Ok(ret)
Ok(PlaceWithHirId::new(hir_id, var_ty, PlaceBase::Upvar(upvar_id), Vec::new()))
}

#[instrument(level = "debug", skip(self), ret)]
pub(crate) fn cat_rvalue(
&self,
hir_id: hir::HirId,
span: Span,
expr_ty: Ty<'tcx>,
) -> PlaceWithHirId<'tcx> {
debug!("cat_rvalue hir_id={:?}, expr_ty={:?}, span={:?}", hir_id, expr_ty, span);
let ret = PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Rvalue, Vec::new());
debug!("cat_rvalue ret={:?}", ret);
ret
PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Rvalue, Vec::new())
}

#[instrument(level = "debug", skip(self, node), ret)]
pub(crate) fn cat_projection<N: HirNode>(
&self,
node: &N,
base_place: PlaceWithHirId<'tcx>,
ty: Ty<'tcx>,
kind: ProjectionKind,
) -> PlaceWithHirId<'tcx> {
let place_ty = base_place.place.ty();
let mut projections = base_place.place.projections;

let node_ty = self.typeck_results.node_type(node.hir_id());
// Opaque types can't have field projections, but we can instead convert
// the current place in-place (heh) to the hidden type, and then apply all
// follow up projections on that.
if node_ty != place_ty && place_ty.has_opaque_types() {
projections.push(Projection { kind: ProjectionKind::OpaqueCast, ty: node_ty });
}
projections.push(Projection { kind, ty });
let ret = PlaceWithHirId::new(
PlaceWithHirId::new(
node.hir_id(),
base_place.place.base_ty,
base_place.place.base,
projections,
);
debug!("cat_field ret {:?}", ret);
ret
)
}

#[instrument(level = "debug", skip(self))]
Expand All @@ -497,7 +497,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
self.cat_deref(expr, base)
}

#[instrument(level = "debug", skip(self, node))]
#[instrument(level = "debug", skip(self, node), ret)]
fn cat_deref(
&self,
node: &impl HirNode,
Expand All @@ -514,14 +514,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
let mut projections = base_place.place.projections;
projections.push(Projection { kind: ProjectionKind::Deref, ty: deref_ty });

let ret = PlaceWithHirId::new(
Ok(PlaceWithHirId::new(
node.hir_id(),
base_place.place.base_ty,
base_place.place.base,
projections,
);
debug!("cat_deref ret {:?}", ret);
Ok(ret)
))
}

pub(crate) fn cat_pattern<F>(
Expand Down Expand Up @@ -603,6 +601,13 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
}
}

/// Here, `place` is the `PlaceWithHirId` being matched and pat is the pattern it
/// is being matched against.
///
/// In general, the way that this works is that we walk down the pattern,
/// constructing a `PlaceWithHirId` that represents the path that will be taken
/// to reach the value being matched.
#[instrument(skip(self, op), ret, level = "debug")]
fn cat_pattern_<F>(
&self,
mut place_with_id: PlaceWithHirId<'tcx>,
Expand All @@ -612,15 +617,6 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
where
F: FnMut(&PlaceWithHirId<'tcx>, &hir::Pat<'_>),
{
// Here, `place` is the `PlaceWithHirId` being matched and pat is the pattern it
// is being matched against.
//
// In general, the way that this works is that we walk down the pattern,
// constructing a `PlaceWithHirId` that represents the path that will be taken
// to reach the value being matched.

debug!("cat_pattern(pat={:?}, place_with_id={:?})", pat, place_with_id);

// If (pattern) adjustments are active for this pattern, adjust the `PlaceWithHirId` correspondingly.
// `PlaceWithHirId`s are constructed differently from patterns. For example, in
//
Expand Down Expand Up @@ -654,11 +650,11 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
// `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)`
// and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`.
for _ in 0..self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(0, |v| v.len()) {
debug!("cat_pattern: applying adjustment to place_with_id={:?}", place_with_id);
debug!("applying adjustment to place_with_id={:?}", place_with_id);
place_with_id = self.cat_deref(pat, place_with_id)?;
}
let place_with_id = place_with_id; // lose mutability
debug!("cat_pattern: applied adjustment derefs to get place_with_id={:?}", place_with_id);
debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id);

// Invoke the callback, but only now, after the `place_with_id` has adjusted.
//
Expand Down
27 changes: 13 additions & 14 deletions compiler/rustc_hir_typeck/src/upvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,12 +264,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.demand_eqtype(span, closure_kind.to_ty(self.tcx), closure_kind_ty);

// If we have an origin, store it.
if let Some(origin) = origin {
let origin = if enable_precise_capture(span) {
(origin.0, origin.1)
} else {
(origin.0, Place { projections: vec![], ..origin.1 })
};
if let Some(mut origin) = origin {
if !enable_precise_capture(span) {
// Without precise captures, we just capture the base and ignore
// the projections.
origin.1.projections.clear()
}

self.typeck_results
.borrow_mut()
Expand All @@ -294,10 +294,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

// Equate the type variables for the upvars with the actual types.
let final_upvar_tys = self.final_upvar_tys(closure_def_id);
debug!(
"analyze_closure: id={:?} args={:?} final_upvar_tys={:?}",
closure_hir_id, args, final_upvar_tys
);
debug!(?closure_hir_id, ?args, ?final_upvar_tys);

// Build a tuple (U0..Un) of the final upvar types U0..Un
// and unify the upvar tuple type in the closure with it:
Expand Down Expand Up @@ -338,10 +335,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let upvar_ty = captured_place.place.ty();
let capture = captured_place.info.capture_kind;

debug!(
"final_upvar_tys: place={:?} upvar_ty={:?} capture={:?}, mutability={:?}",
captured_place.place, upvar_ty, capture, captured_place.mutability,
);
debug!(?captured_place.place, ?upvar_ty, ?capture, ?captured_place.mutability);

apply_capture_kind_on_capture_ty(self.tcx, upvar_ty, capture, captured_place.region)
})
Expand Down Expand Up @@ -679,6 +673,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match (p1.kind, p2.kind) {
// Paths are the same, continue to next loop.
(ProjectionKind::Deref, ProjectionKind::Deref) => {}
(ProjectionKind::OpaqueCast, ProjectionKind::OpaqueCast) => {}
(ProjectionKind::Field(i1, _), ProjectionKind::Field(i2, _))
if i1 == i2 => {}

Expand All @@ -701,10 +696,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
l @ (ProjectionKind::Index
| ProjectionKind::Subslice
| ProjectionKind::Deref
| ProjectionKind::OpaqueCast
| ProjectionKind::Field(..)),
r @ (ProjectionKind::Index
| ProjectionKind::Subslice
| ProjectionKind::Deref
| ProjectionKind::OpaqueCast
| ProjectionKind::Field(..)),
) => bug!(
"ProjectionKinds Index or Subslice were unexpected: ({:?}, {:?})",
Expand Down Expand Up @@ -1890,6 +1887,7 @@ fn restrict_capture_precision(
return (place, curr_mode);
}
ProjectionKind::Deref => {}
ProjectionKind::OpaqueCast => {}
ProjectionKind::Field(..) => {} // ignore
}
}
Expand Down Expand Up @@ -1946,6 +1944,7 @@ fn construct_place_string<'tcx>(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String
ProjectionKind::Deref => String::from("Deref"),
ProjectionKind::Index => String::from("Index"),
ProjectionKind::Subslice => String::from("Subslice"),
ProjectionKind::OpaqueCast => String::from("OpaqueCast"),
};
if i != 0 {
projections_str.push(',');
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/hir/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ pub enum ProjectionKind {

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

/// A conversion from an opaque type to its hidden type so we can
/// do further projections on it.
OpaqueCast,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_middle/src/ty/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ impl<'tcx> CapturedPlace<'tcx> {
// Ignore derefs for now, as they are likely caused by
// autoderefs that don't appear in the original code.
HirProjectionKind::Deref => {}
// Just change the type to the hidden type, so we can actually project.
HirProjectionKind::OpaqueCast => {}
proj => bug!("Unexpected projection {:?} in captured place", proj),
}
ty = proj.ty;
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_mir_build/src/build/expr/as_place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,9 @@ fn strip_prefix<'a, 'tcx>(
}
assert_matches!(iter.next(), Some(ProjectionElem::Field(..)));
}
HirProjectionKind::OpaqueCast => {
assert_matches!(iter.next(), Some(ProjectionElem::OpaqueCast(..)));
}
HirProjectionKind::Index | HirProjectionKind::Subslice => {
bug!("unexpected projection kind: {:?}", projection);
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_mir_build/src/thir/cx/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1072,6 +1072,9 @@ impl<'tcx> Cx<'tcx> {
variant_index,
name: field,
},
HirProjectionKind::OpaqueCast => {
ExprKind::Use { source: self.thir.exprs.push(captured_place_expr) }
}
HirProjectionKind::Index | HirProjectionKind::Subslice => {
// We don't capture these projections, so we can ignore them here
continue;
Expand Down
2 changes: 2 additions & 0 deletions src/tools/clippy/clippy_utils/src/sugg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1011,6 +1011,8 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
},
// note: unable to trigger `Subslice` kind in tests
ProjectionKind::Subslice => (),
// Doesn't have surface syntax. Only occurs in patterns.
ProjectionKind::OpaqueCast => (),
ProjectionKind::Deref => {
// Explicit derefs are typically handled later on, but
// some items do not need explicit deref, such as array accesses,
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#![feature(type_alias_impl_trait)]
// check-pass
// revisions: default edition2021
//[edition2021] compile-flags: --edition 2021

fn main() {
type T = impl Copy;
Expand Down

0 comments on commit 2ddd80a

Please sign in to comment.