Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Experimental] Split out an UnsafeCoerceUnsized trait from CoerceUnsized #88239

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions compiler/rustc_codegen_cranelift/example/mini_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,18 @@ pub trait Sized {}
#[lang = "unsize"]
pub trait Unsize<T: ?Sized> {}

#[lang = "coerce_unsized"]
pub trait CoerceUnsized<T> {}
#[lang = "unsafe_coerce_unsized"]
pub trait UnsafeCoerceUnsized<T> {}

impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
#[lang = "coerce_unsized"]
pub unsafe trait CoerceUnsized<T>: UnsafeCoerceUnsized<T> {}

impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> UnsafeCoerceUnsized<&'a U> for &'b T {}
unsafe impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> UnsafeCoerceUnsized<&'a mut U> for &'a mut T {}
unsafe impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> UnsafeCoerceUnsized<*const U> for *const T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> UnsafeCoerceUnsized<*mut U> for *mut T {}

#[lang = "dispatch_from_dyn"]
pub trait DispatchFromDyn<T> {}
Expand Down
301 changes: 151 additions & 150 deletions compiler/rustc_hir/src/lang_items.rs

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1437,10 +1437,10 @@ impl EncodeContext<'a, 'tcx> {
None
};

// if this is an impl of `CoerceUnsized`, create its
// if this is an impl of `UnsafeCoerceUnsized`, create its
// "unsized info", else just store None
let coerce_unsized_info = trait_ref.and_then(|t| {
if Some(t.def_id) == self.tcx.lang_items().coerce_unsized_trait() {
if Some(t.def_id) == self.tcx.lang_items().unsafe_coerce_unsized_trait() {
Some(self.tcx.at(item.span).coerce_unsized_info(def_id))
} else {
None
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_middle/src/mir/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub enum UnsafetyViolationDetails {
MutationOfLayoutConstrainedField,
BorrowOfLayoutConstrainedField,
CallToFunctionWith,
UnsafeCoerceUnsized,
}

impl UnsafetyViolationDetails {
Expand Down Expand Up @@ -102,6 +103,11 @@ impl UnsafetyViolationDetails {
"call to function with `#[target_feature]`",
"can only be called if the required target features are available",
),
UnsafeCoerceUnsized => (
"performing unsizing coercion",
"unsizing coercion performed on a type that did not implement `CoerceUnsized` \
will cause undefined behavior if data is invalid",
),
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_mir/src/borrow_check/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2109,8 +2109,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
CastKind::Pointer(PointerCast::Unsize) => {
let &ty = ty;
let trait_ref = ty::TraitRef {
def_id: tcx
.require_lang_item(LangItem::CoerceUnsized, Some(self.last_span)),
def_id: tcx.require_lang_item(
LangItem::UnsafeCoerceUnsized,
Some(self.last_span),
),
substs: tcx.mk_substs_trait(op.ty(body, tcx), &[ty.into()]),
};

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/monomorphize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fn custom_coerce_unsize_info<'tcx>(
source_ty: Ty<'tcx>,
target_ty: Ty<'tcx>,
) -> CustomCoerceUnsized {
let def_id = tcx.require_lang_item(LangItem::CoerceUnsized, None);
let def_id = tcx.require_lang_item(LangItem::UnsafeCoerceUnsized, None);

let trait_ref = ty::Binder::dummy(ty::TraitRef {
def_id,
Expand Down
40 changes: 40 additions & 0 deletions compiler/rustc_mir/src/transform/check_unsafety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::hir_id::HirId;
use rustc_hir::intravisit;
use rustc_hir::LangItem;
use rustc_hir::Node;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{Obligation, ObligationCause};
use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
use rustc_session::lint::Level;
use rustc_trait_selection::traits::{SelectionContext, SelectionError};

use std::ops::Bound;

Expand Down Expand Up @@ -132,6 +136,42 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
self.register_violations(&violations, &unsafe_blocks);
}
},
Rvalue::Cast(kind, source, target_ty) => {
if let CastKind::Pointer(ty::adjustment::PointerCast::Unsize) = kind {
let tcx = self.tcx;
let source_ty = source.ty(self.body, tcx);
let def_id = tcx.require_lang_item(LangItem::CoerceUnsized, None);
let trait_ref = ty::Binder::dummy(ty::TraitRef {
def_id,
substs: tcx.mk_substs_trait(source_ty, &[(*target_ty).into()]),
});

let trait_ref = tcx.erase_regions(trait_ref);
let param_env = self.param_env;
tcx.infer_ctxt().enter(|infcx| {
let mut selcx = SelectionContext::new(&infcx);

let obligation_cause = ObligationCause::dummy();
let obligation = Obligation::new(
obligation_cause,
param_env,
trait_ref.to_poly_trait_predicate(),
);
match selcx.select(&obligation) {
Ok(None) | Err(SelectionError::Unimplemented) => {
self.require_unsafe(
UnsafetyViolationKind::General,
UnsafetyViolationDetails::UnsafeCoerceUnsized,
);
}
Ok(Some(_)) => {}
Err(e) => {
bug!("Encountered error `{:?}` selecting `{:?}` during unsafety checking", e, trait_ref);
}
}
});
}
}
_ => {}
}
self.super_rvalue(rvalue, location);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_passes/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ impl LanguageItemCollector<'tcx> {

// Miscellaneous
| LangItem::Unsize
| LangItem::UnsafeCoerceUnsized
| LangItem::CoerceUnsized
| LangItem::DispatchFromDyn
| LangItem::Fn
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1361,6 +1361,7 @@ symbols! {
unrestricted_attribute_tokens,
unsafe_block_in_unsafe_fn,
unsafe_cell,
unsafe_coerce_unsized,
unsafe_no_drop_flag,
unsize,
unsized_fn_params,
Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_traits/src/chalk/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
Some(chalk_solve::rust_ir::WellKnownTrait::Unsize)
} else if lang_items.unpin_trait() == Some(def_id) {
Some(chalk_solve::rust_ir::WellKnownTrait::Unpin)
} else if lang_items.coerce_unsized_trait() == Some(def_id) {
} else if lang_items.unsafe_coerce_unsized_trait() == Some(def_id) {
// FIXME: Rename to UnsafeCoerceUnsized
Some(chalk_solve::rust_ir::WellKnownTrait::CoerceUnsized)
} else {
None
Expand Down Expand Up @@ -566,7 +567,8 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
FnOnce => lang_items.fn_once_trait(),
Unsize => lang_items.unsize_trait(),
Unpin => lang_items.unpin_trait(),
CoerceUnsized => lang_items.coerce_unsized_trait(),
// FIXME: Should actually be UnsafeCoerceUnsized here.
CoerceUnsized => lang_items.unsafe_coerce_unsized_trait(),
DiscriminantKind => lang_items.discriminant_kind_trait(),
};
def_id.map(chalk_ir::TraitId)
Expand Down
36 changes: 19 additions & 17 deletions compiler/rustc_typeck/src/check/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,20 +507,20 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
debug!("coerce_unsized: resolved source={:?} target={:?}", source, target);

// These 'if' statements require some explanation.
// The `CoerceUnsized` trait is special - it is only
// possible to write `impl CoerceUnsized<B> for A` where
// The `UnsafeCoerceUnsized` trait is special - it is only
// possible to write `impl UnsafeCoerceUnsized<B> for A` where
// A and B have 'matching' fields. This rules out the following
// two types of blanket impls:
//
// `impl<T> CoerceUnsized<T> for SomeType`
// `impl<T> CoerceUnsized<SomeType> for T`
// `impl<T> UnsafeCoerceUnsized<T> for SomeType`
// `impl<T> UnsafeCoerceUnsized<SomeType> for T`
//
// Both of these trigger a special `CoerceUnsized`-related error (E0376)
// Both of these trigger a special `UnsafeCoerceUnsized`-related error (E0376)
//
// We can take advantage of this fact to avoid performing unnecessary work.
// If either `source` or `target` is a type variable, then any applicable impl
// would need to be generic over the self-type (`impl<T> CoerceUnsized<SomeType> for T`)
// or generic over the `CoerceUnsized` type parameter (`impl<T> CoerceUnsized<T> for
// would need to be generic over the self-type (`impl<T> UnsafeCoerceUnsized<SomeType> for T`)
// or generic over the `UnsafeCoerceUnsized` type parameter (`impl<T> UnsafeCoerceUnsized<T> for
// SomeType`).
//
// However, these are exactly the kinds of impls which are forbidden by
Expand All @@ -536,12 +536,14 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
return Err(TypeError::Mismatch);
}

let traits =
(self.tcx.lang_items().unsize_trait(), self.tcx.lang_items().coerce_unsized_trait());
let (unsize_did, coerce_unsized_did) = if let (Some(u), Some(cu)) = traits {
let traits = (
self.tcx.lang_items().unsize_trait(),
self.tcx.lang_items().unsafe_coerce_unsized_trait(),
);
let (unsize_did, unsafe_coerce_unsized_did) = if let (Some(u), Some(cu)) = traits {
(u, cu)
} else {
debug!("missing Unsize or CoerceUnsized traits");
debug!("missing Unsize or UnsafeCoerceUnsized traits");
return Err(TypeError::Mismatch);
};

Expand All @@ -550,7 +552,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
// we can't unify [T] with U. But to properly support DST, we need to allow
// that, at which point we will need extra checks on the target here.

// Handle reborrows before selecting `Source: CoerceUnsized<Target>`.
// Handle reborrows before selecting `Source: UnsafeCoerceUnsized<Target>`.
let reborrow = match (source.kind(), target.kind()) {
(&ty::Ref(_, ty_a, mutbl_a), &ty::Ref(_, _, mutbl_b)) => {
coerce_mutbls(mutbl_a, mutbl_b)?;
Expand Down Expand Up @@ -592,7 +594,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
let coerce_source = reborrow.as_ref().map_or(source, |&(_, ref r)| r.target);

// Setup either a subtyping or a LUB relationship between
// the `CoerceUnsized` target type and the expected type.
// the `UnsafeCoerceUnsized` target type and the expected type.
// We only have the latter, so we use an inference variable
// for the former and let type inference do the rest.
let origin = TypeVariableOrigin {
Expand All @@ -610,7 +612,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {

let mut selcx = traits::SelectionContext::new(self);

// Create an obligation for `Source: CoerceUnsized<Target>`.
// Create an obligation for `Source: UnsafeCoerceUnsized<Target>`.
let cause = ObligationCause::new(
self.cause.span,
self.body_id,
Expand All @@ -628,7 +630,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
self.tcx,
self.fcx.param_env,
cause,
coerce_unsized_did,
unsafe_coerce_unsized_did,
0,
coerce_source,
&[coerce_target.into()]
Expand All @@ -637,10 +639,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
let mut has_unsized_tuple_coercion = false;
let mut has_trait_upcasting_coercion = false;

// Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
// Keep resolving `UnsafeCoerceUnsized` and `Unsize` predicates to avoid
// emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
// inference might unify those two inner type variables later.
let traits = [coerce_unsized_did, unsize_did];
let traits = [unsafe_coerce_unsized_did, unsize_did];
while !queue.is_empty() {
let obligation = queue.remove(0);
debug!("coerce_unsized resolve step: {:?}", obligation);
Expand Down
26 changes: 15 additions & 11 deletions compiler/rustc_typeck/src/coherence/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) {
Checker { tcx, trait_def_id }
.check(lang_items.drop_trait(), visit_implementation_of_drop)
.check(lang_items.copy_trait(), visit_implementation_of_copy)
.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized)
.check(
lang_items.unsafe_coerce_unsized_trait(),
visit_implementation_of_unsafe_coerce_unsized,
)
.check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn);
}

Expand Down Expand Up @@ -109,8 +112,8 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
}
}

fn visit_implementation_of_coerce_unsized(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) {
debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did);
fn visit_implementation_of_unsafe_coerce_unsized(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) {
debug!("visit_implementation_of_unsafe_coerce_unsized: impl_did={:?}", impl_did);

// Just compute this for the side-effects, in particular reporting
// errors; other parts of the code may demand it for the info of
Expand Down Expand Up @@ -294,15 +297,16 @@ pub fn coerce_unsized_info(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedI
let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did.expect_local());
let span = tcx.hir().span(impl_hir_id);

let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, Some(span));
let unsafe_coerce_unsized_trait =
tcx.require_lang_item(LangItem::UnsafeCoerceUnsized, Some(span));

let unsize_trait = tcx.lang_items().require(LangItem::Unsize).unwrap_or_else(|err| {
tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err));
});

let source = tcx.type_of(impl_did);
let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
assert_eq!(trait_ref.def_id, coerce_unsized_trait);
assert_eq!(trait_ref.def_id, unsafe_coerce_unsized_trait);
let target = trait_ref.substs.type_at(1);
debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target);

Expand Down Expand Up @@ -434,7 +438,7 @@ pub fn coerce_unsized_info(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedI
}

// Collect up all fields that were significantly changed
// i.e., those that contain T in coerce_unsized T -> U
// i.e., those that contain T in unsafe_coerce_unsized T -> U
Some((i, a, b))
})
.collect::<Vec<_>>();
Expand All @@ -444,7 +448,7 @@ pub fn coerce_unsized_info(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedI
tcx.sess,
span,
E0374,
"the trait `CoerceUnsized` may only be implemented \
"the trait `UnsafeCoerceUnsized` may only be implemented \
for a coercion between structures with one field \
being coerced, none found"
)
Expand All @@ -465,11 +469,11 @@ pub fn coerce_unsized_info(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedI
span,
E0375,
"implementing the trait \
`CoerceUnsized` requires multiple \
`UnsafeCoerceUnsized` requires multiple \
coercions"
)
.note(
"`CoerceUnsized` may only be implemented for \
"`UnsafeCoerceUnsized` may only be implemented for \
a coercion between structures with one field being coerced",
)
.note(&format!(
Expand All @@ -490,15 +494,15 @@ pub fn coerce_unsized_info(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedI

let (i, a, b) = diff_fields[0];
let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
(a, b, coerce_unsized_trait, Some(kind))
(a, b, unsafe_coerce_unsized_trait, Some(kind))
}

_ => {
struct_span_err!(
tcx.sess,
span,
E0376,
"the trait `CoerceUnsized` may only be implemented \
"the trait `UnsafeCoerceUnsized` may only be implemented \
for a coercion between structures"
)
.emit();
Expand Down
6 changes: 5 additions & 1 deletion library/alloc/src/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ use core::marker::{Unpin, Unsize};
use core::mem;
use core::ops::{
CoerceUnsized, Deref, DerefMut, DispatchFromDyn, Generator, GeneratorState, Receiver,
UnsafeCoerceUnsized,
};
use core::pin::Pin;
use core::ptr::{self, Unique};
Expand Down Expand Up @@ -1652,7 +1653,10 @@ impl<Args, F: Fn<Args> + ?Sized, A: Allocator> Fn<Args> for Box<F, A> {
}

#[unstable(feature = "coerce_unsized", issue = "27732")]
impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Box<U, A>> for Box<T, A> {}
impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> UnsafeCoerceUnsized<Box<U, A>> for Box<T, A> {}

#[unstable(feature = "coerce_unsized", issue = "27732")]
unsafe impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Box<U, A>> for Box<T, A> {}

#[unstable(feature = "dispatch_from_dyn", issue = "none")]
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Box<U>> for Box<T, Global> {}
Expand Down
Loading