Skip to content

Commit 2bcb3dd

Browse files
Rollup merge of rust-lang#154229 - Zoxc:erase-auto-traits, r=nnethercote
Ensure `ErasedData` only implements appropriate auto traits This uses an external type to prevent auto traits to be inferred on `ErasedData`. That inference is incorrect since it may not store the type it declares. This also implements `DynSend` and `DynSync` on `ErasedData` which are checked by bounds on `erase_val`. Some diagnostics bounds were missing `DynSync`, which is also added here.
2 parents 6d8bc65 + f5aa518 commit 2bcb3dd

File tree

4 files changed

+35
-14
lines changed

4 files changed

+35
-14
lines changed

compiler/rustc_errors/src/decorate_diag.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/// This module provides types and traits for buffering lints until later in compilation.
22
use rustc_ast::node_id::NodeId;
33
use rustc_data_structures::fx::FxIndexMap;
4-
use rustc_data_structures::sync::DynSend;
4+
use rustc_data_structures::sync::{DynSend, DynSync};
55
use rustc_error_messages::MultiSpan;
66
use rustc_lint_defs::{BuiltinLintDiag, Lint, LintId};
77

@@ -10,7 +10,14 @@ use crate::{Diag, DiagCtxtHandle, Diagnostic, Level};
1010
/// We can't implement `Diagnostic` for `BuiltinLintDiag`, because decorating some of its
1111
/// variants requires types we don't have yet. So, handle that case separately.
1212
pub enum DecorateDiagCompat {
13-
Dynamic(Box<dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + DynSend + 'static>),
13+
Dynamic(
14+
Box<
15+
dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()>
16+
+ DynSync
17+
+ DynSend
18+
+ 'static,
19+
>,
20+
),
1421
Builtin(BuiltinLintDiag),
1522
}
1623

@@ -20,7 +27,7 @@ impl std::fmt::Debug for DecorateDiagCompat {
2027
}
2128
}
2229

23-
impl<D: for<'a> Diagnostic<'a, ()> + DynSend + 'static> From<D> for DecorateDiagCompat {
30+
impl<D: for<'a> Diagnostic<'a, ()> + DynSync + DynSend + 'static> From<D> for DecorateDiagCompat {
2431
#[inline]
2532
fn from(d: D) -> Self {
2633
Self::Dynamic(Box::new(|dcx, level| d.into_diag(dcx, level)))
@@ -83,7 +90,7 @@ impl LintBuffer {
8390
}
8491

8592
pub fn dyn_buffer_lint<
86-
F: for<'a> FnOnce(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + DynSend + 'static,
93+
F: for<'a> FnOnce(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + DynSync + DynSend + 'static,
8794
>(
8895
&mut self,
8996
lint: &'static Lint,

compiler/rustc_errors/src/diagnostic.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::panic;
77
use std::path::PathBuf;
88
use std::thread::panicking;
99

10-
use rustc_data_structures::sync::DynSend;
10+
use rustc_data_structures::sync::{DynSend, DynSync};
1111
use rustc_error_messages::{DiagArgMap, DiagArgName, DiagArgValue, IntoDiagArg};
1212
use rustc_lint_defs::{Applicability, LintExpectationId};
1313
use rustc_macros::{Decodable, Encodable};
@@ -120,7 +120,9 @@ where
120120
}
121121

122122
impl<'a> Diagnostic<'a, ()>
123-
for Box<dyn for<'b> FnOnce(DiagCtxtHandle<'b>, Level) -> Diag<'b, ()> + DynSend + 'static>
123+
for Box<
124+
dyn for<'b> FnOnce(DiagCtxtHandle<'b>, Level) -> Diag<'b, ()> + DynSync + DynSend + 'static,
125+
>
124126
{
125127
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
126128
self(dcx, level)

compiler/rustc_middle/src/query/erase.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,37 @@
77
88
use std::ffi::OsStr;
99
use std::intrinsics::transmute_unchecked;
10+
use std::marker::PhantomData;
1011
use std::mem::MaybeUninit;
1112

1213
use rustc_ast::tokenstream::TokenStream;
1314
use rustc_data_structures::steal::Steal;
15+
use rustc_data_structures::sync::{DynSend, DynSync};
1416
use rustc_span::{ErrorGuaranteed, Spanned};
1517

1618
use crate::mir::mono::{MonoItem, NormalizationErrorInMono};
1719
use crate::ty::{self, Ty, TyCtxt};
1820
use crate::{mir, thir, traits};
1921

22+
unsafe extern "C" {
23+
type NoAutoTraits;
24+
}
25+
2026
/// Internal implementation detail of [`Erased`].
2127
#[derive(Copy, Clone)]
2228
pub struct ErasedData<Storage: Copy> {
2329
/// We use `MaybeUninit` here to make sure it's legal to store a transmuted
2430
/// value that isn't actually of type `Storage`.
2531
data: MaybeUninit<Storage>,
32+
/// `Storage` is an erased type, so we use an external type here to opt-out of auto traits
33+
/// as those would be incorrect.
34+
no_auto_traits: PhantomData<NoAutoTraits>,
2635
}
2736

37+
// SAFETY: The bounds on `erase_val` ensure the types we erase are `DynSync` and `DynSend`
38+
unsafe impl<Storage: Copy> DynSync for ErasedData<Storage> {}
39+
unsafe impl<Storage: Copy> DynSend for ErasedData<Storage> {}
40+
2841
/// Trait for types that can be erased into [`Erased<Self>`].
2942
///
3043
/// Erasing and unerasing values is performed by [`erase_val`] and [`restore_val`].
@@ -54,13 +67,11 @@ pub type Erased<T: Erasable> = ErasedData<impl Copy>;
5467
///
5568
/// `Erased<T>` and `Erased<U>` are type-checked as distinct types, but codegen
5669
/// can see whether they actually have the same storage type.
57-
///
58-
/// FIXME: This might have soundness issues with erasable types that don't
59-
/// implement the same auto-traits as `[u8; _]`; see
60-
/// <https://github.com/rust-lang/rust/pull/151715#discussion_r2740113250>
6170
#[inline(always)]
6271
#[define_opaque(Erased)]
63-
pub fn erase_val<T: Erasable>(value: T) -> Erased<T> {
72+
// The `DynSend` and `DynSync` bounds on `T` are used to
73+
// justify the safety of the implementations of these traits for `ErasedData`.
74+
pub fn erase_val<T: Erasable + DynSend + DynSync>(value: T) -> Erased<T> {
6475
// Ensure the sizes match
6576
const {
6677
if size_of::<T>() != size_of::<T::Storage>() {
@@ -78,6 +89,7 @@ pub fn erase_val<T: Erasable>(value: T) -> Erased<T> {
7889
//
7990
// SAFETY: It is safe to transmute to MaybeUninit for types with the same sizes.
8091
data: unsafe { transmute_unchecked::<T, MaybeUninit<T::Storage>>(value) },
92+
no_auto_traits: PhantomData,
8193
}
8294
}
8395

@@ -88,7 +100,7 @@ pub fn erase_val<T: Erasable>(value: T) -> Erased<T> {
88100
#[inline(always)]
89101
#[define_opaque(Erased)]
90102
pub fn restore_val<T: Erasable>(erased_value: Erased<T>) -> T {
91-
let ErasedData { data }: ErasedData<<T as Erasable>::Storage> = erased_value;
103+
let ErasedData { data, .. }: ErasedData<<T as Erasable>::Storage> = erased_value;
92104
// See comment in `erase_val` for why we use `transmute_unchecked`.
93105
//
94106
// SAFETY: Due to the use of impl Trait in `Erased` the only way to safely create an instance

compiler/rustc_session/src/parse.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::sync::Arc;
77
use rustc_ast::attr::AttrIdGenerator;
88
use rustc_ast::node_id::NodeId;
99
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
10-
use rustc_data_structures::sync::{AppendOnlyVec, DynSend, Lock};
10+
use rustc_data_structures::sync::{AppendOnlyVec, DynSend, DynSync, Lock};
1111
use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter;
1212
use rustc_errors::emitter::{EmitterWithNote, stderr_destination};
1313
use rustc_errors::{
@@ -332,7 +332,7 @@ impl ParseSess {
332332
}
333333

334334
pub fn dyn_buffer_lint<
335-
F: for<'a> FnOnce(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + DynSend + 'static,
335+
F: for<'a> FnOnce(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + DynSync + DynSend + 'static,
336336
>(
337337
&self,
338338
lint: &'static Lint,

0 commit comments

Comments
 (0)