Skip to content

Commit

Permalink
subtyping_projections
Browse files Browse the repository at this point in the history
  • Loading branch information
ouz-a committed Oct 2, 2023
1 parent 1770912 commit 3148e6a
Show file tree
Hide file tree
Showing 28 changed files with 431 additions and 5 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2828,6 +2828,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Subtype(_)
| ProjectionElem::Index(_) => kind,
},
place_ty.projection_ty(tcx, elem),
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_borrowck/src/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
ProjectionElem::Downcast(..) if opt.including_downcast => return None,
ProjectionElem::Downcast(..) => (),
ProjectionElem::OpaqueCast(..) => (),
ProjectionElem::Subtype(..) => (),
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 @@ -317,6 +318,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
PlaceRef { local, projection: [proj_base @ .., elem] } => match elem {
ProjectionElem::Deref
| ProjectionElem::Index(..)
| ProjectionElem::Subtype(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => {
PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
[
..,
ProjectionElem::Index(_)
| ProjectionElem::Subtype(_)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::OpaqueCast { .. }
| ProjectionElem::Subslice { .. }
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1803,6 +1803,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
for (place_base, elem) in place.iter_projections().rev() {
match elem {
ProjectionElem::Index(_/*operand*/) |
ProjectionElem::Subtype(_) |
ProjectionElem::OpaqueCast(_) |
ProjectionElem::ConstantIndex { .. } |
// assigning to P[i] requires P to be valid.
Expand Down Expand Up @@ -2191,6 +2192,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
| ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Subtype(..)
| ProjectionElem::OpaqueCast { .. }
| ProjectionElem::Downcast(..) => {
let upvar_field_projection = self.is_upvar_field_projection(place);
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_borrowck/src/places_conflict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ fn place_components_conflict<'tcx>(
}

(ProjectionElem::Deref, _, Deep)
| (ProjectionElem::Subtype(_), _, _)
| (ProjectionElem::Deref, _, AccessDepth::Drop)
| (ProjectionElem::Field { .. }, _, _)
| (ProjectionElem::Index { .. }, _, _)
Expand Down Expand Up @@ -359,6 +360,7 @@ fn place_projection_conflict<'tcx>(
(
ProjectionElem::Index(..),
ProjectionElem::Index(..)
| ProjectionElem::Subtype(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. },
)
Expand Down Expand Up @@ -503,6 +505,7 @@ fn place_projection_conflict<'tcx>(
debug!("place_element_conflict: DISJOINT-OR-EQ-SLICE-SUBSLICES");
Overlap::EqualOrDisjoint
}
(ProjectionElem::Subtype(_), _) => Overlap::EqualOrDisjoint,
(
ProjectionElem::Deref
| ProjectionElem::Field(..)
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_borrowck/src/prefixes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> {
cursor = cursor_base;
continue 'cursor;
}
ProjectionElem::Subtype(..) => continue 'cursor,
ProjectionElem::Deref => {
// (handled below)
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
span_mirbug_and_err!(self, place, "deref of non-pointer {:?}", base_ty)
}))
}
ProjectionElem::Subtype(ty) => PlaceTy::from_ty(ty),
ProjectionElem::Index(i) => {
let index_ty = Place::from(i).ty(self.body(), tcx).ty;
if index_ty != tcx.types.usize {
Expand Down Expand Up @@ -2556,6 +2557,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}
ProjectionElem::Field(..)
| ProjectionElem::Subtype(..)
| ProjectionElem::Downcast(..)
| ProjectionElem::OpaqueCast(..)
| ProjectionElem::Index(..)
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_cranelift/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,9 @@ pub(crate) fn codegen_place<'tcx>(

for elem in place.projection {
match elem {
PlaceElem::Subtype(_) => {
continue;
}
PlaceElem::Deref => {
cplace = cplace.place_deref(fx);
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_ssa/src/mir/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
subslice
}
mir::ProjectionElem::Downcast(_, v) => cg_base.project_downcast(bx, v),
mir::ProjectionElem::Subtype(_) => continue,
};
}
debug!("codegen_place(place={:?}) => {:?}", place_ref, cg_base);
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_const_eval/src/interpret/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let mut op = self.local_to_op(self.frame(), mir_place.local, layout)?;
// Using `try_fold` turned out to be bad for performance, hence the loop.
for elem in mir_place.projection.iter() {
if elem.is_subtype() {
continue;
}
op = self.project(&op, elem)?
}

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 @@ -332,6 +332,7 @@ where
self.project_constant_index(base, offset, min_length, from_end)?
}
Subslice { from, to, from_end } => self.project_subslice(base, from, to, from_end)?,
Subtype(ty) => base.transmute(self.layout_of(ty)?, self)?,
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
| ProjectionElem::Downcast(..)
| ProjectionElem::OpaqueCast(..)
| ProjectionElem::Subslice { .. }
| ProjectionElem::Subtype(..)
| ProjectionElem::Field(..)
| ProjectionElem::Index(_) => {}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ where
ProjectionElem::Index(index) if in_local(index) => return true,

ProjectionElem::Deref
| ProjectionElem::Subtype(_)
| ProjectionElem::Field(_, _)
| ProjectionElem::OpaqueCast(_)
| ProjectionElem::ConstantIndex { .. }
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_const_eval/src/transform/promote_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,9 @@ impl<'tcx> Validator<'_, 'tcx> {
return Err(Unpromotable);
}

ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {}
ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subtype(_)
| ProjectionElem::Subslice { .. } => {}

ProjectionElem::Index(local) => {
let mut promotable = false;
Expand Down
24 changes: 24 additions & 0 deletions compiler/rustc_const_eval/src/transform/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,29 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {

impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
match operand {
Operand::Copy(place) | Operand::Move(place) => {
if let Some(stmt) = self.body.stmt_at(location).left() {
match &stmt.kind {
StatementKind::Assign(box (lval, rvalue)) => {
let place_ty = place.ty(&self.body.local_decls, self.tcx).ty;
let lval_ty = lval.ty(&self.body.local_decls, self.tcx).ty;

if !place.is_subtype()
&& place_ty != lval_ty
&& rvalue.ty(&self.body.local_decls, self.tcx) != lval_ty
&& (rvalue.ty(&self.body.local_decls, self.tcx).is_closure()
!= lval_ty.is_closure())
{
self.fail(location, format!("Subtyping is not allowed between types {place_ty:#?} and {lval_ty:#?}"))
}
}
_ => (),
}
}
}
_ => (),
}
// This check is somewhat expensive, so only run it when -Zvalidate-mir is passed.
if self.tcx.sess.opts.unstable_opts.validate_mir
&& self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial)
Expand Down Expand Up @@ -1088,6 +1111,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
// LHS and RHS of the assignment must have the same type.
let left_ty = dest.ty(&self.body.local_decls, self.tcx).ty;
let right_ty = rvalue.ty(&self.body.local_decls, self.tcx);

if !self.mir_assign_valid_types(right_ty, left_ty) {
self.fail(
location,
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_middle/src/mir/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1075,6 +1075,8 @@ pub enum ProjectionElem<V, T> {
/// Like an explicit cast from an opaque type to a concrete type, but without
/// requiring an intermediate variable.
OpaqueCast(T),

Subtype(T),
}

/// Alias for projections as they appear in places, where the base is a place
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/mir/tcx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ impl<'tcx> PlaceTy<'tcx> {
}
ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(&self, f, fty)),
ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(handle_opaque_cast(&self, ty)),
ProjectionElem::Subtype(_) => PlaceTy::from_ty(self.ty),
};
debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
answer
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_middle/src/mir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1110,6 +1110,7 @@ macro_rules! visit_place_fns {
if ty != new_ty { Some(PlaceElem::OpaqueCast(new_ty)) } else { None }
}
PlaceElem::Deref
| PlaceElem::Subtype { .. }
| PlaceElem::ConstantIndex { .. }
| PlaceElem::Subslice { .. }
| PlaceElem::Downcast(..) => None,
Expand Down Expand Up @@ -1175,7 +1176,9 @@ macro_rules! visit_place_fns {
location: Location,
) {
match elem {
ProjectionElem::OpaqueCast(ty) | ProjectionElem::Field(_, ty) => {
ProjectionElem::OpaqueCast(ty)
| ProjectionElem::Subtype(ty)
| ProjectionElem::Field(_, ty) => {
self.visit_ty(ty, TyContext::Location(location));
}
ProjectionElem::Index(local) => {
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_mir_build/src/build/expr/as_place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ fn convert_to_hir_projections_and_truncate_for_capture(
continue;
}
// These do not affect anything, they just make sure we know the right type.
ProjectionElem::OpaqueCast(_) => continue,
ProjectionElem::OpaqueCast(_) | ProjectionElem::Subtype(..) => continue,
ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => {
Expand Down Expand Up @@ -709,6 +709,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ProjectionElem::Field(..)
| ProjectionElem::Downcast(..)
| ProjectionElem::OpaqueCast(..)
| ProjectionElem::Subtype(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => (),
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ impl<'tcx> Lift for PlaceElem<'tcx> {
ProjectionElem::ConstantIndex { offset, min_length, from_end }
}
ProjectionElem::Downcast(a, u) => ProjectionElem::Downcast(a, u),
ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty.lift()),
}
}
}
3 changes: 3 additions & 0 deletions compiler/rustc_mir_dataflow/src/move_paths/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
let mut union_path = None;

for (place_ref, elem) in data.rev_lookup.un_derefer.iter_projections(place.as_ref()) {
if elem.is_subtype() {
continue;
}
let body = self.builder.body;
let tcx = self.builder.tcx;
let place_ty = place_ref.ty(body, tcx).ty;
Expand Down
57 changes: 57 additions & 0 deletions compiler/rustc_mir_transform/src/add_subtyping_projections.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use crate::MirPass;
use rustc_index::IndexVec;
use rustc_middle::mir::patch::MirPatch;
use rustc_middle::mir::visit::MutVisitor;
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;

pub struct Subtyper;

pub struct SubTypeCheker<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
patcher: MirPatch<'tcx>,
local_decls: &'a IndexVec<Local, LocalDecl<'tcx>>,
}

impl<'a, 'tcx> MutVisitor<'tcx> for SubTypeCheker<'a, 'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}

fn visit_assign(
&mut self,
place: &mut Place<'tcx>,
rvalue: &mut Rvalue<'tcx>,
location: Location,
) {
let place_ty = place.ty(self.local_decls, self.tcx);
let rval_ty = rvalue.ty(self.local_decls, self.tcx);
if place_ty.ty != rval_ty {
let temp = self
.patcher
.new_temp(rval_ty, self.local_decls[place.as_ref().local].source_info.span);
let new_place =
Place::from(temp).project_deeper(&[ProjectionElem::Subtype(place_ty.ty)], self.tcx);
self.patcher.add_assign(location, new_place, rvalue.clone());
let new_rval = Rvalue::Use(Operand::Move(new_place));
*rvalue = new_rval;
}
}
}

pub fn subtype_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let patch = MirPatch::new(body);
let mut checker = SubTypeCheker { tcx, patcher: patch, local_decls: &body.local_decls };

for (bb, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
checker.visit_basic_block_data(bb, data);
}

checker.patcher.apply(body);
}

impl<'tcx> MirPass<'tcx> for Subtyper {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
subtype_finder(tcx, body);
}
}
2 changes: 2 additions & 0 deletions compiler/rustc_mir_transform/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ mod check_packed_ref;
pub mod check_unsafety;
mod remove_place_mention;
// This pass is public to allow external drivers to perform MIR cleanup
mod add_subtyping_projections;
pub mod cleanup_post_borrowck;
mod const_debuginfo;
mod const_goto;
Expand Down Expand Up @@ -466,6 +467,7 @@ pub fn run_analysis_to_runtime_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'
/// After this series of passes, no lifetime analysis based on borrowing can be done.
fn run_analysis_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let passes: &[&dyn MirPass<'tcx>] = &[
&add_subtyping_projections::Subtyper,
&cleanup_post_borrowck::CleanupPostBorrowck,
&remove_noop_landing_pads::RemoveNoopLandingPads,
&simplify::SimplifyCfg::EarlyOpt,
Expand Down
1 change: 1 addition & 0 deletions src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &B
| ProjectionElem::Downcast(..)
| ProjectionElem::Subslice { .. }
| ProjectionElem::Deref
| ProjectionElem::Subtype(_)
| ProjectionElem::Index(_) => {},
}
}
Expand Down
Loading

0 comments on commit 3148e6a

Please sign in to comment.