Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 60 additions & 32 deletions compiler/rustc_middle/src/ty/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::hir::place::{
Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind,
};
use crate::query::Providers;
use crate::ty::Ty;
use crate::{mir, ty};

/// Captures are represented using fields inside a structure.
Expand Down Expand Up @@ -96,26 +97,30 @@ impl<'tcx> CapturedPlace<'tcx> {
}

/// Returns a symbol of the captured upvar, which looks like `name__field1__field2`.
pub fn to_symbol(&self) -> Symbol {
pub fn to_symbol(&self, tcx: TyCtxt<'tcx>) -> Symbol {
let mut symbol = self.var_ident.to_string();

let typing_env = typing_env_for_place(tcx, &self.place);
let mut ty = self.place.base_ty;
for proj in self.place.projections.iter() {
match proj.kind {
HirProjectionKind::Field(idx, variant) => match ty.kind() {
ty::Tuple(_) => write!(&mut symbol, "__{}", idx.index()).unwrap(),
ty::Adt(def, ..) => {
write!(
&mut symbol,
"__{}",
def.variant(variant).fields[idx].name.as_str(),
)
.unwrap();
HirProjectionKind::Field(idx, variant) => {
let ty = normalize_place_ty(tcx, typing_env, ty);
match ty.kind() {
ty::Tuple(_) => write!(&mut symbol, "__{}", idx.index()).unwrap(),
ty::Adt(def, ..) => {
write!(
&mut symbol,
"__{}",
def.variant(variant).fields[idx].name.as_str(),
)
.unwrap();
}
ty => {
bug!("Unexpected type {:?} for `Field` projection", ty)
}
}
ty => {
bug!("Unexpected type {:?} for `Field` projection", ty)
}
},
}

HirProjectionKind::UnwrapUnsafeBinder => {
write!(&mut symbol, "__unwrap").unwrap();
Expand Down Expand Up @@ -304,41 +309,64 @@ pub fn place_to_string_for_capture<'tcx>(tcx: TyCtxt<'tcx>, place: &HirPlace<'tc
_ => bug!("Capture_information should only contain upvars"),
};

for (i, proj) in place.projections.iter().enumerate() {
let typing_env = typing_env_for_place(tcx, place);
let mut ty = place.base_ty;
for proj in place.projections.iter() {
match proj.kind {
HirProjectionKind::Deref => {
curr_string = format!("*{curr_string}");
}
HirProjectionKind::Field(idx, variant) => match place.ty_before_projection(i).kind() {
ty::Adt(def, ..) => {
curr_string = format!(
"{}.{}",
curr_string,
def.variant(variant).fields[idx].name.as_str()
);
}
ty::Tuple(_) => {
curr_string = format!("{}.{}", curr_string, idx.index());
}
_ => {
bug!(
"Field projection applied to a type other than Adt or Tuple: {:?}.",
place.ty_before_projection(i).kind()
)
HirProjectionKind::Field(idx, variant) => {
let ty = normalize_place_ty(tcx, typing_env, ty);
match ty.kind() {
ty::Adt(def, ..) => {
curr_string = format!(
"{}.{}",
curr_string,
def.variant(variant).fields[idx].name.as_str()
);
}
ty::Tuple(_) => {
curr_string = format!("{}.{}", curr_string, idx.index());
}
ty => {
bug!(
"Field projection applied to a type other than Adt or Tuple: {:?}.",
ty
)
}
}
},
}
HirProjectionKind::UnwrapUnsafeBinder => {
curr_string = format!("unwrap_binder!({curr_string})");
}
// Just change the type to the hidden type, so we can actually project.
HirProjectionKind::OpaqueCast => {}
proj => bug!("{:?} unexpected because it isn't captured", proj),
}
ty = proj.ty;
}

curr_string
}

fn typing_env_for_place<'tcx>(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> ty::TypingEnv<'tcx> {
match place.base {
HirPlaceBase::Upvar(upvar_id) => {
ty::TypingEnv::post_analysis(tcx, upvar_id.closure_expr_id)
}
_ => ty::TypingEnv::fully_monomorphized(),
}
}

fn normalize_place_ty<'tcx>(
tcx: TyCtxt<'tcx>,
typing_env: ty::TypingEnv<'tcx>,
ty: Ty<'tcx>,
) -> Ty<'tcx> {
tcx.normalize_erasing_regions(typing_env, ty)
}

#[derive(Eq, Clone, PartialEq, Debug, TyEncodable, TyDecodable, Copy, HashStable, Hash)]
#[derive(TypeFoldable, TypeVisitable)]
pub enum BorrowKind {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_mir_build/src/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub(crate) fn closure_saved_names_of_captured_variables<'tcx>(
tcx.closure_captures(def_id)
.iter()
.map(|captured_place| {
let name = captured_place.to_symbol();
let name = captured_place.to_symbol(tcx);
match captured_place.info.capture_kind {
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => name,
ty::UpvarCapture::ByRef(..) => Symbol::intern(&format!("_ref__{name}")),
Expand Down Expand Up @@ -995,7 +995,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
.zip_eq(capture_tys)
.enumerate()
.map(|(i, (captured_place, ty))| {
let name = captured_place.to_symbol();
let name = captured_place.to_symbol(tcx);

let capture = captured_place.info.capture_kind;
let var_id = match captured_place.place.base {
Expand Down
30 changes: 30 additions & 0 deletions tests/ui/traits/next-solver/normalize-capture-place-151579.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Regression test for #151579
//@ compile-flags: -Znext-solver=globally
//@ edition:2018

#![deny(rust_2021_incompatible_closure_captures)]

struct Dummy;

trait Trait {
type Assoc;
}

impl Trait for Dummy {
type Assoc = (*mut i32,);
}

struct SyncPointer(<Dummy as Trait>::Assoc);
unsafe impl Sync for SyncPointer {}

fn test_assoc_capture(a: SyncPointer) {
let _ = move || {
//~^ ERROR: changes to closure capture
let _x = a.0.0;
};
}

fn main() {
let ptr = SyncPointer((std::ptr::null_mut(),));
test_assoc_capture(ptr);
}
23 changes: 23 additions & 0 deletions tests/ui/traits/next-solver/normalize-capture-place-151579.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
error: changes to closure capture in Rust 2021 will affect which traits the closure implements
--> $DIR/normalize-capture-place-151579.rs:21:13
|
LL | let _ = move || {
| ^^^^^^^ in Rust 2018, this closure implements `Sync` as `a` implements `Sync`, but in Rust 2021, this closure will no longer implement `Sync` because `a` is not fully captured and `a.0.0` does not implement `Sync`
LL |
LL | let _x = a.0.0;
| ----- in Rust 2018, this closure captures all of `a`, but in Rust 2021, it will only capture `a.0.0`
|
= note: for more information, see <https://doc.rust-lang.org/edition-guide/rust-2021/disjoint-capture-in-closures.html>
note: the lint level is defined here
--> $DIR/normalize-capture-place-151579.rs:5:9
|
LL | #![deny(rust_2021_incompatible_closure_captures)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: add a dummy let to cause `a` to be fully captured
|
LL ~ let _ = move || {
LL + let _ = &a;
|

error: aborting due to 1 previous error

Loading