Auto merge of #148931 - Zalathar:rollup-yfyhpcw, r=Zalathar

Rollup of 15 pull requests

Successful merges:

 - rust-lang/rust#148543 (Correctly link to associated trait items in reexports)
 - rust-lang/rust#148808 (Some resolve cleanups)
 - rust-lang/rust#148812 (coverage: Associate hole spans with expansion tree nodes )
 - rust-lang/rust#148826 (CStr docs: Fix CStr vs &CStr confusion)
 - rust-lang/rust#148850 (Implement `Read::read_array`)
 - rust-lang/rust#148867 (Refactor `Box::take`)
 - rust-lang/rust#148870 (Remove unused LLVMModuleRef argument)
 - rust-lang/rust#148878 (error when ABI does not support guaranteed tail calls)
 - rust-lang/rust#148901 (Disable rustdoc-test-builder test partially for SGX target.)
 - rust-lang/rust#148902 (add missing s390x target feature to std detect test)
 - rust-lang/rust#148904 (waffle: stop watching codegen ssa)
 - rust-lang/rust#148906 (Expose fmt::Arguments::from_str as unstable.)
 - rust-lang/rust#148907 (add assembly test for infinite recursion with `become`)
 - rust-lang/rust#148928 (Move & adjust some `!`-adjacent tests)
 - rust-lang/rust#148929 (ignore `build-rust-analyzer` even if it's a symlink)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-11-14 05:29:52 +00:00
commit c880acdd31
38 changed files with 426 additions and 147 deletions

2
.gitignore vendored
View file

@ -48,7 +48,7 @@ no_llvm_build
/llvm/
/mingw-build/
/build
/build-rust-analyzer/
/build-rust-analyzer
/dist/
/unicode-downloads
/target

View file

@ -276,6 +276,51 @@ impl ExternAbi {
_ => CVariadicStatus::NotSupported,
}
}
/// Returns whether the ABI supports guaranteed tail calls.
#[cfg(feature = "nightly")]
pub fn supports_guaranteed_tail_call(self) -> bool {
match self {
Self::CmseNonSecureCall | Self::CmseNonSecureEntry => {
// See https://godbolt.org/z/9jhdeqErv. The CMSE calling conventions clear registers
// before returning, and hence cannot guarantee a tail call.
false
}
Self::AvrInterrupt
| Self::AvrNonBlockingInterrupt
| Self::Msp430Interrupt
| Self::RiscvInterruptM
| Self::RiscvInterruptS
| Self::X86Interrupt => {
// See https://godbolt.org/z/Edfjnxxcq. Interrupts cannot be called directly.
false
}
Self::GpuKernel | Self::PtxKernel => {
// See https://godbolt.org/z/jq5TE5jK1.
false
}
Self::Custom => {
// This ABI does not support calls at all (except via assembly).
false
}
Self::C { .. }
| Self::System { .. }
| Self::Rust
| Self::RustCall
| Self::RustCold
| Self::RustInvalid
| Self::Unadjusted
| Self::EfiApi
| Self::Aapcs { .. }
| Self::Cdecl { .. }
| Self::Stdcall { .. }
| Self::Fastcall { .. }
| Self::Thiscall { .. }
| Self::Vectorcall { .. }
| Self::SysV64 { .. }
| Self::Win64 { .. } => true,
}
}
}
pub fn all_names() -> Vec<&'static str> {

View file

@ -683,7 +683,7 @@ pub(crate) unsafe fn llvm_optimize(
// Here we map the old arguments to the new arguments, with an offset of 1 to make sure
// that we don't use the newly added `%dyn_ptr`.
unsafe {
llvm::LLVMRustOffloadMapper(cx.llmod(), old_fn, new_fn);
llvm::LLVMRustOffloadMapper(old_fn, new_fn);
}
llvm::set_linkage(new_fn, llvm::get_linkage(old_fn));

View file

@ -2025,7 +2025,7 @@ unsafe extern "C" {
) -> &Attribute;
// Operations on functions
pub(crate) fn LLVMRustOffloadMapper<'a>(M: &'a Module, Fn: &'a Value, Fn: &'a Value);
pub(crate) fn LLVMRustOffloadMapper<'a>(Fn: &'a Value, Fn: &'a Value);
pub(crate) fn LLVMRustGetOrInsertFunction<'a>(
M: &'a Module,
Name: *const c_char,

View file

@ -1099,6 +1099,9 @@ pub struct Indeterminate;
pub struct DeriveResolution {
pub path: ast::Path,
pub item: Annotatable,
// FIXME: currently this field is only used in `is_none`/`is_some` conditions. However, the
// `Arc<SyntaxExtension>` will be used if the FIXME in `MacroExpander::fully_expand_fragment`
// is completed.
pub exts: Option<Arc<SyntaxExtension>>,
pub is_const: bool,
}

View file

@ -144,9 +144,7 @@ extern "C" void LLVMRustPrintStatistics(RustStringRef OutBuf) {
llvm::PrintStatistics(OS);
}
extern "C" void LLVMRustOffloadMapper(LLVMModuleRef M, LLVMValueRef OldFn,
LLVMValueRef NewFn) {
llvm::Module *module = llvm::unwrap(M);
extern "C" void LLVMRustOffloadMapper(LLVMValueRef OldFn, LLVMValueRef NewFn) {
llvm::Function *oldFn = llvm::unwrap<llvm::Function>(OldFn);
llvm::Function *newFn = llvm::unwrap<llvm::Function>(NewFn);

View file

@ -135,6 +135,10 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
self.report_abi_mismatch(expr.span, caller_sig.abi, callee_sig.abi);
}
if !callee_sig.abi.supports_guaranteed_tail_call() {
self.report_unsupported_abi(expr.span, callee_sig.abi);
}
// FIXME(explicit_tail_calls): this currently fails for cases where opaques are used.
// e.g.
// ```
@ -358,6 +362,16 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
self.found_errors = Err(err);
}
fn report_unsupported_abi(&mut self, sp: Span, callee_abi: ExternAbi) {
let err = self
.tcx
.dcx()
.struct_span_err(sp, "ABI does not support guaranteed tail calls")
.with_note(format!("`become` is not supported for `extern {callee_abi}` functions"))
.emit();
self.found_errors = Err(err);
}
fn report_signature_mismatch(
&mut self,
sp: Span,

View file

@ -1,7 +1,12 @@
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry};
use rustc_middle::mir;
use rustc_middle::mir::coverage::BasicCoverageBlock;
use rustc_span::{ExpnId, ExpnKind, Span};
use crate::coverage::from_mir;
use crate::coverage::graph::CoverageGraph;
use crate::coverage::hir_info::ExtractedHirInfo;
#[derive(Clone, Copy, Debug)]
pub(crate) struct SpanWithBcb {
pub(crate) span: Span,
@ -70,6 +75,10 @@ pub(crate) struct ExpnNode {
pub(crate) spans: Vec<SpanWithBcb>,
/// Expansions whose call-site is in this expansion.
pub(crate) child_expn_ids: FxIndexSet<ExpnId>,
/// Hole spans belonging to this expansion, to be carved out from the
/// code spans during span refinement.
pub(crate) hole_spans: Vec<Span>,
}
impl ExpnNode {
@ -88,17 +97,27 @@ impl ExpnNode {
spans: vec![],
child_expn_ids: FxIndexSet::default(),
hole_spans: vec![],
}
}
}
/// Given a collection of span/BCB pairs from potentially-different syntax contexts,
/// Extracts raw span/BCB pairs from potentially-different syntax contexts, and
/// arranges them into an "expansion tree" based on their expansion call-sites.
pub(crate) fn build_expn_tree(spans: impl IntoIterator<Item = SpanWithBcb>) -> ExpnTree {
pub(crate) fn build_expn_tree(
mir_body: &mir::Body<'_>,
hir_info: &ExtractedHirInfo,
graph: &CoverageGraph,
) -> ExpnTree {
let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, graph);
let mut nodes = FxIndexMap::default();
let new_node = |&expn_id: &ExpnId| ExpnNode::new(expn_id);
for span_with_bcb in spans {
for from_mir::RawSpanFromMir { raw_span, bcb } in raw_spans {
let span_with_bcb = SpanWithBcb { span: raw_span, bcb };
// Create a node for this span's enclosing expansion, and add the span to it.
let expn_id = span_with_bcb.span.ctxt().outer_expn();
let node = nodes.entry(expn_id).or_insert_with_key(new_node);
@ -123,5 +142,13 @@ pub(crate) fn build_expn_tree(spans: impl IntoIterator<Item = SpanWithBcb>) -> E
}
}
// Associate each hole span (extracted from HIR) with its corresponding
// expansion tree node.
for &hole_span in &hir_info.hole_spans {
let expn_id = hole_span.ctxt().outer_expn();
let Some(node) = nodes.get_mut(&expn_id) else { continue };
node.hole_spans.push(hole_span);
}
ExpnTree { nodes }
}

View file

@ -142,19 +142,3 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
| TerminatorKind::InlineAsm { .. } => Some(terminator.source_info.span),
}
}
#[derive(Debug)]
pub(crate) struct Hole {
pub(crate) span: Span,
}
impl Hole {
pub(crate) fn merge_if_overlapping_or_adjacent(&mut self, other: &mut Self) -> bool {
if !self.span.overlaps_or_adjacent(other.span) {
return false;
}
self.span = self.span.to(other.span);
true
}
}

View file

@ -5,6 +5,7 @@ use rustc_middle::mir::coverage::{
use rustc_middle::mir::{self, BasicBlock, StatementKind};
use rustc_middle::ty::TyCtxt;
use crate::coverage::expansion;
use crate::coverage::graph::CoverageGraph;
use crate::coverage::hir_info::ExtractedHirInfo;
use crate::coverage::spans::extract_refined_covspans;
@ -23,10 +24,12 @@ pub(crate) fn extract_mappings_from_mir<'tcx>(
hir_info: &ExtractedHirInfo,
graph: &CoverageGraph,
) -> ExtractedMappings {
let expn_tree = expansion::build_expn_tree(mir_body, hir_info, graph);
let mut mappings = vec![];
// Extract ordinary code mappings from MIR statement/terminator spans.
extract_refined_covspans(tcx, mir_body, hir_info, graph, &mut mappings);
extract_refined_covspans(tcx, hir_info, graph, &expn_tree, &mut mappings);
extract_branch_mappings(mir_body, hir_info, graph, &mut mappings);

View file

@ -9,6 +9,7 @@ use crate::coverage::mappings::ExtractedMappings;
mod counters;
mod expansion;
mod from_mir;
mod graph;
mod hir_info;
mod mappings;

View file

@ -1,22 +1,18 @@
use rustc_middle::mir;
use rustc_middle::mir::coverage::{Mapping, MappingKind, START_BCB};
use rustc_middle::ty::TyCtxt;
use rustc_span::source_map::SourceMap;
use rustc_span::{BytePos, DesugaringKind, ExpnId, ExpnKind, MacroKind, Span};
use tracing::instrument;
use crate::coverage::expansion::{self, ExpnTree, SpanWithBcb};
use crate::coverage::expansion::{ExpnTree, SpanWithBcb};
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
use crate::coverage::hir_info::ExtractedHirInfo;
use crate::coverage::spans::from_mir::{Hole, RawSpanFromMir};
mod from_mir;
pub(super) fn extract_refined_covspans<'tcx>(
tcx: TyCtxt<'tcx>,
mir_body: &mir::Body<'tcx>,
hir_info: &ExtractedHirInfo,
graph: &CoverageGraph,
expn_tree: &ExpnTree,
mappings: &mut Vec<Mapping>,
) {
if hir_info.is_async_fn {
@ -32,22 +28,32 @@ pub(super) fn extract_refined_covspans<'tcx>(
let &ExtractedHirInfo { body_span, .. } = hir_info;
let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, graph);
// Use the raw spans to build a tree of expansions for this function.
let expn_tree = expansion::build_expn_tree(
raw_spans
.into_iter()
.map(|RawSpanFromMir { raw_span, bcb }| SpanWithBcb { span: raw_span, bcb }),
);
// If there somehow isn't an expansion tree node corresponding to the
// body span, return now and don't create any mappings.
let Some(node) = expn_tree.get(body_span.ctxt().outer_expn()) else { return };
let mut covspans = vec![];
let mut push_covspan = |covspan: Covspan| {
for &SpanWithBcb { span, bcb } in &node.spans {
covspans.push(Covspan { span, bcb });
}
// For each expansion with its call-site in the body span, try to
// distill a corresponding covspan.
for &child_expn_id in &node.child_expn_ids {
if let Some(covspan) = single_covspan_for_child_expn(tcx, graph, &expn_tree, child_expn_id)
{
covspans.push(covspan);
}
}
covspans.retain(|covspan: &Covspan| {
let covspan_span = covspan.span;
// Discard any spans not contained within the function body span.
// Also discard any spans that fill the entire body, because they tend
// to represent compiler-inserted code, e.g. implicitly returning `()`.
if !body_span.contains(covspan_span) || body_span.source_equal(covspan_span) {
return;
return false;
}
// Each pushed covspan should have the same context as the body span.
@ -57,27 +63,11 @@ pub(super) fn extract_refined_covspans<'tcx>(
false,
"span context mismatch: body_span={body_span:?}, covspan.span={covspan_span:?}"
);
return;
return false;
}
covspans.push(covspan);
};
if let Some(node) = expn_tree.get(body_span.ctxt().outer_expn()) {
for &SpanWithBcb { span, bcb } in &node.spans {
push_covspan(Covspan { span, bcb });
}
// For each expansion with its call-site in the body span, try to
// distill a corresponding covspan.
for &child_expn_id in &node.child_expn_ids {
if let Some(covspan) =
single_covspan_for_child_expn(tcx, graph, &expn_tree, child_expn_id)
{
push_covspan(covspan);
}
}
}
true
});
// Only proceed if we found at least one usable span.
if covspans.is_empty() {
@ -107,14 +97,8 @@ pub(super) fn extract_refined_covspans<'tcx>(
covspans.dedup_by(|b, a| a.span.source_equal(b.span));
// Sort the holes, and merge overlapping/adjacent holes.
let mut holes = hir_info
.hole_spans
.iter()
.copied()
// Discard any holes that aren't directly visible within the body span.
.filter(|&hole_span| body_span.contains(hole_span) && body_span.eq_ctxt(hole_span))
.map(|span| Hole { span })
.collect::<Vec<_>>();
let mut holes = node.hole_spans.iter().copied().map(|span| Hole { span }).collect::<Vec<_>>();
holes.sort_by(|a, b| compare_spans(a.span, b.span));
holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b));
@ -295,3 +279,19 @@ fn ensure_non_empty_span(source_map: &SourceMap, span: Span) -> Option<Span> {
})
.ok()?
}
#[derive(Debug)]
struct Hole {
span: Span,
}
impl Hole {
fn merge_if_overlapping_or_adjacent(&mut self, other: &mut Self) -> bool {
if !self.span.overlaps_or_adjacent(other.span) {
return false;
}
self.span = self.span.to(other.span);
true
}
}

View file

@ -458,14 +458,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let mut result = Err(Determinacy::Determined);
for derive in parent_scope.derives {
let parent_scope = &ParentScope { derives: &[], ..*parent_scope };
match this.reborrow().resolve_macro_path(
match this.reborrow().resolve_derive_macro_path(
derive,
MacroKind::Derive,
parent_scope,
true,
force,
ignore_import,
None,
) {
Ok((Some(ext), _)) => {
if ext.helper_attrs.contains(&ident.name) {

View file

@ -396,14 +396,11 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
for (i, resolution) in entry.resolutions.iter_mut().enumerate() {
if resolution.exts.is_none() {
resolution.exts = Some(
match self.cm().resolve_macro_path(
match self.cm().resolve_derive_macro_path(
&resolution.path,
MacroKind::Derive,
&parent_scope,
true,
force,
None,
None,
) {
Ok((Some(ext), _)) => {
if !ext.helper_attrs.is_empty() {
@ -571,7 +568,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
path,
kind,
parent_scope,
true,
force,
deleg_impl,
invoc_in_mod_inert_attr.map(|def_id| (def_id, node_id)),
@ -713,26 +709,22 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
Ok((ext, res))
}
pub(crate) fn resolve_macro_path<'r>(
pub(crate) fn resolve_derive_macro_path<'r>(
self: CmResolver<'r, 'ra, 'tcx>,
path: &ast::Path,
kind: MacroKind,
parent_scope: &ParentScope<'ra>,
trace: bool,
force: bool,
ignore_import: Option<Import<'ra>>,
suggestion_span: Option<Span>,
) -> Result<(Option<Arc<SyntaxExtension>>, Res), Determinacy> {
self.resolve_macro_or_delegation_path(
path,
kind,
MacroKind::Derive,
parent_scope,
trace,
force,
None,
None,
ignore_import,
suggestion_span,
None,
)
}
@ -741,7 +733,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
ast_path: &ast::Path,
kind: MacroKind,
parent_scope: &ParentScope<'ra>,
trace: bool,
force: bool,
deleg_impl: Option<LocalDefId>,
invoc_in_mod_inert_attr: Option<(LocalDefId, NodeId)>,
@ -780,16 +771,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
PathResult::Module(..) => unreachable!(),
};
if trace {
self.multi_segment_macro_resolutions.borrow_mut(&self).push((
path,
path_span,
kind,
*parent_scope,
res.ok(),
ns,
));
}
self.multi_segment_macro_resolutions.borrow_mut(&self).push((
path,
path_span,
kind,
*parent_scope,
res.ok(),
ns,
));
self.prohibit_imported_non_macro_attrs(None, res.ok(), path_span);
res
@ -807,15 +796,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
return Err(Determinacy::Undetermined);
}
if trace {
self.single_segment_macro_resolutions.borrow_mut(&self).push((
path[0].ident,
kind,
*parent_scope,
binding.ok(),
suggestion_span,
));
}
self.single_segment_macro_resolutions.borrow_mut(&self).push((
path[0].ident,
kind,
*parent_scope,
binding.ok(),
suggestion_span,
));
let res = binding.map(|binding| binding.res());
self.prohibit_imported_non_macro_attrs(binding.ok(), res.ok(), path_span);

View file

@ -725,9 +725,9 @@ impl<T, A: Allocator> Box<T, A> {
#[unstable(feature = "box_take", issue = "147212")]
pub fn take(boxed: Self) -> (T, Box<mem::MaybeUninit<T>, A>) {
unsafe {
let (raw, alloc) = Box::into_raw_with_allocator(boxed);
let (raw, alloc) = Box::into_non_null_with_allocator(boxed);
let value = raw.read();
let uninit = Box::from_raw_in(raw.cast::<mem::MaybeUninit<T>>(), alloc);
let uninit = Box::from_non_null_in(raw.cast_uninit(), alloc);
(value, uninit)
}
}

View file

@ -116,6 +116,7 @@
#![feature(exact_size_is_empty)]
#![feature(extend_one)]
#![feature(extend_one_unchecked)]
#![feature(fmt_arguments_from_str)]
#![feature(fmt_internals)]
#![feature(fn_traits)]
#![feature(formatting_options)]

View file

@ -15,18 +15,18 @@ use crate::{fmt, ops, slice, str};
// actually reference libstd or liballoc in intra-doc links. so, the best we can do is remove the
// links to `CString` and `String` for now until a solution is developed
/// Representation of a borrowed C string.
/// A dynamically-sized view of a C string.
///
/// This type represents a borrowed reference to a nul-terminated
/// The type `&CStr` represents a reference to a borrowed nul-terminated
/// array of bytes. It can be constructed safely from a <code>&[[u8]]</code>
/// slice, or unsafely from a raw `*const c_char`. It can be expressed as a
/// literal in the form `c"Hello world"`.
///
/// The `CStr` can then be converted to a Rust <code>&[str]</code> by performing
/// The `&CStr` can then be converted to a Rust <code>&[str]</code> by performing
/// UTF-8 validation, or into an owned `CString`.
///
/// `&CStr` is to `CString` as <code>&[str]</code> is to `String`: the former
/// in each pair are borrowed references; the latter are owned
/// in each pair are borrowing references; the latter are owned
/// strings.
///
/// Note that this structure does **not** have a guaranteed layout (the `repr(transparent)`

View file

@ -734,7 +734,21 @@ impl<'a> Arguments<'a> {
unsafe { Arguments { template: mem::transmute(template), args: mem::transmute(args) } }
}
// Same as `from_str`, but not const.
// Used by format_args!() expansion when arguments are inlined,
// e.g. format_args!("{}", 123), which is not allowed in const.
#[inline]
pub fn from_str_nonconst(s: &'static str) -> Arguments<'a> {
Arguments::from_str(s)
}
}
impl<'a> Arguments<'a> {
/// Create a `fmt::Arguments` object for a single static string.
///
/// Formatting this `fmt::Arguments` will just produce the string as-is.
#[inline]
#[unstable(feature = "fmt_arguments_from_str", issue = "148905")]
pub const fn from_str(s: &'static str) -> Arguments<'a> {
// SAFETY: This is the "static str" representation of fmt::Arguments; see above.
unsafe {
@ -744,14 +758,6 @@ impl<'a> Arguments<'a> {
}
}
}
// Same as `from_str`, but not const.
// Used by format_args!() expansion when arguments are inlined,
// e.g. format_args!("{}", 123), which is not allowed in const.
#[inline]
pub fn from_str_nonconst(s: &'static str) -> Arguments<'a> {
Arguments::from_str(s)
}
}
#[doc(hidden)]

View file

@ -991,7 +991,7 @@ pub(crate) mod builtin {
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_diagnostic_item = "format_args_macro"]
#[allow_internal_unsafe]
#[allow_internal_unstable(fmt_internals)]
#[allow_internal_unstable(fmt_internals, fmt_arguments_from_str)]
#[rustc_builtin_macro]
#[macro_export]
macro_rules! format_args {
@ -1005,7 +1005,7 @@ pub(crate) mod builtin {
///
/// This macro will be removed once `format_args` is allowed in const contexts.
#[unstable(feature = "const_format_args", issue = "none")]
#[allow_internal_unstable(fmt_internals, const_fmt_arguments_new)]
#[allow_internal_unstable(fmt_internals, fmt_arguments_from_str)]
#[rustc_builtin_macro]
#[macro_export]
macro_rules! const_format_args {
@ -1020,7 +1020,7 @@ pub(crate) mod builtin {
reason = "`format_args_nl` is only for internal \
language use and is subject to change"
)]
#[allow_internal_unstable(fmt_internals)]
#[allow_internal_unstable(fmt_internals, fmt_arguments_from_str)]
#[rustc_builtin_macro]
#[doc(hidden)]
#[macro_export]

View file

@ -330,7 +330,7 @@ pub use self::{
stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout},
util::{Empty, Repeat, Sink, empty, repeat, sink},
};
use crate::mem::take;
use crate::mem::{MaybeUninit, take};
use crate::ops::{Deref, DerefMut};
use crate::{cmp, fmt, slice, str, sys};
@ -1242,6 +1242,46 @@ pub trait Read {
{
Take { inner: self, len: limit, limit }
}
/// Read and return a fixed array of bytes from this source.
///
/// This function uses an array sized based on a const generic size known at compile time. You
/// can specify the size with turbofish (`reader.read_array::<8>()`), or let type inference
/// determine the number of bytes needed based on how the return value gets used. For instance,
/// this function works well with functions like [`u64::from_le_bytes`] to turn an array of
/// bytes into an integer of the same size.
///
/// Like `read_exact`, if this function encounters an "end of file" before reading the desired
/// number of bytes, it returns an error of the kind [`ErrorKind::UnexpectedEof`].
///
/// ```
/// #![feature(read_array)]
/// use std::io::Cursor;
/// use std::io::prelude::*;
///
/// fn main() -> std::io::Result<()> {
/// let mut buf = Cursor::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2]);
/// let x = u64::from_le_bytes(buf.read_array()?);
/// let y = u32::from_be_bytes(buf.read_array()?);
/// let z = u16::from_be_bytes(buf.read_array()?);
/// assert_eq!(x, 0x807060504030201);
/// assert_eq!(y, 0x9080706);
/// assert_eq!(z, 0x504);
/// Ok(())
/// }
/// ```
#[unstable(feature = "read_array", issue = "148848")]
fn read_array<const N: usize>(&mut self) -> Result<[u8; N]>
where
Self: Sized,
{
let mut buf = [MaybeUninit::uninit(); N];
let mut borrowed_buf = BorrowedBuf::from(buf.as_mut_slice());
self.read_buf_exact(borrowed_buf.unfilled())?;
// Guard against incorrect `read_buf_exact` implementations.
assert_eq!(borrowed_buf.len(), N);
Ok(unsafe { MaybeUninit::array_assume_init(buf) })
}
}
/// Reads all bytes from a [reader][Read] into a new [`String`].

View file

@ -348,6 +348,7 @@
#![feature(int_from_ascii)]
#![feature(ip)]
#![feature(lazy_get)]
#![feature(maybe_uninit_array_assume_init)]
#![feature(maybe_uninit_slice)]
#![feature(maybe_uninit_write_slice)]
#![feature(panic_can_unwind)]

View file

@ -16,6 +16,7 @@
all(target_arch = "powerpc64", target_os = "linux"),
feature(stdarch_powerpc_feature_detection)
)]
#![cfg_attr(all(target_arch = "s390x", target_os = "linux"), feature(s390x_target_feature))]
#[test]
#[cfg(all(target_arch = "arm", any(target_os = "linux", target_os = "android")))]

View file

@ -838,9 +838,23 @@ fn print_higher_ranked_params_with_space(
pub(crate) fn print_anchor(did: DefId, text: Symbol, cx: &Context<'_>) -> impl Display {
fmt::from_fn(move |f| {
if let Ok(HrefInfo { url, kind, rust_path }) = href(did, cx) {
let tcx = cx.tcx();
let def_kind = tcx.def_kind(did);
let anchor = if matches!(
def_kind,
DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst | DefKind::Variant
) {
let parent_def_id = tcx.parent(did);
let item_type =
ItemType::from_def_kind(def_kind, Some(tcx.def_kind(parent_def_id)));
format!("#{}.{}", item_type.as_str(), tcx.item_name(did))
} else {
String::new()
};
write!(
f,
r#"<a class="{kind}" href="{url}" title="{kind} {path}">{text}</a>"#,
r#"<a class="{kind}" href="{url}{anchor}" title="{kind} {path}">{text}</a>"#,
path = join_path_syms(rust_path),
text = EscapeBodyText(text.as_str()),
)

View file

@ -0,0 +1,21 @@
//@ add-minicore
//@ assembly-output: emit-asm
//@ compile-flags: --target x86_64-unknown-linux-gnu -Copt-level=0 -Cllvm-args=-x86-asm-syntax=intel
//@ needs-llvm-components: x86
#![expect(incomplete_features)]
#![feature(no_core, explicit_tail_calls)]
#![crate_type = "lib"]
#![no_core]
extern crate minicore;
use minicore::*;
// Test that an infinite loop via guaranteed tail calls does not blow the stack.
// CHECK-LABEL: inf
// CHECK: mov rax, qword ptr [rip + inf@GOTPCREL]
// CHECK: jmp rax
#[unsafe(no_mangle)]
fn inf() -> ! {
become inf()
}

View file

@ -1,4 +1,7 @@
// skip-filecheck
//
// check that we mark blocks with `!` locals as unreachable.
// (and currently don't do the same for other uninhabited types)
#![feature(never_type)]
#[derive(Copy, Clone)]

View file

@ -1,7 +1,7 @@
// skip-filecheck
//@ edition: 2021
// In ed 2021 and below, we don't fallback `!` to `()`.
// In ed 2021 and below, we fallback `!` to `()`.
// This would introduce a `! -> ()` coercion which would
// be UB if we didn't disallow this explicitly.

View file

@ -25,7 +25,10 @@ fn main() {
// Some targets (for example wasm) cannot execute doctests directly even with a runner,
// so only exercise the success path when the target can run on the host.
if target().contains("wasm") || std::env::var_os("REMOTE_TEST_CLIENT").is_some() {
if target().contains("wasm")
|| target().contains("sgx")
|| std::env::var_os("REMOTE_TEST_CLIENT").is_some()
{
return;
}

View file

@ -0,0 +1,19 @@
// This test ensures that reexports of associated items links to the associated items.
// Regression test for <https://github.com/rust-lang/rust/issues/148008>.
#![feature(import_trait_associated_functions)]
#![crate_name = "foo"]
//@ has 'foo/index.html'
pub trait Test {
fn method();
const CONST: u8;
type Type;
}
//@ has - '//*[@id="reexport.method"]//a[@href="trait.Test.html#tymethod.method"]' 'method'
//@ has - '//*[@id="reexport.CONST"]//a[@href="trait.Test.html#associatedconstant.CONST"]' 'CONST'
//@ has - '//*[@id="reexport.Type"]//a[@href="trait.Test.html#associatedtype.Type"]' 'Type'
pub use self::Test::{method, CONST, Type};

View file

@ -1,4 +1,6 @@
// Regression test for #122561
// Regression test for <https://github.com/rust-lang/rust/issues/122561>.
//
// Tests suggestions for type mismatch of loop expressions.
fn for_infinite() -> bool {
for i in 0.. {

View file

@ -1,5 +1,5 @@
warning: denote infinite loops with `loop { ... }`
--> $DIR/coerce-loop-issue-122561.rs:47:5
--> $DIR/coerce-loop-issue-122561.rs:49:5
|
LL | while true {
| ^^^^^^^^^^ help: use `loop`
@ -7,13 +7,13 @@ LL | while true {
= note: `#[warn(while_true)]` on by default
warning: denote infinite loops with `loop { ... }`
--> $DIR/coerce-loop-issue-122561.rs:71:5
--> $DIR/coerce-loop-issue-122561.rs:73:5
|
LL | while true {
| ^^^^^^^^^^ help: use `loop`
error[E0308]: mismatched types
--> $DIR/coerce-loop-issue-122561.rs:41:24
--> $DIR/coerce-loop-issue-122561.rs:43:24
|
LL | fn for_in_arg(a: &[(); for x in 0..2 {}]) -> bool {
| ^^^^^^^^^^^^^^^^ expected `usize`, found `()`
@ -25,7 +25,7 @@ LL | fn for_in_arg(a: &[(); for x in 0..2 {} /* `usize` value */]) -> bool {
| +++++++++++++++++++
error[E0308]: mismatched types
--> $DIR/coerce-loop-issue-122561.rs:4:5
--> $DIR/coerce-loop-issue-122561.rs:6:5
|
LL | fn for_infinite() -> bool {
| ---- expected `bool` because of return type
@ -43,7 +43,7 @@ LL + /* `bool` value */
|
error[E0308]: mismatched types
--> $DIR/coerce-loop-issue-122561.rs:11:5
--> $DIR/coerce-loop-issue-122561.rs:13:5
|
LL | fn for_finite() -> String {
| ------ expected `String` because of return type
@ -61,7 +61,7 @@ LL + /* `String` value */
|
error[E0308]: mismatched types
--> $DIR/coerce-loop-issue-122561.rs:18:5
--> $DIR/coerce-loop-issue-122561.rs:20:5
|
LL | fn for_zero_times() -> bool {
| ---- expected `bool` because of return type
@ -79,7 +79,7 @@ LL + /* `bool` value */
|
error[E0308]: mismatched types
--> $DIR/coerce-loop-issue-122561.rs:25:5
--> $DIR/coerce-loop-issue-122561.rs:27:5
|
LL | fn for_never_type() -> ! {
| - expected `!` because of return type
@ -98,7 +98,7 @@ LL + /* `loop {}` or `panic!("...")` */
|
error[E0308]: mismatched types
--> $DIR/coerce-loop-issue-122561.rs:33:32
--> $DIR/coerce-loop-issue-122561.rs:35:32
|
LL | fn for_single_line() -> bool { for i in 0.. { return false; } }
| ---- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
@ -112,7 +112,7 @@ LL | fn for_single_line() -> bool { for i in 0.. { return false; } /* `bool` val
| ++++++++++++++++++
error[E0308]: mismatched types
--> $DIR/coerce-loop-issue-122561.rs:47:5
--> $DIR/coerce-loop-issue-122561.rs:49:5
|
LL | fn while_inifinite() -> bool {
| ---- expected `bool` because of return type
@ -131,7 +131,7 @@ LL + /* `bool` value */
|
error[E0308]: mismatched types
--> $DIR/coerce-loop-issue-122561.rs:56:5
--> $DIR/coerce-loop-issue-122561.rs:58:5
|
LL | fn while_finite() -> bool {
| ---- expected `bool` because of return type
@ -151,7 +151,7 @@ LL + /* `bool` value */
|
error[E0308]: mismatched types
--> $DIR/coerce-loop-issue-122561.rs:64:5
--> $DIR/coerce-loop-issue-122561.rs:66:5
|
LL | fn while_zero_times() -> bool {
| ---- expected `bool` because of return type
@ -169,7 +169,7 @@ LL + /* `bool` value */
|
error[E0308]: mismatched types
--> $DIR/coerce-loop-issue-122561.rs:71:5
--> $DIR/coerce-loop-issue-122561.rs:73:5
|
LL | fn while_never_type() -> ! {
| - expected `!` because of return type
@ -188,7 +188,7 @@ LL + /* `loop {}` or `panic!("...")` */
|
error[E0308]: mismatched types
--> $DIR/coerce-loop-issue-122561.rs:85:5
--> $DIR/coerce-loop-issue-122561.rs:87:5
|
LL | / for i in 0.. {
LL | |
@ -203,7 +203,7 @@ LL + /* `i32` value */
|
error[E0308]: mismatched types
--> $DIR/coerce-loop-issue-122561.rs:92:9
--> $DIR/coerce-loop-issue-122561.rs:94:9
|
LL | / for i in 0..5 {
LL | |
@ -218,7 +218,7 @@ LL + /* `usize` value */
|
error[E0308]: mismatched types
--> $DIR/coerce-loop-issue-122561.rs:98:9
--> $DIR/coerce-loop-issue-122561.rs:100:9
|
LL | / while false {
LL | |
@ -233,7 +233,7 @@ LL + /* `usize` value */
|
error[E0308]: mismatched types
--> $DIR/coerce-loop-issue-122561.rs:104:23
--> $DIR/coerce-loop-issue-122561.rs:106:23
|
LL | let _ = |a: &[(); for x in 0..2 {}]| {};
| ^^^^^^^^^^^^^^^^ expected `usize`, found `()`

View file

@ -34,7 +34,7 @@ const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) };
#[derive(Copy, Clone)]
enum Never {}
// An enum with 3 variants of which some are uninhabited -- so the uninhabited variants *do*
// An enum with 4 variants of which only some are uninhabited -- so the uninhabited variants *do*
// have a discriminant.
enum UninhDiscriminant {
A,

View file

@ -1,10 +1,18 @@
// Regression test for <https://github.com/rust-lang/rust/issues/120337>.
//
// This checks that const eval doesn't cause an ICE when reading an uninhabited
// variant. (N.B. this is UB, but not currently detected by rustc)
//
//@ check-pass
#![feature(never_type)]
#[derive(Copy, Clone)]
pub enum E { A(!), }
pub union U { u: (), e: E, }
pub const C: () = {
let E::A(ref a) = unsafe { &(&U { u: () }).e};
let E::A(ref a) = unsafe { &(&U { u: () }).e };
};
fn main() {}

View file

@ -0,0 +1,38 @@
//@ add-minicore
//@ ignore-backends: gcc
//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
//@ needs-llvm-components: arm
#![expect(incomplete_features)]
#![feature(no_core, explicit_tail_calls, abi_cmse_nonsecure_call)]
#![no_core]
extern crate minicore;
use minicore::*;
unsafe extern "C" {
safe fn magic() -> extern "cmse-nonsecure-call" fn(u32, u32) -> u32;
}
// The `cmse-nonsecure-call` ABI can only occur on function pointers:
//
// - a `cmse-nonsecure-call` definition throws an error
// - a `cmse-nonsecure-call` become in a definition with any other ABI is an ABI mismatch
#[no_mangle]
extern "cmse-nonsecure-call" fn become_nonsecure_call_1(x: u32, y: u32) -> u32 {
//~^ ERROR the `"cmse-nonsecure-call"` ABI is only allowed on function pointers
unsafe {
let f = magic();
become f(1, 2)
//~^ ERROR ABI does not support guaranteed tail calls
}
}
#[no_mangle]
extern "C" fn become_nonsecure_call_2(x: u32, y: u32) -> u32 {
unsafe {
let f = magic();
become f(1, 2)
//~^ ERROR mismatched function ABIs
//~| ERROR ABI does not support guaranteed tail calls
}
}

View file

@ -0,0 +1,34 @@
error[E0781]: the `"cmse-nonsecure-call"` ABI is only allowed on function pointers
--> $DIR/cmse-nonsecure-call.rs:21:1
|
LL | extern "cmse-nonsecure-call" fn become_nonsecure_call_1(x: u32, y: u32) -> u32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: ABI does not support guaranteed tail calls
--> $DIR/cmse-nonsecure-call.rs:25:9
|
LL | become f(1, 2)
| ^^^^^^^^^^^^^^
|
= note: `become` is not supported for `extern "cmse-nonsecure-call"` functions
error: mismatched function ABIs
--> $DIR/cmse-nonsecure-call.rs:34:9
|
LL | become f(1, 2)
| ^^^^^^^^^^^^^^
|
= note: `become` requires caller and callee to have the same ABI
= note: caller ABI is `"C"`, while callee ABI is `"cmse-nonsecure-call"`
error: ABI does not support guaranteed tail calls
--> $DIR/cmse-nonsecure-call.rs:34:9
|
LL | become f(1, 2)
| ^^^^^^^^^^^^^^
|
= note: `become` is not supported for `extern "cmse-nonsecure-call"` functions
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0781`.

View file

@ -0,0 +1,22 @@
//@ add-minicore
//@ ignore-backends: gcc
//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
//@ needs-llvm-components: arm
#![expect(incomplete_features)]
#![feature(no_core, explicit_tail_calls, cmse_nonsecure_entry)]
#![no_core]
extern crate minicore;
use minicore::*;
#[inline(never)]
extern "cmse-nonsecure-entry" fn entry(c: bool, x: u32, y: u32) -> u32 {
if c { x } else { y }
}
// A `cmse-nonsecure-entry` clears registers before returning, so a tail call cannot be guaranteed.
#[unsafe(no_mangle)]
extern "cmse-nonsecure-entry" fn become_nonsecure_entry(c: bool, x: u32, y: u32) -> u32 {
become entry(c, x, y)
//~^ ERROR ABI does not support guaranteed tail calls
}

View file

@ -0,0 +1,10 @@
error: ABI does not support guaranteed tail calls
--> $DIR/cmse-nonsecure-entry.rs:20:5
|
LL | become entry(c, x, y)
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: `become` is not supported for `extern "cmse-nonsecure-entry"` functions
error: aborting due to 1 previous error

View file

@ -922,9 +922,6 @@ cc = ["@davidtwco", "@wesleywiser"]
[mentions."compiler/rustc_codegen_cranelift"]
cc = ["@bjorn3"]
[mentions."compiler/rustc_codegen_ssa"]
cc = ["@WaffleLapkin"]
[mentions."compiler/rustc_codegen_gcc"]
cc = ["@antoyo", "@GuillaumeGomez"]