Skip to content

Commit a122dde

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 a122dde

File tree

16 files changed

+95
-0
lines changed

16 files changed

+95
-0
lines changed

Diff for: compiler/rustc_middle/src/ty/context.rs

+4
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
585585
self.trait_def(trait_def_id).implement_via_object
586586
}
587587

588+
fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool {
589+
self.trait_def(trait_def_id).safety == hir::Safety::Unsafe
590+
}
591+
588592
fn is_impl_trait_in_trait(self, def_id: DefId) -> bool {
589593
self.is_impl_trait_in_trait(def_id)
590594
}

Diff for: compiler/rustc_middle/src/ty/sty.rs

+8
Original file line numberDiff line numberDiff line change
@@ -978,6 +978,14 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
978978
fn async_destructor_ty(self, interner: TyCtxt<'tcx>) -> Ty<'tcx> {
979979
self.async_destructor_ty(interner)
980980
}
981+
982+
fn has_unsafe_fields(self) -> bool {
983+
if let ty::Adt(adt_def, ..) = self.kind() {
984+
adt_def.all_fields().any(|x| x.safety == hir::Safety::Unsafe)
985+
} else {
986+
false
987+
}
988+
}
981989
}
982990

983991
/// Type utilities

Diff for: compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

+8
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,14 @@ where
169169
return result;
170170
}
171171

172+
// Only consider auto impls of unsafe traits when there are no unsafe
173+
// fields.
174+
if ecx.cx().trait_is_unsafe(goal.predicate.def_id())
175+
&& goal.predicate.self_ty().has_unsafe_fields()
176+
{
177+
return Err(NoSolution);
178+
}
179+
172180
// We only look into opaque types during analysis for opaque types
173181
// outside of their defining scope. Doing so for opaques in the
174182
// defining scope may require calling `typeck` on the same item we're

Diff for: compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+9
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use rustc_infer::traits::{
1818
use rustc_middle::ty::fast_reject::DeepRejectCtxt;
1919
use rustc_middle::ty::{self, ToPolyTraitRef, Ty, TypeVisitableExt, TypingMode};
2020
use rustc_middle::{bug, span_bug};
21+
use rustc_type_ir::Interner;
2122
use tracing::{debug, instrument, trace};
2223

2324
use super::SelectionCandidate::*;
@@ -794,6 +795,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
794795
| ty::Never
795796
| ty::Tuple(_)
796797
| ty::CoroutineWitness(..) => {
798+
use rustc_type_ir::inherent::*;
799+
800+
// Only consider auto impls of unsafe traits when there are
801+
// no unsafe fields.
802+
if self.tcx().trait_is_unsafe(def_id) && self_ty.has_unsafe_fields() {
803+
return;
804+
}
805+
797806
// Only consider auto impls if there are no manual impls for the root of `self_ty`.
798807
//
799808
// For example, we only consider auto candidates for `&i32: Auto` if no explicit impl

Diff for: compiler/rustc_type_ir/src/inherent.rs

+3
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ pub trait Ty<I: Interner<Ty = Self>>:
136136
matches!(self.kind(), ty::FnPtr(..))
137137
}
138138

139+
/// Checks whether this type is an ADT that has unsafe fields.
140+
fn has_unsafe_fields(self) -> bool;
141+
139142
fn fn_sig(self, interner: I) -> ty::Binder<I, ty::FnSig<I>> {
140143
match self.kind() {
141144
ty::FnPtr(sig_tys, hdr) => sig_tys.with(hdr),

Diff for: compiler/rustc_type_ir/src/interner.rs

+3
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,9 @@ pub trait Interner:
270270

271271
fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool;
272272

273+
/// Returns `true` if this is an `unsafe trait`.
274+
fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool;
275+
273276
fn is_impl_trait_in_trait(self, def_id: Self::DefId) -> bool;
274277

275278
fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed;

Diff for: tests/ui/unsafe-fields/auto-traits.current.stderr

+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:24: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`.

Diff for: tests/ui/unsafe-fields/auto-traits.next.stderr

+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:24: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`.

Diff for: tests/ui/unsafe-fields/auto-traits.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//@ compile-flags: --crate-type=lib
2+
//@ revisions: current next
3+
//@[next] compile-flags: -Znext-solver
4+
5+
#![feature(auto_traits)]
6+
#![feature(unsafe_fields)]
7+
#![allow(incomplete_features)]
8+
9+
enum UnsafeEnum {
10+
Safe(u8),
11+
Unsafe { unsafe field: u8 },
12+
}
13+
14+
auto trait SafeAuto {}
15+
16+
fn impl_safe_auto(_: impl SafeAuto) {}
17+
18+
unsafe auto trait UnsafeAuto {}
19+
20+
fn impl_unsafe_auto(_: impl UnsafeAuto) {}
21+
22+
fn tests() {
23+
impl_safe_auto(UnsafeEnum::Safe(42));
24+
impl_unsafe_auto(UnsafeEnum::Safe(42));
25+
//~^ ERROR the trait bound `UnsafeEnum: UnsafeAuto` is not satisfied
26+
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)