diff options
Diffstat (limited to 'compiler/rustc_const_eval')
| -rw-r--r-- | compiler/rustc_const_eval/messages.ftl | 4 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/check_consts/check.rs | 14 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/check_consts/ops.rs | 28 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/errors.rs | 18 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/cast.rs | 17 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/memory.rs | 14 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/traits.rs | 67 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/validity.rs | 8 |
8 files changed, 81 insertions, 89 deletions
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 2dc9d57e3b5..73a9a1569f6 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -198,7 +198,7 @@ const_eval_invalid_vtable_pointer = using {$pointer} as vtable pointer but it does not point to a vtable const_eval_invalid_vtable_trait = - using vtable for trait `{$vtable_trait}` but trait `{$expected_trait}` was expected + using vtable for `{$vtable_dyn_type}` but `{$expected_dyn_type}` was expected const_eval_lazy_lock = consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` @@ -459,7 +459,7 @@ const_eval_validation_invalid_fn_ptr = {$front_matter}: encountered {$value}, bu const_eval_validation_invalid_ref_meta = {$front_matter}: encountered invalid reference metadata: total size is bigger than largest supported object const_eval_validation_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object const_eval_validation_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer -const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wide pointer vtable: expected `{$ref_trait}`, but encountered `{$vtable_trait}` +const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wide pointer vtable: expected `{$expected_dyn_type}`, but encountered `{$vtable_dyn_type}` const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!` const_eval_validation_null_box = {$front_matter}: encountered a null box diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 2c33ff9d219..2cbf242fcf2 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -317,7 +317,6 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { { self.error_emitted = Some(guar); } - self.check_op_spanned(ops::StaticAccess, span) } /// Returns whether this place can possibly escape the evaluation of the current const/static @@ -440,6 +439,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { | PointerCoercion::UnsafeFnPointer | PointerCoercion::ClosureFnPointer(_) | PointerCoercion::ReifyFnPointer, + _, ), _, _, @@ -447,8 +447,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // These are all okay; they only change the type, not the data. } - Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize), _, _) => { - // Unsizing is implemented for CTFE. + Rvalue::Cast( + CastKind::PointerCoercion(PointerCoercion::Unsize | PointerCoercion::DynStar, _), + _, + _, + ) => { + // Unsizing and `dyn*` coercions are implemented for CTFE. } Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => { @@ -458,10 +462,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // Since no pointer can ever get exposed (rejected above), this is easy to support. } - Rvalue::Cast(CastKind::DynStar, _, _) => { - // `dyn*` coercion is implemented for CTFE. - } - Rvalue::Cast(_, _, _) => {} Rvalue::NullaryOp( diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index df8313d0e70..6eb33c29e1d 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -16,7 +16,6 @@ use rustc_middle::ty::{ suggest_constraining_type_param, }; use rustc_middle::util::{CallDesugaringKind, CallKind, call_kind}; -use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{BytePos, Pos, Span, Symbol}; use rustc_trait_selection::traits::SelectionContext; @@ -477,33 +476,6 @@ impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast { } } -/// An access to a (non-thread-local) `static`. -#[derive(Debug)] -pub(crate) struct StaticAccess; -impl<'tcx> NonConstOp<'tcx> for StaticAccess { - fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { - if let hir::ConstContext::Static(_) = ccx.const_kind() { - Status::Allowed - } else { - Status::Unstable(sym::const_refs_to_static) - } - } - - #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable - fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { - let mut err = feature_err( - &ccx.tcx.sess, - sym::const_refs_to_static, - span, - format!("referencing statics in {}s is unstable", ccx.const_kind(),), - ); - err - .note("`static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable.") - .help("to fix this, the value can be extracted to a `const` and then used."); - err - } -} - /// An access to a thread-local `static`. #[derive(Debug)] pub(crate) struct ThreadLocalAccess; diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index c38d7f3d03c..c60bacb8506 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -522,12 +522,9 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { UnterminatedCString(ptr) | InvalidFunctionPointer(ptr) | InvalidVTablePointer(ptr) => { diag.arg("pointer", ptr); } - InvalidVTableTrait { expected_trait, vtable_trait } => { - diag.arg("expected_trait", expected_trait.to_string()); - diag.arg( - "vtable_trait", - vtable_trait.map(|t| t.to_string()).unwrap_or_else(|| format!("<trivial>")), - ); + InvalidVTableTrait { expected_dyn_type, vtable_dyn_type } => { + diag.arg("expected_dyn_type", expected_dyn_type.to_string()); + diag.arg("vtable_dyn_type", vtable_dyn_type.to_string()); } PointerUseAfterFree(alloc_id, msg) => { diag.arg("alloc_id", alloc_id) @@ -777,12 +774,9 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { DanglingPtrNoProvenance { pointer, .. } => { err.arg("pointer", pointer); } - InvalidMetaWrongTrait { expected_trait: ref_trait, vtable_trait } => { - err.arg("ref_trait", ref_trait.to_string()); - err.arg( - "vtable_trait", - vtable_trait.map(|t| t.to_string()).unwrap_or_else(|| format!("<trivial>")), - ); + InvalidMetaWrongTrait { vtable_dyn_type, expected_dyn_type } => { + err.arg("vtable_dyn_type", vtable_dyn_type.to_string()); + err.arg("expected_dyn_type", expected_dyn_type.to_string()); } NullPtr { .. } | ConstRefToMutable diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 198aa1bbd5b..85e7d91c2ad 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -32,7 +32,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if cast_ty == dest.layout.ty { dest.layout } else { self.layout_of(cast_ty)? }; // FIXME: In which cases should we trigger UB when the source is uninit? match cast_kind { - CastKind::PointerCoercion(PointerCoercion::Unsize) => { + CastKind::PointerCoercion(PointerCoercion::Unsize, _) => { self.unsize_into(src, cast_layout, dest)?; } @@ -68,11 +68,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { CastKind::PointerCoercion( PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, + _, ) => { bug!("{cast_kind:?} casts are for borrowck only, not runtime MIR"); } - CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => { + CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _) => { // All reifications must be monomorphic, bail out otherwise. ensure_monomorphic_enough(*self.tcx, src.layout.ty)?; @@ -94,7 +95,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer) => { + CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer, _) => { let src = self.read_immediate(src)?; match cast_ty.kind() { ty::FnPtr(..) => { @@ -105,7 +106,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)) => { + CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_), _) => { // All reifications must be monomorphic, bail out otherwise. ensure_monomorphic_enough(*self.tcx, src.layout.ty)?; @@ -125,10 +126,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - CastKind::DynStar => { + CastKind::PointerCoercion(PointerCoercion::DynStar, _) => { if let ty::Dynamic(data, _, ty::DynStar) = cast_ty.kind() { // Initial cast from sized to dyn trait - let vtable = self.get_vtable_ptr(src.layout.ty, data.principal())?; + let vtable = self.get_vtable_ptr(src.layout.ty, data)?; let vtable = Scalar::from_maybe_pointer(vtable, self); let data = self.read_immediate(src)?.to_scalar(); let _assert_pointer_like = data.to_pointer(self)?; @@ -446,12 +447,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } // Get the destination trait vtable and return that. - let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?; + let new_vptr = self.get_vtable_ptr(ty, data_b)?; self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) } (_, &ty::Dynamic(data, _, ty::Dyn)) => { // Initial cast from sized to dyn trait - let vtable = self.get_vtable_ptr(src_pointee_ty, data.principal())?; + let vtable = self.get_vtable_ptr(src_pointee_ty, data)?; let ptr = self.read_pointer(src)?; let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx); self.write_immediate(val, dest) diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index e5fdf592ec9..c3b506d848c 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -943,12 +943,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if offset.bytes() != 0 { throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))) } - let Some(GlobalAlloc::VTable(ty, vtable_trait)) = self.tcx.try_get_global_alloc(alloc_id) + let Some(GlobalAlloc::VTable(ty, vtable_dyn_type)) = + self.tcx.try_get_global_alloc(alloc_id) else { throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))) }; - if let Some(expected_trait) = expected_trait { - self.check_vtable_for_type(vtable_trait, expected_trait)?; + if let Some(expected_dyn_type) = expected_trait { + self.check_vtable_for_type(vtable_dyn_type, expected_dyn_type)?; } Ok(ty) } @@ -1113,11 +1114,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for DumpAllocs<'a, 'tcx, M> { Some(GlobalAlloc::Function { instance, .. }) => { write!(fmt, " (fn: {instance})")?; } - Some(GlobalAlloc::VTable(ty, Some(trait_ref))) => { - write!(fmt, " (vtable: impl {trait_ref} for {ty})")?; - } - Some(GlobalAlloc::VTable(ty, None)) => { - write!(fmt, " (vtable: impl <auto trait> for {ty})")?; + Some(GlobalAlloc::VTable(ty, dyn_ty)) => { + write!(fmt, " (vtable: impl {dyn_ty} for {ty})")?; } Some(GlobalAlloc::Static(did)) => { write!(fmt, " (static: {})", self.ecx.tcx.def_path_str(did))?; diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index b5eef0fd8c9..8eead6018ac 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -1,6 +1,6 @@ use rustc_middle::mir::interpret::{InterpResult, Pointer}; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, Ty, TyCtxt, VtblEntry}; +use rustc_middle::ty::{self, ExistentialPredicateStableCmpExt, Ty, TyCtxt, VtblEntry}; use rustc_target::abi::{Align, Size}; use tracing::trace; @@ -11,26 +11,25 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Creates a dynamic vtable for the given type and vtable origin. This is used only for /// objects. /// - /// The `trait_ref` encodes the erased self type. Hence, if we are making an object `Foo<Trait>` - /// from a value of type `Foo<T>`, then `trait_ref` would map `T: Trait`. `None` here means that - /// this is an auto trait without any methods, so we only need the basic vtable (drop, size, - /// align). + /// The `dyn_ty` encodes the erased self type. Hence, if we are making an object + /// `Foo<dyn Trait<Assoc = A> + Send>` from a value of type `Foo<T>`, then `dyn_ty` + /// would be `Trait<Assoc = A> + Send`. If this list doesn't have a principal trait ref, + /// we only need the basic vtable prefix (drop, size, align). pub fn get_vtable_ptr( &self, ty: Ty<'tcx>, - poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, + dyn_ty: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, ) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> { - trace!("get_vtable(trait_ref={:?})", poly_trait_ref); + trace!("get_vtable(ty={ty:?}, dyn_ty={dyn_ty:?})"); - let (ty, poly_trait_ref) = self.tcx.erase_regions((ty, poly_trait_ref)); + let (ty, dyn_ty) = self.tcx.erase_regions((ty, dyn_ty)); // All vtables must be monomorphic, bail out otherwise. ensure_monomorphic_enough(*self.tcx, ty)?; - ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?; + ensure_monomorphic_enough(*self.tcx, dyn_ty)?; let salt = M::get_global_alloc_salt(self, None); - let vtable_symbolic_allocation = - self.tcx.reserve_and_set_vtable_alloc(ty, poly_trait_ref, salt); + let vtable_symbolic_allocation = self.tcx.reserve_and_set_vtable_alloc(ty, dyn_ty, salt); let vtable_ptr = self.global_root_pointer(Pointer::from(vtable_symbolic_allocation))?; Ok(vtable_ptr.into()) } @@ -64,17 +63,45 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// expected trait type. pub(super) fn check_vtable_for_type( &self, - vtable_trait: Option<ty::PolyExistentialTraitRef<'tcx>>, - expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + vtable_dyn_type: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + expected_dyn_type: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, ) -> InterpResult<'tcx> { - let eq = match (expected_trait.principal(), vtable_trait) { - (Some(a), Some(b)) => self.eq_in_param_env(a, b), - (None, None) => true, - _ => false, - }; - if !eq { - throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); + // We check validity by comparing the lists of predicates for equality. We *could* instead + // check that the dynamic type to which the vtable belongs satisfies all the expected + // predicates, but that would likely be a lot slower and seems unnecessarily permissive. + + // FIXME: we are skipping auto traits for now, but might revisit this in the future. + let mut sorted_vtable: Vec<_> = vtable_dyn_type.without_auto_traits().collect(); + let mut sorted_expected: Vec<_> = expected_dyn_type.without_auto_traits().collect(); + // `skip_binder` here is okay because `stable_cmp` doesn't look at binders + sorted_vtable.sort_by(|a, b| a.skip_binder().stable_cmp(*self.tcx, &b.skip_binder())); + sorted_vtable.dedup(); + sorted_expected.sort_by(|a, b| a.skip_binder().stable_cmp(*self.tcx, &b.skip_binder())); + sorted_expected.dedup(); + + if sorted_vtable.len() != sorted_expected.len() { + throw_ub!(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type }); + } + + for (a_pred, b_pred) in std::iter::zip(sorted_vtable, sorted_expected) { + let is_eq = match (a_pred.skip_binder(), b_pred.skip_binder()) { + ( + ty::ExistentialPredicate::Trait(a_data), + ty::ExistentialPredicate::Trait(b_data), + ) => self.eq_in_param_env(a_pred.rebind(a_data), b_pred.rebind(b_data)), + + ( + ty::ExistentialPredicate::Projection(a_data), + ty::ExistentialPredicate::Projection(b_data), + ) => self.eq_in_param_env(a_pred.rebind(a_data), b_pred.rebind(b_data)), + + _ => false, + }; + if !is_eq { + throw_ub!(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type }); + } } + Ok(()) } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index ff3c6120f0c..203cceccd9d 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -452,8 +452,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { self.path, Ub(DanglingIntPointer{ .. } | InvalidVTablePointer(..)) => InvalidVTablePtr { value: format!("{vtable}") }, - Ub(InvalidVTableTrait { expected_trait, vtable_trait }) => { - InvalidMetaWrongTrait { expected_trait, vtable_trait: *vtable_trait } + Ub(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type }) => { + InvalidMetaWrongTrait { vtable_dyn_type, expected_dyn_type } }, ); } @@ -1281,8 +1281,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, self.path, // It's not great to catch errors here, since we can't give a very good path, // but it's better than ICEing. - Ub(InvalidVTableTrait { expected_trait, vtable_trait }) => { - InvalidMetaWrongTrait { expected_trait, vtable_trait: *vtable_trait } + Ub(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type }) => { + InvalidMetaWrongTrait { vtable_dyn_type, expected_dyn_type: *expected_dyn_type } }, ); } |
