Auto merge of #154137 - JonathanBrouwer:rollup-hTMxxjl, r=JonathanBrouwer

Rollup of 5 pull requests

Successful merges:

 - rust-lang/rust#154103 (coretests: Expand ieee754 parsing and printing tests to f16)
 - rust-lang/rust#152669 (rustc_public: add `vtable_entries()` to `TraitRef`)
 - rust-lang/rust#153776 (Remove redundant `is_dyn_thread_safe` checks)
 - rust-lang/rust#154121 (Fix typos and markdown errors)
 - rust-lang/rust#154126 (refactor(attribute parser): move check_custom_mir to attribute parser)
This commit is contained in:
bors
2026-03-20 14:18:58 +00:00
26 changed files with 599 additions and 339 deletions
@@ -169,7 +169,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
return None; return None;
}; };
let Some(classname) = nv.value_as_str() else { let Some(classname) = nv.value_as_str() else {
// `#[rustc_objc_class = ...]` is expected to be used as an implementatioin detail // `#[rustc_objc_class = ...]` is expected to be used as an implementation detail
// inside a standard library macro, but `cx.expected_string_literal` exposes too much. // inside a standard library macro, but `cx.expected_string_literal` exposes too much.
// Use a custom error message instead. // Use a custom error message instead.
cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span }); cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span });
@@ -201,7 +201,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
return None; return None;
}; };
let Some(methname) = nv.value_as_str() else { let Some(methname) = nv.value_as_str() else {
// `#[rustc_objc_selector = ...]` is expected to be used as an implementatioin detail // `#[rustc_objc_selector = ...]` is expected to be used as an implementation detail
// inside a standard library macro, but `cx.expected_string_literal` exposes too much. // inside a standard library macro, but `cx.expected_string_literal` exposes too much.
// Use a custom error message instead. // Use a custom error message instead.
cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span }); cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span });
@@ -9,6 +9,7 @@
use crate::attributes::SingleAttributeParser; use crate::attributes::SingleAttributeParser;
use crate::context::{AcceptContext, Stage}; use crate::context::{AcceptContext, Stage};
use crate::parser::ArgParser; use crate::parser::ArgParser;
use crate::session_diagnostics;
use crate::target_checking::AllowedTargets; use crate::target_checking::AllowedTargets;
use crate::target_checking::Policy::Allow; use crate::target_checking::Policy::Allow;
@@ -57,6 +58,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
let dialect = parse_dialect(cx, dialect, &mut failed); let dialect = parse_dialect(cx, dialect, &mut failed);
let phase = parse_phase(cx, phase, &mut failed); let phase = parse_phase(cx, phase, &mut failed);
check_custom_mir(cx, dialect, phase, &mut failed);
if failed { if failed {
return None; return None;
@@ -138,3 +140,51 @@ fn parse_phase<S: Stage>(
Some((phase, span)) Some((phase, span))
} }
fn check_custom_mir<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
dialect: Option<(MirDialect, Span)>,
phase: Option<(MirPhase, Span)>,
failed: &mut bool,
) {
let attr_span = cx.attr_span;
let Some((dialect, dialect_span)) = dialect else {
if let Some((_, phase_span)) = phase {
*failed = true;
cx.emit_err(session_diagnostics::CustomMirPhaseRequiresDialect {
attr_span,
phase_span,
});
}
return;
};
match dialect {
MirDialect::Analysis => {
if let Some((MirPhase::Optimized, phase_span)) = phase {
*failed = true;
cx.emit_err(session_diagnostics::CustomMirIncompatibleDialectAndPhase {
dialect,
phase: MirPhase::Optimized,
attr_span,
dialect_span,
phase_span,
});
}
}
MirDialect::Built => {
if let Some((phase, phase_span)) = phase {
*failed = true;
cx.emit_err(session_diagnostics::CustomMirIncompatibleDialectAndPhase {
dialect,
phase,
attr_span,
dialect_span,
phase_span,
});
}
}
MirDialect::Runtime => {}
}
}
@@ -7,6 +7,7 @@
}; };
use rustc_feature::AttributeTemplate; use rustc_feature::AttributeTemplate;
use rustc_hir::AttrPath; use rustc_hir::AttrPath;
use rustc_hir::attrs::{MirDialect, MirPhase};
use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{Span, Symbol}; use rustc_span::{Span, Symbol};
use rustc_target::spec::TargetTuple; use rustc_target::spec::TargetTuple;
@@ -1023,3 +1024,25 @@ pub(crate) struct UnsupportedInstructionSet<'a> {
pub instruction_set: Symbol, pub instruction_set: Symbol,
pub current_target: &'a TargetTuple, pub current_target: &'a TargetTuple,
} }
#[derive(Diagnostic)]
#[diag("`dialect` key required")]
pub(crate) struct CustomMirPhaseRequiresDialect {
#[primary_span]
pub attr_span: Span,
#[label("`phase` argument requires a `dialect` argument")]
pub phase_span: Span,
}
#[derive(Diagnostic)]
#[diag("the {$dialect} dialect is not compatible with the {$phase} phase")]
pub(crate) struct CustomMirIncompatibleDialectAndPhase {
pub dialect: MirDialect,
pub phase: MirPhase,
#[primary_span]
pub attr_span: Span,
#[label("this dialect...")]
pub dialect_span: Span,
#[label("... is not compatible with this phase")]
pub phase_span: Span,
}
@@ -4,13 +4,13 @@ Date: Sun, 15 Feb 2026 14:06:49 +0000
Subject: [PATCH] Disable f16 math tests for cranelift Subject: [PATCH] Disable f16 math tests for cranelift
--- ---
coretests/tests/floats/mod.rs | 26 +++++++++++++------------- coretests/tests/num/floats.rs | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-) 1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/coretests/tests/floats/mod.rs b/coretests/tests/floats/mod.rs diff --git a/coretests/tests/floats/mod.rs b/coretests/tests/floats/mod.rs
index c61961f8584..d7b4fa20322 100644 index c61961f8584..d7b4fa20322 100644
--- a/coretests/tests/floats/mod.rs --- a/coretests/tests/num/floats.rs
+++ b/coretests/tests/floats/mod.rs +++ b/coretests/tests/num/floats.rs
@@ -1534,7 +1534,7 @@ fn s_nan() -> Float { @@ -1534,7 +1534,7 @@ fn s_nan() -> Float {
name: powf, name: powf,
attrs: { attrs: {
@@ -128,6 +128,5 @@ index c61961f8584..d7b4fa20322 100644
f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))],
}, },
test { test {
-- --
2.50.1 2.50.1
+1 -1
View File
@@ -437,7 +437,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
assert_eq!( assert_eq!(
None, None,
num_untupled.replace(tupled_arg_tys.len()), num_untupled.replace(tupled_arg_tys.len()),
"Replaced existing num_tupled" "Replaced existing num_untupled"
); );
return LocalRef::Place(place); return LocalRef::Place(place);
@@ -188,53 +188,6 @@ pub fn assert_dyn_send<T: ?Sized + PointeeSized + DynSend>() {}
pub fn assert_dyn_send_val<T: ?Sized + PointeeSized + DynSend>(_t: &T) {} pub fn assert_dyn_send_val<T: ?Sized + PointeeSized + DynSend>(_t: &T) {}
pub fn assert_dyn_send_sync_val<T: ?Sized + PointeeSized + DynSync + DynSend>(_t: &T) {} pub fn assert_dyn_send_sync_val<T: ?Sized + PointeeSized + DynSync + DynSend>(_t: &T) {}
#[derive(Copy, Clone)]
pub struct FromDyn<T>(T);
impl<T> FromDyn<T> {
#[inline(always)]
pub fn from(val: T) -> Self {
// Check that `sync::is_dyn_thread_safe()` is true on creation so we can
// implement `Send` and `Sync` for this structure when `T`
// implements `DynSend` and `DynSync` respectively.
assert!(crate::sync::is_dyn_thread_safe());
FromDyn(val)
}
#[inline(always)]
pub fn derive<O>(&self, val: O) -> FromDyn<O> {
// We already did the check for `sync::is_dyn_thread_safe()` when creating `Self`
FromDyn(val)
}
#[inline(always)]
pub fn into_inner(self) -> T {
self.0
}
}
// `FromDyn` is `Send` if `T` is `DynSend`, since it ensures that sync::is_dyn_thread_safe() is true.
unsafe impl<T: DynSend> Send for FromDyn<T> {}
// `FromDyn` is `Sync` if `T` is `DynSync`, since it ensures that sync::is_dyn_thread_safe() is true.
unsafe impl<T: DynSync> Sync for FromDyn<T> {}
impl<T> std::ops::Deref for FromDyn<T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> std::ops::DerefMut for FromDyn<T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
// A wrapper to convert a struct that is already a `Send` or `Sync` into // A wrapper to convert a struct that is already a `Send` or `Sync` into
// an instance of `DynSend` and `DynSync`, since the compiler cannot infer // an instance of `DynSend` and `DynSync`, since the compiler cannot infer
// it automatically in some cases. (e.g. Box<dyn Send / Sync>) // it automatically in some cases. (e.g. Box<dyn Send / Sync>)
+49 -1
View File
@@ -34,7 +34,9 @@
pub use self::freeze::{FreezeLock, FreezeReadGuard, FreezeWriteGuard}; pub use self::freeze::{FreezeLock, FreezeReadGuard, FreezeWriteGuard};
#[doc(no_inline)] #[doc(no_inline)]
pub use self::lock::{Lock, LockGuard, Mode}; pub use self::lock::{Lock, LockGuard, Mode};
pub use self::mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode}; pub use self::mode::{
FromDyn, check_dyn_thread_safe, is_dyn_thread_safe, set_dyn_thread_safe_mode,
};
pub use self::parallel::{ pub use self::parallel::{
broadcast, par_fns, par_for_each_in, par_join, par_map, parallel_guard, spawn, broadcast, par_fns, par_for_each_in, par_join, par_map, parallel_guard, spawn,
try_par_for_each_in, try_par_for_each_in,
@@ -64,12 +66,20 @@ mod atomic {
mod mode { mod mode {
use std::sync::atomic::{AtomicU8, Ordering}; use std::sync::atomic::{AtomicU8, Ordering};
use crate::sync::{DynSend, DynSync};
const UNINITIALIZED: u8 = 0; const UNINITIALIZED: u8 = 0;
const DYN_NOT_THREAD_SAFE: u8 = 1; const DYN_NOT_THREAD_SAFE: u8 = 1;
const DYN_THREAD_SAFE: u8 = 2; const DYN_THREAD_SAFE: u8 = 2;
static DYN_THREAD_SAFE_MODE: AtomicU8 = AtomicU8::new(UNINITIALIZED); static DYN_THREAD_SAFE_MODE: AtomicU8 = AtomicU8::new(UNINITIALIZED);
// Whether thread safety is enabled (due to running under multiple threads).
#[inline]
pub fn check_dyn_thread_safe() -> Option<FromDyn<()>> {
is_dyn_thread_safe().then_some(FromDyn(()))
}
// Whether thread safety is enabled (due to running under multiple threads). // Whether thread safety is enabled (due to running under multiple threads).
#[inline] #[inline]
pub fn is_dyn_thread_safe() -> bool { pub fn is_dyn_thread_safe() -> bool {
@@ -99,6 +109,44 @@ pub fn set_dyn_thread_safe_mode(mode: bool) {
// Check that the mode was either uninitialized or was already set to the requested mode. // Check that the mode was either uninitialized or was already set to the requested mode.
assert!(previous.is_ok() || previous == Err(set)); assert!(previous.is_ok() || previous == Err(set));
} }
#[derive(Copy, Clone)]
pub struct FromDyn<T>(T);
impl<T> FromDyn<T> {
#[inline(always)]
pub fn derive<O>(&self, val: O) -> FromDyn<O> {
// We already did the check for `sync::is_dyn_thread_safe()` when creating `Self`
FromDyn(val)
}
#[inline(always)]
pub fn into_inner(self) -> T {
self.0
}
}
// `FromDyn` is `Send` if `T` is `DynSend`, since it ensures that sync::is_dyn_thread_safe() is true.
unsafe impl<T: DynSend> Send for FromDyn<T> {}
// `FromDyn` is `Sync` if `T` is `DynSync`, since it ensures that sync::is_dyn_thread_safe() is true.
unsafe impl<T: DynSync> Sync for FromDyn<T> {}
impl<T> std::ops::Deref for FromDyn<T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> std::ops::DerefMut for FromDyn<T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
} }
/// This makes locks panic if they are already held. /// This makes locks panic if they are already held.
@@ -57,8 +57,8 @@ fn serial_join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
} }
pub fn spawn(func: impl FnOnce() + DynSend + 'static) { pub fn spawn(func: impl FnOnce() + DynSend + 'static) {
if mode::is_dyn_thread_safe() { if let Some(proof) = mode::check_dyn_thread_safe() {
let func = FromDyn::from(func); let func = proof.derive(func);
rustc_thread_pool::spawn(|| { rustc_thread_pool::spawn(|| {
(func.into_inner())(); (func.into_inner())();
}); });
@@ -73,8 +73,8 @@ pub fn spawn(func: impl FnOnce() + DynSend + 'static) {
/// Use that for the longest running function for better scheduling. /// Use that for the longest running function for better scheduling.
pub fn par_fns(funcs: &mut [&mut (dyn FnMut() + DynSend)]) { pub fn par_fns(funcs: &mut [&mut (dyn FnMut() + DynSend)]) {
parallel_guard(|guard: &ParallelGuard| { parallel_guard(|guard: &ParallelGuard| {
if mode::is_dyn_thread_safe() { if let Some(proof) = mode::check_dyn_thread_safe() {
let funcs = FromDyn::from(funcs); let funcs = proof.derive(funcs);
rustc_thread_pool::scope(|s| { rustc_thread_pool::scope(|s| {
let Some((first, rest)) = funcs.into_inner().split_at_mut_checked(1) else { let Some((first, rest)) = funcs.into_inner().split_at_mut_checked(1) else {
return; return;
@@ -84,7 +84,7 @@ pub fn par_fns(funcs: &mut [&mut (dyn FnMut() + DynSend)]) {
// order when using a single thread. This ensures the execution order matches // order when using a single thread. This ensures the execution order matches
// that of a single threaded rustc. // that of a single threaded rustc.
for f in rest.iter_mut().rev() { for f in rest.iter_mut().rev() {
let f = FromDyn::from(f); let f = proof.derive(f);
s.spawn(|_| { s.spawn(|_| {
guard.run(|| (f.into_inner())()); guard.run(|| (f.into_inner())());
}); });
@@ -108,13 +108,13 @@ pub fn par_join<A, B, RA: DynSend, RB: DynSend>(oper_a: A, oper_b: B) -> (RA, RB
A: FnOnce() -> RA + DynSend, A: FnOnce() -> RA + DynSend,
B: FnOnce() -> RB + DynSend, B: FnOnce() -> RB + DynSend,
{ {
if mode::is_dyn_thread_safe() { if let Some(proof) = mode::check_dyn_thread_safe() {
let oper_a = FromDyn::from(oper_a); let oper_a = proof.derive(oper_a);
let oper_b = FromDyn::from(oper_b); let oper_b = proof.derive(oper_b);
let (a, b) = parallel_guard(|guard| { let (a, b) = parallel_guard(|guard| {
rustc_thread_pool::join( rustc_thread_pool::join(
move || guard.run(move || FromDyn::from(oper_a.into_inner()())), move || guard.run(move || proof.derive(oper_a.into_inner()())),
move || guard.run(move || FromDyn::from(oper_b.into_inner()())), move || guard.run(move || proof.derive(oper_b.into_inner()())),
) )
}); });
(a.unwrap().into_inner(), b.unwrap().into_inner()) (a.unwrap().into_inner(), b.unwrap().into_inner())
@@ -127,8 +127,9 @@ fn par_slice<I: DynSend>(
items: &mut [I], items: &mut [I],
guard: &ParallelGuard, guard: &ParallelGuard,
for_each: impl Fn(&mut I) + DynSync + DynSend, for_each: impl Fn(&mut I) + DynSync + DynSend,
proof: FromDyn<()>,
) { ) {
let for_each = FromDyn::from(for_each); let for_each = proof.derive(for_each);
let mut items = for_each.derive(items); let mut items = for_each.derive(items);
rustc_thread_pool::scope(|s| { rustc_thread_pool::scope(|s| {
let proof = items.derive(()); let proof = items.derive(());
@@ -150,9 +151,9 @@ pub fn par_for_each_in<I: DynSend, T: IntoIterator<Item = I>>(
for_each: impl Fn(&I) + DynSync + DynSend, for_each: impl Fn(&I) + DynSync + DynSend,
) { ) {
parallel_guard(|guard| { parallel_guard(|guard| {
if mode::is_dyn_thread_safe() { if let Some(proof) = mode::check_dyn_thread_safe() {
let mut items: Vec<_> = t.into_iter().collect(); let mut items: Vec<_> = t.into_iter().collect();
par_slice(&mut items, guard, |i| for_each(&*i)) par_slice(&mut items, guard, |i| for_each(&*i), proof)
} else { } else {
t.into_iter().for_each(|i| { t.into_iter().for_each(|i| {
guard.run(|| for_each(&i)); guard.run(|| for_each(&i));
@@ -173,16 +174,21 @@ pub fn try_par_for_each_in<T: IntoIterator, E: DynSend>(
<T as IntoIterator>::Item: DynSend, <T as IntoIterator>::Item: DynSend,
{ {
parallel_guard(|guard| { parallel_guard(|guard| {
if mode::is_dyn_thread_safe() { if let Some(proof) = mode::check_dyn_thread_safe() {
let mut items: Vec<_> = t.into_iter().collect(); let mut items: Vec<_> = t.into_iter().collect();
let error = Mutex::new(None); let error = Mutex::new(None);
par_slice(&mut items, guard, |i| { par_slice(
if let Err(err) = for_each(&*i) { &mut items,
*error.lock() = Some(err); guard,
} |i| {
}); if let Err(err) = for_each(&*i) {
*error.lock() = Some(err);
}
},
proof,
);
if let Some(err) = error.into_inner() { Err(err) } else { Ok(()) } if let Some(err) = error.into_inner() { Err(err) } else { Ok(()) }
} else { } else {
@@ -196,15 +202,20 @@ pub fn par_map<I: DynSend, T: IntoIterator<Item = I>, R: DynSend, C: FromIterato
map: impl Fn(I) -> R + DynSync + DynSend, map: impl Fn(I) -> R + DynSync + DynSend,
) -> C { ) -> C {
parallel_guard(|guard| { parallel_guard(|guard| {
if mode::is_dyn_thread_safe() { if let Some(proof) = mode::check_dyn_thread_safe() {
let map = FromDyn::from(map); let map = proof.derive(map);
let mut items: Vec<(Option<I>, Option<R>)> = let mut items: Vec<(Option<I>, Option<R>)> =
t.into_iter().map(|i| (Some(i), None)).collect(); t.into_iter().map(|i| (Some(i), None)).collect();
par_slice(&mut items, guard, |i| { par_slice(
i.1 = Some(map(i.0.take().unwrap())); &mut items,
}); guard,
|i| {
i.1 = Some(map(i.0.take().unwrap()));
},
proof,
);
items.into_iter().filter_map(|i| i.1).collect() items.into_iter().filter_map(|i| i.1).collect()
} else { } else {
@@ -214,8 +225,8 @@ pub fn par_map<I: DynSend, T: IntoIterator<Item = I>, R: DynSend, C: FromIterato
} }
pub fn broadcast<R: DynSend>(op: impl Fn(usize) -> R + DynSync) -> Vec<R> { pub fn broadcast<R: DynSend>(op: impl Fn(usize) -> R + DynSync) -> Vec<R> {
if mode::is_dyn_thread_safe() { if let Some(proof) = mode::check_dyn_thread_safe() {
let op = FromDyn::from(op); let op = proof.derive(op);
let results = rustc_thread_pool::broadcast(|context| op.derive(op(context.index()))); let results = rustc_thread_pool::broadcast(|context| op.derive(op(context.index())));
results.into_iter().map(|r| r.into_inner()).collect() results.into_iter().map(|r| r.into_inner()).collect()
} else { } else {
+1 -1
View File
@@ -192,7 +192,7 @@ pub enum DefKind {
/// These are all represented with the same `ExprKind::Closure` in the AST and HIR, /// These are all represented with the same `ExprKind::Closure` in the AST and HIR,
/// which makes it difficult to distinguish these during def collection. Therefore, /// which makes it difficult to distinguish these during def collection. Therefore,
/// we treat them all the same, and code which needs to distinguish them can match /// we treat them all the same, and code which needs to distinguish them can match
/// or `hir::ClosureKind` or `type_of`. /// on `hir::ClosureKind` or `type_of`.
Closure, Closure,
/// The definition of a synthetic coroutine body created by the lowering of a /// The definition of a synthetic coroutine body created by the lowering of a
/// coroutine-closure, such as an async closure. /// coroutine-closure, such as an async closure.
+1 -5
View File
@@ -1,8 +1,4 @@
//! This module implements some validity checks for attributes. //! This module lists attribute targets, with conversions from other types.
//! In particular it verifies that `#[inline]` and `#[repr]` attributes are
//! attached to items that actually support them and if there are
//! conflicts between multiple such attributes attached to the same
//! item.
use std::fmt::{self, Display}; use std::fmt::{self, Display};
+4 -5
View File
@@ -183,7 +183,6 @@ pub(crate) fn run_in_thread_pool_with_globals<
use std::process; use std::process;
use rustc_data_structures::defer; use rustc_data_structures::defer;
use rustc_data_structures::sync::FromDyn;
use rustc_middle::ty::tls; use rustc_middle::ty::tls;
use rustc_query_impl::break_query_cycles; use rustc_query_impl::break_query_cycles;
@@ -191,7 +190,7 @@ pub(crate) fn run_in_thread_pool_with_globals<
let registry = sync::Registry::new(std::num::NonZero::new(threads).unwrap()); let registry = sync::Registry::new(std::num::NonZero::new(threads).unwrap());
if !sync::is_dyn_thread_safe() { let Some(proof) = sync::check_dyn_thread_safe() else {
return run_in_thread_with_globals( return run_in_thread_with_globals(
thread_stack_size, thread_stack_size,
edition, edition,
@@ -204,9 +203,9 @@ pub(crate) fn run_in_thread_pool_with_globals<
f(current_gcx, jobserver_proxy) f(current_gcx, jobserver_proxy)
}, },
); );
} };
let current_gcx = FromDyn::from(CurrentGcx::new()); let current_gcx = proof.derive(CurrentGcx::new());
let current_gcx2 = current_gcx.clone(); let current_gcx2 = current_gcx.clone();
let proxy = Proxy::new(); let proxy = Proxy::new();
@@ -278,7 +277,7 @@ pub(crate) fn run_in_thread_pool_with_globals<
// `Send` in the parallel compiler. // `Send` in the parallel compiler.
rustc_span::create_session_globals_then(edition, extra_symbols, Some(sm_inputs), || { rustc_span::create_session_globals_then(edition, extra_symbols, Some(sm_inputs), || {
rustc_span::with_session_globals(|session_globals| { rustc_span::with_session_globals(|session_globals| {
let session_globals = FromDyn::from(session_globals); let session_globals = proof.derive(session_globals);
builder builder
.build_scoped( .build_scoped(
// Initialize each new worker thread when created. // Initialize each new worker thread when created.
+2 -46
View File
@@ -23,7 +23,7 @@
use rustc_hir::attrs::diagnostic::Directive; use rustc_hir::attrs::diagnostic::Directive;
use rustc_hir::attrs::{ use rustc_hir::attrs::{
AttributeKind, DocAttribute, DocInline, EiiDecl, EiiImpl, EiiImplResolution, InlineAttr, AttributeKind, DocAttribute, DocInline, EiiDecl, EiiImpl, EiiImplResolution, InlineAttr,
MirDialect, MirPhase, ReprAttr, SanitizerSet, ReprAttr, SanitizerSet,
}; };
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalModDefId; use rustc_hir::def_id::LocalModDefId;
@@ -212,9 +212,6 @@ fn check_attributes(
Attribute::Parsed(AttributeKind::MayDangle(attr_span)) => { Attribute::Parsed(AttributeKind::MayDangle(attr_span)) => {
self.check_may_dangle(hir_id, *attr_span) self.check_may_dangle(hir_id, *attr_span)
} }
&Attribute::Parsed(AttributeKind::CustomMir(dialect, phase, attr_span)) => {
self.check_custom_mir(dialect, phase, attr_span)
}
&Attribute::Parsed(AttributeKind::Sanitize { on_set, off_set, rtsan: _, span: attr_span}) => { &Attribute::Parsed(AttributeKind::Sanitize { on_set, off_set, rtsan: _, span: attr_span}) => {
self.check_sanitize(attr_span, on_set | off_set, span, target); self.check_sanitize(attr_span, on_set | off_set, span, target);
}, },
@@ -254,6 +251,7 @@ fn check_attributes(
| AttributeKind::Coverage (..) | AttributeKind::Coverage (..)
| AttributeKind::CrateName { .. } | AttributeKind::CrateName { .. }
| AttributeKind::CrateType(..) | AttributeKind::CrateType(..)
| AttributeKind::CustomMir(..)
| AttributeKind::DebuggerVisualizer(..) | AttributeKind::DebuggerVisualizer(..)
| AttributeKind::DefaultLibAllocator | AttributeKind::DefaultLibAllocator
// `#[doc]` is actually a lot more than just doc comments, so is checked below // `#[doc]` is actually a lot more than just doc comments, so is checked below
@@ -1913,48 +1911,6 @@ fn check_const_continue(&self, hir_id: HirId, attr_span: Span, target: Target) {
self.dcx().emit_err(errors::ConstContinueAttr { attr_span, node_span }); self.dcx().emit_err(errors::ConstContinueAttr { attr_span, node_span });
}; };
} }
fn check_custom_mir(
&self,
dialect: Option<(MirDialect, Span)>,
phase: Option<(MirPhase, Span)>,
attr_span: Span,
) {
let Some((dialect, dialect_span)) = dialect else {
if let Some((_, phase_span)) = phase {
self.dcx()
.emit_err(errors::CustomMirPhaseRequiresDialect { attr_span, phase_span });
}
return;
};
match dialect {
MirDialect::Analysis => {
if let Some((MirPhase::Optimized, phase_span)) = phase {
self.dcx().emit_err(errors::CustomMirIncompatibleDialectAndPhase {
dialect,
phase: MirPhase::Optimized,
attr_span,
dialect_span,
phase_span,
});
}
}
MirDialect::Built => {
if let Some((phase, phase_span)) = phase {
self.dcx().emit_err(errors::CustomMirIncompatibleDialectAndPhase {
dialect,
phase,
attr_span,
dialect_span,
phase_span,
});
}
}
MirDialect::Runtime => {}
}
}
} }
impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
-23
View File
@@ -7,7 +7,6 @@
MultiSpan, msg, MultiSpan, msg,
}; };
use rustc_hir::Target; use rustc_hir::Target;
use rustc_hir::attrs::{MirDialect, MirPhase};
use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_middle::ty::{MainDefinition, Ty}; use rustc_middle::ty::{MainDefinition, Ty};
use rustc_span::{DUMMY_SP, Ident, Span, Symbol}; use rustc_span::{DUMMY_SP, Ident, Span, Symbol};
@@ -1274,28 +1273,6 @@ pub(crate) struct ReprAlignShouldBeAlignStatic {
pub item: &'static str, pub item: &'static str,
} }
#[derive(Diagnostic)]
#[diag("`dialect` key required")]
pub(crate) struct CustomMirPhaseRequiresDialect {
#[primary_span]
pub attr_span: Span,
#[label("`phase` argument requires a `dialect` argument")]
pub phase_span: Span,
}
#[derive(Diagnostic)]
#[diag("the {$dialect} dialect is not compatible with the {$phase} phase")]
pub(crate) struct CustomMirIncompatibleDialectAndPhase {
pub dialect: MirDialect,
pub phase: MirPhase,
#[primary_span]
pub attr_span: Span,
#[label("this dialect...")]
pub dialect_span: Span,
#[label("... is not compatible with this phase")]
pub phase_span: Span,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag("`eii_macro_for` is only valid on functions")] #[diag("`eii_macro_for` is only valid on functions")]
pub(crate) struct EiiImplNotFunction { pub(crate) struct EiiImplNotFunction {
@@ -20,7 +20,8 @@
AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, CoroutineDef, Discr, FieldDef, FnDef, AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, CoroutineDef, Discr, FieldDef, FnDef,
ForeignDef, ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates, ForeignDef, ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates,
Generics, ImplDef, ImplTrait, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span, Generics, ImplDef, ImplTrait, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span,
TraitDecl, TraitDef, Ty, TyConst, TyConstId, TyKind, UintTy, VariantDef, VariantIdx, TraitDecl, TraitDef, TraitRef, Ty, TyConst, TyConstId, TyKind, UintTy, VariantDef, VariantIdx,
VtblEntry,
}; };
use crate::unstable::{RustcInternal, Stable, new_item_kind}; use crate::unstable::{RustcInternal, Stable, new_item_kind};
use crate::{ use crate::{
@@ -838,6 +839,25 @@ pub(crate) fn associated_items(&self, def_id: DefId) -> AssocItems {
let did = tables[def_id]; let did = tables[def_id];
cx.associated_items(did).iter().map(|assoc| assoc.stable(&mut *tables, cx)).collect() cx.associated_items(did).iter().map(|assoc| assoc.stable(&mut *tables, cx)).collect()
} }
/// Get all vtable entries of a trait.
pub(crate) fn vtable_entries(&self, trait_ref: &TraitRef) -> Vec<VtblEntry> {
let mut tables = self.tables.borrow_mut();
let cx = &*self.cx.borrow();
cx.vtable_entries(trait_ref.internal(&mut *tables, cx.tcx))
.iter()
.map(|v| v.stable(&mut *tables, cx))
.collect()
}
/// Returns the vtable entry at the given index.
///
/// Returns `None` if the index is out of bounds.
pub(crate) fn vtable_entry(&self, trait_ref: &TraitRef, idx: usize) -> Option<VtblEntry> {
let mut tables = self.tables.borrow_mut();
let cx = &*self.cx.borrow();
cx.vtable_entry(trait_ref.internal(&mut *tables, cx.tcx), idx).stable(&mut *tables, cx)
}
} }
// A thread local variable that stores a pointer to [`CompilerInterface`]. // A thread local variable that stores a pointer to [`CompilerInterface`].
+29 -1
View File
@@ -9,7 +9,7 @@
use crate::abi::{FnAbi, Layout}; use crate::abi::{FnAbi, Layout};
use crate::crate_def::{CrateDef, CrateDefType}; use crate::crate_def::{CrateDef, CrateDefType};
use crate::mir::alloc::{AllocId, read_target_int, read_target_uint}; use crate::mir::alloc::{AllocId, read_target_int, read_target_uint};
use crate::mir::mono::StaticDef; use crate::mir::mono::{Instance, StaticDef};
use crate::target::MachineInfo; use crate::target::MachineInfo;
use crate::{AssocItems, Filename, IndexedVal, Opaque, ThreadLocalIndex}; use crate::{AssocItems, Filename, IndexedVal, Opaque, ThreadLocalIndex};
@@ -1440,6 +1440,18 @@ pub fn self_ty(&self) -> Ty {
}; };
self_ty self_ty
} }
/// Retrieve all vtable entries.
pub fn vtable_entries(&self) -> Vec<VtblEntry> {
with(|cx| cx.vtable_entries(self))
}
/// Returns the vtable entry at the given index.
///
/// Returns `None` if the index is out of bounds.
pub fn vtable_entry(&self, idx: usize) -> Option<VtblEntry> {
with(|cx| cx.vtable_entry(self, idx))
}
} }
#[derive(Clone, Debug, Eq, PartialEq, Serialize)] #[derive(Clone, Debug, Eq, PartialEq, Serialize)]
@@ -1656,3 +1668,19 @@ pub fn is_impl_trait_in_trait(&self) -> bool {
matches!(self.kind, AssocKind::Type { data: AssocTypeData::Rpitit(_) }) matches!(self.kind, AssocKind::Type { data: AssocTypeData::Rpitit(_) })
} }
} }
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub enum VtblEntry {
/// destructor of this type (used in vtable header)
MetadataDropInPlace,
/// layout size of this type (used in vtable header)
MetadataSize,
/// layout align of this type (used in vtable header)
MetadataAlign,
/// non-dispatchable associated function that is excluded from trait object
Vacant,
/// dispatchable associated function
Method(Instance),
/// pointer to a separate supertrait vtable, can be used by trait upcasting coercion
TraitVPtr(TraitRef),
}
@@ -1139,3 +1139,25 @@ fn stable<'cx>(
crate::ty::Discr { val: self.val, ty: self.ty.stable(tables, cx) } crate::ty::Discr { val: self.val, ty: self.ty.stable(tables, cx) }
} }
} }
impl<'tcx> Stable<'tcx> for rustc_middle::ty::VtblEntry<'tcx> {
type T = crate::ty::VtblEntry;
fn stable<'cx>(
&self,
tables: &mut Tables<'cx, BridgeTys>,
cx: &CompilerCtxt<'cx, BridgeTys>,
) -> Self::T {
use crate::ty::VtblEntry;
match self {
ty::VtblEntry::MetadataDropInPlace => VtblEntry::MetadataDropInPlace,
ty::VtblEntry::MetadataSize => VtblEntry::MetadataSize,
ty::VtblEntry::MetadataAlign => VtblEntry::MetadataAlign,
ty::VtblEntry::Vacant => VtblEntry::Vacant,
ty::VtblEntry::Method(instance) => VtblEntry::Method(instance.stable(tables, cx)),
ty::VtblEntry::TraitVPtr(trait_ref) => {
VtblEntry::TraitVPtr(trait_ref.stable(tables, cx))
}
}
}
}
@@ -18,7 +18,7 @@
AdtDef, AdtKind, AssocItem, Binder, ClosureKind, CoroutineArgsExt, EarlyBinder, AdtDef, AdtKind, AssocItem, Binder, ClosureKind, CoroutineArgsExt, EarlyBinder,
ExistentialTraitRef, FnSig, GenericArgsRef, Instance, InstanceKind, IntrinsicDef, List, ExistentialTraitRef, FnSig, GenericArgsRef, Instance, InstanceKind, IntrinsicDef, List,
PolyFnSig, ScalarInt, TraitDef, TraitRef, Ty, TyCtxt, TyKind, TypeVisitableExt, UintTy, PolyFnSig, ScalarInt, TraitDef, TraitRef, Ty, TyCtxt, TyKind, TypeVisitableExt, UintTy,
ValTree, VariantDef, ValTree, VariantDef, VtblEntry,
}; };
use rustc_middle::{mir, ty}; use rustc_middle::{mir, ty};
use rustc_session::cstore::ForeignModule; use rustc_session::cstore::ForeignModule;
@@ -757,4 +757,16 @@ pub fn associated_items(&self, def_id: DefId) -> Vec<AssocItem> {
}; };
assoc_items assoc_items
} }
/// Get all vtable entries of a trait.
pub fn vtable_entries(&self, trait_ref: TraitRef<'tcx>) -> Vec<VtblEntry<'tcx>> {
self.tcx.vtable_entries(trait_ref).to_vec()
}
/// Returns the vtable entry at the given index.
///
/// Returns `None` if the index is out of bounds.
pub fn vtable_entry(&self, trait_ref: TraitRef<'tcx>, idx: usize) -> Option<VtblEntry<'tcx>> {
self.vtable_entries(trait_ref).get(idx).copied()
}
} }
-1
View File
@@ -182,7 +182,6 @@ fn $test() $block
mod const_ptr; mod const_ptr;
mod convert; mod convert;
mod ffi; mod ffi;
mod floats;
mod fmt; mod fmt;
mod future; mod future;
mod hash; mod hash;
@@ -0,0 +1,166 @@
//! IEEE 754 floating point compliance tests
//!
//! To understand IEEE 754's requirements on a programming language, one must understand that the
//! requirements of IEEE 754 rest on the total programming environment, and not entirely on any
//! one component. That means the hardware, language, and even libraries are considered part of
//! conforming floating point support in a programming environment.
//!
//! A programming language's duty, accordingly, is:
//! 1. offer access to the hardware where the hardware offers support
//! 2. provide operations that fulfill the remaining requirements of the standard
//! 3. provide the ability to write additional software that can fulfill those requirements
//!
//! This may be fulfilled in any combination that the language sees fit. However, to claim that
//! a language supports IEEE 754 is to suggest that it has fulfilled requirements 1 and 2, without
//! deferring minimum requirements to libraries. This is because support for IEEE 754 is defined
//! as complete support for at least one specified floating point type as an "arithmetic" and
//! "interchange" format, plus specified type conversions to "external character sequences" and
//! integer types.
//!
//! For our purposes,
//! "interchange format" => f16, f32, f64, f128
//! "arithmetic format" => f16, f32, f64, f128, and any "soft floats"
//! "external character sequence" => str from any float
//! "integer format" => {i,u}{8,16,32,64,128}
//!
//! None of these tests are against Rust's own implementation. They are only tests against the
//! standard. That is why they accept wildly diverse inputs or may seem to duplicate other tests.
//! Please consider this carefully when adding, removing, or reorganizing these tests. They are
//! here so that it is clear what tests are required by the standard and what can be changed.
use core::fmt;
use core::str::FromStr;
use crate::num::{assert_biteq, float_test};
/// ToString uses the default fmt::Display impl without special concerns, and bypasses other parts
/// of the formatting infrastructure, which makes it ideal for testing here.
#[track_caller]
fn string_roundtrip<T>(x: T) -> T
where
T: FromStr<Err: fmt::Debug> + fmt::Display,
{
x.to_string().parse::<T>().unwrap()
}
// FIXME(f128): Tests are disabled while we don't have parsing / printing
// We must preserve signs on all numbers. That includes zero.
// -0 and 0 are == normally, so test bit equality.
float_test! {
name: preserve_signed_zero,
attrs: {
const: #[cfg(false)],
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(false)],
},
test {
let neg0 = flt(-0.0);
let pos0 = flt(0.0);
assert_biteq!(neg0, string_roundtrip(neg0));
assert_biteq!(pos0, string_roundtrip(pos0));
assert_ne!(neg0.to_bits(), pos0.to_bits());
}
}
float_test! {
name: preserve_signed_infinity,
attrs: {
const: #[cfg(false)],
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(false)],
},
test {
let neg_inf = Float::NEG_INFINITY;
let pos_inf = Float::INFINITY;
assert_biteq!(neg_inf, string_roundtrip(neg_inf));
assert_biteq!(pos_inf, string_roundtrip(pos_inf));
assert_ne!(neg_inf.to_bits(), pos_inf.to_bits());
}
}
float_test! {
name: infinity_to_str,
attrs: {
const: #[cfg(false)],
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(false)],
},
test {
assert!(
match Float::INFINITY.to_string().to_lowercase().as_str() {
"+infinity" | "infinity" => true,
"+inf" | "inf" => true,
_ => false,
},
"Infinity must write to a string as some casing of inf or infinity, with an optional +."
);
assert!(
match Float::NEG_INFINITY.to_string().to_lowercase().as_str() {
"-infinity" | "-inf" => true,
_ => false,
},
"Negative Infinity must write to a string as some casing of -inf or -infinity"
);
}
}
float_test! {
name: nan_to_str,
attrs: {
const: #[cfg(false)],
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(false)],
},
test {
assert!(
match Float::NAN.to_string().to_lowercase().as_str() {
"nan" | "+nan" | "-nan" => true,
_ => false,
},
"NaNs must write to a string as some casing of nan."
)
}
}
float_test! {
name: infinity_from_str,
attrs: {
const: #[cfg(false)],
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(false)],
},
test {
// "+"?("inf"|"infinity") in any case => Infinity
assert_biteq!(Float::INFINITY, Float::from_str("infinity").unwrap());
assert_biteq!(Float::INFINITY, Float::from_str("inf").unwrap());
assert_biteq!(Float::INFINITY, Float::from_str("+infinity").unwrap());
assert_biteq!(Float::INFINITY, Float::from_str("+inf").unwrap());
// yes! this means you are weLcOmE tO mY iNfInItElY tWiStEd MiNd
assert_biteq!(Float::INFINITY, Float::from_str("+iNfInItY").unwrap());
// "-inf"|"-infinity" in any case => Negative Infinity
assert_biteq!(Float::NEG_INFINITY, Float::from_str("-infinity").unwrap());
assert_biteq!(Float::NEG_INFINITY, Float::from_str("-inf").unwrap());
assert_biteq!(Float::NEG_INFINITY, Float::from_str("-INF").unwrap());
assert_biteq!(Float::NEG_INFINITY, Float::from_str("-INFinity").unwrap());
}
}
float_test! {
name: qnan_from_str,
attrs: {
const: #[cfg(false)],
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(false)],
},
test {
// ("+"|"-"")?"s"?"nan" in any case => qNaN
assert!("nan".parse::<Float>().unwrap().is_nan());
assert!("-nan".parse::<Float>().unwrap().is_nan());
assert!("+nan".parse::<Float>().unwrap().is_nan());
assert!("+NAN".parse::<Float>().unwrap().is_nan());
assert!("-NaN".parse::<Float>().unwrap().is_nan());
}
}
@@ -2,7 +2,7 @@
use std::num::FpCategory as Fp; use std::num::FpCategory as Fp;
use std::ops::{Add, Div, Mul, Rem, Sub}; use std::ops::{Add, Div, Mul, Rem, Sub};
trait TestableFloat: Sized { pub(crate) trait TestableFloat: Sized {
const BITS: u32; const BITS: u32;
/// Unsigned int with the same size, for converting to/from bits. /// Unsigned int with the same size, for converting to/from bits.
type Int; type Int;
@@ -224,7 +224,7 @@ impl TestableFloat for f128 {
} }
/// Determine the tolerance for values of the argument type. /// Determine the tolerance for values of the argument type.
const fn lim_for_ty<T: TestableFloat + Copy>(_x: T) -> T { pub(crate) const fn lim_for_ty<T: TestableFloat + Copy>(_x: T) -> T {
T::APPROX T::APPROX
} }
@@ -232,7 +232,9 @@ const fn lim_for_ty<T: TestableFloat + Copy>(_x: T) -> T {
/// Verify that floats are within a tolerance of each other. /// Verify that floats are within a tolerance of each other.
macro_rules! assert_approx_eq { macro_rules! assert_approx_eq {
($a:expr, $b:expr $(,)?) => {{ assert_approx_eq!($a, $b, $crate::floats::lim_for_ty($a)) }}; ($a:expr, $b:expr $(,)?) => {{
assert_approx_eq!($a, $b, $crate::num::floats::lim_for_ty($a))
}};
($a:expr, $b:expr, $lim:expr) => {{ ($a:expr, $b:expr, $lim:expr) => {{
let (a, b) = (&$a, &$b); let (a, b) = (&$a, &$b);
let diff = (*a - *b).abs(); let diff = (*a - *b).abs();
@@ -270,8 +272,8 @@ macro_rules! assert_biteq {
// We rely on the `Float` type being brought in scope by the macros below. // We rely on the `Float` type being brought in scope by the macros below.
l: Float = l, l: Float = l,
r: Float = r, r: Float = r,
lb: <Float as TestableFloat>::Int = l.to_bits(), lb: <Float as $crate::num::floats::TestableFloat>::Int = l.to_bits(),
rb: <Float as TestableFloat>::Int = r.to_bits(), rb: <Float as $crate::num::floats::TestableFloat>::Int = r.to_bits(),
width: usize = ((bits / 4) + 2) as usize, width: usize = ((bits / 4) + 2) as usize,
); );
}}; }};
@@ -312,6 +314,7 @@ macro_rules! float_test {
test $test:block test $test:block
) => { ) => {
mod $name { mod $name {
#[allow(unused_imports)]
use super::*; use super::*;
#[test] #[test]
@@ -360,6 +363,7 @@ const fn flt (x: Float) -> Float { x }
$( $( #[$const_meta] )+ )? $( $( #[$const_meta] )+ )?
mod const_ { mod const_ {
#[allow(unused_imports)]
use super::*; use super::*;
#[test] #[test]
@@ -410,6 +414,9 @@ const fn flt (x: Float) -> Float { x }
}; };
} }
pub(crate) use assert_biteq;
pub(crate) use float_test;
float_test! { float_test! {
name: num, name: num,
attrs: { attrs: {
-158
View File
@@ -1,158 +0,0 @@
//! IEEE 754 floating point compliance tests
//!
//! To understand IEEE 754's requirements on a programming language, one must understand that the
//! requirements of IEEE 754 rest on the total programming environment, and not entirely on any
//! one component. That means the hardware, language, and even libraries are considered part of
//! conforming floating point support in a programming environment.
//!
//! A programming language's duty, accordingly, is:
//! 1. offer access to the hardware where the hardware offers support
//! 2. provide operations that fulfill the remaining requirements of the standard
//! 3. provide the ability to write additional software that can fulfill those requirements
//!
//! This may be fulfilled in any combination that the language sees fit. However, to claim that
//! a language supports IEEE 754 is to suggest that it has fulfilled requirements 1 and 2, without
//! deferring minimum requirements to libraries. This is because support for IEEE 754 is defined
//! as complete support for at least one specified floating point type as an "arithmetic" and
//! "interchange" format, plus specified type conversions to "external character sequences" and
//! integer types.
//!
//! For our purposes,
//! "interchange format" => f32, f64
//! "arithmetic format" => f32, f64, and any "soft floats"
//! "external character sequence" => str from any float
//! "integer format" => {i,u}{8,16,32,64,128}
//!
//! None of these tests are against Rust's own implementation. They are only tests against the
//! standard. That is why they accept wildly diverse inputs or may seem to duplicate other tests.
//! Please consider this carefully when adding, removing, or reorganizing these tests. They are
//! here so that it is clear what tests are required by the standard and what can be changed.
use ::core::str::FromStr;
// IEEE 754 for many tests is applied to specific bit patterns.
// These generally are not applicable to NaN, however.
macro_rules! assert_biteq {
($lhs:expr, $rhs:expr) => {
assert_eq!($lhs.to_bits(), $rhs.to_bits())
};
}
// ToString uses the default fmt::Display impl without special concerns, and bypasses other parts
// of the formatting infrastructure, which makes it ideal for testing here.
macro_rules! roundtrip {
($f:expr => $t:ty) => {
($f).to_string().parse::<$t>().unwrap()
};
}
macro_rules! assert_floats_roundtrip {
($f:ident) => {
assert_biteq!(f32::$f, roundtrip!(f32::$f => f32));
assert_biteq!(f64::$f, roundtrip!(f64::$f => f64));
};
($f:expr) => {
assert_biteq!($f as f32, roundtrip!($f => f32));
assert_biteq!($f as f64, roundtrip!($f => f64));
}
}
macro_rules! assert_floats_bitne {
($lhs:ident, $rhs:ident) => {
assert_ne!(f32::$lhs.to_bits(), f32::$rhs.to_bits());
assert_ne!(f64::$lhs.to_bits(), f64::$rhs.to_bits());
};
($lhs:expr, $rhs:expr) => {
assert_ne!(f32::to_bits($lhs), f32::to_bits($rhs));
assert_ne!(f64::to_bits($lhs), f64::to_bits($rhs));
};
}
// We must preserve signs on all numbers. That includes zero.
// -0 and 0 are == normally, so test bit equality.
#[test]
fn preserve_signed_zero() {
assert_floats_roundtrip!(-0.0);
assert_floats_roundtrip!(0.0);
assert_floats_bitne!(0.0, -0.0);
}
#[test]
fn preserve_signed_infinity() {
assert_floats_roundtrip!(INFINITY);
assert_floats_roundtrip!(NEG_INFINITY);
assert_floats_bitne!(INFINITY, NEG_INFINITY);
}
#[test]
fn infinity_to_str() {
assert!(match f32::INFINITY.to_string().to_lowercase().as_str() {
"+infinity" | "infinity" => true,
"+inf" | "inf" => true,
_ => false,
});
assert!(
match f64::INFINITY.to_string().to_lowercase().as_str() {
"+infinity" | "infinity" => true,
"+inf" | "inf" => true,
_ => false,
},
"Infinity must write to a string as some casing of inf or infinity, with an optional +."
);
}
#[test]
fn neg_infinity_to_str() {
assert!(match f32::NEG_INFINITY.to_string().to_lowercase().as_str() {
"-infinity" | "-inf" => true,
_ => false,
});
assert!(
match f64::NEG_INFINITY.to_string().to_lowercase().as_str() {
"-infinity" | "-inf" => true,
_ => false,
},
"Negative Infinity must write to a string as some casing of -inf or -infinity"
)
}
#[test]
fn nan_to_str() {
assert!(
match f32::NAN.to_string().to_lowercase().as_str() {
"nan" | "+nan" | "-nan" => true,
_ => false,
},
"NaNs must write to a string as some casing of nan."
)
}
// "+"?("inf"|"infinity") in any case => Infinity
#[test]
fn infinity_from_str() {
assert_biteq!(f32::INFINITY, f32::from_str("infinity").unwrap());
assert_biteq!(f32::INFINITY, f32::from_str("inf").unwrap());
assert_biteq!(f32::INFINITY, f32::from_str("+infinity").unwrap());
assert_biteq!(f32::INFINITY, f32::from_str("+inf").unwrap());
// yes! this means you are weLcOmE tO mY iNfInItElY tWiStEd MiNd
assert_biteq!(f32::INFINITY, f32::from_str("+iNfInItY").unwrap());
}
// "-inf"|"-infinity" in any case => Negative Infinity
#[test]
fn neg_infinity_from_str() {
assert_biteq!(f32::NEG_INFINITY, f32::from_str("-infinity").unwrap());
assert_biteq!(f32::NEG_INFINITY, f32::from_str("-inf").unwrap());
assert_biteq!(f32::NEG_INFINITY, f32::from_str("-INF").unwrap());
assert_biteq!(f32::NEG_INFINITY, f32::from_str("-INFinity").unwrap());
}
// ("+"|"-"")?"s"?"nan" in any case => qNaN
#[test]
fn qnan_from_str() {
assert!("nan".parse::<f32>().unwrap().is_nan());
assert!("-nan".parse::<f32>().unwrap().is_nan());
assert!("+nan".parse::<f32>().unwrap().is_nan());
assert!("+NAN".parse::<f32>().unwrap().is_nan());
assert!("-NaN".parse::<f32>().unwrap().is_nan());
}
+4 -1
View File
@@ -25,9 +25,10 @@
mod carryless_mul; mod carryless_mul;
mod const_from; mod const_from;
mod dec2flt; mod dec2flt;
mod float_ieee754_flt2dec_dec2flt;
mod float_iter_sum_identity; mod float_iter_sum_identity;
mod floats;
mod flt2dec; mod flt2dec;
mod ieee754;
mod int_log; mod int_log;
mod int_sqrt; mod int_sqrt;
mod midpoint; mod midpoint;
@@ -36,6 +37,8 @@
mod ops; mod ops;
mod wrapping; mod wrapping;
use floats::{assert_biteq, float_test};
/// Adds the attribute to all items in the block. /// Adds the attribute to all items in the block.
macro_rules! cfg_block { macro_rules! cfg_block {
($(#[$attr:meta]{$($it:item)*})*) => {$($( ($(#[$attr:meta]{$($it:item)*})*) => {$($(
+2 -2
View File
@@ -2121,7 +2121,7 @@ fn output_base_name(&self) -> Utf8PathBuf {
} }
/// Prints a message to (captured) stdout if `config.verbose` is true. /// Prints a message to (captured) stdout if `config.verbose` is true.
/// The message is also logged to `tracing::debug!` regardles of verbosity. /// The message is also logged to `tracing::debug!` regardless of verbosity.
/// ///
/// Use `format_args!` as the argument to perform formatting if required. /// Use `format_args!` as the argument to perform formatting if required.
fn logv(&self, message: impl fmt::Display) { fn logv(&self, message: impl fmt::Display) {
@@ -2748,7 +2748,7 @@ fn compare_output(
return CompareOutcome::Same; return CompareOutcome::Same;
} }
if expected_lines.is_empty() { if expected_lines.is_empty() {
// if we have no lines to check, force a full overwite // if we have no lines to check, force a full overwrite
("", actual) ("", actual)
} else { } else {
// this prints/blesses the subset, not the actual // this prints/blesses the subset, not the actual
@@ -0,0 +1,149 @@
//@ run-pass
// Test that users are able to use rustc_public to retrieve vtable info.
//@ ignore-stage1
//@ ignore-cross-compile
//@ ignore-remote
#![feature(rustc_private)]
extern crate rustc_middle;
extern crate rustc_driver;
extern crate rustc_interface;
#[macro_use]
extern crate rustc_public;
use rustc_public::ty::VtblEntry;
use rustc_public::CrateDef;
use std::io::Write;
use std::ops::ControlFlow;
const CRATE_NAME: &str = "vtable_test";
/// This function uses the rustc_public APIs to test the `vtable_entries()`.
fn test_vtable_entries() -> ControlFlow<()> {
let local_crate = rustc_public::local_crate();
let local_impls = local_crate.trait_impls();
let child_impl = local_impls
.iter()
.find(|i| i.trimmed_name() == "<Concrete as Child>")
.expect("Could not find <Concrete as Child>");
let child_trait_ref = child_impl.trait_impl().value;
let entries = child_trait_ref.vtable_entries();
match &entries[..] {
[
VtblEntry::MetadataDropInPlace,
VtblEntry::MetadataSize,
VtblEntry::MetadataAlign,
VtblEntry::Method(primary),
VtblEntry::Method(secondary),
VtblEntry::TraitVPtr(secondary_vptr),
VtblEntry::Method(child),
] => {
assert!(
primary.name().contains("primary"),
"Expected primary method at index 3"
);
assert!(
secondary.name().contains("secondary"),
"Expected secondary method at index 4"
);
let vptr_str = secondary_vptr.def_id.name();
assert!(
vptr_str.contains("Secondary"),
"Expected Secondary VPtr at index 5"
);
assert!(
child.name().contains("child"),
"Expected child method at index 6"
);
}
_ => panic!(
"Unexpected vtable layout for <Concrete as Child>. Found: {:#?}",
entries
),
}
let vacant_impl = local_impls
.iter()
.find(|i| i.trimmed_name() == "<Concrete as WithVacant>")
.expect("Could not find <Concrete as WithVacant>");
let vacant_trait_ref = vacant_impl.trait_impl().value;
let vacant_entries = vacant_trait_ref.vtable_entries();
match &vacant_entries[..] {
[
VtblEntry::MetadataDropInPlace,
VtblEntry::MetadataSize,
VtblEntry::MetadataAlign,
VtblEntry::Method(valid),
] => {
assert!(valid.name().contains("valid"), "Expected valid method");
}
_ => panic!(
"Unexpected vtable layout for <Concrete as WithVacant>. Found: {:#?}",
vacant_entries
),
}
ControlFlow::Continue(())
}
fn main() {
let path = "vtable_input.rs";
generate_input(&path).unwrap();
let args = &[
"rustc".to_string(),
"--crate-type=lib".to_string(),
"--crate-name".to_string(),
CRATE_NAME.to_string(),
path.to_string(),
];
run!(args, test_vtable_entries).unwrap();
}
fn generate_input(path: &str) -> std::io::Result<()> {
let mut file = std::fs::File::create(path)?;
write!(
file,
r#"
pub struct Concrete;
pub trait Primary {{
fn primary(&self);
}}
pub trait Secondary {{
fn secondary(&self);
}}
pub trait Child: Primary + Secondary {{
fn child(&self);
}}
impl Primary for Concrete {{
fn primary(&self) {{}}
}}
impl Secondary for Concrete {{
fn secondary(&self) {{}}
}}
impl Child for Concrete {{
fn child(&self) {{}}
}}
pub trait WithVacant {{
fn valid(&self);
fn excluded<T>(&self, meow: T) where Self: Sized;
}}
impl WithVacant for Concrete {{
fn valid(&self) {{}}
fn excluded<T>(&self, meow: T) {{}}
}}
fn main() {{}}
"#
)?;
Ok(())
}
+4 -7
View File
@@ -133,7 +133,7 @@ Runtime panics and error handling generate backtraces to assist in debugging and
This directory was originally meant to contain tests related to time complexity and benchmarking. This directory was originally meant to contain tests related to time complexity and benchmarking.
However, only a single test was ever added to this category: https://github.com/rust-lang/rust/pull/32062 However, only a single test was ever added to this category: <https://github.com/rust-lang/rust/pull/32062>
**FIXME**: It is also unclear what would happen were this test to "fail" - would it cause the test suite to remain stuck on this test for a much greater duration than normal? **FIXME**: It is also unclear what would happen were this test to "fail" - would it cause the test suite to remain stuck on this test for a much greater duration than normal?
@@ -694,9 +694,7 @@ This test category revolves around trait objects with `Sized` having illegal ope
Tests on lifetime elision in impl function signatures. See [Lifetime elision | Nomicon](https://doc.rust-lang.org/nomicon/lifetime-elision.html). Tests on lifetime elision in impl function signatures. See [Lifetime elision | Nomicon](https://doc.rust-lang.org/nomicon/lifetime-elision.html).
## `tests/ui/impl-restriction/` ## `tests/ui/impl-restriction/`
Tests for `#![feature(impl_restriction)]`. See [Tracking issue for restrictions #105077 Tests for `#![feature(impl_restriction)]`. See [Tracking issue for restrictions #105077](https://github.com/rust-lang/rust/issues/105077).
](https://github.com/rust-lang/rust/issues/105077).
## `tests/ui/impl-trait/` ## `tests/ui/impl-trait/`
@@ -796,8 +794,7 @@ See [Type Layout | Reference](https://doc.rust-lang.org/reference/type-layout.ht
## `tests/ui/lazy-type-alias/` ## `tests/ui/lazy-type-alias/`
Tests for `#![feature(lazy_type_alias)]`. See [Tracking issue for lazy type aliases #112792 Tests for `#![feature(lazy_type_alias)]`. See [Tracking issue for lazy type aliases #112792](https://github.com/rust-lang/rust/issues/112792).
](https://github.com/rust-lang/rust/issues/112792).
## `tests/ui/lazy-type-alias-impl-trait/` ## `tests/ui/lazy-type-alias-impl-trait/`
@@ -852,7 +849,7 @@ See:
Tests exercising analysis for unused variables, unreachable statements, functions which are supposed to return a value but do not, as well as values moved elsewhere before they could be used by a function. Tests exercising analysis for unused variables, unreachable statements, functions which are supposed to return a value but do not, as well as values moved elsewhere before they could be used by a function.
**FIXME**: This seems unrelated to "liveness" as defined in the rustc compiler guide. Is this misleadingly named? https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/lifetime_parameters.html#liveness-and-universal-regions **FIXME**: This seems unrelated to "liveness" as defined in the rustc compiler guide. Is this misleadingly named? <https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/lifetime_parameters.html#liveness-and-universal-regions>
## `tests/ui/loop-match` ## `tests/ui/loop-match`
+3
View File
@@ -20,6 +20,7 @@ extend-exclude = [
# right now. Entries should look like `mipsel = "mipsel"`. # right now. Entries should look like `mipsel = "mipsel"`.
# #
# tidy-alphabetical-start # tidy-alphabetical-start
anser = "anser" # an ANSI parsing package used by rust-analyzer
arange = "arange" # short for A-range arange = "arange" # short for A-range
childs = "childs" childs = "childs"
clonable = "clonable" clonable = "clonable"
@@ -29,10 +30,12 @@ makro = "makro" # deliberate misspelling to avoid `macro` keyword
misformed = "misformed" misformed = "misformed"
moreso = "moreso" moreso = "moreso"
numer = "numer" # short for numerator, not a typo for "number" numer = "numer" # short for numerator, not a typo for "number"
old-skool = "old-skool" # variant spelling of "old-school"
optin = "optin" # short for opt-in optin = "optin" # short for opt-in
publically = "publically" publically = "publically"
rplace = "rplace" # short for R-place rplace = "rplace" # short for R-place
splitted = "splitted" splitted = "splitted"
sythetic = "sythetic" # typo in vendored LLVM sources
taits = "taits" # lowercase for TAITs (type alias impl trait) taits = "taits" # lowercase for TAITs (type alias impl trait)
targetting = "targetting" targetting = "targetting"
unparseable = "unparseable" unparseable = "unparseable"