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;
};
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
+1 -1
View File
@@ -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>)
+49 -1
View File
@@ -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 {
+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,
/// 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 -5
View File
@@ -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};
+4 -5
View File
@@ -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.
+2 -46
View File
@@ -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> {
-23
View File
@@ -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`].
+29 -1
View File
@@ -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()
}
}
-1
View File
@@ -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: {
-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 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)*})*) => {$($(
+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.
/// 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
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.
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`
+3
View File
@@ -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"