Skip to content

Commit baec50e

Browse files
committed
do not implement unsafe auto traits for types with unsafe fields
If a type has unsafe fields, its safety invariants are not simply the conjunction of its field types' safety invariants. Consequently, it's invalid to reason about the safety properties of these types in a purely structural manner — i.e., the manner in which `auto` traits are implemented. Makes progress towards #132922.
1 parent 0e98766 commit baec50e

File tree

12 files changed

+60
-0
lines changed

12 files changed

+60
-0
lines changed

compiler/rustc_middle/src/ty/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1837,6 +1837,11 @@ impl<'tcx> TyCtxt<'tcx> {
18371837
self.trait_def(trait_def_id).has_auto_impl
18381838
}
18391839

1840+
/// Returns `true` if this is an `unsafe trait`.
1841+
pub fn trait_is_unsafe(self, trait_def_id: DefId) -> bool {
1842+
self.trait_def(trait_def_id).safety == Safety::Unsafe
1843+
}
1844+
18401845
/// Returns `true` if this is coinductive, either because it is
18411846
/// an auto trait or because it has the `#[rustc_coinductive]` attribute.
18421847
pub fn trait_is_coinductive(self, trait_def_id: DefId) -> bool {

compiler/rustc_middle/src/ty/util.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,15 @@ impl<'tcx> Ty<'tcx> {
12881288
}
12891289
}
12901290

1291+
/// Checks whether this type directly contains unsafe fields.
1292+
pub fn has_unsafe_fields(self) -> bool {
1293+
if let ty::Adt(adt_def, ..) = self.kind() {
1294+
adt_def.all_fields().any(|x| x.safety == hir::Safety::Unsafe)
1295+
} else {
1296+
false
1297+
}
1298+
}
1299+
12911300
/// Get morphology of the async drop glue, needed for types which do not
12921301
/// use async drop. To get async drop glue morphology for a definition see
12931302
/// [`TyCtxt::async_drop_glue_morphology`]. Used for `AsyncDestruct::Destructor`

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+6
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
794794
| ty::Never
795795
| ty::Tuple(_)
796796
| ty::CoroutineWitness(..) => {
797+
// Only consider auto impls of unsafe traits when there are
798+
// no unsafe fields.
799+
if self.tcx().trait_is_unsafe(def_id) && self_ty.has_unsafe_fields() {
800+
return;
801+
}
802+
797803
// Only consider auto impls if there are no manual impls for the root of `self_ty`.
798804
//
799805
// For example, we only consider auto candidates for `&i32: Auto` if no explicit impl

tests/ui/unsafe-fields/auto-traits.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//@ compile-flags: --crate-type=lib
2+
3+
#![feature(auto_traits)]
4+
#![feature(unsafe_fields)]
5+
#![allow(dead_code, incomplete_features, unconditional_recursion)]
6+
7+
enum UnsafeEnum {
8+
Safe(u8),
9+
Unsafe { unsafe field: u8 },
10+
}
11+
12+
auto trait SafeAuto {}
13+
14+
fn impl_safe_auto(_: impl SafeAuto) {
15+
impl_safe_auto(UnsafeEnum::Safe(42))
16+
}
17+
18+
unsafe auto trait UnsafeAuto {}
19+
20+
fn impl_unsafe_auto(_: impl UnsafeAuto) {
21+
impl_unsafe_auto(UnsafeEnum::Safe(42))
22+
//~^ ERROR the trait bound `UnsafeEnum: UnsafeAuto` is not satisfied
23+
}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0277]: the trait bound `UnsafeEnum: UnsafeAuto` is not satisfied
2+
--> $DIR/auto-traits.rs:21:22
3+
|
4+
LL | impl_unsafe_auto(UnsafeEnum::Safe(42))
5+
| ---------------- ^^^^^^^^^^^^^^^^^^^^ the trait `UnsafeAuto` is not implemented for `UnsafeEnum`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
note: required by a bound in `impl_unsafe_auto`
10+
--> $DIR/auto-traits.rs:20:29
11+
|
12+
LL | fn impl_unsafe_auto(_: impl UnsafeAuto) {
13+
| ^^^^^^^^^^ required by this bound in `impl_unsafe_auto`
14+
15+
error: aborting due to 1 previous error
16+
17+
For more information about this error, try `rustc --explain E0277`.
File renamed without changes.

0 commit comments

Comments
 (0)