mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-26 13:01:27 +03:00
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:
@@ -169,7 +169,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
|
||||
return None;
|
||||
};
|
||||
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.
|
||||
// Use a custom error message instead.
|
||||
cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span });
|
||||
@@ -201,7 +201,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
|
||||
return None;
|
||||
};
|
||||
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.
|
||||
// Use a custom error message instead.
|
||||
cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span });
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
use crate::attributes::SingleAttributeParser;
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
use crate::session_diagnostics;
|
||||
use crate::target_checking::AllowedTargets;
|
||||
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 phase = parse_phase(cx, phase, &mut failed);
|
||||
check_custom_mir(cx, dialect, phase, &mut failed);
|
||||
|
||||
if failed {
|
||||
return None;
|
||||
@@ -138,3 +140,51 @@ fn parse_phase<S: Stage>(
|
||||
|
||||
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_hir::AttrPath;
|
||||
use rustc_hir::attrs::{MirDialect, MirPhase};
|
||||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||
use rustc_span::{Span, Symbol};
|
||||
use rustc_target::spec::TargetTuple;
|
||||
@@ -1023,3 +1024,25 @@ pub(crate) struct UnsupportedInstructionSet<'a> {
|
||||
pub instruction_set: Symbol,
|
||||
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
|
||||
|
||||
---
|
||||
coretests/tests/floats/mod.rs | 26 +++++++++++++-------------
|
||||
coretests/tests/num/floats.rs | 26 +++++++++++++-------------
|
||||
1 file changed, 13 insertions(+), 13 deletions(-)
|
||||
|
||||
diff --git a/coretests/tests/floats/mod.rs b/coretests/tests/floats/mod.rs
|
||||
index c61961f8584..d7b4fa20322 100644
|
||||
--- a/coretests/tests/floats/mod.rs
|
||||
+++ b/coretests/tests/floats/mod.rs
|
||||
--- a/coretests/tests/num/floats.rs
|
||||
+++ b/coretests/tests/num/floats.rs
|
||||
@@ -1534,7 +1534,7 @@ fn s_nan() -> Float {
|
||||
name: powf,
|
||||
attrs: {
|
||||
@@ -128,6 +128,5 @@ index c61961f8584..d7b4fa20322 100644
|
||||
f128: #[cfg(all(not(miri), target_has_reliable_f128_math))],
|
||||
},
|
||||
test {
|
||||
--
|
||||
--
|
||||
2.50.1
|
||||
|
||||
|
||||
@@ -437,7 +437,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
assert_eq!(
|
||||
None,
|
||||
num_untupled.replace(tupled_arg_tys.len()),
|
||||
"Replaced existing num_tupled"
|
||||
"Replaced existing num_untupled"
|
||||
);
|
||||
|
||||
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_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
|
||||
// an instance of `DynSend` and `DynSync`, since the compiler cannot infer
|
||||
// it automatically in some cases. (e.g. Box<dyn Send / Sync>)
|
||||
|
||||
@@ -34,7 +34,9 @@
|
||||
pub use self::freeze::{FreezeLock, FreezeReadGuard, FreezeWriteGuard};
|
||||
#[doc(no_inline)]
|
||||
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::{
|
||||
broadcast, par_fns, par_for_each_in, par_join, par_map, parallel_guard, spawn,
|
||||
try_par_for_each_in,
|
||||
@@ -64,12 +66,20 @@ mod atomic {
|
||||
mod mode {
|
||||
use std::sync::atomic::{AtomicU8, Ordering};
|
||||
|
||||
use crate::sync::{DynSend, DynSync};
|
||||
|
||||
const UNINITIALIZED: u8 = 0;
|
||||
const DYN_NOT_THREAD_SAFE: u8 = 1;
|
||||
const DYN_THREAD_SAFE: u8 = 2;
|
||||
|
||||
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).
|
||||
#[inline]
|
||||
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.
|
||||
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.
|
||||
|
||||
@@ -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) {
|
||||
if mode::is_dyn_thread_safe() {
|
||||
let func = FromDyn::from(func);
|
||||
if let Some(proof) = mode::check_dyn_thread_safe() {
|
||||
let func = proof.derive(func);
|
||||
rustc_thread_pool::spawn(|| {
|
||||
(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.
|
||||
pub fn par_fns(funcs: &mut [&mut (dyn FnMut() + DynSend)]) {
|
||||
parallel_guard(|guard: &ParallelGuard| {
|
||||
if mode::is_dyn_thread_safe() {
|
||||
let funcs = FromDyn::from(funcs);
|
||||
if let Some(proof) = mode::check_dyn_thread_safe() {
|
||||
let funcs = proof.derive(funcs);
|
||||
rustc_thread_pool::scope(|s| {
|
||||
let Some((first, rest)) = funcs.into_inner().split_at_mut_checked(1) else {
|
||||
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
|
||||
// that of a single threaded rustc.
|
||||
for f in rest.iter_mut().rev() {
|
||||
let f = FromDyn::from(f);
|
||||
let f = proof.derive(f);
|
||||
s.spawn(|_| {
|
||||
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,
|
||||
B: FnOnce() -> RB + DynSend,
|
||||
{
|
||||
if mode::is_dyn_thread_safe() {
|
||||
let oper_a = FromDyn::from(oper_a);
|
||||
let oper_b = FromDyn::from(oper_b);
|
||||
if let Some(proof) = mode::check_dyn_thread_safe() {
|
||||
let oper_a = proof.derive(oper_a);
|
||||
let oper_b = proof.derive(oper_b);
|
||||
let (a, b) = parallel_guard(|guard| {
|
||||
rustc_thread_pool::join(
|
||||
move || guard.run(move || FromDyn::from(oper_a.into_inner()())),
|
||||
move || guard.run(move || FromDyn::from(oper_b.into_inner()())),
|
||||
move || guard.run(move || proof.derive(oper_a.into_inner()())),
|
||||
move || guard.run(move || proof.derive(oper_b.into_inner()())),
|
||||
)
|
||||
});
|
||||
(a.unwrap().into_inner(), b.unwrap().into_inner())
|
||||
@@ -127,8 +127,9 @@ fn par_slice<I: DynSend>(
|
||||
items: &mut [I],
|
||||
guard: &ParallelGuard,
|
||||
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);
|
||||
rustc_thread_pool::scope(|s| {
|
||||
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,
|
||||
) {
|
||||
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();
|
||||
par_slice(&mut items, guard, |i| for_each(&*i))
|
||||
par_slice(&mut items, guard, |i| for_each(&*i), proof)
|
||||
} else {
|
||||
t.into_iter().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,
|
||||
{
|
||||
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 error = Mutex::new(None);
|
||||
|
||||
par_slice(&mut items, guard, |i| {
|
||||
if let Err(err) = for_each(&*i) {
|
||||
*error.lock() = Some(err);
|
||||
}
|
||||
});
|
||||
par_slice(
|
||||
&mut items,
|
||||
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(()) }
|
||||
} 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,
|
||||
) -> C {
|
||||
parallel_guard(|guard| {
|
||||
if mode::is_dyn_thread_safe() {
|
||||
let map = FromDyn::from(map);
|
||||
if let Some(proof) = mode::check_dyn_thread_safe() {
|
||||
let map = proof.derive(map);
|
||||
|
||||
let mut items: Vec<(Option<I>, Option<R>)> =
|
||||
t.into_iter().map(|i| (Some(i), None)).collect();
|
||||
|
||||
par_slice(&mut items, guard, |i| {
|
||||
i.1 = Some(map(i.0.take().unwrap()));
|
||||
});
|
||||
par_slice(
|
||||
&mut items,
|
||||
guard,
|
||||
|i| {
|
||||
i.1 = Some(map(i.0.take().unwrap()));
|
||||
},
|
||||
proof,
|
||||
);
|
||||
|
||||
items.into_iter().filter_map(|i| i.1).collect()
|
||||
} 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> {
|
||||
if mode::is_dyn_thread_safe() {
|
||||
let op = FromDyn::from(op);
|
||||
if let Some(proof) = mode::check_dyn_thread_safe() {
|
||||
let op = proof.derive(op);
|
||||
let results = rustc_thread_pool::broadcast(|context| op.derive(op(context.index())));
|
||||
results.into_iter().map(|r| r.into_inner()).collect()
|
||||
} else {
|
||||
|
||||
@@ -192,7 +192,7 @@ pub enum DefKind {
|
||||
/// 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,
|
||||
/// 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,
|
||||
/// The definition of a synthetic coroutine body created by the lowering of a
|
||||
/// coroutine-closure, such as an async closure.
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
//! This module implements some validity checks for attributes.
|
||||
//! 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.
|
||||
//! This module lists attribute targets, with conversions from other types.
|
||||
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
|
||||
@@ -183,7 +183,6 @@ pub(crate) fn run_in_thread_pool_with_globals<
|
||||
use std::process;
|
||||
|
||||
use rustc_data_structures::defer;
|
||||
use rustc_data_structures::sync::FromDyn;
|
||||
use rustc_middle::ty::tls;
|
||||
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());
|
||||
|
||||
if !sync::is_dyn_thread_safe() {
|
||||
let Some(proof) = sync::check_dyn_thread_safe() else {
|
||||
return run_in_thread_with_globals(
|
||||
thread_stack_size,
|
||||
edition,
|
||||
@@ -204,9 +203,9 @@ pub(crate) fn run_in_thread_pool_with_globals<
|
||||
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 proxy = Proxy::new();
|
||||
@@ -278,7 +277,7 @@ pub(crate) fn run_in_thread_pool_with_globals<
|
||||
// `Send` in the parallel compiler.
|
||||
rustc_span::create_session_globals_then(edition, extra_symbols, Some(sm_inputs), || {
|
||||
rustc_span::with_session_globals(|session_globals| {
|
||||
let session_globals = FromDyn::from(session_globals);
|
||||
let session_globals = proof.derive(session_globals);
|
||||
builder
|
||||
.build_scoped(
|
||||
// Initialize each new worker thread when created.
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
use rustc_hir::attrs::diagnostic::Directive;
|
||||
use rustc_hir::attrs::{
|
||||
AttributeKind, DocAttribute, DocInline, EiiDecl, EiiImpl, EiiImplResolution, InlineAttr,
|
||||
MirDialect, MirPhase, ReprAttr, SanitizerSet,
|
||||
ReprAttr, SanitizerSet,
|
||||
};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::LocalModDefId;
|
||||
@@ -212,9 +212,6 @@ fn check_attributes(
|
||||
Attribute::Parsed(AttributeKind::MayDangle(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}) => {
|
||||
self.check_sanitize(attr_span, on_set | off_set, span, target);
|
||||
},
|
||||
@@ -254,6 +251,7 @@ fn check_attributes(
|
||||
| AttributeKind::Coverage (..)
|
||||
| AttributeKind::CrateName { .. }
|
||||
| AttributeKind::CrateType(..)
|
||||
| AttributeKind::CustomMir(..)
|
||||
| AttributeKind::DebuggerVisualizer(..)
|
||||
| AttributeKind::DefaultLibAllocator
|
||||
// `#[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 });
|
||||
};
|
||||
}
|
||||
|
||||
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> {
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
MultiSpan, msg,
|
||||
};
|
||||
use rustc_hir::Target;
|
||||
use rustc_hir::attrs::{MirDialect, MirPhase};
|
||||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||
use rustc_middle::ty::{MainDefinition, Ty};
|
||||
use rustc_span::{DUMMY_SP, Ident, Span, Symbol};
|
||||
@@ -1274,28 +1273,6 @@ pub(crate) struct ReprAlignShouldBeAlignStatic {
|
||||
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)]
|
||||
#[diag("`eii_macro_for` is only valid on functions")]
|
||||
pub(crate) struct EiiImplNotFunction {
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, CoroutineDef, Discr, FieldDef, FnDef,
|
||||
ForeignDef, ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates,
|
||||
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::{
|
||||
@@ -838,6 +839,25 @@ pub(crate) fn associated_items(&self, def_id: DefId) -> AssocItems {
|
||||
let did = tables[def_id];
|
||||
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`].
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
use crate::abi::{FnAbi, Layout};
|
||||
use crate::crate_def::{CrateDef, CrateDefType};
|
||||
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::{AssocItems, Filename, IndexedVal, Opaque, ThreadLocalIndex};
|
||||
|
||||
@@ -1440,6 +1440,18 @@ pub fn 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)]
|
||||
@@ -1656,3 +1668,19 @@ pub fn is_impl_trait_in_trait(&self) -> bool {
|
||||
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) }
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
ExistentialTraitRef, FnSig, GenericArgsRef, Instance, InstanceKind, IntrinsicDef, List,
|
||||
PolyFnSig, ScalarInt, TraitDef, TraitRef, Ty, TyCtxt, TyKind, TypeVisitableExt, UintTy,
|
||||
ValTree, VariantDef,
|
||||
ValTree, VariantDef, VtblEntry,
|
||||
};
|
||||
use rustc_middle::{mir, ty};
|
||||
use rustc_session::cstore::ForeignModule;
|
||||
@@ -757,4 +757,16 @@ pub fn associated_items(&self, def_id: DefId) -> Vec<AssocItem> {
|
||||
};
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +182,6 @@ fn $test() $block
|
||||
mod const_ptr;
|
||||
mod convert;
|
||||
mod ffi;
|
||||
mod floats;
|
||||
mod fmt;
|
||||
mod future;
|
||||
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::ops::{Add, Div, Mul, Rem, Sub};
|
||||
|
||||
trait TestableFloat: Sized {
|
||||
pub(crate) trait TestableFloat: Sized {
|
||||
const BITS: u32;
|
||||
/// Unsigned int with the same size, for converting to/from bits.
|
||||
type Int;
|
||||
@@ -224,7 +224,7 @@ impl TestableFloat for f128 {
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
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) => {{
|
||||
let (a, b) = (&$a, &$b);
|
||||
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.
|
||||
l: Float = l,
|
||||
r: Float = r,
|
||||
lb: <Float as TestableFloat>::Int = l.to_bits(),
|
||||
rb: <Float as TestableFloat>::Int = r.to_bits(),
|
||||
lb: <Float as $crate::num::floats::TestableFloat>::Int = l.to_bits(),
|
||||
rb: <Float as $crate::num::floats::TestableFloat>::Int = r.to_bits(),
|
||||
width: usize = ((bits / 4) + 2) as usize,
|
||||
);
|
||||
}};
|
||||
@@ -312,6 +314,7 @@ macro_rules! float_test {
|
||||
test $test:block
|
||||
) => {
|
||||
mod $name {
|
||||
#[allow(unused_imports)]
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
@@ -360,6 +363,7 @@ const fn flt (x: Float) -> Float { x }
|
||||
|
||||
$( $( #[$const_meta] )+ )?
|
||||
mod const_ {
|
||||
#[allow(unused_imports)]
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
@@ -410,6 +414,9 @@ const fn flt (x: Float) -> Float { x }
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use assert_biteq;
|
||||
pub(crate) use float_test;
|
||||
|
||||
float_test! {
|
||||
name: num,
|
||||
attrs: {
|
||||
@@ -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());
|
||||
}
|
||||
@@ -25,9 +25,10 @@
|
||||
mod carryless_mul;
|
||||
mod const_from;
|
||||
mod dec2flt;
|
||||
mod float_ieee754_flt2dec_dec2flt;
|
||||
mod float_iter_sum_identity;
|
||||
mod floats;
|
||||
mod flt2dec;
|
||||
mod ieee754;
|
||||
mod int_log;
|
||||
mod int_sqrt;
|
||||
mod midpoint;
|
||||
@@ -36,6 +37,8 @@
|
||||
mod ops;
|
||||
mod wrapping;
|
||||
|
||||
use floats::{assert_biteq, float_test};
|
||||
|
||||
/// Adds the attribute to all items in the block.
|
||||
macro_rules! cfg_block {
|
||||
($(#[$attr:meta]{$($it:item)*})*) => {$($(
|
||||
|
||||
@@ -2121,7 +2121,7 @@ fn output_base_name(&self) -> Utf8PathBuf {
|
||||
}
|
||||
|
||||
/// 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.
|
||||
fn logv(&self, message: impl fmt::Display) {
|
||||
@@ -2748,7 +2748,7 @@ fn compare_output(
|
||||
return CompareOutcome::Same;
|
||||
}
|
||||
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)
|
||||
} else {
|
||||
// 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
@@ -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.
|
||||
|
||||
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?
|
||||
|
||||
@@ -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/ui/impl-restriction/`
|
||||
Tests for `#![feature(impl_restriction)]`. See [Tracking issue for restrictions #105077
|
||||
](https://github.com/rust-lang/rust/issues/105077).
|
||||
|
||||
Tests for `#![feature(impl_restriction)]`. See [Tracking issue for restrictions #105077](https://github.com/rust-lang/rust/issues/105077).
|
||||
|
||||
## `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 for `#![feature(lazy_type_alias)]`. See [Tracking issue for lazy type aliases #112792
|
||||
](https://github.com/rust-lang/rust/issues/112792).
|
||||
Tests for `#![feature(lazy_type_alias)]`. See [Tracking issue for lazy type aliases #112792](https://github.com/rust-lang/rust/issues/112792).
|
||||
|
||||
## `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.
|
||||
|
||||
**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`
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ extend-exclude = [
|
||||
# right now. Entries should look like `mipsel = "mipsel"`.
|
||||
#
|
||||
# tidy-alphabetical-start
|
||||
anser = "anser" # an ANSI parsing package used by rust-analyzer
|
||||
arange = "arange" # short for A-range
|
||||
childs = "childs"
|
||||
clonable = "clonable"
|
||||
@@ -29,10 +30,12 @@ makro = "makro" # deliberate misspelling to avoid `macro` keyword
|
||||
misformed = "misformed"
|
||||
moreso = "moreso"
|
||||
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
|
||||
publically = "publically"
|
||||
rplace = "rplace" # short for R-place
|
||||
splitted = "splitted"
|
||||
sythetic = "sythetic" # typo in vendored LLVM sources
|
||||
taits = "taits" # lowercase for TAITs (type alias impl trait)
|
||||
targetting = "targetting"
|
||||
unparseable = "unparseable"
|
||||
|
||||
Reference in New Issue
Block a user