Skip to content

Commit eb0f3ed

Browse files
committed
Auto merge of rust-lang#115025 - ouz-a:ouz_testing, r=lcnr
Make subtyping explicit in MIR This adds new mir-opt that pushes new `ProjectionElem` called `ProjectionElem::Subtype(T)` to `Rvalue` of a subtyped assignment so we can unsoundness issues like rust-lang#107205 Addresses rust-lang#112651 r? `@lcnr`
2 parents 9998f4a + 5d753ab commit eb0f3ed

File tree

34 files changed

+282
-65
lines changed

34 files changed

+282
-65
lines changed

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2828,6 +2828,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
28282828
}
28292829
ProjectionElem::ConstantIndex { .. }
28302830
| ProjectionElem::Subslice { .. }
2831+
| ProjectionElem::Subtype(_)
28312832
| ProjectionElem::Index(_) => kind,
28322833
},
28332834
place_ty.projection_ty(tcx, elem),

compiler/rustc_borrowck/src/diagnostics/mod.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
242242
ProjectionElem::Downcast(..) if opt.including_downcast => return None,
243243
ProjectionElem::Downcast(..) => (),
244244
ProjectionElem::OpaqueCast(..) => (),
245+
ProjectionElem::Subtype(..) => (),
245246
ProjectionElem::Field(field, _ty) => {
246247
// FIXME(project-rfc_2229#36): print capture precisely here.
247248
if let Some(field) = self.is_upvar_field_projection(PlaceRef {
@@ -322,7 +323,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
322323
PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx)
323324
}
324325
ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx),
325-
ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(*ty),
326+
ProjectionElem::Subtype(ty) | ProjectionElem::OpaqueCast(ty) => {
327+
PlaceTy::from_ty(*ty)
328+
}
326329
ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type),
327330
},
328331
};

compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs

+1
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
159159
[
160160
..,
161161
ProjectionElem::Index(_)
162+
| ProjectionElem::Subtype(_)
162163
| ProjectionElem::ConstantIndex { .. }
163164
| ProjectionElem::OpaqueCast { .. }
164165
| ProjectionElem::Subslice { .. }

compiler/rustc_borrowck/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1803,6 +1803,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
18031803
for (place_base, elem) in place.iter_projections().rev() {
18041804
match elem {
18051805
ProjectionElem::Index(_/*operand*/) |
1806+
ProjectionElem::Subtype(_) |
18061807
ProjectionElem::OpaqueCast(_) |
18071808
ProjectionElem::ConstantIndex { .. } |
18081809
// assigning to P[i] requires P to be valid.
@@ -2191,6 +2192,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
21912192
| ProjectionElem::Index(..)
21922193
| ProjectionElem::ConstantIndex { .. }
21932194
| ProjectionElem::Subslice { .. }
2195+
| ProjectionElem::Subtype(..)
21942196
| ProjectionElem::OpaqueCast { .. }
21952197
| ProjectionElem::Downcast(..) => {
21962198
let upvar_field_projection = self.is_upvar_field_projection(place);

compiler/rustc_borrowck/src/places_conflict.rs

+2
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ fn place_components_conflict<'tcx>(
249249
| (ProjectionElem::ConstantIndex { .. }, _, _)
250250
| (ProjectionElem::Subslice { .. }, _, _)
251251
| (ProjectionElem::OpaqueCast { .. }, _, _)
252+
| (ProjectionElem::Subtype(_), _, _)
252253
| (ProjectionElem::Downcast { .. }, _, _) => {
253254
// Recursive case. This can still be disjoint on a
254255
// further iteration if this a shallow access and
@@ -508,6 +509,7 @@ fn place_projection_conflict<'tcx>(
508509
| ProjectionElem::Field(..)
509510
| ProjectionElem::Index(..)
510511
| ProjectionElem::ConstantIndex { .. }
512+
| ProjectionElem::Subtype(_)
511513
| ProjectionElem::OpaqueCast { .. }
512514
| ProjectionElem::Subslice { .. }
513515
| ProjectionElem::Downcast(..),

compiler/rustc_borrowck/src/prefixes.rs

+3
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> {
8989
cursor = cursor_base;
9090
continue 'cursor;
9191
}
92+
ProjectionElem::Subtype(..) => {
93+
panic!("Subtype projection is not allowed before borrow check")
94+
}
9295
ProjectionElem::Deref => {
9396
// (handled below)
9497
}

compiler/rustc_borrowck/src/type_check/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,9 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
716716
}
717717
PlaceTy::from_ty(fty)
718718
}
719+
ProjectionElem::Subtype(_) => {
720+
bug!("ProjectionElem::Subtype shouldn't exist in borrowck")
721+
}
719722
ProjectionElem::OpaqueCast(ty) => {
720723
let ty = self.sanitize_type(place, ty);
721724
let ty = self.cx.normalize(ty, location);
@@ -2563,6 +2566,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
25632566
| ProjectionElem::Subslice { .. } => {
25642567
// other field access
25652568
}
2569+
ProjectionElem::Subtype(_) => {
2570+
bug!("ProjectionElem::Subtype shouldn't exist in borrowck")
2571+
}
25662572
}
25672573
}
25682574
}

compiler/rustc_codegen_cranelift/src/base.rs

+1
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,7 @@ pub(crate) fn codegen_place<'tcx>(
876876
cplace = cplace.place_deref(fx);
877877
}
878878
PlaceElem::OpaqueCast(ty) => bug!("encountered OpaqueCast({ty}) in codegen"),
879+
PlaceElem::Subtype(ty) => cplace = cplace.place_transmute_type(fx, fx.monomorphize(ty)),
879880
PlaceElem::Field(field, _ty) => {
880881
cplace = cplace.place_field(fx, field);
881882
}

compiler/rustc_codegen_cranelift/src/value_and_place.rs

+10
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,16 @@ impl<'tcx> CPlace<'tcx> {
674674
}
675675
}
676676

677+
/// Used for `ProjectionElem::Subtype`, `ty` has to be monomorphized before
678+
/// passed on.
679+
pub(crate) fn place_transmute_type(
680+
self,
681+
fx: &mut FunctionCx<'_, '_, 'tcx>,
682+
ty: Ty<'tcx>,
683+
) -> CPlace<'tcx> {
684+
CPlace { inner: self.inner, layout: fx.layout_of(ty) }
685+
}
686+
677687
pub(crate) fn place_field(
678688
self,
679689
fx: &mut FunctionCx<'_, '_, 'tcx>,

compiler/rustc_codegen_ssa/src/mir/place.rs

+1
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
466466
mir::ProjectionElem::OpaqueCast(ty) => {
467467
bug!("encountered OpaqueCast({ty}) in codegen")
468468
}
469+
mir::ProjectionElem::Subtype(ty) => cg_base.project_type(bx, self.monomorphize(ty)),
469470
mir::ProjectionElem::Index(index) => {
470471
let index = &mir::Operand::Copy(mir::Place::from(index));
471472
let index = self.codegen_operand(bx, index);

compiler/rustc_const_eval/src/interpret/eval_context.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_middle::ty::layout::{
1313
self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers,
1414
TyAndLayout,
1515
};
16-
use rustc_middle::ty::{self, GenericArgsRef, ParamEnv, Ty, TyCtxt, TypeFoldable};
16+
use rustc_middle::ty::{self, GenericArgsRef, ParamEnv, Ty, TyCtxt, TypeFoldable, Variance};
1717
use rustc_mir_dataflow::storage::always_storage_live_locals;
1818
use rustc_session::Limit;
1919
use rustc_span::Span;
@@ -384,7 +384,7 @@ pub(super) fn mir_assign_valid_types<'tcx>(
384384
// all normal lifetimes are erased, higher-ranked types with their
385385
// late-bound lifetimes are still around and can lead to type
386386
// differences.
387-
if util::is_subtype(tcx, param_env, src.ty, dest.ty) {
387+
if util::relate_types(tcx, param_env, Variance::Covariant, src.ty, dest.ty) {
388388
// Make sure the layout is equal, too -- just to be safe. Miri really
389389
// needs layout equality. For performance reason we skip this check when
390390
// the types are equal. Equal types *can* have different layouts when

compiler/rustc_const_eval/src/interpret/projection.rs

+2
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,8 @@ where
319319
OpaqueCast(ty) => {
320320
span_bug!(self.cur_span(), "OpaqueCast({ty}) encountered after borrowck")
321321
}
322+
// We don't want anything happening here, this is here as a dummy.
323+
Subtype(_) => base.transmute(base.layout(), self)?,
322324
Field(field, _) => self.project_field(base, field.index())?,
323325
Downcast(_, variant) => self.project_downcast(base, variant)?,
324326
Deref => self.deref_pointer(&base.to_op(self)?)?.into(),

compiler/rustc_const_eval/src/transform/check_consts/check.rs

+1
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
664664
| ProjectionElem::Downcast(..)
665665
| ProjectionElem::OpaqueCast(..)
666666
| ProjectionElem::Subslice { .. }
667+
| ProjectionElem::Subtype(..)
667668
| ProjectionElem::Field(..)
668669
| ProjectionElem::Index(_) => {}
669670
}

compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs

+1
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ where
306306
ProjectionElem::Index(index) if in_local(index) => return true,
307307

308308
ProjectionElem::Deref
309+
| ProjectionElem::Subtype(_)
309310
| ProjectionElem::Field(_, _)
310311
| ProjectionElem::OpaqueCast(_)
311312
| ProjectionElem::ConstantIndex { .. }

compiler/rustc_const_eval/src/transform/promote_consts.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,9 @@ impl<'tcx> Validator<'_, 'tcx> {
357357
return Err(Unpromotable);
358358
}
359359

360-
ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {}
360+
ProjectionElem::ConstantIndex { .. }
361+
| ProjectionElem::Subtype(_)
362+
| ProjectionElem::Subslice { .. } => {}
361363

362364
ProjectionElem::Index(local) => {
363365
let mut promotable = false;

compiler/rustc_const_eval/src/transform/validate.rs

+30-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use rustc_infer::traits::Reveal;
77
use rustc_middle::mir::interpret::Scalar;
88
use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
99
use rustc_middle::mir::*;
10-
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitableExt};
10+
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitableExt, Variance};
1111
use rustc_mir_dataflow::impls::MaybeStorageLive;
1212
use rustc_mir_dataflow::storage::always_storage_live_locals;
1313
use rustc_mir_dataflow::{Analysis, ResultsCursor};
@@ -16,6 +16,8 @@ use rustc_target::spec::abi::Abi;
1616

1717
use crate::util::is_within_packed;
1818

19+
use crate::util::relate_types;
20+
1921
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
2022
enum EdgeKind {
2123
Unwind,
@@ -602,7 +604,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
602604
return true;
603605
}
604606

605-
crate::util::is_subtype(self.tcx, self.param_env, src, dest)
607+
// After borrowck subtyping should be fully explicit via
608+
// `Subtype` projections.
609+
let variance = if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
610+
Variance::Invariant
611+
} else {
612+
Variance::Covariant
613+
};
614+
615+
crate::util::relate_types(self.tcx, self.param_env, variance, src, dest)
606616
}
607617
}
608618

@@ -753,6 +763,23 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
753763
}
754764
}
755765
}
766+
ProjectionElem::Subtype(ty) => {
767+
if !relate_types(
768+
self.tcx,
769+
self.param_env,
770+
Variance::Covariant,
771+
ty,
772+
place_ref.ty(&self.body.local_decls, self.tcx).ty,
773+
) {
774+
self.fail(
775+
location,
776+
format!(
777+
"Failed subtyping {ty:#?} and {:#?}",
778+
place_ref.ty(&self.body.local_decls, self.tcx).ty
779+
),
780+
)
781+
}
782+
}
756783
_ => {}
757784
}
758785
self.super_projection_elem(place_ref, elem, context, location);
@@ -1088,6 +1115,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
10881115
// LHS and RHS of the assignment must have the same type.
10891116
let left_ty = dest.ty(&self.body.local_decls, self.tcx).ty;
10901117
let right_ty = rvalue.ty(&self.body.local_decls, self.tcx);
1118+
10911119
if !self.mir_assign_valid_types(right_ty, left_ty) {
10921120
self.fail(
10931121
location,

compiler/rustc_const_eval/src/util/compare_types.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
66
use rustc_infer::infer::TyCtxtInferExt;
77
use rustc_middle::traits::{DefiningAnchor, ObligationCause};
8-
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
8+
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt, Variance};
99
use rustc_trait_selection::traits::ObligationCtxt;
1010

1111
/// Returns whether the two types are equal up to subtyping.
@@ -24,16 +24,22 @@ pub fn is_equal_up_to_subtyping<'tcx>(
2424
}
2525

2626
// Check for subtyping in either direction.
27-
is_subtype(tcx, param_env, src, dest) || is_subtype(tcx, param_env, dest, src)
27+
relate_types(tcx, param_env, Variance::Covariant, src, dest)
28+
|| relate_types(tcx, param_env, Variance::Covariant, dest, src)
2829
}
2930

3031
/// Returns whether `src` is a subtype of `dest`, i.e. `src <: dest`.
3132
///
33+
/// When validating assignments, the variance should be `Covariant`. When checking
34+
/// during `MirPhase` >= `MirPhase::Runtime(RuntimePhase::Initial)` variance should be `Invariant`
35+
/// because we want to check for type equality.
36+
///
3237
/// This mostly ignores opaque types as it can be used in constraining contexts
3338
/// while still computing the final underlying type.
34-
pub fn is_subtype<'tcx>(
39+
pub fn relate_types<'tcx>(
3540
tcx: TyCtxt<'tcx>,
3641
param_env: ParamEnv<'tcx>,
42+
variance: Variance,
3743
src: Ty<'tcx>,
3844
dest: Ty<'tcx>,
3945
) -> bool {
@@ -48,7 +54,7 @@ pub fn is_subtype<'tcx>(
4854
let cause = ObligationCause::dummy();
4955
let src = ocx.normalize(&cause, param_env, src);
5056
let dest = ocx.normalize(&cause, param_env, dest);
51-
match ocx.sub(&cause, param_env, src, dest) {
57+
match ocx.relate(&cause, param_env, variance, src, dest) {
5258
Ok(()) => {}
5359
Err(_) => return false,
5460
};

compiler/rustc_const_eval/src/util/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ mod type_name;
77

88
pub use self::alignment::{is_disaligned, is_within_packed};
99
pub use self::check_validity_requirement::check_validity_requirement;
10-
pub use self::compare_types::{is_equal_up_to_subtyping, is_subtype};
10+
pub use self::compare_types::{is_equal_up_to_subtyping, relate_types};
1111
pub use self::type_name::type_name;
1212

1313
/// Classify whether an operator is "left-homogeneous", i.e., the LHS has the

compiler/rustc_middle/src/mir/pretty.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,7 @@ fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) ->
11031103
for &elem in projection.iter().rev() {
11041104
match elem {
11051105
ProjectionElem::OpaqueCast(_)
1106+
| ProjectionElem::Subtype(_)
11061107
| ProjectionElem::Downcast(_, _)
11071108
| ProjectionElem::Field(_, _) => {
11081109
write!(fmt, "(").unwrap();
@@ -1125,6 +1126,9 @@ fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) ->
11251126
ProjectionElem::OpaqueCast(ty) => {
11261127
write!(fmt, " as {ty})")?;
11271128
}
1129+
ProjectionElem::Subtype(ty) => {
1130+
write!(fmt, " as subtype {ty})")?;
1131+
}
11281132
ProjectionElem::Downcast(Some(name), _index) => {
11291133
write!(fmt, " as {name})")?;
11301134
}

compiler/rustc_middle/src/mir/statement.rs

+3
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ impl<V, T> ProjectionElem<V, T> {
5757
Self::Field(_, _)
5858
| Self::Index(_)
5959
| Self::OpaqueCast(_)
60+
| Self::Subtype(_)
6061
| Self::ConstantIndex { .. }
6162
| Self::Subslice { .. }
6263
| Self::Downcast(_, _) => false,
@@ -70,6 +71,7 @@ impl<V, T> ProjectionElem<V, T> {
7071
Self::Deref | Self::Index(_) => false,
7172
Self::Field(_, _)
7273
| Self::OpaqueCast(_)
74+
| Self::Subtype(_)
7375
| Self::ConstantIndex { .. }
7476
| Self::Subslice { .. }
7577
| Self::Downcast(_, _) => true,
@@ -95,6 +97,7 @@ impl<V, T> ProjectionElem<V, T> {
9597
| Self::Field(_, _) => true,
9698
Self::ConstantIndex { from_end: true, .. }
9799
| Self::Index(_)
100+
| Self::Subtype(_)
98101
| Self::OpaqueCast(_)
99102
| Self::Subslice { .. } => false,
100103
}

compiler/rustc_middle/src/mir/syntax.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,18 @@ pub enum ProjectionElem<V, T> {
10751075
/// Like an explicit cast from an opaque type to a concrete type, but without
10761076
/// requiring an intermediate variable.
10771077
OpaqueCast(T),
1078+
1079+
/// A `Subtype(T)` projection is applied to any `StatementKind::Assign` where
1080+
/// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping
1081+
/// explicit during optimizations and codegen.
1082+
///
1083+
/// This projection doesn't impact the runtime behavior of the program except for potentially changing
1084+
/// some type metadata of the interpreter or codegen backend.
1085+
///
1086+
/// This goal is achieved with mir_transform pass `Subtyper`, which runs right after
1087+
/// borrowchecker, as we only care about subtyping that can affect trait selection and
1088+
/// `TypeId`.
1089+
Subtype(T),
10781090
}
10791091

10801092
/// Alias for projections as they appear in places, where the base is a place

compiler/rustc_middle/src/mir/tcx.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ impl<'tcx> PlaceTy<'tcx> {
6969
param_env: ty::ParamEnv<'tcx>,
7070
elem: &ProjectionElem<V, T>,
7171
mut handle_field: impl FnMut(&Self, FieldIdx, T) -> Ty<'tcx>,
72-
mut handle_opaque_cast: impl FnMut(&Self, T) -> Ty<'tcx>,
72+
mut handle_opaque_cast_and_subtype: impl FnMut(&Self, T) -> Ty<'tcx>,
7373
) -> PlaceTy<'tcx>
7474
where
7575
V: ::std::fmt::Debug,
@@ -110,7 +110,12 @@ impl<'tcx> PlaceTy<'tcx> {
110110
PlaceTy { ty: self.ty, variant_index: Some(index) }
111111
}
112112
ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(&self, f, fty)),
113-
ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(handle_opaque_cast(&self, ty)),
113+
ProjectionElem::OpaqueCast(ty) => {
114+
PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
115+
}
116+
ProjectionElem::Subtype(ty) => {
117+
PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
118+
}
114119
};
115120
debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
116121
answer

0 commit comments

Comments
 (0)