From b20307b3e9d3d17d8e43c273de3bac6d66e0d9c5 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Fri, 31 Jan 2025 03:51:00 +0100 Subject: [PATCH 1/9] improve doc tests for (min/max/minmax).* functions - add tests for `a == b` where missing - try to make all the tests more similar - try to use more illustrative test values --- library/core/src/cmp.rs | 146 +++++++++++++++++++++++++++++++++++----- 1 file changed, 128 insertions(+), 18 deletions(-) diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 97974d195fec6..34bba5f5f2ee3 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -973,6 +973,24 @@ pub trait Ord: Eq + PartialOrd { /// assert_eq!(1.max(2), 2); /// assert_eq!(2.max(2), 2); /// ``` + /// ``` + /// use std::cmp::Ordering; + /// + /// #[derive(Eq)] + /// struct Equal(&'static str); + /// + /// impl PartialEq for Equal { + /// fn eq(&self, other: &Self) -> bool { true } + /// } + /// impl PartialOrd for Equal { + /// fn partial_cmp(&self, other: &Self) -> Option { Some(Ordering::Equal) } + /// } + /// impl Ord for Equal { + /// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } + /// } + /// + /// assert_eq!(Equal("self").max(Equal("other")).0, "other"); + /// ``` #[stable(feature = "ord_max_min", since = "1.21.0")] #[inline] #[must_use] @@ -994,6 +1012,24 @@ pub trait Ord: Eq + PartialOrd { /// assert_eq!(1.min(2), 1); /// assert_eq!(2.min(2), 2); /// ``` + /// ``` + /// use std::cmp::Ordering; + /// + /// #[derive(Eq)] + /// struct Equal(&'static str); + /// + /// impl PartialEq for Equal { + /// fn eq(&self, other: &Self) -> bool { true } + /// } + /// impl PartialOrd for Equal { + /// fn partial_cmp(&self, other: &Self) -> Option { Some(Ordering::Equal) } + /// } + /// impl Ord for Equal { + /// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } + /// } + /// + /// assert_eq!(Equal("self").min(Equal("other")).0, "self"); + /// ``` #[stable(feature = "ord_max_min", since = "1.21.0")] #[inline] #[must_use] @@ -1414,6 +1450,24 @@ pub macro PartialOrd($item:item) { /// assert_eq!(cmp::min(1, 2), 1); /// assert_eq!(cmp::min(2, 2), 2); /// ``` +/// ``` +/// use std::cmp::{self, Ordering}; +/// +/// #[derive(Eq)] +/// struct Equal(&'static str); +/// +/// impl PartialEq for Equal { +/// fn eq(&self, other: &Self) -> bool { true } +/// } +/// impl PartialOrd for Equal { +/// fn partial_cmp(&self, other: &Self) -> Option { Some(Ordering::Equal) } +/// } +/// impl Ord for Equal { +/// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } +/// } +/// +/// assert_eq!(cmp::min(Equal("v1"), Equal("v2")).0, "v1"); +/// ``` #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] @@ -1431,11 +1485,16 @@ pub fn min(v1: T, v2: T) -> T { /// ``` /// use std::cmp; /// -/// let result = cmp::min_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs())); -/// assert_eq!(result, 1); +/// let abs_cmp = |x: &i32, y: &i32| x.abs().cmp(&y.abs()); /// -/// let result = cmp::min_by(-2, 3, |x: &i32, y: &i32| x.abs().cmp(&y.abs())); -/// assert_eq!(result, -2); +/// let result = cmp::min_by(2, -1, abs_cmp); +/// assert_eq!(result, -1); +/// +/// let result = cmp::min_by(2, -3, abs_cmp); +/// assert_eq!(result, 2); +/// +/// let result = cmp::min_by(1, -1, abs_cmp); +/// assert_eq!(result, 1); /// ``` #[inline] #[must_use] @@ -1456,11 +1515,14 @@ pub fn min_by Ordering>(v1: T, v2: T, compare: F) -> T { /// ``` /// use std::cmp; /// -/// let result = cmp::min_by_key(-2, 1, |x: &i32| x.abs()); -/// assert_eq!(result, 1); +/// let result = cmp::min_by_key(2, -1, |x: &i32| x.abs()); +/// assert_eq!(result, -1); /// -/// let result = cmp::min_by_key(-2, 2, |x: &i32| x.abs()); -/// assert_eq!(result, -2); +/// let result = cmp::min_by_key(2, -3, |x: &i32| x.abs()); +/// assert_eq!(result, 2); +/// +/// let result = cmp::min_by_key(1, -1, |x: &i32| x.abs()); +/// assert_eq!(result, 1); /// ``` #[inline] #[must_use] @@ -1483,6 +1545,24 @@ pub fn min_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { /// assert_eq!(cmp::max(1, 2), 2); /// assert_eq!(cmp::max(2, 2), 2); /// ``` +/// ``` +/// use std::cmp::{self, Ordering}; +/// +/// #[derive(Eq)] +/// struct Equal(&'static str); +/// +/// impl PartialEq for Equal { +/// fn eq(&self, other: &Self) -> bool { true } +/// } +/// impl PartialOrd for Equal { +/// fn partial_cmp(&self, other: &Self) -> Option { Some(Ordering::Equal) } +/// } +/// impl Ord for Equal { +/// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } +/// } +/// +/// assert_eq!(cmp::max(Equal("v1"), Equal("v2")).0, "v2"); +/// ``` #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] @@ -1500,11 +1580,16 @@ pub fn max(v1: T, v2: T) -> T { /// ``` /// use std::cmp; /// -/// let result = cmp::max_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs())); +/// let abs_cmp = |x: &i32, y: &i32| x.abs().cmp(&y.abs()); +/// +/// let result = cmp::max_by(3, -2, abs_cmp) ; +/// assert_eq!(result, 3); +/// +/// let result = cmp::max_by(1, -2, abs_cmp); /// assert_eq!(result, -2); /// -/// let result = cmp::max_by(-2, 2, |x: &i32, y: &i32| x.abs().cmp(&y.abs())) ; -/// assert_eq!(result, 2); +/// let result = cmp::max_by(1, -1, abs_cmp); +/// assert_eq!(result, -1); /// ``` #[inline] #[must_use] @@ -1525,11 +1610,14 @@ pub fn max_by Ordering>(v1: T, v2: T, compare: F) -> T { /// ``` /// use std::cmp; /// -/// let result = cmp::max_by_key(-2, 1, |x: &i32| x.abs()); +/// let result = cmp::max_by_key(3, -2, |x: &i32| x.abs()); +/// assert_eq!(result, 3); +/// +/// let result = cmp::max_by_key(1, -2, |x: &i32| x.abs()); /// assert_eq!(result, -2); /// -/// let result = cmp::max_by_key(-2, 2, |x: &i32| x.abs()); -/// assert_eq!(result, 2); +/// let result = cmp::max_by_key(1, -1, |x: &i32| x.abs()); +/// assert_eq!(result, -1); /// ``` #[inline] #[must_use] @@ -1549,13 +1637,32 @@ pub fn max_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { /// use std::cmp; /// /// assert_eq!(cmp::minmax(1, 2), [1, 2]); -/// assert_eq!(cmp::minmax(2, 2), [2, 2]); +/// assert_eq!(cmp::minmax(2, 1), [1, 2]); /// /// // You can destructure the result using array patterns /// let [min, max] = cmp::minmax(42, 17); /// assert_eq!(min, 17); /// assert_eq!(max, 42); /// ``` +/// ``` +/// #![feature(cmp_minmax)] +/// use std::cmp::{self, Ordering}; +/// +/// #[derive(Eq)] +/// struct Equal(&'static str); +/// +/// impl PartialEq for Equal { +/// fn eq(&self, other: &Self) -> bool { true } +/// } +/// impl PartialOrd for Equal { +/// fn partial_cmp(&self, other: &Self) -> Option { Some(Ordering::Equal) } +/// } +/// impl Ord for Equal { +/// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } +/// } +/// +/// assert_eq!(cmp::minmax(Equal("v1"), Equal("v2")).map(|v| v.0), ["v1", "v2"]); +/// ``` #[inline] #[must_use] #[unstable(feature = "cmp_minmax", issue = "115939")] @@ -1576,11 +1683,14 @@ where /// #![feature(cmp_minmax)] /// use std::cmp; /// -/// assert_eq!(cmp::minmax_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs())), [1, -2]); -/// assert_eq!(cmp::minmax_by(-2, 2, |x: &i32, y: &i32| x.abs().cmp(&y.abs())), [-2, 2]); +/// let abs_cmp = |x: &i32, y: &i32| x.abs().cmp(&y.abs()); +/// +/// assert_eq!(cmp::minmax_by(-2, 1, abs_cmp), [1, -2]); +/// assert_eq!(cmp::minmax_by(-1, 2, abs_cmp), [-1, 2]); +/// assert_eq!(cmp::minmax_by(-2, 2, abs_cmp), [-2, 2]); /// /// // You can destructure the result using array patterns -/// let [min, max] = cmp::minmax_by(-42, 17, |x: &i32, y: &i32| x.abs().cmp(&y.abs())); +/// let [min, max] = cmp::minmax_by(-42, 17, abs_cmp); /// assert_eq!(min, 17); /// assert_eq!(max, -42); /// ``` From c5835cd648da820fa4875376bb3fb15bb4511e0b Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Thu, 30 Jan 2025 17:27:16 +0100 Subject: [PATCH 2/9] implement all min/max fns in terms of `<`/`is_lt` `<` seems to be the "lucky one" for llvm --- library/core/src/cmp.rs | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 34bba5f5f2ee3..594236cf1d96f 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -999,7 +999,7 @@ pub trait Ord: Eq + PartialOrd { where Self: Sized, { - max_by(self, other, Ord::cmp) + if other < self { self } else { other } } /// Compares and returns the minimum of two values. @@ -1038,7 +1038,7 @@ pub trait Ord: Eq + PartialOrd { where Self: Sized, { - min_by(self, other, Ord::cmp) + if other < self { other } else { self } } /// Restrict a value to a certain interval. @@ -1500,10 +1500,7 @@ pub fn min(v1: T, v2: T) -> T { #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn min_by Ordering>(v1: T, v2: T, compare: F) -> T { - match compare(&v1, &v2) { - Ordering::Less | Ordering::Equal => v1, - Ordering::Greater => v2, - } + if compare(&v2, &v1).is_lt() { v2 } else { v1 } } /// Returns the element that gives the minimum value from the specified function. @@ -1528,7 +1525,7 @@ pub fn min_by Ordering>(v1: T, v2: T, compare: F) -> T { #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn min_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { - min_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2))) + if f(&v2) < f(&v1) { v2 } else { v1 } } /// Compares and returns the maximum of two values. @@ -1595,10 +1592,7 @@ pub fn max(v1: T, v2: T) -> T { #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn max_by Ordering>(v1: T, v2: T, compare: F) -> T { - match compare(&v1, &v2) { - Ordering::Less | Ordering::Equal => v2, - Ordering::Greater => v1, - } + if compare(&v2, &v1).is_lt() { v1 } else { v2 } } /// Returns the element that gives the maximum value from the specified function. @@ -1623,7 +1617,7 @@ pub fn max_by Ordering>(v1: T, v2: T, compare: F) -> T { #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn max_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { - max_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2))) + if f(&v2) < f(&v1) { v1 } else { v2 } } /// Compares and sorts two values, returning minimum and maximum. @@ -1670,7 +1664,7 @@ pub fn minmax(v1: T, v2: T) -> [T; 2] where T: Ord, { - if v1 <= v2 { [v1, v2] } else { [v2, v1] } + if v2 < v1 { [v2, v1] } else { [v1, v2] } } /// Returns minimum and maximum values with respect to the specified comparison function. @@ -1701,7 +1695,7 @@ pub fn minmax_by(v1: T, v2: T, compare: F) -> [T; 2] where F: FnOnce(&T, &T) -> Ordering, { - if compare(&v1, &v2).is_le() { [v1, v2] } else { [v2, v1] } + if compare(&v2, &v1).is_lt() { [v2, v1] } else { [v1, v2] } } /// Returns minimum and maximum values with respect to the specified key function. @@ -1730,7 +1724,7 @@ where F: FnMut(&T) -> K, K: Ord, { - minmax_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2))) + if f(&v2) < f(&v1) { [v2, v1] } else { [v1, v2] } } // Implementation of PartialEq, Eq, PartialOrd and Ord for primitive types From fc1a9186dcdad111fd99ddd38bd961a8a205c380 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 31 Jan 2025 01:24:37 +0000 Subject: [PATCH 3/9] Implement MIR, CTFE, and codegen for unsafe binders --- compiler/rustc_ast/src/ast.rs | 2 +- .../src/diagnostics/conflict_errors.rs | 3 +- .../rustc_borrowck/src/diagnostics/mod.rs | 7 +-- .../src/diagnostics/mutability_errors.rs | 3 +- compiler/rustc_borrowck/src/lib.rs | 14 ++++- .../rustc_borrowck/src/places_conflict.rs | 7 ++- .../src/polonius/legacy/loan_invalidations.rs | 4 ++ compiler/rustc_borrowck/src/prefixes.rs | 4 ++ compiler/rustc_borrowck/src/type_check/mod.rs | 46 +++++++++++++++- compiler/rustc_codegen_cranelift/src/base.rs | 4 +- compiler/rustc_codegen_ssa/src/mir/place.rs | 3 ++ compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 9 +++- .../src/check_consts/check.rs | 4 ++ .../src/check_consts/qualifs.rs | 5 +- .../src/check_consts/resolver.rs | 3 +- .../src/interpret/projection.rs | 1 + .../rustc_const_eval/src/interpret/step.rs | 5 ++ compiler/rustc_hir_typeck/src/expr.rs | 2 - compiler/rustc_middle/src/mir/pretty.rs | 10 ++++ compiler/rustc_middle/src/mir/statement.rs | 12 +++-- compiler/rustc_middle/src/mir/syntax.rs | 7 +++ compiler/rustc_middle/src/mir/tcx.rs | 6 +++ .../rustc_middle/src/mir/type_foldable.rs | 2 + compiler/rustc_middle/src/mir/visit.rs | 13 ++++- compiler/rustc_middle/src/thir.rs | 13 +++++ compiler/rustc_middle/src/thir/visit.rs | 3 ++ compiler/rustc_mir_build/messages.ftl | 12 +++++ .../src/builder/expr/as_place.rs | 23 ++++++-- .../src/builder/expr/as_rvalue.rs | 17 +++++- .../src/builder/expr/category.rs | 7 ++- .../rustc_mir_build/src/builder/expr/into.rs | 7 ++- .../rustc_mir_build/src/check_unsafety.rs | 27 ++++++++++ compiler/rustc_mir_build/src/errors.rs | 38 +++++++++++++ compiler/rustc_mir_build/src/thir/cx/expr.rs | 16 +++++- .../src/thir/pattern/check_match.rs | 7 +-- compiler/rustc_mir_build/src/thir/print.rs | 18 +++++++ .../src/impls/borrowed_locals.rs | 3 +- .../src/move_paths/abs_domain.rs | 1 + .../src/move_paths/builder.rs | 6 ++- .../src/dataflow_const_prop.rs | 3 +- compiler/rustc_mir_transform/src/dest_prop.rs | 3 ++ compiler/rustc_mir_transform/src/gvn.rs | 8 +++ .../src/known_panics_lint.rs | 7 ++- .../rustc_mir_transform/src/promote_consts.rs | 7 ++- compiler/rustc_mir_transform/src/validate.rs | 3 +- .../rustc_smir/src/rustc_smir/convert/mir.rs | 2 + compiler/rustc_span/src/symbol.rs | 1 - compiler/rustc_ty_utils/src/consts.rs | 8 +++ .../clippy_utils/src/qualify_min_const_fn.rs | 3 +- tests/ui/unsafe-binders/expr.rs | 4 +- tests/ui/unsafe-binders/expr.stderr | 16 +----- tests/ui/unsafe-binders/mismatch.rs | 17 ++---- tests/ui/unsafe-binders/mismatch.stderr | 47 +++++++--------- tests/ui/unsafe-binders/moves.rs | 38 +++++++++++++ tests/ui/unsafe-binders/moves.stderr | 53 +++++++++++++++++++ 55 files changed, 493 insertions(+), 101 deletions(-) create mode 100644 tests/ui/unsafe-binders/moves.rs create mode 100644 tests/ui/unsafe-binders/moves.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index ad942e9b494ae..b6f331d316cc9 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1657,7 +1657,7 @@ impl GenBlockKind { } /// Whether we're unwrapping or wrapping an unsafe binder -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(Encodable, Decodable, HashStable_Generic)] pub enum UnsafeBinderCastKind { // e.g. `&i32` -> `unsafe<'a> &'a i32` diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 07dcbba019ade..e51b1b42d0934 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3915,7 +3915,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Subtype(_) - | ProjectionElem::Index(_) => kind, + | ProjectionElem::Index(_) + | ProjectionElem::UnwrapUnsafeBinder(_) => kind, }, place_ty.projection_ty(tcx, elem), ) diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index bd6f77156ca99..6255ba4f5a1ce 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -370,6 +370,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ProjectionElem::Downcast(..) => (), ProjectionElem::OpaqueCast(..) => (), ProjectionElem::Subtype(..) => (), + ProjectionElem::UnwrapUnsafeBinder(_) => (), ProjectionElem::Field(field, _ty) => { // FIXME(project-rfc_2229#36): print capture precisely here. if let Some(field) = self.is_upvar_field_projection(PlaceRef { @@ -450,9 +451,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx) } ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx), - ProjectionElem::Subtype(ty) | ProjectionElem::OpaqueCast(ty) => { - PlaceTy::from_ty(*ty) - } + ProjectionElem::Subtype(ty) + | ProjectionElem::OpaqueCast(ty) + | ProjectionElem::UnwrapUnsafeBinder(ty) => PlaceTy::from_ty(*ty), ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type), }, }; diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index e841a5e4c948a..706dd7135f73b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -167,7 +167,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { | ProjectionElem::ConstantIndex { .. } | ProjectionElem::OpaqueCast { .. } | ProjectionElem::Subslice { .. } - | ProjectionElem::Downcast(..), + | ProjectionElem::Downcast(..) + | ProjectionElem::UnwrapUnsafeBinder(_), ], } => bug!("Unexpected immutable place."), } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index decfab502bb59..077d8f49df88c 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1398,6 +1398,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { self.consume_operand(location, (operand, span), state); } } + + Rvalue::WrapUnsafeBinder(op, _) => { + self.consume_operand(location, (op, span), state); + } } } @@ -1770,7 +1774,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { // So it's safe to skip these. ProjectionElem::OpaqueCast(_) | ProjectionElem::Subtype(_) - | ProjectionElem::Downcast(_, _) => (), + | ProjectionElem::Downcast(_, _) + | ProjectionElem::UnwrapUnsafeBinder(_) => (), } place_ty = place_ty.projection_ty(tcx, elem); @@ -2004,6 +2009,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { // FIXME: is this true even if P is an adt with a dtor? { } + ProjectionElem::UnwrapUnsafeBinder(_) => { + check_parent_of_field(self, location, place_base, span, state); + } + // assigning to (*P) requires P to be initialized ProjectionElem::Deref => { self.check_if_full_path_is_moved( @@ -2384,7 +2393,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | ProjectionElem::Subslice { .. } | ProjectionElem::Subtype(..) | ProjectionElem::OpaqueCast { .. } - | ProjectionElem::Downcast(..) => { + | ProjectionElem::Downcast(..) + | ProjectionElem::UnwrapUnsafeBinder(_) => { let upvar_field_projection = self.is_upvar_field_projection(place); if let Some(field) = upvar_field_projection { let upvar = &self.upvars[field.index()]; diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs index 679e111caa984..cf3e82426e8a0 100644 --- a/compiler/rustc_borrowck/src/places_conflict.rs +++ b/compiler/rustc_borrowck/src/places_conflict.rs @@ -250,7 +250,8 @@ fn place_components_conflict<'tcx>( | (ProjectionElem::Subslice { .. }, _, _) | (ProjectionElem::OpaqueCast { .. }, _, _) | (ProjectionElem::Subtype(_), _, _) - | (ProjectionElem::Downcast { .. }, _, _) => { + | (ProjectionElem::Downcast { .. }, _, _) + | (ProjectionElem::UnwrapUnsafeBinder(_), _, _) => { // Recursive case. This can still be disjoint on a // further iteration if this a shallow access and // there's a deref later on, e.g., a borrow @@ -519,5 +520,9 @@ fn place_projection_conflict<'tcx>( pi1_elem, pi2_elem ), + + (ProjectionElem::UnwrapUnsafeBinder(_), _) => { + todo!() + } } } diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index f79bcf5af5564..0d1d8642bcacc 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -325,6 +325,10 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { self.consume_operand(location, operand); } } + + Rvalue::WrapUnsafeBinder(op, _) => { + self.consume_operand(location, op); + } } } diff --git a/compiler/rustc_borrowck/src/prefixes.rs b/compiler/rustc_borrowck/src/prefixes.rs index fc7e6e586410a..83cca38a5c090 100644 --- a/compiler/rustc_borrowck/src/prefixes.rs +++ b/compiler/rustc_borrowck/src/prefixes.rs @@ -66,6 +66,10 @@ impl<'tcx> Iterator for Prefixes<'tcx> { self.next = Some(cursor_base); return Some(cursor); } + ProjectionElem::UnwrapUnsafeBinder(_) => { + self.next = Some(cursor_base); + return Some(cursor); + } ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::OpaqueCast { .. } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index a2ef5588f48f8..92492bfdb8d31 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -302,6 +302,25 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { ) .unwrap(); } + ProjectionElem::UnwrapUnsafeBinder(ty) => { + let ty::UnsafeBinder(binder_ty) = *base_ty.ty.kind() else { + unreachable!(); + }; + let found_ty = self.typeck.infcx.instantiate_binder_with_fresh_vars( + self.body().source_info(location).span, + BoundRegionConversionTime::HigherRankedType, + binder_ty.into(), + ); + self.typeck + .relate_types( + ty, + context.ambient_variance(), + found_ty, + location.to_locations(), + ConstraintCategory::Boring, + ) + .unwrap(); + } ProjectionElem::Subtype(_) => { bug!("ProjectionElem::Subtype shouldn't exist in borrowck") } @@ -2233,6 +2252,27 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.check_operand(right, location); } + Rvalue::WrapUnsafeBinder(op, ty) => { + self.check_operand(op, location); + let operand_ty = op.ty(self.body, self.tcx()); + + let ty::UnsafeBinder(binder_ty) = *ty.kind() else { + unreachable!(); + }; + let expected_ty = self.infcx.instantiate_binder_with_fresh_vars( + self.body().source_info(location).span, + BoundRegionConversionTime::HigherRankedType, + binder_ty.into(), + ); + self.sub_types( + operand_ty, + expected_ty, + location.to_locations(), + ConstraintCategory::Boring, + ) + .unwrap(); + } + Rvalue::RawPtr(..) | Rvalue::ThreadLocalRef(..) | Rvalue::Len(..) @@ -2258,7 +2298,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | Rvalue::NullaryOp(..) | Rvalue::CopyForDeref(..) | Rvalue::UnaryOp(..) - | Rvalue::Discriminant(..) => None, + | Rvalue::Discriminant(..) + | Rvalue::WrapUnsafeBinder(..) => None, Rvalue::Aggregate(aggregate, _) => match **aggregate { AggregateKind::Adt(_, _, _, user_ty, _) => user_ty, @@ -2450,7 +2491,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | ProjectionElem::OpaqueCast(..) | ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } => { + | ProjectionElem::Subslice { .. } + | ProjectionElem::UnwrapUnsafeBinder(_) => { // other field access } ProjectionElem::Subtype(_) => { diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 34066eb83fc02..ea37e19099a87 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -983,7 +983,9 @@ pub(crate) fn codegen_place<'tcx>( cplace = cplace.place_deref(fx); } PlaceElem::OpaqueCast(ty) => bug!("encountered OpaqueCast({ty}) in codegen"), - PlaceElem::Subtype(ty) => cplace = cplace.place_transmute_type(fx, fx.monomorphize(ty)), + PlaceElem::Subtype(ty) | PlaceElem::UnwrapUnsafeBinder(ty) => { + cplace = cplace.place_transmute_type(fx, fx.monomorphize(ty)); + } PlaceElem::Field(field, _ty) => { cplace = cplace.place_field(fx, field); } diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index c634f864ffb89..73a416768021d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -502,6 +502,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bug!("encountered OpaqueCast({ty}) in codegen") } mir::ProjectionElem::Subtype(ty) => cg_base.project_type(bx, self.monomorphize(ty)), + mir::ProjectionElem::UnwrapUnsafeBinder(ty) => { + cg_base.project_type(bx, self.monomorphize(ty)) + } mir::ProjectionElem::Index(index) => { let index = &mir::Operand::Copy(mir::Place::from(index)); let index = self.codegen_operand(bx, index); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index d7fc5e8e673fd..85de3238b3e77 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -823,6 +823,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandRef { val: OperandValue::Immediate(val), layout: box_layout } } + mir::Rvalue::WrapUnsafeBinder(ref operand, binder_ty) => { + let operand = self.codegen_operand(bx, operand); + let binder_ty = self.monomorphize(binder_ty); + let layout = bx.cx().layout_of(binder_ty); + OperandRef { val: operand.val, layout } + } } } @@ -1123,7 +1129,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::Discriminant(..) | mir::Rvalue::NullaryOp(..) | mir::Rvalue::ThreadLocalRef(_) | - mir::Rvalue::Use(..) => // (*) + mir::Rvalue::Use(..) | + mir::Rvalue::WrapUnsafeBinder(..) => // (*) true, // Arrays are always aggregates, so it's not worth checking anything here. // (If it's really `[(); N]` or `[T; 0]` and we use the place path, fine.) diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 16ead1b978543..83fef418228f1 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -723,6 +723,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { ); } } + + Rvalue::WrapUnsafeBinder(..) => { + // Unsafe binders are always trivial to create. + } } } diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 5d368b600a024..9c99782d2238c 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -258,6 +258,8 @@ where in_place::(cx, in_local, place.as_ref()) } + Rvalue::WrapUnsafeBinder(op, _) => in_operand::(cx, in_local, op), + Rvalue::Aggregate(kind, operands) => { // Return early if we know that the struct or enum being constructed is always // qualified. @@ -297,7 +299,8 @@ where | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(_, _) - | ProjectionElem::Index(_) => {} + | ProjectionElem::Index(_) + | ProjectionElem::UnwrapUnsafeBinder(_) => {} } let base_ty = place_base.ty(cx.body, cx.tcx); diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index 79df63a9e849a..8cee282311f03 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -202,7 +202,8 @@ where | mir::Rvalue::NullaryOp(..) | mir::Rvalue::UnaryOp(..) | mir::Rvalue::Discriminant(..) - | mir::Rvalue::Aggregate(..) => {} + | mir::Rvalue::Aggregate(..) + | mir::Rvalue::WrapUnsafeBinder(..) => {} } } diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 996142d7b03bd..8ecb3e13d5ceb 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -381,6 +381,7 @@ where OpaqueCast(ty) => { span_bug!(self.cur_span(), "OpaqueCast({ty}) encountered after borrowck") } + UnwrapUnsafeBinder(target) => base.transmute(self.layout_of(target)?, self)?, // We don't want anything happening here, this is here as a dummy. Subtype(_) => base.transmute(base.layout(), self)?, Field(field, _) => self.project_field(base, field.index())?, diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index d9c0ff5acd116..beae5047e7efd 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -277,6 +277,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let discr = self.discriminant_for_variant(op.layout.ty, variant)?; self.write_immediate(*discr, &dest)?; } + + WrapUnsafeBinder(ref op, _ty) => { + let op = self.eval_operand(op, None)?; + self.copy_op_allow_transmute(&op, &dest)?; + } } trace!("{:?}", self.dump_place(&dest)); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index f79667e59bae1..4abc6da4e6529 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1658,8 +1658,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir_ty: Option<&'tcx hir::Ty<'tcx>>, expected: Expectation<'tcx>, ) -> Ty<'tcx> { - self.dcx().span_err(inner_expr.span, "unsafe binder casts are not fully implemented"); - match kind { hir::UnsafeBinderCastKind::Wrap => { let ascribed_ty = diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index a318bacb866d1..09d7e60e19941 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1248,6 +1248,10 @@ impl<'tcx> Debug for Rvalue<'tcx> { ShallowInitBox(ref place, ref ty) => { with_no_trimmed_paths!(write!(fmt, "ShallowInitBox({place:?}, {ty})")) } + + WrapUnsafeBinder(ref op, ty) => { + with_no_trimmed_paths!(write!(fmt, "wrap_binder!({op:?}; {ty})")) + } } } } @@ -1308,6 +1312,9 @@ fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {} + ProjectionElem::UnwrapUnsafeBinder(_) => { + write!(fmt, "unwrap_binder!(")?; + } } } @@ -1356,6 +1363,9 @@ fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> ProjectionElem::Subslice { from, to, from_end: false } => { write!(fmt, "[{from:?}..{to:?}]")?; } + ProjectionElem::UnwrapUnsafeBinder(ty) => { + write!(fmt, "; {ty})")?; + } } } diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 609d5647d04cf..d345c99f902f2 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -62,7 +62,8 @@ impl ProjectionElem { | Self::Subtype(_) | Self::ConstantIndex { .. } | Self::Subslice { .. } - | Self::Downcast(_, _) => false, + | Self::Downcast(_, _) + | Self::UnwrapUnsafeBinder(..) => false, } } @@ -76,7 +77,8 @@ impl ProjectionElem { | Self::Subtype(_) | Self::ConstantIndex { .. } | Self::Subslice { .. } - | Self::Downcast(_, _) => true, + | Self::Downcast(_, _) + | Self::UnwrapUnsafeBinder(..) => true, } } @@ -102,6 +104,9 @@ impl ProjectionElem { | Self::Subtype(_) | Self::OpaqueCast(_) | Self::Subslice { .. } => false, + + // FIXME(unsafe_binders): Figure this out. + Self::UnwrapUnsafeBinder(..) => false, } } } @@ -443,7 +448,8 @@ impl<'tcx> Rvalue<'tcx> { | Rvalue::UnaryOp(_, _) | Rvalue::Discriminant(_) | Rvalue::Aggregate(_, _) - | Rvalue::ShallowInitBox(_, _) => true, + | Rvalue::ShallowInitBox(_, _) + | Rvalue::WrapUnsafeBinder(_, _) => true, } } } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 5868b64f6b5cb..8afa95057242c 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1275,6 +1275,10 @@ pub enum ProjectionElem { /// requiring an intermediate variable. OpaqueCast(T), + /// A transmute from an unsafe binder to the type that it wraps. This is a projection + /// of a place, so it doesn't necessarily constitute a move out of the binder. + UnwrapUnsafeBinder(T), + /// A `Subtype(T)` projection is applied to any `StatementKind::Assign` where /// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping /// explicit during optimizations and codegen. @@ -1492,6 +1496,9 @@ pub enum Rvalue<'tcx> { /// optimizations and codegen backends that previously had to handle deref operations anywhere /// in a place. CopyForDeref(Place<'tcx>), + + /// Wraps a value in an unsafe binder. + WrapUnsafeBinder(Operand<'tcx>, Ty<'tcx>), } #[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 4d11492e94d31..49449426fa408 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -146,6 +146,11 @@ impl<'tcx> PlaceTy<'tcx> { ProjectionElem::Subtype(ty) => { PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty)) } + + // FIXME(unsafe_binders): Rename `handle_opaque_cast_and_subtype` to be more general. + ProjectionElem::UnwrapUnsafeBinder(ty) => { + PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty)) + } }; debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer); answer @@ -241,6 +246,7 @@ impl<'tcx> Rvalue<'tcx> { }, Rvalue::ShallowInitBox(_, ty) => Ty::new_box(tcx, ty), Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty, + Rvalue::WrapUnsafeBinder(_, ty) => ty, } } diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs index b59b9e55fe8a0..9893dd0484cbe 100644 --- a/compiler/rustc_middle/src/mir/type_foldable.rs +++ b/compiler/rustc_middle/src/mir/type_foldable.rs @@ -1,6 +1,7 @@ //! `TypeFoldable` implementations for MIR types use rustc_ast::InlineAsmTemplatePiece; +use rustc_hir::UnsafeBinderCastKind; use rustc_hir::def_id::LocalDefId; use super::*; @@ -21,6 +22,7 @@ TrivialTypeTraversalImpls! { SwitchTargets, CoroutineKind, CoroutineSavedLocal, + UnsafeBinderCastKind, } TrivialTypeTraversalImpls! { diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 95de08ce9c874..e4c238b73fcef 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -781,6 +781,11 @@ macro_rules! make_mir_visitor { self.visit_operand(operand, location); self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } + + Rvalue::WrapUnsafeBinder(op, ty) => { + self.visit_operand(op, location); + self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); + } } } @@ -1151,6 +1156,11 @@ macro_rules! visit_place_fns { self.visit_ty(&mut new_ty, TyContext::Location(location)); if ty != new_ty { Some(PlaceElem::Subtype(new_ty)) } else { None } } + PlaceElem::UnwrapUnsafeBinder(ty) => { + let mut new_ty = ty; + self.visit_ty(&mut new_ty, TyContext::Location(location)); + if ty != new_ty { Some(PlaceElem::UnwrapUnsafeBinder(new_ty)) } else { None } + } PlaceElem::Deref | PlaceElem::ConstantIndex { .. } | PlaceElem::Subslice { .. } @@ -1219,7 +1229,8 @@ macro_rules! visit_place_fns { match elem { ProjectionElem::OpaqueCast(ty) | ProjectionElem::Subtype(ty) - | ProjectionElem::Field(_, ty) => { + | ProjectionElem::Field(_, ty) + | ProjectionElem::UnwrapUnsafeBinder(ty) => { self.visit_ty(ty, TyContext::Location(location)); } ProjectionElem::Index(local) => { diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 86014c34b4584..d56046136c7a9 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -489,6 +489,19 @@ pub enum ExprKind<'tcx> { user_ty: UserTy<'tcx>, user_ty_span: Span, }, + /// An unsafe binder cast on a place, e.g. `unwrap_binder!(*ptr)`. + PlaceUnwrapUnsafeBinder { + source: ExprId, + }, + /// An unsafe binder cast on a value, e.g. `unwrap_binder!(rvalue())`, + /// which makes a temporary. + ValueUnwrapUnsafeBinder { + source: ExprId, + }, + /// Construct an unsafe binder, e.g. `wrap_binder(&ref)`. + WrapUnsafeBinder { + source: ExprId, + }, /// A closure definition. Closure(Box>), /// A literal. diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 64bac12b2666a..2aeb13942a382 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -136,6 +136,9 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( | ValueTypeAscription { source, user_ty: _, user_ty_span: _ } => { visitor.visit_expr(&visitor.thir()[source]) } + PlaceUnwrapUnsafeBinder { source } + | ValueUnwrapUnsafeBinder { source } + | WrapUnsafeBinder { source } => visitor.visit_expr(&visitor.thir()[source]), Closure(box ClosureExpr { closure_id: _, args: _, diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 053775b493789..2c9a961b9132d 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -361,6 +361,18 @@ mir_build_unreachable_pattern = unreachable pattern .unreachable_pattern_let_binding = there is a binding of the same name; if you meant to pattern match against the value of that binding, that is a feature of constants that is not available for `let` bindings .suggestion = remove the match arm +mir_build_unsafe_binder_cast_requires_unsafe = + unsafe binder cast is unsafe and requires unsafe block + .label = unsafe binder cast + .note = casting to or from an `unsafe<...>` binder type is unsafe since it erases lifetime + information that may be required to uphold safety guarantees of a type + +mir_build_unsafe_binder_cast_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = + unsafe binder cast is unsafe and requires unsafe block or unsafe fn + .label = unsafe binder cast + .note = casting to or from an `unsafe<...>` binder type is unsafe since it erases lifetime + information that may be required to uphold safety guarantees of a type + mir_build_unsafe_field_requires_unsafe = use of unsafe field is unsafe and requires unsafe block .note = unsafe fields may carry library invariants diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index 0086775e9f46d..482f1e3840b14 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -105,7 +105,8 @@ fn convert_to_hir_projections_and_truncate_for_capture( ProjectionElem::OpaqueCast(_) | ProjectionElem::Subtype(..) => continue, ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } => { + | ProjectionElem::Subslice { .. } + | ProjectionElem::UnwrapUnsafeBinder(_) => { // We don't capture array-access projections. // We can stop here as arrays are captured completely. break; @@ -523,6 +524,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.and(PlaceBuilder::from(temp)) } + ExprKind::PlaceUnwrapUnsafeBinder { source } => { + let place_builder = unpack!( + block = this.expr_as_place(block, source, mutability, fake_borrow_temps,) + ); + block.and(place_builder.project(PlaceElem::UnwrapUnsafeBinder(expr.ty))) + } + ExprKind::ValueUnwrapUnsafeBinder { source } => { + let source_expr = &this.thir[source]; + let temp = unpack!( + block = this.as_temp(block, source_expr.temp_lifetime, source, mutability) + ); + block.and(PlaceBuilder::from(temp).project(PlaceElem::UnwrapUnsafeBinder(expr.ty))) + } + ExprKind::Array { .. } | ExprKind::Tuple { .. } | ExprKind::Adt { .. } @@ -560,7 +575,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::OffsetOf { .. } | ExprKind::Yield { .. } | ExprKind::ThreadLocalRef(_) - | ExprKind::Call { .. } => { + | ExprKind::Call { .. } + | ExprKind::WrapUnsafeBinder { .. } => { // these are not places, so we need to make a temporary. debug_assert!(!matches!(Category::of(&expr.kind), Some(Category::Place))); let temp = @@ -776,7 +792,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ProjectionElem::OpaqueCast(..) | ProjectionElem::Subtype(..) | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } => (), + | ProjectionElem::Subslice { .. } + | ProjectionElem::UnwrapUnsafeBinder(_) => (), } } } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index 9961c2488ef42..e7713f0a1d63b 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -508,6 +508,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.and(Rvalue::Use(Operand::Constant(Box::new(constant)))) } + ExprKind::WrapUnsafeBinder { source } => { + let source = unpack!( + block = this.as_operand( + block, + scope, + source, + LocalInfo::Boring, + NeedsTemporary::Maybe + ) + ); + block.and(Rvalue::WrapUnsafeBinder(source, expr.ty)) + } + ExprKind::Yield { .. } | ExprKind::Block { .. } | ExprKind::Match { .. } @@ -532,7 +545,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::Become { .. } | ExprKind::InlineAsm { .. } | ExprKind::PlaceTypeAscription { .. } - | ExprKind::ValueTypeAscription { .. } => { + | ExprKind::ValueTypeAscription { .. } + | ExprKind::PlaceUnwrapUnsafeBinder { .. } + | ExprKind::ValueUnwrapUnsafeBinder { .. } => { // these do not have corresponding `Rvalue` variants, // so make an operand and then return that debug_assert!(!matches!( diff --git a/compiler/rustc_mir_build/src/builder/expr/category.rs b/compiler/rustc_mir_build/src/builder/expr/category.rs index e0349e3e3f668..ca55d36bfc659 100644 --- a/compiler/rustc_mir_build/src/builder/expr/category.rs +++ b/compiler/rustc_mir_build/src/builder/expr/category.rs @@ -41,7 +41,9 @@ impl Category { | ExprKind::UpvarRef { .. } | ExprKind::VarRef { .. } | ExprKind::PlaceTypeAscription { .. } - | ExprKind::ValueTypeAscription { .. } => Some(Category::Place), + | ExprKind::ValueTypeAscription { .. } + | ExprKind::PlaceUnwrapUnsafeBinder { .. } + | ExprKind::ValueUnwrapUnsafeBinder { .. } => Some(Category::Place), ExprKind::LogicalOp { .. } | ExprKind::Match { .. } @@ -68,7 +70,8 @@ impl Category { | ExprKind::Assign { .. } | ExprKind::AssignOp { .. } | ExprKind::ThreadLocalRef(_) - | ExprKind::OffsetOf { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), + | ExprKind::OffsetOf { .. } + | ExprKind::WrapUnsafeBinder { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), ExprKind::ConstBlock { .. } | ExprKind::Literal { .. } diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 928156572d50a..b25cd0f4426bd 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -554,7 +554,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::VarRef { .. } | ExprKind::UpvarRef { .. } | ExprKind::PlaceTypeAscription { .. } - | ExprKind::ValueTypeAscription { .. } => { + | ExprKind::ValueTypeAscription { .. } + | ExprKind::PlaceUnwrapUnsafeBinder { .. } + | ExprKind::ValueUnwrapUnsafeBinder { .. } => { debug_assert!(Category::of(&expr.kind) == Some(Category::Place)); let place = unpack!(block = this.as_place(block, expr_id)); @@ -613,7 +615,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::ConstParam { .. } | ExprKind::ThreadLocalRef(_) | ExprKind::StaticRef { .. } - | ExprKind::OffsetOf { .. } => { + | ExprKind::OffsetOf { .. } + | ExprKind::WrapUnsafeBinder { .. } => { debug_assert!(match Category::of(&expr.kind).unwrap() { // should be handled above Category::Rvalue(RvalueFunc::Into) => false, diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 995bc311b7c48..ee7f28c974b76 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -439,6 +439,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { | ExprKind::NeverToAny { .. } | ExprKind::PlaceTypeAscription { .. } | ExprKind::ValueTypeAscription { .. } + | ExprKind::PlaceUnwrapUnsafeBinder { .. } + | ExprKind::ValueUnwrapUnsafeBinder { .. } + | ExprKind::WrapUnsafeBinder { .. } | ExprKind::PointerCoercion { .. } | ExprKind::Repeat { .. } | ExprKind::StaticRef { .. } @@ -680,6 +683,11 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { } } } + ExprKind::PlaceUnwrapUnsafeBinder { .. } + | ExprKind::ValueUnwrapUnsafeBinder { .. } + | ExprKind::WrapUnsafeBinder { .. } => { + self.requires_unsafe(expr.span, UnsafeBinderCast); + } _ => {} } visit::walk_expr(self, expr); @@ -728,6 +736,7 @@ enum UnsafeOpKind { /// (e.g., with `-C target-feature`). build_enabled: Vec, }, + UnsafeBinderCast, } use UnsafeOpKind::*; @@ -891,6 +900,15 @@ impl UnsafeOpKind { unsafe_not_inherited_note, }, ), + UnsafeBinderCast => tcx.emit_node_span_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnUnsafeBinderCastRequiresUnsafe { + span, + unsafe_not_inherited_note, + }, + ), } } @@ -1099,6 +1117,15 @@ impl UnsafeOpKind { function: tcx.def_path_str(*function), }); } + UnsafeBinderCast if unsafe_op_in_unsafe_fn_allowed => { + dcx.emit_err(UnsafeBinderCastRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + span, + unsafe_not_inherited_note, + }); + } + UnsafeBinderCast => { + dcx.emit_err(UnsafeBinderCastRequiresUnsafe { span, unsafe_not_inherited_note }); + } } } } diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 1f87bf0dbbbd6..e6aa191238ae8 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -160,6 +160,18 @@ pub(crate) struct UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe pub(crate) unsafe_not_inherited_note: Option, } +#[derive(LintDiagnostic)] +#[diag( + mir_build_unsafe_binder_cast_requires_unsafe, + code = E0133, +)] +pub(crate) struct UnsafeOpInUnsafeFnUnsafeBinderCastRequiresUnsafe { + #[label] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + #[derive(LintDiagnostic)] #[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe, code = E0133)] #[help] @@ -494,6 +506,32 @@ pub(crate) struct CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { pub(crate) unsafe_not_inherited_note: Option, } +#[derive(Diagnostic)] +#[diag( + mir_build_unsafe_binder_cast_requires_unsafe, + code = E0133, +)] +pub(crate) struct UnsafeBinderCastRequiresUnsafe { + #[primary_span] + #[label] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + +#[derive(Diagnostic)] +#[diag( + mir_build_unsafe_binder_cast_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, + code = E0133, +)] +pub(crate) struct UnsafeBinderCastRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + #[derive(Subdiagnostic)] #[label(mir_build_unsafe_not_inherited)] pub(crate) struct UnsafeNotInheritedNote { diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 9cdf08d749b01..795ac6b4bea54 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -1,5 +1,6 @@ use itertools::Itertools; use rustc_abi::{FIRST_VARIANT, FieldIdx}; +use rustc_ast::UnsafeBinderCastKind; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; @@ -910,8 +911,19 @@ impl<'tcx> Cx<'tcx> { } } - hir::ExprKind::UnsafeBinderCast(_kind, _source, _ty) => { - unreachable!("unsafe binders are not yet implemented") + hir::ExprKind::UnsafeBinderCast(UnsafeBinderCastKind::Unwrap, source, _ty) => { + // FIXME(unsafe_binders): Take into account the ascribed type, too. + let mirrored = self.mirror_expr(source); + if source.is_syntactic_place_expr() { + ExprKind::PlaceUnwrapUnsafeBinder { source: mirrored } + } else { + ExprKind::ValueUnwrapUnsafeBinder { source: mirrored } + } + } + hir::ExprKind::UnsafeBinderCast(UnsafeBinderCastKind::Wrap, source, _ty) => { + // FIXME(unsafe_binders): Take into account the ascribed type, too. + let mirrored = self.mirror_expr(source); + ExprKind::WrapUnsafeBinder { source: mirrored } } hir::ExprKind::DropTemps(source) => ExprKind::Use { source: self.mirror_expr(source) }, diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index e0a1117f905c6..021edd5057150 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -326,9 +326,10 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { | Use { source } | PointerCoercion { source, .. } | PlaceTypeAscription { source, .. } - | ValueTypeAscription { source, .. } => { - self.is_known_valid_scrutinee(&self.thir()[*source]) - } + | ValueTypeAscription { source, .. } + | PlaceUnwrapUnsafeBinder { source } + | ValueUnwrapUnsafeBinder { source } + | WrapUnsafeBinder { source } => self.is_known_valid_scrutinee(&self.thir()[*source]), // These diverge. Become { .. } | Break { .. } | Continue { .. } | Return { .. } => true, diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 2bcdb67c58a98..6ef4031e6e047 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -475,6 +475,24 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { self.print_expr(*source, depth_lvl + 2); print_indented!(self, "}", depth_lvl); } + PlaceUnwrapUnsafeBinder { source } => { + print_indented!(self, "PlaceUnwrapUnsafeBinder {", depth_lvl); + print_indented!(self, "source:", depth_lvl + 1); + self.print_expr(*source, depth_lvl + 2); + print_indented!(self, "}", depth_lvl); + } + ValueUnwrapUnsafeBinder { source } => { + print_indented!(self, "ValueUnwrapUnsafeBinder {", depth_lvl); + print_indented!(self, "source:", depth_lvl + 1); + self.print_expr(*source, depth_lvl + 2); + print_indented!(self, "}", depth_lvl); + } + WrapUnsafeBinder { source } => { + print_indented!(self, "WrapUnsafeBinder {", depth_lvl); + print_indented!(self, "source:", depth_lvl + 1); + self.print_expr(*source, depth_lvl + 2); + print_indented!(self, "}", depth_lvl); + } Closure(closure_expr) => { print_indented!(self, "Closure {", depth_lvl); print_indented!(self, "closure_expr:", depth_lvl + 1); diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index df4b1a5341731..9abb83434321d 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -97,7 +97,8 @@ where | Rvalue::UnaryOp(..) | Rvalue::Discriminant(..) | Rvalue::Aggregate(..) - | Rvalue::CopyForDeref(..) => {} + | Rvalue::CopyForDeref(..) + | Rvalue::WrapUnsafeBinder(..) => {} } } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs index d79d2c316ee78..d056ad3d4b4ca 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs @@ -32,6 +32,7 @@ impl<'tcx> Lift for PlaceElem<'tcx> { } ProjectionElem::Downcast(a, u) => ProjectionElem::Downcast(a, u), ProjectionElem::Subtype(_ty) => ProjectionElem::Subtype(()), + ProjectionElem::UnwrapUnsafeBinder(_ty) => ProjectionElem::UnwrapUnsafeBinder(()), } } } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index d1b3a389e9e5d..6e00e427a46c7 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -208,7 +208,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | ty::Infer(_) | ty::Error(_) | ty::Placeholder(_) => bug!( - "When Place contains ProjectionElem::Field it's type shouldn't be {place_ty:#?}" + "When Place contains ProjectionElem::Field its type shouldn't be {place_ty:#?}" ), }, ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { @@ -226,6 +226,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { } _ => bug!("Unexpected type {place_ty:#?}"), }, + ProjectionElem::UnwrapUnsafeBinder(_) => {} // `OpaqueCast`:Only transmutes the type, so no moves there. // `Downcast` :Only changes information about a `Place` without moving. // `Subtype` :Only transmutes the type, so moves. @@ -399,7 +400,8 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | Rvalue::Repeat(ref operand, _) | Rvalue::Cast(_, ref operand, _) | Rvalue::ShallowInitBox(ref operand, _) - | Rvalue::UnaryOp(_, ref operand) => self.gather_operand(operand), + | Rvalue::UnaryOp(_, ref operand) + | Rvalue::WrapUnsafeBinder(ref operand, _) => self.gather_operand(operand), Rvalue::BinaryOp(ref _binop, box (ref lhs, ref rhs)) => { self.gather_operand(lhs); self.gather_operand(rhs); diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 8879e029346da..90173da17f0fc 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -504,7 +504,8 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { | Rvalue::Cast(..) | Rvalue::BinaryOp(..) | Rvalue::Aggregate(..) - | Rvalue::ShallowInitBox(..) => { + | Rvalue::ShallowInitBox(..) + | Rvalue::WrapUnsafeBinder(..) => { // No modification is possible through these r-values. return ValueOrPlace::TOP; } diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 41de1b58b91bb..7395ad496dbdf 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -575,6 +575,9 @@ impl WriteInfo { self.add_operand(op); } } + Rvalue::WrapUnsafeBinder(op, _) => { + self.add_operand(op); + } Rvalue::ThreadLocalRef(_) | Rvalue::NullaryOp(_, _) | Rvalue::Ref(_, _, _) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 16e15fa12e07f..c261e25100d38 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -476,6 +476,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty), ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty), + ProjectionElem::UnwrapUnsafeBinder(ty) => { + ProjectionElem::UnwrapUnsafeBinder(ty) + } // This should have been replaced by a `ConstantIndex` earlier. ProjectionElem::Index(_) => return None, }; @@ -713,6 +716,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty), ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty), + ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(ty), }; Some(self.insert(Value::Projection(value, proj))) @@ -867,6 +871,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { self.simplify_place_projection(place, location); return self.new_pointer(*place, AddressKind::Address(mutbl)); } + Rvalue::WrapUnsafeBinder(ref mut op, _) => { + return self.simplify_operand(op, location); + } // Operations. Rvalue::Len(ref mut place) => return self.simplify_len(place, location), @@ -931,6 +938,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ProjectionElem::Downcast(symbol, idx) => ProjectionElem::Downcast(symbol, idx), ProjectionElem::OpaqueCast(idx) => ProjectionElem::OpaqueCast(idx), ProjectionElem::Subtype(idx) => ProjectionElem::Subtype(idx), + ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(ty), }) } diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index f4ac5c6aa80d0..2864cc0b9fe0c 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -444,7 +444,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { | Rvalue::Cast(..) | Rvalue::ShallowInitBox(..) | Rvalue::Discriminant(..) - | Rvalue::NullaryOp(..) => {} + | Rvalue::NullaryOp(..) + | Rvalue::WrapUnsafeBinder(..) => {} } // FIXME we need to revisit this for #67176 @@ -546,7 +547,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let val: Value<'_> = match *rvalue { ThreadLocalRef(_) => return None, - Use(ref operand) => self.eval_operand(operand)?.into(), + Use(ref operand) | WrapUnsafeBinder(ref operand, _) => { + self.eval_operand(operand)?.into() + } CopyForDeref(place) => self.eval_place(place)?.into(), diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 6aa3343bb6e3b..9101c9bfc9aeb 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -293,7 +293,8 @@ impl<'tcx> Validator<'_, 'tcx> { // Recurse directly. ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subtype(_) - | ProjectionElem::Subslice { .. } => {} + | ProjectionElem::Subslice { .. } + | ProjectionElem::UnwrapUnsafeBinder(_) => {} // Never recurse. ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) => { @@ -426,7 +427,9 @@ impl<'tcx> Validator<'_, 'tcx> { fn validate_rvalue(&mut self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> { match rvalue { - Rvalue::Use(operand) | Rvalue::Repeat(operand, _) => { + Rvalue::Use(operand) + | Rvalue::Repeat(operand, _) + | Rvalue::WrapUnsafeBinder(operand, _) => { self.validate_operand(operand)?; } Rvalue::CopyForDeref(place) => { diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 5881264cba52e..6df8495ceb3c5 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1361,7 +1361,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { | Rvalue::ThreadLocalRef(_) | Rvalue::RawPtr(_, _) | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks, _) - | Rvalue::Discriminant(_) => {} + | Rvalue::Discriminant(_) + | Rvalue::WrapUnsafeBinder(..) => {} } self.super_rvalue(rvalue, location); } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index aee98d7d410ea..6be184fc963b8 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -217,6 +217,7 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> { stable_mir::mir::Rvalue::ShallowInitBox(op.stable(tables), ty.stable(tables)) } CopyForDeref(place) => stable_mir::mir::Rvalue::CopyForDeref(place.stable(tables)), + WrapUnsafeBinder(..) => todo!("FIXME(unsafe_binders):"), } } } @@ -395,6 +396,7 @@ impl<'tcx> Stable<'tcx> for mir::PlaceElem<'tcx> { Downcast(_, idx) => stable_mir::mir::ProjectionElem::Downcast(idx.stable(tables)), OpaqueCast(ty) => stable_mir::mir::ProjectionElem::OpaqueCast(ty.stable(tables)), Subtype(ty) => stable_mir::mir::ProjectionElem::Subtype(ty.stable(tables)), + UnwrapUnsafeBinder(..) => todo!("FIXME(unsafe_binders):"), } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index dbf2dee98a3bd..4ca42269ec1af 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2151,7 +2151,6 @@ symbols! { unwrap, unwrap_binder, unwrap_or, - unwrap_unsafe_binder, use_extern_macros, use_nested_groups, used, diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 931c36137ee5f..4038a1d68faec 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -116,6 +116,11 @@ fn recurse_build<'tcx>( | &ExprKind::ValueTypeAscription { source, .. } => { recurse_build(tcx, body, source, root_span)? } + &ExprKind::PlaceUnwrapUnsafeBinder { .. } + | &ExprKind::ValueUnwrapUnsafeBinder { .. } + | &ExprKind::WrapUnsafeBinder { .. } => { + todo!("FIXME(unsafe_binders)") + } &ExprKind::Literal { lit, neg } => { let sp = node.span; tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) @@ -347,6 +352,9 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { | thir::ExprKind::Adt(_) | thir::ExprKind::PlaceTypeAscription { .. } | thir::ExprKind::ValueTypeAscription { .. } + | thir::ExprKind::PlaceUnwrapUnsafeBinder { .. } + | thir::ExprKind::ValueUnwrapUnsafeBinder { .. } + | thir::ExprKind::WrapUnsafeBinder { .. } | thir::ExprKind::Closure(_) | thir::ExprKind::Literal { .. } | thir::ExprKind::NonHirLiteral { .. } diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 287bdc9a6fd6f..916017b09ddd1 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -289,7 +289,8 @@ fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &B | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::Subtype(_) - | ProjectionElem::Index(_) => {}, + | ProjectionElem::Index(_) + | ProjectionElem::UnwrapUnsafeBinder(_) => {}, } } diff --git a/tests/ui/unsafe-binders/expr.rs b/tests/ui/unsafe-binders/expr.rs index 0fe68751f0ae8..d437d8f8ac07e 100644 --- a/tests/ui/unsafe-binders/expr.rs +++ b/tests/ui/unsafe-binders/expr.rs @@ -1,3 +1,5 @@ +//@ check-pass + #![feature(unsafe_binders)] //~^ WARN the feature `unsafe_binders` is incomplete @@ -7,8 +9,6 @@ fn main() { unsafe { let x = 1; let binder: unsafe<'a> &'a i32 = wrap_binder!(&x); - //~^ ERROR unsafe binder casts are not fully implemented let rx = *unwrap_binder!(binder); - //~^ ERROR unsafe binder casts are not fully implemented } } diff --git a/tests/ui/unsafe-binders/expr.stderr b/tests/ui/unsafe-binders/expr.stderr index 78a288e10a3c0..07026e18e1252 100644 --- a/tests/ui/unsafe-binders/expr.stderr +++ b/tests/ui/unsafe-binders/expr.stderr @@ -1,5 +1,5 @@ warning: the feature `unsafe_binders` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/expr.rs:1:12 + --> $DIR/expr.rs:3:12 | LL | #![feature(unsafe_binders)] | ^^^^^^^^^^^^^^ @@ -7,17 +7,5 @@ LL | #![feature(unsafe_binders)] = note: see issue #130516 for more information = note: `#[warn(incomplete_features)]` on by default -error: unsafe binder casts are not fully implemented - --> $DIR/expr.rs:9:55 - | -LL | let binder: unsafe<'a> &'a i32 = wrap_binder!(&x); - | ^^ - -error: unsafe binder casts are not fully implemented - --> $DIR/expr.rs:11:34 - | -LL | let rx = *unwrap_binder!(binder); - | ^^^^^^ - -error: aborting due to 2 previous errors; 1 warning emitted +warning: 1 warning emitted diff --git a/tests/ui/unsafe-binders/mismatch.rs b/tests/ui/unsafe-binders/mismatch.rs index 731fe2d1ce9b9..840d938cbe98c 100644 --- a/tests/ui/unsafe-binders/mismatch.rs +++ b/tests/ui/unsafe-binders/mismatch.rs @@ -5,38 +5,31 @@ use std::unsafe_binder::{wrap_binder, unwrap_binder}; fn a() { let _: unsafe<'a> &'a i32 = wrap_binder!(&()); - //~^ ERROR unsafe binder casts are not fully implemented - //~| ERROR mismatched types + //~^ ERROR mismatched types } fn b() { let _: i32 = wrap_binder!(&()); - //~^ ERROR unsafe binder casts are not fully implemented - //~| ERROR `wrap_binder!()` can only wrap into unsafe binder + //~^ ERROR `wrap_binder!()` can only wrap into unsafe binder } fn c() { let y = 1; unwrap_binder!(y); - //~^ ERROR unsafe binder casts are not fully implemented - //~| ERROR expected unsafe binder, found integer as input + //~^ ERROR expected unsafe binder, found integer as input } fn d() { let unknown = Default::default(); + //~^ ERROR type annotations needed unwrap_binder!(unknown); - //~^ ERROR unsafe binder casts are not fully implemented - // FIXME(unsafe_binders): This should report ambiguity once we've removed - // the error above which taints the infcx. } fn e() { let x = wrap_binder!(&42); - //~^ ERROR unsafe binder casts are not fully implemented + //~^ ERROR type annotations needed // Currently, type inference doesn't flow backwards for unsafe binders. // It could, perhaps, but that may cause even more surprising corners. - // FIXME(unsafe_binders): This should report ambiguity once we've removed - // the error above which taints the infcx. let _: unsafe<'a> &'a i32 = x; } diff --git a/tests/ui/unsafe-binders/mismatch.stderr b/tests/ui/unsafe-binders/mismatch.stderr index a720e5dbdc1fe..f64db92eb655a 100644 --- a/tests/ui/unsafe-binders/mismatch.stderr +++ b/tests/ui/unsafe-binders/mismatch.stderr @@ -7,12 +7,6 @@ LL | #![feature(unsafe_binders)] = note: see issue #130516 for more information = note: `#[warn(incomplete_features)]` on by default -error: unsafe binder casts are not fully implemented - --> $DIR/mismatch.rs:7:46 - | -LL | let _: unsafe<'a> &'a i32 = wrap_binder!(&()); - | ^^^ - error[E0308]: mismatched types --> $DIR/mismatch.rs:7:46 | @@ -22,14 +16,8 @@ LL | let _: unsafe<'a> &'a i32 = wrap_binder!(&()); = note: expected reference `&i32` found reference `&()` -error: unsafe binder casts are not fully implemented - --> $DIR/mismatch.rs:13:31 - | -LL | let _: i32 = wrap_binder!(&()); - | ^^^ - error: `wrap_binder!()` can only wrap into unsafe binder, not `i32` - --> $DIR/mismatch.rs:13:18 + --> $DIR/mismatch.rs:12:18 | LL | let _: i32 = wrap_binder!(&()); | ^^^^^^^^^^^^^^^^^ @@ -37,32 +25,35 @@ LL | let _: i32 = wrap_binder!(&()); = note: unsafe binders are the only valid output of wrap = note: this error originates in the macro `wrap_binder` (in Nightly builds, run with -Z macro-backtrace for more info) -error: unsafe binder casts are not fully implemented - --> $DIR/mismatch.rs:20:20 - | -LL | unwrap_binder!(y); - | ^ - error: expected unsafe binder, found integer as input of `unwrap_binder!()` - --> $DIR/mismatch.rs:20:20 + --> $DIR/mismatch.rs:18:20 | LL | unwrap_binder!(y); | ^ | = note: only an unsafe binder type can be unwrapped -error: unsafe binder casts are not fully implemented - --> $DIR/mismatch.rs:27:20 +error[E0282]: type annotations needed + --> $DIR/mismatch.rs:23:9 | +LL | let unknown = Default::default(); + | ^^^^^^^ +LL | LL | unwrap_binder!(unknown); - | ^^^^^^^ + | ------- type must be known at this point + | +help: consider giving `unknown` an explicit type + | +LL | let unknown: /* Type */ = Default::default(); + | ++++++++++++ -error: unsafe binder casts are not fully implemented - --> $DIR/mismatch.rs:34:26 +error[E0282]: type annotations needed + --> $DIR/mismatch.rs:29:26 | LL | let x = wrap_binder!(&42); - | ^^^ + | ^^^ cannot infer type -error: aborting due to 8 previous errors; 1 warning emitted +error: aborting due to 5 previous errors; 1 warning emitted -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0282, E0308. +For more information about an error, try `rustc --explain E0282`. diff --git a/tests/ui/unsafe-binders/moves.rs b/tests/ui/unsafe-binders/moves.rs new file mode 100644 index 0000000000000..1f61d2bf144f0 --- /dev/null +++ b/tests/ui/unsafe-binders/moves.rs @@ -0,0 +1,38 @@ +#![feature(unsafe_binders)] +//~^ WARN the feature `unsafe_binders` is incomplete + +use std::unsafe_binder::{wrap_binder, unwrap_binder}; +use std::mem::drop; + +struct NotCopy; + +fn use_after_wrap() { + unsafe { + let base = NotCopy; + let binder: unsafe<> NotCopy = wrap_binder!(base); + drop(base); + //~^ ERROR use of moved value: `base` + } +} + +fn move_out_of_wrap() { + unsafe { + let binder: unsafe<> NotCopy = wrap_binder!(NotCopy); + drop(unwrap_binder!(binder)); + drop(unwrap_binder!(binder)); + //~^ ERROR use of moved value: `binder` + } +} + +fn not_conflicting() { + unsafe { + let binder: unsafe<> (NotCopy, NotCopy) = wrap_binder!((NotCopy, NotCopy)); + drop(unwrap_binder!(binder).0); + drop(unwrap_binder!(binder).1); + // ^ NOT a problem. + drop(unwrap_binder!(binder).0); + //~^ ERROR use of moved value: `binder.0` + } +} + +fn main() {} diff --git a/tests/ui/unsafe-binders/moves.stderr b/tests/ui/unsafe-binders/moves.stderr new file mode 100644 index 0000000000000..d90823bf7e2f9 --- /dev/null +++ b/tests/ui/unsafe-binders/moves.stderr @@ -0,0 +1,53 @@ +warning: the feature `unsafe_binders` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/moves.rs:1:12 + | +LL | #![feature(unsafe_binders)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #130516 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0382]: use of moved value: `base` + --> $DIR/moves.rs:13:14 + | +LL | let base = NotCopy; + | ---- move occurs because `base` has type `NotCopy`, which does not implement the `Copy` trait +LL | let binder: unsafe<> NotCopy = wrap_binder!(base); + | ---- value moved here +LL | drop(base); + | ^^^^ value used here after move + | +note: if `NotCopy` implemented `Clone`, you could clone the value + --> $DIR/moves.rs:7:1 + | +LL | struct NotCopy; + | ^^^^^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | let binder: unsafe<> NotCopy = wrap_binder!(base); + | ---- you could clone this value + +error[E0382]: use of moved value: `binder` + --> $DIR/moves.rs:22:14 + | +LL | drop(unwrap_binder!(binder)); + | ---------------------- value moved here +LL | drop(unwrap_binder!(binder)); + | ^^^^^^^^^^^^^^^^^^^^^^ value used here after move + | + = note: move occurs because `binder` has type `NotCopy`, which does not implement the `Copy` trait + = note: this error originates in the macro `unwrap_binder` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0382]: use of moved value: `binder.0` + --> $DIR/moves.rs:33:14 + | +LL | drop(unwrap_binder!(binder).0); + | ------------------------ value moved here +... +LL | drop(unwrap_binder!(binder).0); + | ^^^^^^^^^^^^^^^^^^^^^^^^ value used here after move + | + = note: move occurs because `binder.0` has type `NotCopy`, which does not implement the `Copy` trait + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. From b63341e892447f73888f858052bca57495ee8d67 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 31 Jan 2025 02:04:10 +0000 Subject: [PATCH 4/9] Enforce unsafe binders must be Copy (for now) --- compiler/rustc_codegen_cranelift/src/base.rs | 4 + .../rustc_trait_selection/src/traits/wf.rs | 21 +++- .../clippy_utils/src/qualify_min_const_fn.rs | 1 + tests/ui/unsafe-binders/moves.rs | 15 +-- tests/ui/unsafe-binders/moves.stderr | 98 ++++++++++++------- 5 files changed, 98 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index ea37e19099a87..97d8baa61573c 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -915,6 +915,10 @@ fn codegen_stmt<'tcx>( } crate::discriminant::codegen_set_discriminant(fx, lval, variant_index); } + Rvalue::WrapUnsafeBinder(ref operand, _to_ty) => { + let operand = codegen_operand(fx, operand); + lval.write_cvalue_transmute(fx, operand); + } } } StatementKind::StorageLive(_) diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 20b675bcb76b7..0afe483f721bb 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -828,8 +828,25 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { // Let the visitor iterate into the argument/return // types appearing in the fn signature. } - ty::UnsafeBinder(_) => { - // FIXME(unsafe_binders): We should also recurse into the binder here. + ty::UnsafeBinder(ty) => { + // FIXME(unsafe_binders): For now, we have no way to express + // that a type must be `ManuallyDrop` OR `Copy` (or a pointer). + if !ty.has_escaping_bound_vars() { + self.out.push(traits::Obligation::new( + self.tcx(), + self.cause(ObligationCauseCode::Misc), + self.param_env, + ty.map_bound(|ty| { + ty::TraitRef::new( + self.tcx(), + self.tcx().require_lang_item(LangItem::Copy, Some(self.span)), + [ty], + ) + }), + )); + } + + // We recurse into the binder below. } ty::Dynamic(data, r, _) => { diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 916017b09ddd1..0aaef91e48a6d 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -116,6 +116,7 @@ fn check_rvalue<'tcx>( Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body, msrv), Rvalue::Repeat(operand, _) | Rvalue::Use(operand) + | Rvalue::WrapUnsafeBinder(operand, _) | Rvalue::Cast( CastKind::PointerWithExposedProvenance | CastKind::IntToInt diff --git a/tests/ui/unsafe-binders/moves.rs b/tests/ui/unsafe-binders/moves.rs index 1f61d2bf144f0..5bfcee62402d6 100644 --- a/tests/ui/unsafe-binders/moves.rs +++ b/tests/ui/unsafe-binders/moves.rs @@ -1,17 +1,20 @@ +//@ known-bug: unknown + #![feature(unsafe_binders)] -//~^ WARN the feature `unsafe_binders` is incomplete +// FIXME(unsafe_binders) ~^ WARN the feature `unsafe_binders` is incomplete use std::unsafe_binder::{wrap_binder, unwrap_binder}; -use std::mem::drop; +use std::mem::{drop, ManuallyDrop}; -struct NotCopy; +struct NotCopyInner; +type NotCopy = ManuallyDrop; fn use_after_wrap() { unsafe { let base = NotCopy; let binder: unsafe<> NotCopy = wrap_binder!(base); drop(base); - //~^ ERROR use of moved value: `base` + // FIXME(unsafe_binders) ~^ ERROR use of moved value: `base` } } @@ -20,7 +23,7 @@ fn move_out_of_wrap() { let binder: unsafe<> NotCopy = wrap_binder!(NotCopy); drop(unwrap_binder!(binder)); drop(unwrap_binder!(binder)); - //~^ ERROR use of moved value: `binder` + // FIXME(unsafe_binders) ~^ ERROR use of moved value: `binder` } } @@ -31,7 +34,7 @@ fn not_conflicting() { drop(unwrap_binder!(binder).1); // ^ NOT a problem. drop(unwrap_binder!(binder).0); - //~^ ERROR use of moved value: `binder.0` + // FIXME(unsafe_binders) ~^ ERROR use of moved value: `binder.0` } } diff --git a/tests/ui/unsafe-binders/moves.stderr b/tests/ui/unsafe-binders/moves.stderr index d90823bf7e2f9..ca5079640087f 100644 --- a/tests/ui/unsafe-binders/moves.stderr +++ b/tests/ui/unsafe-binders/moves.stderr @@ -1,5 +1,37 @@ +error[E0423]: expected value, found type alias `NotCopy` + --> $DIR/moves.rs:14:20 + | +LL | let base = NotCopy; + | ^^^^^^^ + | + = note: can't use a type alias as a constructor + +error[E0423]: expected value, found type alias `NotCopy` + --> $DIR/moves.rs:23:53 + | +LL | let binder: unsafe<> NotCopy = wrap_binder!(NotCopy); + | ^^^^^^^ + | + = note: can't use a type alias as a constructor + +error[E0423]: expected value, found type alias `NotCopy` + --> $DIR/moves.rs:32:65 + | +LL | let binder: unsafe<> (NotCopy, NotCopy) = wrap_binder!((NotCopy, NotCopy)); + | ^^^^^^^ + | + = note: can't use a type alias as a constructor + +error[E0423]: expected value, found type alias `NotCopy` + --> $DIR/moves.rs:32:74 + | +LL | let binder: unsafe<> (NotCopy, NotCopy) = wrap_binder!((NotCopy, NotCopy)); + | ^^^^^^^ + | + = note: can't use a type alias as a constructor + warning: the feature `unsafe_binders` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/moves.rs:1:12 + --> $DIR/moves.rs:3:12 | LL | #![feature(unsafe_binders)] | ^^^^^^^^^^^^^^ @@ -7,47 +39,47 @@ LL | #![feature(unsafe_binders)] = note: see issue #130516 for more information = note: `#[warn(incomplete_features)]` on by default -error[E0382]: use of moved value: `base` - --> $DIR/moves.rs:13:14 +error[E0277]: the trait bound `NotCopyInner: Copy` is not satisfied + --> $DIR/moves.rs:15:21 | -LL | let base = NotCopy; - | ---- move occurs because `base` has type `NotCopy`, which does not implement the `Copy` trait LL | let binder: unsafe<> NotCopy = wrap_binder!(base); - | ---- value moved here -LL | drop(base); - | ^^^^ value used here after move + | ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `NotCopyInner` | -note: if `NotCopy` implemented `Clone`, you could clone the value - --> $DIR/moves.rs:7:1 + = note: required for `ManuallyDrop` to implement `Copy` +help: consider annotating `NotCopyInner` with `#[derive(Copy)]` + | +LL + #[derive(Copy)] +LL | struct NotCopyInner; | -LL | struct NotCopy; - | ^^^^^^^^^^^^^^ consider implementing `Clone` for this type -... -LL | let binder: unsafe<> NotCopy = wrap_binder!(base); - | ---- you could clone this value -error[E0382]: use of moved value: `binder` - --> $DIR/moves.rs:22:14 +error[E0277]: the trait bound `NotCopyInner: Copy` is not satisfied + --> $DIR/moves.rs:23:21 + | +LL | let binder: unsafe<> NotCopy = wrap_binder!(NotCopy); + | ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `NotCopyInner` + | + = note: required for `ManuallyDrop` to implement `Copy` +help: consider annotating `NotCopyInner` with `#[derive(Copy)]` | -LL | drop(unwrap_binder!(binder)); - | ---------------------- value moved here -LL | drop(unwrap_binder!(binder)); - | ^^^^^^^^^^^^^^^^^^^^^^ value used here after move +LL + #[derive(Copy)] +LL | struct NotCopyInner; | - = note: move occurs because `binder` has type `NotCopy`, which does not implement the `Copy` trait - = note: this error originates in the macro `unwrap_binder` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0382]: use of moved value: `binder.0` - --> $DIR/moves.rs:33:14 +error[E0277]: the trait bound `NotCopyInner: Copy` is not satisfied + --> $DIR/moves.rs:32:21 + | +LL | let binder: unsafe<> (NotCopy, NotCopy) = wrap_binder!((NotCopy, NotCopy)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `NotCopyInner` + | + = note: required for `ManuallyDrop` to implement `Copy` + = note: required because it appears within the type `(ManuallyDrop, ManuallyDrop)` +help: consider annotating `NotCopyInner` with `#[derive(Copy)]` | -LL | drop(unwrap_binder!(binder).0); - | ------------------------ value moved here -... -LL | drop(unwrap_binder!(binder).0); - | ^^^^^^^^^^^^^^^^^^^^^^^^ value used here after move +LL + #[derive(Copy)] +LL | struct NotCopyInner; | - = note: move occurs because `binder.0` has type `NotCopy`, which does not implement the `Copy` trait -error: aborting due to 3 previous errors; 1 warning emitted +error: aborting due to 7 previous errors; 1 warning emitted -For more information about this error, try `rustc --explain E0382`. +Some errors have detailed explanations: E0277, E0423. +For more information about an error, try `rustc --explain E0277`. From 442b9a93872096a6e51e23f36f14f2dacf5520ce Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 31 Jan 2025 17:19:32 +0000 Subject: [PATCH 5/9] Validation --- .../rustc_const_eval/src/interpret/step.rs | 2 + compiler/rustc_mir_transform/src/validate.rs | 40 ++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index beae5047e7efd..abe73c43d8a94 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -279,6 +279,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } WrapUnsafeBinder(ref op, _ty) => { + // Constructing an unsafe binder acts like a transmute + // since the operand's layout does not change. let op = self.eval_operand(op, None)?; self.copy_op_allow_transmute(&op, &dest)?; } diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 6df8495ceb3c5..e282eaf761c10 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -807,6 +807,25 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ) } } + ProjectionElem::UnwrapUnsafeBinder(unwrapped_ty) => { + let binder_ty = place_ref.ty(&self.body.local_decls, self.tcx); + let ty::UnsafeBinder(binder_ty) = *binder_ty.ty.kind() else { + self.fail( + location, + format!("WrapUnsafeBinder does not produce a ty::UnsafeBinder"), + ); + return; + }; + let binder_inner_ty = self.tcx.instantiate_bound_regions_with_erased(*binder_ty); + if !self.mir_assign_valid_types(unwrapped_ty, binder_inner_ty) { + self.fail( + location, + format!( + "Cannot unwrap unsafe binder {binder_ty:?} into type {unwrapped_ty:?}" + ), + ); + } + } _ => {} } self.super_projection_elem(place_ref, elem, context, location); @@ -1361,8 +1380,25 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { | Rvalue::ThreadLocalRef(_) | Rvalue::RawPtr(_, _) | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks, _) - | Rvalue::Discriminant(_) - | Rvalue::WrapUnsafeBinder(..) => {} + | Rvalue::Discriminant(_) => {} + + Rvalue::WrapUnsafeBinder(op, ty) => { + let unwrapped_ty = op.ty(self.body, self.tcx); + let ty::UnsafeBinder(binder_ty) = *ty.kind() else { + self.fail( + location, + format!("WrapUnsafeBinder does not produce a ty::UnsafeBinder"), + ); + return; + }; + let binder_inner_ty = self.tcx.instantiate_bound_regions_with_erased(*binder_ty); + if !self.mir_assign_valid_types(unwrapped_ty, binder_inner_ty) { + self.fail( + location, + format!("Cannot wrap {unwrapped_ty:?} into unsafe binder {binder_ty:?}"), + ); + } + } } self.super_rvalue(rvalue, location); } From 6fa6168e719ec3bbfdd345621af5969b4f5d2e2d Mon Sep 17 00:00:00 2001 From: Slanterns Date: Sat, 1 Feb 2025 02:10:02 +0800 Subject: [PATCH 6/9] stabilize `once_wait` --- library/std/src/sync/once_lock.rs | 4 +--- library/std/src/sync/poison/once.rs | 6 ++---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index 49f2dafd8fd9c..6fc0abbed9e15 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -174,8 +174,6 @@ impl OnceLock { /// /// Waiting for a computation on another thread to finish: /// ```rust - /// #![feature(once_wait)] - /// /// use std::thread; /// use std::sync::OnceLock; /// @@ -189,7 +187,7 @@ impl OnceLock { /// }) /// ``` #[inline] - #[unstable(feature = "once_wait", issue = "127527")] + #[stable(feature = "once_wait", since = "CURRENT_RUSTC_VERSION")] pub fn wait(&self) -> &T { self.once.wait_force(); diff --git a/library/std/src/sync/poison/once.rs b/library/std/src/sync/poison/once.rs index 27db4b634fb28..528b11ca0c1e6 100644 --- a/library/std/src/sync/poison/once.rs +++ b/library/std/src/sync/poison/once.rs @@ -269,8 +269,6 @@ impl Once { /// # Example /// /// ```rust - /// #![feature(once_wait)] - /// /// use std::sync::Once; /// use std::thread; /// @@ -289,7 +287,7 @@ impl Once { /// If this [`Once`] has been poisoned because an initialization closure has /// panicked, this method will also panic. Use [`wait_force`](Self::wait_force) /// if this behavior is not desired. - #[unstable(feature = "once_wait", issue = "127527")] + #[stable(feature = "once_wait", since = "CURRENT_RUSTC_VERSION")] pub fn wait(&self) { if !self.inner.is_completed() { self.inner.wait(false); @@ -298,7 +296,7 @@ impl Once { /// Blocks the current thread until initialization has completed, ignoring /// poisoning. - #[unstable(feature = "once_wait", issue = "127527")] + #[stable(feature = "once_wait", since = "CURRENT_RUSTC_VERSION")] pub fn wait_force(&self) { if !self.inner.is_completed() { self.inner.wait(true); From ce7cb312fa2a51d7f2cf4603d9707811f76ed611 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Fri, 31 Jan 2025 21:11:23 -0500 Subject: [PATCH 7/9] Add link attribute for Enzyme's FFI --- compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index ae813fe5ebf89..92b0ce8ffe1fa 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -4,6 +4,8 @@ use libc::{c_char, c_uint}; use super::ffi::{BasicBlock, Metadata, Module, Type, Value}; use crate::llvm::Bool; + +#[link(name = "llvm-wrapper", kind = "static")] extern "C" { // Enzyme pub fn LLVMRustHasMetadata(I: &Value, KindID: c_uint) -> bool; @@ -12,10 +14,13 @@ extern "C" { pub fn LLVMRustDIGetInstMetadata(I: &Value) -> Option<&Metadata>; pub fn LLVMRustEraseInstFromParent(V: &Value); pub fn LLVMRustGetTerminator<'a>(B: &BasicBlock) -> &'a Value; - pub fn LLVMDumpModule(M: &Module); - pub fn LLVMDumpValue(V: &Value); pub fn LLVMRustVerifyFunction(V: &Value, action: LLVMRustVerifierFailureAction) -> Bool; +} +extern "C" { + // Enzyme + pub fn LLVMDumpModule(M: &Module); + pub fn LLVMDumpValue(V: &Value); pub fn LLVMGetFunctionCallConv(F: &Value) -> c_uint; pub fn LLVMGetReturnType(T: &Type) -> &Type; pub fn LLVMGetParams(Fnc: &Value, parms: *mut &Value); From 3d84a49c376067c67d878b6710adc9865d758c62 Mon Sep 17 00:00:00 2001 From: Ross Sullivan Date: Sat, 18 Jan 2025 23:26:47 +0900 Subject: [PATCH 8/9] docs: Documented Send and Sync requirements for Mutex + MutexGuard --- library/std/src/sync/poison/mutex.rs | 32 ++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index 01ef71a187fec..fb43ada637543 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -181,10 +181,29 @@ pub struct Mutex { data: UnsafeCell, } -// these are the only places where `T: Send` matters; all other -// functionality works fine on a single thread. +/// `T` must be `Send` for a [`Mutex`] to be `Send` because it is possible to acquire +/// the owned `T` from the `Mutex` via [`into_inner`]. +/// +/// [`into_inner`]: Mutex::into_inner #[stable(feature = "rust1", since = "1.0.0")] unsafe impl Send for Mutex {} + +/// `T` must be `Send` for [`Mutex`] to be `Sync`. +/// This ensures that the protected data can be accessed safely from multiple threads +/// without causing data races or other unsafe behavior. +/// +/// [`Mutex`] provides mutable access to `T` to one thread at a time. However, it's essential +/// for `T` to be `Send` because it's not safe for non-`Send` structures to be accessed in +/// this manner. For instance, consider [`Rc`], a non-atomic reference counted smart pointer, +/// which is not `Send`. With `Rc`, we can have multiple copies pointing to the same heap +/// allocation with a non-atomic reference count. If we were to use `Mutex>`, it would +/// only protect one instance of `Rc` from shared access, leaving other copies vulnerable +/// to potential data races. +/// +/// Also note that it is not necessary for `T` to be `Sync` as `&T` is only made available +/// to one thread at a time if `T` is not `Sync`. +/// +/// [`Rc`]: crate::rc::Rc #[stable(feature = "rust1", since = "1.0.0")] unsafe impl Sync for Mutex {} @@ -211,8 +230,17 @@ pub struct MutexGuard<'a, T: ?Sized + 'a> { poison: poison::Guard, } +/// A [`MutexGuard`] is not `Send` to maximize platform portablity. +/// +/// On platforms that use POSIX threads (commonly referred to as pthreads) there is a requirement to +/// release mutex locks on the same thread they were acquired. +/// For this reason, [`MutexGuard`] must not implement `Send` to prevent it being dropped from +/// another thread. #[stable(feature = "rust1", since = "1.0.0")] impl !Send for MutexGuard<'_, T> {} + +/// `T` must be `Sync` for a [`MutexGuard`] to be `Sync` +/// because it is possible to get a `&T` from `&MutexGuard` (via `Deref`). #[stable(feature = "mutexguard", since = "1.19.0")] unsafe impl Sync for MutexGuard<'_, T> {} From 55dc6dbcf087f0f5b1705f6ffc9a9fdd7fa5bb63 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Fri, 31 Jan 2025 20:19:25 +0100 Subject: [PATCH 9/9] document ptr comparison being by address --- library/core/src/ptr/const_ptr.rs | 6 ++++-- library/core/src/ptr/mut_ptr.rs | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index ec569291853a5..0c6eaf60d0480 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -1681,7 +1681,7 @@ impl *const [T; N] { } } -// Equality for pointers +/// Pointer equality is by address, as produced by the [`<*const T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for *const T { #[inline] @@ -1691,10 +1691,11 @@ impl PartialEq for *const T { } } +/// Pointer equality is an equivalence relation. #[stable(feature = "rust1", since = "1.0.0")] impl Eq for *const T {} -// Comparison for pointers +/// Pointer comparison is by address, as produced by the `[`<*const T>::addr`](pointer::addr)` method. #[stable(feature = "rust1", since = "1.0.0")] impl Ord for *const T { #[inline] @@ -1710,6 +1711,7 @@ impl Ord for *const T { } } +/// Pointer comparison is by address, as produced by the `[`<*const T>::addr`](pointer::addr)` method. #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for *const T { #[inline] diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 5d9d337f101a5..d1b0104c0fa92 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -2097,7 +2097,7 @@ impl *mut [T; N] { } } -// Equality for pointers +/// Pointer equality is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for *mut T { #[inline(always)] @@ -2107,9 +2107,11 @@ impl PartialEq for *mut T { } } +/// Pointer equality is an equivalence relation. #[stable(feature = "rust1", since = "1.0.0")] impl Eq for *mut T {} +/// Pointer comparison is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] impl Ord for *mut T { #[inline] @@ -2125,6 +2127,7 @@ impl Ord for *mut T { } } +/// Pointer comparison is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for *mut T { #[inline(always)]