Skip to content

Commit

Permalink
Implement MIR and drop semantics for unsafe binders
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Dec 28, 2024
1 parent 3c1e750 commit bfe01d2
Show file tree
Hide file tree
Showing 50 changed files with 538 additions and 90 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1655,7 +1655,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`
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3976,7 +3976,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Subtype(_)
| ProjectionElem::Index(_) => kind,
| ProjectionElem::Index(_)
| ProjectionElem::UnsafeBinderCast(..) => kind,
},
place_ty.projection_ty(tcx, elem),
)
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_borrowck/src/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
ProjectionElem::Downcast(..) => (),
ProjectionElem::OpaqueCast(..) => (),
ProjectionElem::Subtype(..) => (),
ProjectionElem::UnsafeBinderCast(..) => (),
ProjectionElem::Field(field, _ty) => {
// FIXME(project-rfc_2229#36): print capture precisely here.
if let Some(field) = self.is_upvar_field_projection(PlaceRef {
Expand Down Expand Up @@ -323,9 +324,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::UnsafeBinderCast(_, ty) => PlaceTy::from_ty(*ty),
ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type),
},
};
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::OpaqueCast { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Downcast(..),
| ProjectionElem::Downcast(..)
| ProjectionElem::UnsafeBinderCast(..),
],
} => bug!("Unexpected immutable place."),
}
Expand Down
10 changes: 8 additions & 2 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1690,7 +1690,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
// So it's safe to skip these.
ProjectionElem::OpaqueCast(_)
| ProjectionElem::Subtype(_)
| ProjectionElem::Downcast(_, _) => (),
| ProjectionElem::Downcast(_, _)
| ProjectionElem::UnsafeBinderCast(_, _) => (),
}

place_ty = place_ty.projection_ty(tcx, elem);
Expand Down Expand Up @@ -1924,6 +1925,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
// FIXME: is this true even if P is an adt with a dtor?
{ }

ProjectionElem::UnsafeBinderCast(..) => {
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(
Expand Down Expand Up @@ -2304,7 +2309,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
| ProjectionElem::Subslice { .. }
| ProjectionElem::Subtype(..)
| ProjectionElem::OpaqueCast { .. }
| ProjectionElem::Downcast(..) => {
| ProjectionElem::Downcast(..)
| ProjectionElem::UnsafeBinderCast(..) => {
let upvar_field_projection = self.is_upvar_field_projection(place);
if let Some(field) = upvar_field_projection {
let upvar = &self.upvars[field.index()];
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_borrowck/src/places_conflict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,8 @@ fn place_components_conflict<'tcx>(
| (ProjectionElem::Subslice { .. }, _, _)
| (ProjectionElem::OpaqueCast { .. }, _, _)
| (ProjectionElem::Subtype(_), _, _)
| (ProjectionElem::Downcast { .. }, _, _) => {
| (ProjectionElem::Downcast { .. }, _, _)
| (ProjectionElem::UnsafeBinderCast(..), _, _) => {
// 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
Expand Down Expand Up @@ -518,5 +519,9 @@ fn place_projection_conflict<'tcx>(
pi1_elem,
pi2_elem
),

(ProjectionElem::UnsafeBinderCast(..), _) => {
todo!()
}
}
}
4 changes: 4 additions & 0 deletions compiler/rustc_borrowck/src/prefixes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ impl<'tcx> Iterator for Prefixes<'tcx> {
self.next = Some(cursor_base);
return Some(cursor);
}
ProjectionElem::UnsafeBinderCast(..) => {
self.next = Some(cursor_base);
return Some(cursor);
}
ProjectionElem::Downcast(..)
| ProjectionElem::Subslice { .. }
| ProjectionElem::OpaqueCast { .. }
Expand Down
43 changes: 42 additions & 1 deletion compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,46 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
)
.unwrap();
}
ProjectionElem::UnsafeBinderCast(kind, ty) => match kind {
hir::UnsafeBinderCastKind::Wrap => {
let ty::UnsafeBinder(binder_ty) = *ty.kind() else {
unreachable!();
};
let expected_ty = self.typeck.infcx.instantiate_binder_with_fresh_vars(
self.body().source_info(location).span,
BoundRegionConversionTime::HigherRankedType,
binder_ty.into(),
);
self.typeck
.relate_types(
expected_ty,
context.ambient_variance(),
base_ty.ty,
location.to_locations(),
ConstraintCategory::TypeAnnotation,
)
.unwrap();
}
hir::UnsafeBinderCastKind::Unwrap => {
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::TypeAnnotation,
)
.unwrap();
}
},
ProjectionElem::Subtype(_) => {
bug!("ProjectionElem::Subtype shouldn't exist in borrowck")
}
Expand Down Expand Up @@ -2413,7 +2453,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
| ProjectionElem::OpaqueCast(..)
| ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => {
| ProjectionElem::Subslice { .. }
| ProjectionElem::UnsafeBinderCast(..) => {
// other field access
}
ProjectionElem::Subtype(_) => {
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_codegen_cranelift/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -977,7 +977,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::UnsafeBinderCast(_, ty) => {
cplace = cplace.place_transmute_type(fx, fx.monomorphize(ty));
}
PlaceElem::Field(field, _ty) => {
cplace = cplace.place_field(fx, field);
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_ssa/src/mir/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::UnsafeBinderCast(_, 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);
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_const_eval/src/check_consts/qualifs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,8 @@ where
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Downcast(_, _)
| ProjectionElem::Index(_) => {}
| ProjectionElem::Index(_)
| ProjectionElem::UnsafeBinderCast(..) => {}
}

let base_ty = place_base.ty(cx.body, cx.tcx);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_const_eval/src/interpret/projection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ where
OpaqueCast(ty) => {
span_bug!(self.cur_span(), "OpaqueCast({ty}) encountered after borrowck")
}
UnsafeBinderCast(_, 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())?,
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1655,8 +1655,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 =
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_middle/src/mir/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1286,6 +1286,14 @@ fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) ->
ProjectionElem::Index(_)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => {}
ProjectionElem::UnsafeBinderCast(kind, _) => match kind {
hir::UnsafeBinderCastKind::Wrap => {
write!(fmt, "wrap_unsafe_binder!(")?;
}
rustc_ast::UnsafeBinderCastKind::Unwrap => {
write!(fmt, "unwrap_unsafe_binder!(")?;
}
},
}
}

Expand Down Expand Up @@ -1334,6 +1342,9 @@ fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) ->
ProjectionElem::Subslice { from, to, from_end: false } => {
write!(fmt, "[{from:?}..{to:?}]")?;
}
ProjectionElem::UnsafeBinderCast(_, ty) => {
write!(fmt, "; {ty})")?;
}
}
}

Expand Down
9 changes: 7 additions & 2 deletions compiler/rustc_middle/src/mir/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ impl<V, T> ProjectionElem<V, T> {
| Self::Subtype(_)
| Self::ConstantIndex { .. }
| Self::Subslice { .. }
| Self::Downcast(_, _) => false,
| Self::Downcast(_, _)
| Self::UnsafeBinderCast(..) => false,
}
}

Expand All @@ -76,7 +77,8 @@ impl<V, T> ProjectionElem<V, T> {
| Self::Subtype(_)
| Self::ConstantIndex { .. }
| Self::Subslice { .. }
| Self::Downcast(_, _) => true,
| Self::Downcast(_, _)
| Self::UnsafeBinderCast(..) => true,
}
}

Expand All @@ -102,6 +104,9 @@ impl<V, T> ProjectionElem<V, T> {
| Self::Subtype(_)
| Self::OpaqueCast(_)
| Self::Subslice { .. } => false,

// FIXME(unsafe_binders): Figure this out.
Self::UnsafeBinderCast(..) => false,
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_middle/src/mir/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
use rustc_abi::{FieldIdx, VariantIdx};
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece, Mutability};
use rustc_data_structures::packed::Pu128;
use rustc_hir::CoroutineKind;
use rustc_hir::def_id::DefId;
use rustc_hir::{CoroutineKind, UnsafeBinderCastKind};
use rustc_index::IndexVec;
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
use rustc_span::def_id::LocalDefId;
Expand Down Expand Up @@ -1215,6 +1215,8 @@ pub enum ProjectionElem<V, T> {
/// requiring an intermediate variable.
OpaqueCast(T),

UnsafeBinderCast(UnsafeBinderCastKind, 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.
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/mir/tcx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::UnsafeBinderCast(_, ty) => {
PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
}
};
debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
answer
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_middle/src/mir/type_foldable.rs
Original file line number Diff line number Diff line change
@@ -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::*;
Expand All @@ -20,6 +21,7 @@ TrivialTypeTraversalImpls! {
SwitchTargets,
CoroutineKind,
CoroutineSavedLocal,
UnsafeBinderCastKind,
}

TrivialTypeTraversalImpls! {
Expand Down
12 changes: 11 additions & 1 deletion compiler/rustc_middle/src/mir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,15 @@ 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::UnsafeBinderCast(kind, ty) => {
let mut new_ty = ty;
self.visit_ty(&mut new_ty, TyContext::Location(location));
if ty != new_ty {
Some(PlaceElem::UnsafeBinderCast(kind, new_ty))
} else {
None
}
}
PlaceElem::Deref
| PlaceElem::ConstantIndex { .. }
| PlaceElem::Subslice { .. }
Expand Down Expand Up @@ -1208,7 +1217,8 @@ macro_rules! visit_place_fns {
match elem {
ProjectionElem::OpaqueCast(ty)
| ProjectionElem::Subtype(ty)
| ProjectionElem::Field(_, ty) => {
| ProjectionElem::Field(_, ty)
| ProjectionElem::UnsafeBinderCast(_, ty) => {
self.visit_ty(ty, TyContext::Location(location));
}
ProjectionElem::Index(local) => {
Expand Down
12 changes: 11 additions & 1 deletion compiler/rustc_middle/src/thir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rustc_abi::{FieldIdx, Integer, Size, VariantIdx};
use rustc_ast::{AsmMacro, InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::{BindingMode, ByRef, HirId, MatchSource, RangeEnd};
use rustc_hir::{BindingMode, ByRef, HirId, MatchSource, RangeEnd, UnsafeBinderCastKind};
use rustc_index::{IndexVec, newtype_index};
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeVisitable};
use rustc_middle::middle::region;
Expand Down Expand Up @@ -489,6 +489,16 @@ pub enum ExprKind<'tcx> {
user_ty: UserTy<'tcx>,
user_ty_span: Span,
},
/// An unsafe binder cast on a place, e.g. `unwrap_unsafe_binder!(x)`.
PlaceUnsafeBinderCast {
kind: UnsafeBinderCastKind,
source: ExprId,
},
/// An unsafe binder cast on a value, e.g. `wrap_unsafe_binder!(1; unsafe<> i32)`.
ValueUnsafeBinderCast {
kind: UnsafeBinderCastKind,
source: ExprId,
},
/// A closure definition.
Closure(Box<ClosureExpr<'tcx>>),
/// A literal.
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_middle/src/thir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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])
}
PlaceUnsafeBinderCast { source, kind: _ } | ValueUnsafeBinderCast { source, kind: _ } => {
visitor.visit_expr(&visitor.thir()[source])
}
Closure(box ClosureExpr {
closure_id: _,
args: _,
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_mir_build/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,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
Expand Down
Loading

0 comments on commit bfe01d2

Please sign in to comment.