about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2024-12-06 21:21:07 +0100
committerGitHub <noreply@github.com>2024-12-06 21:21:07 +0100
commit6457761f8071e50cbffe4fe0476fb59c059e7c1e (patch)
treeacbff61a184fb6431904be5f0e721f47c9bf9f1a
parent8c78d7a02f29f35ae7ac8252424d7c3e99c7ba41 (diff)
parenta122dde217459c1decbce3452323807154763206 (diff)
Rollup merge of #133934 - jswrenn:unsafe-fields-auto-traits, r=compiler-errors
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. Consequently, auto implementations of unsafe auto traits should not be generated for types with unsafe fields.

Tracking: #132922

r? `@compiler-errors`
-rw-r--r--compiler/rustc_middle/src/ty/context.rs4
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs8
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/trait_goals.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs9
-rw-r--r--compiler/rustc_type_ir/src/inherent.rs3
-rw-r--r--compiler/rustc_type_ir/src/interner.rs3
-rw-r--r--tests/ui/unsafe-fields/auto-traits.current.stderr17
-rw-r--r--tests/ui/unsafe-fields/auto-traits.next.stderr17
-rw-r--r--tests/ui/unsafe-fields/auto-traits.rs26
-rw-r--r--tests/ui/unsafe-fields/auxiliary/unsafe-fields-crate-dep.rs (renamed from tests/ui/auxiliary/unsafe-fields-crate-dep.rs)0
-rw-r--r--tests/ui/unsafe-fields/unsafe-fields-crate.rs (renamed from tests/ui/unsafe-fields-crate.rs)0
-rw-r--r--tests/ui/unsafe-fields/unsafe-fields-crate.stderr (renamed from tests/ui/unsafe-fields-crate.stderr)0
-rw-r--r--tests/ui/unsafe-fields/unsafe-fields-parse.rs (renamed from tests/ui/unsafe-fields-parse.rs)0
-rw-r--r--tests/ui/unsafe-fields/unsafe-fields-parse.stderr (renamed from tests/ui/unsafe-fields-parse.stderr)0
-rw-r--r--tests/ui/unsafe-fields/unsafe-fields.rs (renamed from tests/ui/unsafe-fields.rs)0
-rw-r--r--tests/ui/unsafe-fields/unsafe-fields.stderr (renamed from tests/ui/unsafe-fields.stderr)0
16 files changed, 95 insertions, 0 deletions
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 3cbb2a3acf5..d4835bb07f6 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -585,6 +585,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.trait_def(trait_def_id).implement_via_object
     }
 
+    fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool {
+        self.trait_def(trait_def_id).safety == hir::Safety::Unsafe
+    }
+
     fn is_impl_trait_in_trait(self, def_id: DefId) -> bool {
         self.is_impl_trait_in_trait(def_id)
     }
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 142db8a17f0..474062218c9 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -978,6 +978,14 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
     fn async_destructor_ty(self, interner: TyCtxt<'tcx>) -> Ty<'tcx> {
         self.async_destructor_ty(interner)
     }
+
+    fn has_unsafe_fields(self) -> bool {
+        if let ty::Adt(adt_def, ..) = self.kind() {
+            adt_def.all_fields().any(|x| x.safety == hir::Safety::Unsafe)
+        } else {
+            false
+        }
+    }
 }
 
 /// Type utilities
diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
index 6641d2bf924..12df35d30b8 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -169,6 +169,14 @@ where
             return result;
         }
 
+        // Only consider auto impls of unsafe traits when there are no unsafe
+        // fields.
+        if ecx.cx().trait_is_unsafe(goal.predicate.def_id())
+            && goal.predicate.self_ty().has_unsafe_fields()
+        {
+            return Err(NoSolution);
+        }
+
         // We only look into opaque types during analysis for opaque types
         // outside of their defining scope. Doing so for opaques in the
         // defining scope may require calling `typeck` on the same item we're
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index 32b4567aba4..3e2c8467d32 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -18,6 +18,7 @@ use rustc_infer::traits::{
 use rustc_middle::ty::fast_reject::DeepRejectCtxt;
 use rustc_middle::ty::{self, ToPolyTraitRef, Ty, TypeVisitableExt, TypingMode};
 use rustc_middle::{bug, span_bug};
+use rustc_type_ir::Interner;
 use tracing::{debug, instrument, trace};
 
 use super::SelectionCandidate::*;
@@ -794,6 +795,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 | ty::Never
                 | ty::Tuple(_)
                 | ty::CoroutineWitness(..) => {
+                    use rustc_type_ir::inherent::*;
+
+                    // Only consider auto impls of unsafe traits when there are
+                    // no unsafe fields.
+                    if self.tcx().trait_is_unsafe(def_id) && self_ty.has_unsafe_fields() {
+                        return;
+                    }
+
                     // Only consider auto impls if there are no manual impls for the root of `self_ty`.
                     //
                     // For example, we only consider auto candidates for `&i32: Auto` if no explicit impl
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index a201f2b1c11..f45c94127bd 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -136,6 +136,9 @@ pub trait Ty<I: Interner<Ty = Self>>:
         matches!(self.kind(), ty::FnPtr(..))
     }
 
+    /// Checks whether this type is an ADT that has unsafe fields.
+    fn has_unsafe_fields(self) -> bool;
+
     fn fn_sig(self, interner: I) -> ty::Binder<I, ty::FnSig<I>> {
         match self.kind() {
             ty::FnPtr(sig_tys, hdr) => sig_tys.with(hdr),
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index 0ae688848eb..025ec7ae896 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -270,6 +270,9 @@ pub trait Interner:
 
     fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool;
 
+    /// Returns `true` if this is an `unsafe trait`.
+    fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool;
+
     fn is_impl_trait_in_trait(self, def_id: Self::DefId) -> bool;
 
     fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed;
diff --git a/tests/ui/unsafe-fields/auto-traits.current.stderr b/tests/ui/unsafe-fields/auto-traits.current.stderr
new file mode 100644
index 00000000000..53a97458b7c
--- /dev/null
+++ b/tests/ui/unsafe-fields/auto-traits.current.stderr
@@ -0,0 +1,17 @@
+error[E0277]: the trait bound `UnsafeEnum: UnsafeAuto` is not satisfied
+  --> $DIR/auto-traits.rs:24:22
+   |
+LL |     impl_unsafe_auto(UnsafeEnum::Safe(42));
+   |     ---------------- ^^^^^^^^^^^^^^^^^^^^ the trait `UnsafeAuto` is not implemented for `UnsafeEnum`
+   |     |
+   |     required by a bound introduced by this call
+   |
+note: required by a bound in `impl_unsafe_auto`
+  --> $DIR/auto-traits.rs:20:29
+   |
+LL | fn impl_unsafe_auto(_: impl UnsafeAuto) {}
+   |                             ^^^^^^^^^^ required by this bound in `impl_unsafe_auto`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/unsafe-fields/auto-traits.next.stderr b/tests/ui/unsafe-fields/auto-traits.next.stderr
new file mode 100644
index 00000000000..53a97458b7c
--- /dev/null
+++ b/tests/ui/unsafe-fields/auto-traits.next.stderr
@@ -0,0 +1,17 @@
+error[E0277]: the trait bound `UnsafeEnum: UnsafeAuto` is not satisfied
+  --> $DIR/auto-traits.rs:24:22
+   |
+LL |     impl_unsafe_auto(UnsafeEnum::Safe(42));
+   |     ---------------- ^^^^^^^^^^^^^^^^^^^^ the trait `UnsafeAuto` is not implemented for `UnsafeEnum`
+   |     |
+   |     required by a bound introduced by this call
+   |
+note: required by a bound in `impl_unsafe_auto`
+  --> $DIR/auto-traits.rs:20:29
+   |
+LL | fn impl_unsafe_auto(_: impl UnsafeAuto) {}
+   |                             ^^^^^^^^^^ required by this bound in `impl_unsafe_auto`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/unsafe-fields/auto-traits.rs b/tests/ui/unsafe-fields/auto-traits.rs
new file mode 100644
index 00000000000..e15d0000079
--- /dev/null
+++ b/tests/ui/unsafe-fields/auto-traits.rs
@@ -0,0 +1,26 @@
+//@ compile-flags: --crate-type=lib
+//@ revisions: current next
+//@[next] compile-flags: -Znext-solver
+
+#![feature(auto_traits)]
+#![feature(unsafe_fields)]
+#![allow(incomplete_features)]
+
+enum UnsafeEnum {
+    Safe(u8),
+    Unsafe { unsafe field: u8 },
+}
+
+auto trait SafeAuto {}
+
+fn impl_safe_auto(_: impl SafeAuto) {}
+
+unsafe auto trait UnsafeAuto {}
+
+fn impl_unsafe_auto(_: impl UnsafeAuto) {}
+
+fn tests() {
+    impl_safe_auto(UnsafeEnum::Safe(42));
+    impl_unsafe_auto(UnsafeEnum::Safe(42));
+    //~^ ERROR the trait bound `UnsafeEnum: UnsafeAuto` is not satisfied
+}
diff --git a/tests/ui/auxiliary/unsafe-fields-crate-dep.rs b/tests/ui/unsafe-fields/auxiliary/unsafe-fields-crate-dep.rs
index 95b928402a9..95b928402a9 100644
--- a/tests/ui/auxiliary/unsafe-fields-crate-dep.rs
+++ b/tests/ui/unsafe-fields/auxiliary/unsafe-fields-crate-dep.rs
diff --git a/tests/ui/unsafe-fields-crate.rs b/tests/ui/unsafe-fields/unsafe-fields-crate.rs
index cfb9ad6b544..cfb9ad6b544 100644
--- a/tests/ui/unsafe-fields-crate.rs
+++ b/tests/ui/unsafe-fields/unsafe-fields-crate.rs
diff --git a/tests/ui/unsafe-fields-crate.stderr b/tests/ui/unsafe-fields/unsafe-fields-crate.stderr
index 778c26e0a43..778c26e0a43 100644
--- a/tests/ui/unsafe-fields-crate.stderr
+++ b/tests/ui/unsafe-fields/unsafe-fields-crate.stderr
diff --git a/tests/ui/unsafe-fields-parse.rs b/tests/ui/unsafe-fields/unsafe-fields-parse.rs
index 67277731293..67277731293 100644
--- a/tests/ui/unsafe-fields-parse.rs
+++ b/tests/ui/unsafe-fields/unsafe-fields-parse.rs
diff --git a/tests/ui/unsafe-fields-parse.stderr b/tests/ui/unsafe-fields/unsafe-fields-parse.stderr
index 5a45ab03ffe..5a45ab03ffe 100644
--- a/tests/ui/unsafe-fields-parse.stderr
+++ b/tests/ui/unsafe-fields/unsafe-fields-parse.stderr
diff --git a/tests/ui/unsafe-fields.rs b/tests/ui/unsafe-fields/unsafe-fields.rs
index 637471582d7..637471582d7 100644
--- a/tests/ui/unsafe-fields.rs
+++ b/tests/ui/unsafe-fields/unsafe-fields.rs
diff --git a/tests/ui/unsafe-fields.stderr b/tests/ui/unsafe-fields/unsafe-fields.stderr
index a1c5d2b44cd..a1c5d2b44cd 100644
--- a/tests/ui/unsafe-fields.stderr
+++ b/tests/ui/unsafe-fields/unsafe-fields.stderr