| author | bors <bors@rust-lang.org> 2025-11-14 05:29:52 UTC |
| committer | bors <bors@rust-lang.org> 2025-11-14 05:29:52 UTC |
| log | c880acdd3171dfafdb55be8cd9822a857e99348d |
| tree | a4dd60b38d0715db07918e08982d8326e4cfb853 |
| parent | 6d41834e251a65a994bf0acf415ddafd4adbf71f |
| parent | f589579a1df7e27fa4b6dead0c92f6a45f29c227 |
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: rollup40 files changed, 621 insertions(+), 342 deletions(-)
.gitignore+1-1| ... | ... | @@ -48,7 +48,7 @@ no_llvm_build |
| 48 | 48 | /llvm/ |
| 49 | 49 | /mingw-build/ |
| 50 | 50 | /build |
| 51 | /build-rust-analyzer/ | |
| 51 | /build-rust-analyzer | |
| 52 | 52 | /dist/ |
| 53 | 53 | /unicode-downloads |
| 54 | 54 | /target |
compiler/rustc_abi/src/extern_abi.rs+45| ... | ... | @@ -276,6 +276,51 @@ impl ExternAbi { |
| 276 | 276 | _ => CVariadicStatus::NotSupported, |
| 277 | 277 | } |
| 278 | 278 | } |
| 279 | ||
| 280 | /// Returns whether the ABI supports guaranteed tail calls. | |
| 281 | #[cfg(feature = "nightly")] | |
| 282 | pub fn supports_guaranteed_tail_call(self) -> bool { | |
| 283 | match self { | |
| 284 | Self::CmseNonSecureCall | Self::CmseNonSecureEntry => { | |
| 285 | // See https://godbolt.org/z/9jhdeqErv. The CMSE calling conventions clear registers | |
| 286 | // before returning, and hence cannot guarantee a tail call. | |
| 287 | false | |
| 288 | } | |
| 289 | Self::AvrInterrupt | |
| 290 | | Self::AvrNonBlockingInterrupt | |
| 291 | | Self::Msp430Interrupt | |
| 292 | | Self::RiscvInterruptM | |
| 293 | | Self::RiscvInterruptS | |
| 294 | | Self::X86Interrupt => { | |
| 295 | // See https://godbolt.org/z/Edfjnxxcq. Interrupts cannot be called directly. | |
| 296 | false | |
| 297 | } | |
| 298 | Self::GpuKernel | Self::PtxKernel => { | |
| 299 | // See https://godbolt.org/z/jq5TE5jK1. | |
| 300 | false | |
| 301 | } | |
| 302 | Self::Custom => { | |
| 303 | // This ABI does not support calls at all (except via assembly). | |
| 304 | false | |
| 305 | } | |
| 306 | Self::C { .. } | |
| 307 | | Self::System { .. } | |
| 308 | | Self::Rust | |
| 309 | | Self::RustCall | |
| 310 | | Self::RustCold | |
| 311 | | Self::RustInvalid | |
| 312 | | Self::Unadjusted | |
| 313 | | Self::EfiApi | |
| 314 | | Self::Aapcs { .. } | |
| 315 | | Self::Cdecl { .. } | |
| 316 | | Self::Stdcall { .. } | |
| 317 | | Self::Fastcall { .. } | |
| 318 | | Self::Thiscall { .. } | |
| 319 | | Self::Vectorcall { .. } | |
| 320 | | Self::SysV64 { .. } | |
| 321 | | Self::Win64 { .. } => true, | |
| 322 | } | |
| 323 | } | |
| 279 | 324 | } |
| 280 | 325 | |
| 281 | 326 | pub fn all_names() -> Vec<&'static str> { |
compiler/rustc_codegen_llvm/src/back/write.rs+1-1| ... | ... | @@ -683,7 +683,7 @@ pub(crate) unsafe fn llvm_optimize( |
| 683 | 683 | // Here we map the old arguments to the new arguments, with an offset of 1 to make sure |
| 684 | 684 | // that we don't use the newly added `%dyn_ptr`. |
| 685 | 685 | unsafe { |
| 686 | llvm::LLVMRustOffloadMapper(cx.llmod(), old_fn, new_fn); | |
| 686 | llvm::LLVMRustOffloadMapper(old_fn, new_fn); | |
| 687 | 687 | } |
| 688 | 688 | |
| 689 | 689 | llvm::set_linkage(new_fn, llvm::get_linkage(old_fn)); |
compiler/rustc_codegen_llvm/src/llvm/ffi.rs+1-1| ... | ... | @@ -2025,7 +2025,7 @@ unsafe extern "C" { |
| 2025 | 2025 | ) -> &Attribute; |
| 2026 | 2026 | |
| 2027 | 2027 | // Operations on functions |
| 2028 | pub(crate) fn LLVMRustOffloadMapper<'a>(M: &'a Module, Fn: &'a Value, Fn: &'a Value); | |
| 2028 | pub(crate) fn LLVMRustOffloadMapper<'a>(Fn: &'a Value, Fn: &'a Value); | |
| 2029 | 2029 | pub(crate) fn LLVMRustGetOrInsertFunction<'a>( |
| 2030 | 2030 | M: &'a Module, |
| 2031 | 2031 | Name: *const c_char, |
compiler/rustc_expand/src/base.rs+3| ... | ... | @@ -1099,6 +1099,9 @@ pub struct Indeterminate; |
| 1099 | 1099 | pub struct DeriveResolution { |
| 1100 | 1100 | pub path: ast::Path, |
| 1101 | 1101 | pub item: Annotatable, |
| 1102 | // FIXME: currently this field is only used in `is_none`/`is_some` conditions. However, the | |
| 1103 | // `Arc<SyntaxExtension>` will be used if the FIXME in `MacroExpander::fully_expand_fragment` | |
| 1104 | // is completed. | |
| 1102 | 1105 | pub exts: Option<Arc<SyntaxExtension>>, |
| 1103 | 1106 | pub is_const: bool, |
| 1104 | 1107 | } |
compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp+1-3| ... | ... | @@ -144,9 +144,7 @@ extern "C" void LLVMRustPrintStatistics(RustStringRef OutBuf) { |
| 144 | 144 | llvm::PrintStatistics(OS); |
| 145 | 145 | } |
| 146 | 146 | |
| 147 | extern "C" void LLVMRustOffloadMapper(LLVMModuleRef M, LLVMValueRef OldFn, | |
| 148 | LLVMValueRef NewFn) { | |
| 149 | llvm::Module *module = llvm::unwrap(M); | |
| 147 | extern "C" void LLVMRustOffloadMapper(LLVMValueRef OldFn, LLVMValueRef NewFn) { | |
| 150 | 148 | llvm::Function *oldFn = llvm::unwrap<llvm::Function>(OldFn); |
| 151 | 149 | llvm::Function *newFn = llvm::unwrap<llvm::Function>(NewFn); |
| 152 | 150 |
compiler/rustc_mir_build/src/check_tail_calls.rs+14| ... | ... | @@ -135,6 +135,10 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { |
| 135 | 135 | self.report_abi_mismatch(expr.span, caller_sig.abi, callee_sig.abi); |
| 136 | 136 | } |
| 137 | 137 | |
| 138 | if !callee_sig.abi.supports_guaranteed_tail_call() { | |
| 139 | self.report_unsupported_abi(expr.span, callee_sig.abi); | |
| 140 | } | |
| 141 | ||
| 138 | 142 | // FIXME(explicit_tail_calls): this currently fails for cases where opaques are used. |
| 139 | 143 | // e.g. |
| 140 | 144 | // ``` |
| ... | ... | @@ -358,6 +362,16 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { |
| 358 | 362 | self.found_errors = Err(err); |
| 359 | 363 | } |
| 360 | 364 | |
| 365 | fn report_unsupported_abi(&mut self, sp: Span, callee_abi: ExternAbi) { | |
| 366 | let err = self | |
| 367 | .tcx | |
| 368 | .dcx() | |
| 369 | .struct_span_err(sp, "ABI does not support guaranteed tail calls") | |
| 370 | .with_note(format!("`become` is not supported for `extern {callee_abi}` functions")) | |
| 371 | .emit(); | |
| 372 | self.found_errors = Err(err); | |
| 373 | } | |
| 374 | ||
| 361 | 375 | fn report_signature_mismatch( |
| 362 | 376 | &mut self, |
| 363 | 377 | sp: Span, |
compiler/rustc_mir_transform/src/coverage/expansion.rs+30-3| ... | ... | @@ -1,7 +1,12 @@ |
| 1 | 1 | use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; |
| 2 | use rustc_middle::mir; | |
| 2 | 3 | use rustc_middle::mir::coverage::BasicCoverageBlock; |
| 3 | 4 | use rustc_span::{ExpnId, ExpnKind, Span}; |
| 4 | 5 | |
| 6 | use crate::coverage::from_mir; | |
| 7 | use crate::coverage::graph::CoverageGraph; | |
| 8 | use crate::coverage::hir_info::ExtractedHirInfo; | |
| 9 | ||
| 5 | 10 | #[derive(Clone, Copy, Debug)] |
| 6 | 11 | pub(crate) struct SpanWithBcb { |
| 7 | 12 | pub(crate) span: Span, |
| ... | ... | @@ -70,6 +75,10 @@ pub(crate) struct ExpnNode { |
| 70 | 75 | pub(crate) spans: Vec<SpanWithBcb>, |
| 71 | 76 | /// Expansions whose call-site is in this expansion. |
| 72 | 77 | pub(crate) child_expn_ids: FxIndexSet<ExpnId>, |
| 78 | ||
| 79 | /// Hole spans belonging to this expansion, to be carved out from the | |
| 80 | /// code spans during span refinement. | |
| 81 | pub(crate) hole_spans: Vec<Span>, | |
| 73 | 82 | } |
| 74 | 83 | |
| 75 | 84 | impl ExpnNode { |
| ... | ... | @@ -88,17 +97,27 @@ impl ExpnNode { |
| 88 | 97 | |
| 89 | 98 | spans: vec![], |
| 90 | 99 | child_expn_ids: FxIndexSet::default(), |
| 100 | ||
| 101 | hole_spans: vec![], | |
| 91 | 102 | } |
| 92 | 103 | } |
| 93 | 104 | } |
| 94 | 105 | |
| 95 | /// Given a collection of span/BCB pairs from potentially-different syntax contexts, | |
| 106 | /// Extracts raw span/BCB pairs from potentially-different syntax contexts, and | |
| 96 | 107 | /// arranges them into an "expansion tree" based on their expansion call-sites. |
| 97 | pub(crate) fn build_expn_tree(spans: impl IntoIterator<Item = SpanWithBcb>) -> ExpnTree { | |
| 108 | pub(crate) fn build_expn_tree( | |
| 109 | mir_body: &mir::Body<'_>, | |
| 110 | hir_info: &ExtractedHirInfo, | |
| 111 | graph: &CoverageGraph, | |
| 112 | ) -> ExpnTree { | |
| 113 | let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, graph); | |
| 114 | ||
| 98 | 115 | let mut nodes = FxIndexMap::default(); |
| 99 | 116 | let new_node = |&expn_id: &ExpnId| ExpnNode::new(expn_id); |
| 100 | 117 | |
| 101 | for span_with_bcb in spans { | |
| 118 | for from_mir::RawSpanFromMir { raw_span, bcb } in raw_spans { | |
| 119 | let span_with_bcb = SpanWithBcb { span: raw_span, bcb }; | |
| 120 | ||
| 102 | 121 | // Create a node for this span's enclosing expansion, and add the span to it. |
| 103 | 122 | let expn_id = span_with_bcb.span.ctxt().outer_expn(); |
| 104 | 123 | 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 |
| 123 | 142 | } |
| 124 | 143 | } |
| 125 | 144 | |
| 145 | // Associate each hole span (extracted from HIR) with its corresponding | |
| 146 | // expansion tree node. | |
| 147 | for &hole_span in &hir_info.hole_spans { | |
| 148 | let expn_id = hole_span.ctxt().outer_expn(); | |
| 149 | let Some(node) = nodes.get_mut(&expn_id) else { continue }; | |
| 150 | node.hole_spans.push(hole_span); | |
| 151 | } | |
| 152 | ||
| 126 | 153 | ExpnTree { nodes } |
| 127 | 154 | } |
compiler/rustc_mir_transform/src/coverage/from_mir.rs created+144| ... | ... | @@ -0,0 +1,144 @@ |
| 1 | use std::iter; | |
| 2 | ||
| 3 | use rustc_middle::bug; | |
| 4 | use rustc_middle::mir::coverage::CoverageKind; | |
| 5 | use rustc_middle::mir::{ | |
| 6 | self, FakeReadCause, Statement, StatementKind, Terminator, TerminatorKind, | |
| 7 | }; | |
| 8 | use rustc_span::Span; | |
| 9 | ||
| 10 | use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; | |
| 11 | ||
| 12 | #[derive(Debug)] | |
| 13 | pub(crate) struct RawSpanFromMir { | |
| 14 | /// A span that has been extracted from a MIR statement/terminator, but | |
| 15 | /// hasn't been "unexpanded", so it might not lie within the function body | |
| 16 | /// span and might be part of an expansion with a different context. | |
| 17 | pub(crate) raw_span: Span, | |
| 18 | pub(crate) bcb: BasicCoverageBlock, | |
| 19 | } | |
| 20 | ||
| 21 | /// Generates an initial set of coverage spans from the statements and | |
| 22 | /// terminators in the function's MIR body, each associated with its | |
| 23 | /// corresponding node in the coverage graph. | |
| 24 | /// | |
| 25 | /// This is necessarily an inexact process, because MIR isn't designed to | |
| 26 | /// capture source spans at the level of detail we would want for coverage, | |
| 27 | /// but it's good enough to be better than nothing. | |
| 28 | pub(crate) fn extract_raw_spans_from_mir<'tcx>( | |
| 29 | mir_body: &mir::Body<'tcx>, | |
| 30 | graph: &CoverageGraph, | |
| 31 | ) -> Vec<RawSpanFromMir> { | |
| 32 | let mut raw_spans = vec![]; | |
| 33 | ||
| 34 | // We only care about blocks that are part of the coverage graph. | |
| 35 | for (bcb, bcb_data) in graph.iter_enumerated() { | |
| 36 | let make_raw_span = |raw_span: Span| RawSpanFromMir { raw_span, bcb }; | |
| 37 | ||
| 38 | // A coverage graph node can consist of multiple basic blocks. | |
| 39 | for &bb in &bcb_data.basic_blocks { | |
| 40 | let bb_data = &mir_body[bb]; | |
| 41 | ||
| 42 | let statements = bb_data.statements.iter(); | |
| 43 | raw_spans.extend(statements.filter_map(filtered_statement_span).map(make_raw_span)); | |
| 44 | ||
| 45 | // There's only one terminator, but wrap it in an iterator to | |
| 46 | // mirror the handling of statements. | |
| 47 | let terminator = iter::once(bb_data.terminator()); | |
| 48 | raw_spans.extend(terminator.filter_map(filtered_terminator_span).map(make_raw_span)); | |
| 49 | } | |
| 50 | } | |
| 51 | ||
| 52 | raw_spans | |
| 53 | } | |
| 54 | ||
| 55 | /// If the MIR `Statement` has a span contributive to computing coverage spans, | |
| 56 | /// return it; otherwise return `None`. | |
| 57 | fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> { | |
| 58 | match statement.kind { | |
| 59 | // These statements have spans that are often outside the scope of the executed source code | |
| 60 | // for their parent `BasicBlock`. | |
| 61 | StatementKind::StorageLive(_) | |
| 62 | | StatementKind::StorageDead(_) | |
| 63 | | StatementKind::ConstEvalCounter | |
| 64 | | StatementKind::BackwardIncompatibleDropHint { .. } | |
| 65 | | StatementKind::Nop => None, | |
| 66 | ||
| 67 | // FIXME(#78546): MIR InstrumentCoverage - Can the source_info.span for `FakeRead` | |
| 68 | // statements be more consistent? | |
| 69 | // | |
| 70 | // FakeReadCause::ForGuardBinding, in this example: | |
| 71 | // match somenum { | |
| 72 | // x if x < 1 => { ... } | |
| 73 | // }... | |
| 74 | // The BasicBlock within the match arm code included one of these statements, but the span | |
| 75 | // for it covered the `1` in this source. The actual statements have nothing to do with that | |
| 76 | // source span: | |
| 77 | // FakeRead(ForGuardBinding, _4); | |
| 78 | // where `_4` is: | |
| 79 | // _4 = &_1; (at the span for the first `x`) | |
| 80 | // and `_1` is the `Place` for `somenum`. | |
| 81 | // | |
| 82 | // If and when the Issue is resolved, remove this special case match pattern: | |
| 83 | StatementKind::FakeRead(box (FakeReadCause::ForGuardBinding, _)) => None, | |
| 84 | ||
| 85 | // Retain spans from most other statements. | |
| 86 | StatementKind::FakeRead(_) | |
| 87 | | StatementKind::Intrinsic(..) | |
| 88 | | StatementKind::Coverage( | |
| 89 | // The purpose of `SpanMarker` is to be matched and accepted here. | |
| 90 | CoverageKind::SpanMarker, | |
| 91 | ) | |
| 92 | | StatementKind::Assign(_) | |
| 93 | | StatementKind::SetDiscriminant { .. } | |
| 94 | | StatementKind::Retag(_, _) | |
| 95 | | StatementKind::PlaceMention(..) | |
| 96 | | StatementKind::AscribeUserType(_, _) => Some(statement.source_info.span), | |
| 97 | ||
| 98 | // Block markers are used for branch coverage, so ignore them here. | |
| 99 | StatementKind::Coverage(CoverageKind::BlockMarker { .. }) => None, | |
| 100 | ||
| 101 | // These coverage statements should not exist prior to coverage instrumentation. | |
| 102 | StatementKind::Coverage(CoverageKind::VirtualCounter { .. }) => bug!( | |
| 103 | "Unexpected coverage statement found during coverage instrumentation: {statement:?}" | |
| 104 | ), | |
| 105 | } | |
| 106 | } | |
| 107 | ||
| 108 | /// If the MIR `Terminator` has a span contributive to computing coverage spans, | |
| 109 | /// return it; otherwise return `None`. | |
| 110 | fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> { | |
| 111 | match terminator.kind { | |
| 112 | // These terminators have spans that don't positively contribute to computing a reasonable | |
| 113 | // span of actually executed source code. (For example, SwitchInt terminators extracted from | |
| 114 | // an `if condition { block }` has a span that includes the executed block, if true, | |
| 115 | // but for coverage, the code region executed, up to *and* through the SwitchInt, | |
| 116 | // actually stops before the if's block.) | |
| 117 | TerminatorKind::Unreachable | |
| 118 | | TerminatorKind::Assert { .. } | |
| 119 | | TerminatorKind::Drop { .. } | |
| 120 | | TerminatorKind::SwitchInt { .. } | |
| 121 | | TerminatorKind::FalseEdge { .. } | |
| 122 | | TerminatorKind::Goto { .. } => None, | |
| 123 | ||
| 124 | // Call `func` operand can have a more specific span when part of a chain of calls | |
| 125 | TerminatorKind::Call { ref func, .. } | TerminatorKind::TailCall { ref func, .. } => { | |
| 126 | let mut span = terminator.source_info.span; | |
| 127 | if let mir::Operand::Constant(constant) = func | |
| 128 | && span.contains(constant.span) | |
| 129 | { | |
| 130 | span = constant.span; | |
| 131 | } | |
| 132 | Some(span) | |
| 133 | } | |
| 134 | ||
| 135 | // Retain spans from all other terminators | |
| 136 | TerminatorKind::UnwindResume | |
| 137 | | TerminatorKind::UnwindTerminate(_) | |
| 138 | | TerminatorKind::Return | |
| 139 | | TerminatorKind::Yield { .. } | |
| 140 | | TerminatorKind::CoroutineDrop | |
| 141 | | TerminatorKind::FalseUnwind { .. } | |
| 142 | | TerminatorKind::InlineAsm { .. } => Some(terminator.source_info.span), | |
| 143 | } | |
| 144 | } |
compiler/rustc_mir_transform/src/coverage/mappings.rs+4-1| ... | ... | @@ -5,6 +5,7 @@ use rustc_middle::mir::coverage::{ |
| 5 | 5 | use rustc_middle::mir::{self, BasicBlock, StatementKind}; |
| 6 | 6 | use rustc_middle::ty::TyCtxt; |
| 7 | 7 | |
| 8 | use crate::coverage::expansion; | |
| 8 | 9 | use crate::coverage::graph::CoverageGraph; |
| 9 | 10 | use crate::coverage::hir_info::ExtractedHirInfo; |
| 10 | 11 | use crate::coverage::spans::extract_refined_covspans; |
| ... | ... | @@ -23,10 +24,12 @@ pub(crate) fn extract_mappings_from_mir<'tcx>( |
| 23 | 24 | hir_info: &ExtractedHirInfo, |
| 24 | 25 | graph: &CoverageGraph, |
| 25 | 26 | ) -> ExtractedMappings { |
| 27 | let expn_tree = expansion::build_expn_tree(mir_body, hir_info, graph); | |
| 28 | ||
| 26 | 29 | let mut mappings = vec![]; |
| 27 | 30 | |
| 28 | 31 | // Extract ordinary code mappings from MIR statement/terminator spans. |
| 29 | extract_refined_covspans(tcx, mir_body, hir_info, graph, &mut mappings); | |
| 32 | extract_refined_covspans(tcx, hir_info, graph, &expn_tree, &mut mappings); | |
| 30 | 33 | |
| 31 | 34 | extract_branch_mappings(mir_body, hir_info, graph, &mut mappings); |
| 32 | 35 |
compiler/rustc_mir_transform/src/coverage/mod.rs+1| ... | ... | @@ -9,6 +9,7 @@ use crate::coverage::mappings::ExtractedMappings; |
| 9 | 9 | |
| 10 | 10 | mod counters; |
| 11 | 11 | mod expansion; |
| 12 | mod from_mir; | |
| 12 | 13 | mod graph; |
| 13 | 14 | mod hir_info; |
| 14 | 15 | mod mappings; |
compiler/rustc_mir_transform/src/coverage/spans.rs+42-42| ... | ... | @@ -1,22 +1,18 @@ |
| 1 | use rustc_middle::mir; | |
| 2 | 1 | use rustc_middle::mir::coverage::{Mapping, MappingKind, START_BCB}; |
| 3 | 2 | use rustc_middle::ty::TyCtxt; |
| 4 | 3 | use rustc_span::source_map::SourceMap; |
| 5 | 4 | use rustc_span::{BytePos, DesugaringKind, ExpnId, ExpnKind, MacroKind, Span}; |
| 6 | 5 | use tracing::instrument; |
| 7 | 6 | |
| 8 | use crate::coverage::expansion::{self, ExpnTree, SpanWithBcb}; | |
| 7 | use crate::coverage::expansion::{ExpnTree, SpanWithBcb}; | |
| 9 | 8 | use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; |
| 10 | 9 | use crate::coverage::hir_info::ExtractedHirInfo; |
| 11 | use crate::coverage::spans::from_mir::{Hole, RawSpanFromMir}; | |
| 12 | ||
| 13 | mod from_mir; | |
| 14 | 10 | |
| 15 | 11 | pub(super) fn extract_refined_covspans<'tcx>( |
| 16 | 12 | tcx: TyCtxt<'tcx>, |
| 17 | mir_body: &mir::Body<'tcx>, | |
| 18 | 13 | hir_info: &ExtractedHirInfo, |
| 19 | 14 | graph: &CoverageGraph, |
| 15 | expn_tree: &ExpnTree, | |
| 20 | 16 | mappings: &mut Vec<Mapping>, |
| 21 | 17 | ) { |
| 22 | 18 | if hir_info.is_async_fn { |
| ... | ... | @@ -32,22 +28,32 @@ pub(super) fn extract_refined_covspans<'tcx>( |
| 32 | 28 | |
| 33 | 29 | let &ExtractedHirInfo { body_span, .. } = hir_info; |
| 34 | 30 | |
| 35 | let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, graph); | |
| 36 | // Use the raw spans to build a tree of expansions for this function. | |
| 37 | let expn_tree = expansion::build_expn_tree( | |
| 38 | raw_spans | |
| 39 | .into_iter() | |
| 40 | .map(|RawSpanFromMir { raw_span, bcb }| SpanWithBcb { span: raw_span, bcb }), | |
| 41 | ); | |
| 31 | // If there somehow isn't an expansion tree node corresponding to the | |
| 32 | // body span, return now and don't create any mappings. | |
| 33 | let Some(node) = expn_tree.get(body_span.ctxt().outer_expn()) else { return }; | |
| 42 | 34 | |
| 43 | 35 | let mut covspans = vec![]; |
| 44 | let mut push_covspan = |covspan: Covspan| { | |
| 36 | ||
| 37 | for &SpanWithBcb { span, bcb } in &node.spans { | |
| 38 | covspans.push(Covspan { span, bcb }); | |
| 39 | } | |
| 40 | ||
| 41 | // For each expansion with its call-site in the body span, try to | |
| 42 | // distill a corresponding covspan. | |
| 43 | for &child_expn_id in &node.child_expn_ids { | |
| 44 | if let Some(covspan) = single_covspan_for_child_expn(tcx, graph, &expn_tree, child_expn_id) | |
| 45 | { | |
| 46 | covspans.push(covspan); | |
| 47 | } | |
| 48 | } | |
| 49 | ||
| 50 | covspans.retain(|covspan: &Covspan| { | |
| 45 | 51 | let covspan_span = covspan.span; |
| 46 | 52 | // Discard any spans not contained within the function body span. |
| 47 | 53 | // Also discard any spans that fill the entire body, because they tend |
| 48 | 54 | // to represent compiler-inserted code, e.g. implicitly returning `()`. |
| 49 | 55 | if !body_span.contains(covspan_span) || body_span.source_equal(covspan_span) { |
| 50 | return; | |
| 56 | return false; | |
| 51 | 57 | } |
| 52 | 58 | |
| 53 | 59 | // Each pushed covspan should have the same context as the body span. |
| ... | ... | @@ -57,27 +63,11 @@ pub(super) fn extract_refined_covspans<'tcx>( |
| 57 | 63 | false, |
| 58 | 64 | "span context mismatch: body_span={body_span:?}, covspan.span={covspan_span:?}" |
| 59 | 65 | ); |
| 60 | return; | |
| 66 | return false; | |
| 61 | 67 | } |
| 62 | 68 | |
| 63 | covspans.push(covspan); | |
| 64 | }; | |
| 65 | ||
| 66 | if let Some(node) = expn_tree.get(body_span.ctxt().outer_expn()) { | |
| 67 | for &SpanWithBcb { span, bcb } in &node.spans { | |
| 68 | push_covspan(Covspan { span, bcb }); | |
| 69 | } | |
| 70 | ||
| 71 | // For each expansion with its call-site in the body span, try to | |
| 72 | // distill a corresponding covspan. | |
| 73 | for &child_expn_id in &node.child_expn_ids { | |
| 74 | if let Some(covspan) = | |
| 75 | single_covspan_for_child_expn(tcx, graph, &expn_tree, child_expn_id) | |
| 76 | { | |
| 77 | push_covspan(covspan); | |
| 78 | } | |
| 79 | } | |
| 80 | } | |
| 69 | true | |
| 70 | }); | |
| 81 | 71 | |
| 82 | 72 | // Only proceed if we found at least one usable span. |
| 83 | 73 | if covspans.is_empty() { |
| ... | ... | @@ -107,14 +97,8 @@ pub(super) fn extract_refined_covspans<'tcx>( |
| 107 | 97 | covspans.dedup_by(|b, a| a.span.source_equal(b.span)); |
| 108 | 98 | |
| 109 | 99 | // Sort the holes, and merge overlapping/adjacent holes. |
| 110 | let mut holes = hir_info | |
| 111 | .hole_spans | |
| 112 | .iter() | |
| 113 | .copied() | |
| 114 | // Discard any holes that aren't directly visible within the body span. | |
| 115 | .filter(|&hole_span| body_span.contains(hole_span) && body_span.eq_ctxt(hole_span)) | |
| 116 | .map(|span| Hole { span }) | |
| 117 | .collect::<Vec<_>>(); | |
| 100 | let mut holes = node.hole_spans.iter().copied().map(|span| Hole { span }).collect::<Vec<_>>(); | |
| 101 | ||
| 118 | 102 | holes.sort_by(|a, b| compare_spans(a.span, b.span)); |
| 119 | 103 | holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b)); |
| 120 | 104 | |
| ... | ... | @@ -295,3 +279,19 @@ fn ensure_non_empty_span(source_map: &SourceMap, span: Span) -> Option<Span> { |
| 295 | 279 | }) |
| 296 | 280 | .ok()? |
| 297 | 281 | } |
| 282 | ||
| 283 | #[derive(Debug)] | |
| 284 | struct Hole { | |
| 285 | span: Span, | |
| 286 | } | |
| 287 | ||
| 288 | impl Hole { | |
| 289 | fn merge_if_overlapping_or_adjacent(&mut self, other: &mut Self) -> bool { | |
| 290 | if !self.span.overlaps_or_adjacent(other.span) { | |
| 291 | return false; | |
| 292 | } | |
| 293 | ||
| 294 | self.span = self.span.to(other.span); | |
| 295 | true | |
| 296 | } | |
| 297 | } |
compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs deleted-160| ... | ... | @@ -1,160 +0,0 @@ |
| 1 | use std::iter; | |
| 2 | ||
| 3 | use rustc_middle::bug; | |
| 4 | use rustc_middle::mir::coverage::CoverageKind; | |
| 5 | use rustc_middle::mir::{ | |
| 6 | self, FakeReadCause, Statement, StatementKind, Terminator, TerminatorKind, | |
| 7 | }; | |
| 8 | use rustc_span::Span; | |
| 9 | ||
| 10 | use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; | |
| 11 | ||
| 12 | #[derive(Debug)] | |
| 13 | pub(crate) struct RawSpanFromMir { | |
| 14 | /// A span that has been extracted from a MIR statement/terminator, but | |
| 15 | /// hasn't been "unexpanded", so it might not lie within the function body | |
| 16 | /// span and might be part of an expansion with a different context. | |
| 17 | pub(crate) raw_span: Span, | |
| 18 | pub(crate) bcb: BasicCoverageBlock, | |
| 19 | } | |
| 20 | ||
| 21 | /// Generates an initial set of coverage spans from the statements and | |
| 22 | /// terminators in the function's MIR body, each associated with its | |
| 23 | /// corresponding node in the coverage graph. | |
| 24 | /// | |
| 25 | /// This is necessarily an inexact process, because MIR isn't designed to | |
| 26 | /// capture source spans at the level of detail we would want for coverage, | |
| 27 | /// but it's good enough to be better than nothing. | |
| 28 | pub(crate) fn extract_raw_spans_from_mir<'tcx>( | |
| 29 | mir_body: &mir::Body<'tcx>, | |
| 30 | graph: &CoverageGraph, | |
| 31 | ) -> Vec<RawSpanFromMir> { | |
| 32 | let mut raw_spans = vec![]; | |
| 33 | ||
| 34 | // We only care about blocks that are part of the coverage graph. | |
| 35 | for (bcb, bcb_data) in graph.iter_enumerated() { | |
| 36 | let make_raw_span = |raw_span: Span| RawSpanFromMir { raw_span, bcb }; | |
| 37 | ||
| 38 | // A coverage graph node can consist of multiple basic blocks. | |
| 39 | for &bb in &bcb_data.basic_blocks { | |
| 40 | let bb_data = &mir_body[bb]; | |
| 41 | ||
| 42 | let statements = bb_data.statements.iter(); | |
| 43 | raw_spans.extend(statements.filter_map(filtered_statement_span).map(make_raw_span)); | |
| 44 | ||
| 45 | // There's only one terminator, but wrap it in an iterator to | |
| 46 | // mirror the handling of statements. | |
| 47 | let terminator = iter::once(bb_data.terminator()); | |
| 48 | raw_spans.extend(terminator.filter_map(filtered_terminator_span).map(make_raw_span)); | |
| 49 | } | |
| 50 | } | |
| 51 | ||
| 52 | raw_spans | |
| 53 | } | |
| 54 | ||
| 55 | /// If the MIR `Statement` has a span contributive to computing coverage spans, | |
| 56 | /// return it; otherwise return `None`. | |
| 57 | fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> { | |
| 58 | match statement.kind { | |
| 59 | // These statements have spans that are often outside the scope of the executed source code | |
| 60 | // for their parent `BasicBlock`. | |
| 61 | StatementKind::StorageLive(_) | |
| 62 | | StatementKind::StorageDead(_) | |
| 63 | | StatementKind::ConstEvalCounter | |
| 64 | | StatementKind::BackwardIncompatibleDropHint { .. } | |
| 65 | | StatementKind::Nop => None, | |
| 66 | ||
| 67 | // FIXME(#78546): MIR InstrumentCoverage - Can the source_info.span for `FakeRead` | |
| 68 | // statements be more consistent? | |
| 69 | // | |
| 70 | // FakeReadCause::ForGuardBinding, in this example: | |
| 71 | // match somenum { | |
| 72 | // x if x < 1 => { ... } | |
| 73 | // }... | |
| 74 | // The BasicBlock within the match arm code included one of these statements, but the span | |
| 75 | // for it covered the `1` in this source. The actual statements have nothing to do with that | |
| 76 | // source span: | |
| 77 | // FakeRead(ForGuardBinding, _4); | |
| 78 | // where `_4` is: | |
| 79 | // _4 = &_1; (at the span for the first `x`) | |
| 80 | // and `_1` is the `Place` for `somenum`. | |
| 81 | // | |
| 82 | // If and when the Issue is resolved, remove this special case match pattern: | |
| 83 | StatementKind::FakeRead(box (FakeReadCause::ForGuardBinding, _)) => None, | |
| 84 | ||
| 85 | // Retain spans from most other statements. | |
| 86 | StatementKind::FakeRead(_) | |
| 87 | | StatementKind::Intrinsic(..) | |
| 88 | | StatementKind::Coverage( | |
| 89 | // The purpose of `SpanMarker` is to be matched and accepted here. | |
| 90 | CoverageKind::SpanMarker, | |
| 91 | ) | |
| 92 | | StatementKind::Assign(_) | |
| 93 | | StatementKind::SetDiscriminant { .. } | |
| 94 | | StatementKind::Retag(_, _) | |
| 95 | | StatementKind::PlaceMention(..) | |
| 96 | | StatementKind::AscribeUserType(_, _) => Some(statement.source_info.span), | |
| 97 | ||
| 98 | // Block markers are used for branch coverage, so ignore them here. | |
| 99 | StatementKind::Coverage(CoverageKind::BlockMarker { .. }) => None, | |
| 100 | ||
| 101 | // These coverage statements should not exist prior to coverage instrumentation. | |
| 102 | StatementKind::Coverage(CoverageKind::VirtualCounter { .. }) => bug!( | |
| 103 | "Unexpected coverage statement found during coverage instrumentation: {statement:?}" | |
| 104 | ), | |
| 105 | } | |
| 106 | } | |
| 107 | ||
| 108 | /// If the MIR `Terminator` has a span contributive to computing coverage spans, | |
| 109 | /// return it; otherwise return `None`. | |
| 110 | fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> { | |
| 111 | match terminator.kind { | |
| 112 | // These terminators have spans that don't positively contribute to computing a reasonable | |
| 113 | // span of actually executed source code. (For example, SwitchInt terminators extracted from | |
| 114 | // an `if condition { block }` has a span that includes the executed block, if true, | |
| 115 | // but for coverage, the code region executed, up to *and* through the SwitchInt, | |
| 116 | // actually stops before the if's block.) | |
| 117 | TerminatorKind::Unreachable | |
| 118 | | TerminatorKind::Assert { .. } | |
| 119 | | TerminatorKind::Drop { .. } | |
| 120 | | TerminatorKind::SwitchInt { .. } | |
| 121 | | TerminatorKind::FalseEdge { .. } | |
| 122 | | TerminatorKind::Goto { .. } => None, | |
| 123 | ||
| 124 | // Call `func` operand can have a more specific span when part of a chain of calls | |
| 125 | TerminatorKind::Call { ref func, .. } | TerminatorKind::TailCall { ref func, .. } => { | |
| 126 | let mut span = terminator.source_info.span; | |
| 127 | if let mir::Operand::Constant(constant) = func | |
| 128 | && span.contains(constant.span) | |
| 129 | { | |
| 130 | span = constant.span; | |
| 131 | } | |
| 132 | Some(span) | |
| 133 | } | |
| 134 | ||
| 135 | // Retain spans from all other terminators | |
| 136 | TerminatorKind::UnwindResume | |
| 137 | | TerminatorKind::UnwindTerminate(_) | |
| 138 | | TerminatorKind::Return | |
| 139 | | TerminatorKind::Yield { .. } | |
| 140 | | TerminatorKind::CoroutineDrop | |
| 141 | | TerminatorKind::FalseUnwind { .. } | |
| 142 | | TerminatorKind::InlineAsm { .. } => Some(terminator.source_info.span), | |
| 143 | } | |
| 144 | } | |
| 145 | ||
| 146 | #[derive(Debug)] | |
| 147 | pub(crate) struct Hole { | |
| 148 | pub(crate) span: Span, | |
| 149 | } | |
| 150 | ||
| 151 | impl Hole { | |
| 152 | pub(crate) fn merge_if_overlapping_or_adjacent(&mut self, other: &mut Self) -> bool { | |
| 153 | if !self.span.overlaps_or_adjacent(other.span) { | |
| 154 | return false; | |
| 155 | } | |
| 156 | ||
| 157 | self.span = self.span.to(other.span); | |
| 158 | true | |
| 159 | } | |
| 160 | } |
compiler/rustc_resolve/src/ident.rs+1-4| ... | ... | @@ -458,14 +458,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { |
| 458 | 458 | let mut result = Err(Determinacy::Determined); |
| 459 | 459 | for derive in parent_scope.derives { |
| 460 | 460 | let parent_scope = &ParentScope { derives: &[], ..*parent_scope }; |
| 461 | match this.reborrow().resolve_macro_path( | |
| 461 | match this.reborrow().resolve_derive_macro_path( | |
| 462 | 462 | derive, |
| 463 | MacroKind::Derive, | |
| 464 | 463 | parent_scope, |
| 465 | true, | |
| 466 | 464 | force, |
| 467 | 465 | ignore_import, |
| 468 | None, | |
| 469 | 466 | ) { |
| 470 | 467 | Ok((Some(ext), _)) => { |
| 471 | 468 | if ext.helper_attrs.contains(&ident.name) { |
compiler/rustc_resolve/src/macros.rs+19-32| ... | ... | @@ -396,14 +396,11 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { |
| 396 | 396 | for (i, resolution) in entry.resolutions.iter_mut().enumerate() { |
| 397 | 397 | if resolution.exts.is_none() { |
| 398 | 398 | resolution.exts = Some( |
| 399 | match self.cm().resolve_macro_path( | |
| 399 | match self.cm().resolve_derive_macro_path( | |
| 400 | 400 | &resolution.path, |
| 401 | MacroKind::Derive, | |
| 402 | 401 | &parent_scope, |
| 403 | true, | |
| 404 | 402 | force, |
| 405 | 403 | None, |
| 406 | None, | |
| 407 | 404 | ) { |
| 408 | 405 | Ok((Some(ext), _)) => { |
| 409 | 406 | if !ext.helper_attrs.is_empty() { |
| ... | ... | @@ -571,7 +568,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { |
| 571 | 568 | path, |
| 572 | 569 | kind, |
| 573 | 570 | parent_scope, |
| 574 | true, | |
| 575 | 571 | force, |
| 576 | 572 | deleg_impl, |
| 577 | 573 | invoc_in_mod_inert_attr.map(|def_id| (def_id, node_id)), |
| ... | ... | @@ -713,26 +709,22 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { |
| 713 | 709 | Ok((ext, res)) |
| 714 | 710 | } |
| 715 | 711 | |
| 716 | pub(crate) fn resolve_macro_path<'r>( | |
| 712 | pub(crate) fn resolve_derive_macro_path<'r>( | |
| 717 | 713 | self: CmResolver<'r, 'ra, 'tcx>, |
| 718 | 714 | path: &ast::Path, |
| 719 | kind: MacroKind, | |
| 720 | 715 | parent_scope: &ParentScope<'ra>, |
| 721 | trace: bool, | |
| 722 | 716 | force: bool, |
| 723 | 717 | ignore_import: Option<Import<'ra>>, |
| 724 | suggestion_span: Option<Span>, | |
| 725 | 718 | ) -> Result<(Option<Arc<SyntaxExtension>>, Res), Determinacy> { |
| 726 | 719 | self.resolve_macro_or_delegation_path( |
| 727 | 720 | path, |
| 728 | kind, | |
| 721 | MacroKind::Derive, | |
| 729 | 722 | parent_scope, |
| 730 | trace, | |
| 731 | 723 | force, |
| 732 | 724 | None, |
| 733 | 725 | None, |
| 734 | 726 | ignore_import, |
| 735 | suggestion_span, | |
| 727 | None, | |
| 736 | 728 | ) |
| 737 | 729 | } |
| 738 | 730 | |
| ... | ... | @@ -741,7 +733,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { |
| 741 | 733 | ast_path: &ast::Path, |
| 742 | 734 | kind: MacroKind, |
| 743 | 735 | parent_scope: &ParentScope<'ra>, |
| 744 | trace: bool, | |
| 745 | 736 | force: bool, |
| 746 | 737 | deleg_impl: Option<LocalDefId>, |
| 747 | 738 | invoc_in_mod_inert_attr: Option<(LocalDefId, NodeId)>, |
| ... | ... | @@ -780,16 +771,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { |
| 780 | 771 | PathResult::Module(..) => unreachable!(), |
| 781 | 772 | }; |
| 782 | 773 | |
| 783 | if trace { | |
| 784 | self.multi_segment_macro_resolutions.borrow_mut(&self).push(( | |
| 785 | path, | |
| 786 | path_span, | |
| 787 | kind, | |
| 788 | *parent_scope, | |
| 789 | res.ok(), | |
| 790 | ns, | |
| 791 | )); | |
| 792 | } | |
| 774 | self.multi_segment_macro_resolutions.borrow_mut(&self).push(( | |
| 775 | path, | |
| 776 | path_span, | |
| 777 | kind, | |
| 778 | *parent_scope, | |
| 779 | res.ok(), | |
| 780 | ns, | |
| 781 | )); | |
| 793 | 782 | |
| 794 | 783 | self.prohibit_imported_non_macro_attrs(None, res.ok(), path_span); |
| 795 | 784 | res |
| ... | ... | @@ -807,15 +796,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { |
| 807 | 796 | return Err(Determinacy::Undetermined); |
| 808 | 797 | } |
| 809 | 798 | |
| 810 | if trace { | |
| 811 | self.single_segment_macro_resolutions.borrow_mut(&self).push(( | |
| 812 | path[0].ident, | |
| 813 | kind, | |
| 814 | *parent_scope, | |
| 815 | binding.ok(), | |
| 816 | suggestion_span, | |
| 817 | )); | |
| 818 | } | |
| 799 | self.single_segment_macro_resolutions.borrow_mut(&self).push(( | |
| 800 | path[0].ident, | |
| 801 | kind, | |
| 802 | *parent_scope, | |
| 803 | binding.ok(), | |
| 804 | suggestion_span, | |
| 805 | )); | |
| 819 | 806 | |
| 820 | 807 | let res = binding.map(|binding| binding.res()); |
| 821 | 808 | self.prohibit_imported_non_macro_attrs(binding.ok(), res.ok(), path_span); |
library/alloc/src/boxed.rs+2-2| ... | ... | @@ -725,9 +725,9 @@ impl<T, A: Allocator> Box<T, A> { |
| 725 | 725 | #[unstable(feature = "box_take", issue = "147212")] |
| 726 | 726 | pub fn take(boxed: Self) -> (T, Box<mem::MaybeUninit<T>, A>) { |
| 727 | 727 | unsafe { |
| 728 | let (raw, alloc) = Box::into_raw_with_allocator(boxed); | |
| 728 | let (raw, alloc) = Box::into_non_null_with_allocator(boxed); | |
| 729 | 729 | let value = raw.read(); |
| 730 | let uninit = Box::from_raw_in(raw.cast::<mem::MaybeUninit<T>>(), alloc); | |
| 730 | let uninit = Box::from_non_null_in(raw.cast_uninit(), alloc); | |
| 731 | 731 | (value, uninit) |
| 732 | 732 | } |
| 733 | 733 | } |
library/alloc/src/lib.rs+1| ... | ... | @@ -116,6 +116,7 @@ |
| 116 | 116 | #![feature(exact_size_is_empty)] |
| 117 | 117 | #![feature(extend_one)] |
| 118 | 118 | #![feature(extend_one_unchecked)] |
| 119 | #![feature(fmt_arguments_from_str)] | |
| 119 | 120 | #![feature(fmt_internals)] |
| 120 | 121 | #![feature(fn_traits)] |
| 121 | 122 | #![feature(formatting_options)] |
library/core/src/ffi/c_str.rs+4-4| ... | ... | @@ -15,18 +15,18 @@ use crate::{fmt, ops, slice, str}; |
| 15 | 15 | // actually reference libstd or liballoc in intra-doc links. so, the best we can do is remove the |
| 16 | 16 | // links to `CString` and `String` for now until a solution is developed |
| 17 | 17 | |
| 18 | /// Representation of a borrowed C string. | |
| 18 | /// A dynamically-sized view of a C string. | |
| 19 | 19 | /// |
| 20 | /// This type represents a borrowed reference to a nul-terminated | |
| 20 | /// The type `&CStr` represents a reference to a borrowed nul-terminated | |
| 21 | 21 | /// array of bytes. It can be constructed safely from a <code>&[[u8]]</code> |
| 22 | 22 | /// slice, or unsafely from a raw `*const c_char`. It can be expressed as a |
| 23 | 23 | /// literal in the form `c"Hello world"`. |
| 24 | 24 | /// |
| 25 | /// The `CStr` can then be converted to a Rust <code>&[str]</code> by performing | |
| 25 | /// The `&CStr` can then be converted to a Rust <code>&[str]</code> by performing | |
| 26 | 26 | /// UTF-8 validation, or into an owned `CString`. |
| 27 | 27 | /// |
| 28 | 28 | /// `&CStr` is to `CString` as <code>&[str]</code> is to `String`: the former |
| 29 | /// in each pair are borrowed references; the latter are owned | |
| 29 | /// in each pair are borrowing references; the latter are owned | |
| 30 | 30 | /// strings. |
| 31 | 31 | /// |
| 32 | 32 | /// Note that this structure does **not** have a guaranteed layout (the `repr(transparent)` |
library/core/src/fmt/mod.rs+14-8| ... | ... | @@ -734,7 +734,21 @@ impl<'a> Arguments<'a> { |
| 734 | 734 | unsafe { Arguments { template: mem::transmute(template), args: mem::transmute(args) } } |
| 735 | 735 | } |
| 736 | 736 | |
| 737 | // Same as `from_str`, but not const. | |
| 738 | // Used by format_args!() expansion when arguments are inlined, | |
| 739 | // e.g. format_args!("{}", 123), which is not allowed in const. | |
| 737 | 740 | #[inline] |
| 741 | pub fn from_str_nonconst(s: &'static str) -> Arguments<'a> { | |
| 742 | Arguments::from_str(s) | |
| 743 | } | |
| 744 | } | |
| 745 | ||
| 746 | impl<'a> Arguments<'a> { | |
| 747 | /// Create a `fmt::Arguments` object for a single static string. | |
| 748 | /// | |
| 749 | /// Formatting this `fmt::Arguments` will just produce the string as-is. | |
| 750 | #[inline] | |
| 751 | #[unstable(feature = "fmt_arguments_from_str", issue = "148905")] | |
| 738 | 752 | pub const fn from_str(s: &'static str) -> Arguments<'a> { |
| 739 | 753 | // SAFETY: This is the "static str" representation of fmt::Arguments; see above. |
| 740 | 754 | unsafe { |
| ... | ... | @@ -744,14 +758,6 @@ impl<'a> Arguments<'a> { |
| 744 | 758 | } |
| 745 | 759 | } |
| 746 | 760 | } |
| 747 | ||
| 748 | // Same as `from_str`, but not const. | |
| 749 | // Used by format_args!() expansion when arguments are inlined, | |
| 750 | // e.g. format_args!("{}", 123), which is not allowed in const. | |
| 751 | #[inline] | |
| 752 | pub fn from_str_nonconst(s: &'static str) -> Arguments<'a> { | |
| 753 | Arguments::from_str(s) | |
| 754 | } | |
| 755 | 761 | } |
| 756 | 762 | |
| 757 | 763 | #[doc(hidden)] |
library/core/src/macros/mod.rs+3-3| ... | ... | @@ -991,7 +991,7 @@ pub(crate) mod builtin { |
| 991 | 991 | #[stable(feature = "rust1", since = "1.0.0")] |
| 992 | 992 | #[rustc_diagnostic_item = "format_args_macro"] |
| 993 | 993 | #[allow_internal_unsafe] |
| 994 | #[allow_internal_unstable(fmt_internals)] | |
| 994 | #[allow_internal_unstable(fmt_internals, fmt_arguments_from_str)] | |
| 995 | 995 | #[rustc_builtin_macro] |
| 996 | 996 | #[macro_export] |
| 997 | 997 | macro_rules! format_args { |
| ... | ... | @@ -1005,7 +1005,7 @@ pub(crate) mod builtin { |
| 1005 | 1005 | /// |
| 1006 | 1006 | /// This macro will be removed once `format_args` is allowed in const contexts. |
| 1007 | 1007 | #[unstable(feature = "const_format_args", issue = "none")] |
| 1008 | #[allow_internal_unstable(fmt_internals, const_fmt_arguments_new)] | |
| 1008 | #[allow_internal_unstable(fmt_internals, fmt_arguments_from_str)] | |
| 1009 | 1009 | #[rustc_builtin_macro] |
| 1010 | 1010 | #[macro_export] |
| 1011 | 1011 | macro_rules! const_format_args { |
| ... | ... | @@ -1020,7 +1020,7 @@ pub(crate) mod builtin { |
| 1020 | 1020 | reason = "`format_args_nl` is only for internal \ |
| 1021 | 1021 | language use and is subject to change" |
| 1022 | 1022 | )] |
| 1023 | #[allow_internal_unstable(fmt_internals)] | |
| 1023 | #[allow_internal_unstable(fmt_internals, fmt_arguments_from_str)] | |
| 1024 | 1024 | #[rustc_builtin_macro] |
| 1025 | 1025 | #[doc(hidden)] |
| 1026 | 1026 | #[macro_export] |
library/std/src/io/mod.rs+41-1| ... | ... | @@ -330,7 +330,7 @@ pub use self::{ |
| 330 | 330 | stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, |
| 331 | 331 | util::{Empty, Repeat, Sink, empty, repeat, sink}, |
| 332 | 332 | }; |
| 333 | use crate::mem::take; | |
| 333 | use crate::mem::{MaybeUninit, take}; | |
| 334 | 334 | use crate::ops::{Deref, DerefMut}; |
| 335 | 335 | use crate::{cmp, fmt, slice, str, sys}; |
| 336 | 336 | |
| ... | ... | @@ -1242,6 +1242,46 @@ pub trait Read { |
| 1242 | 1242 | { |
| 1243 | 1243 | Take { inner: self, len: limit, limit } |
| 1244 | 1244 | } |
| 1245 | ||
| 1246 | /// Read and return a fixed array of bytes from this source. | |
| 1247 | /// | |
| 1248 | /// This function uses an array sized based on a const generic size known at compile time. You | |
| 1249 | /// can specify the size with turbofish (`reader.read_array::<8>()`), or let type inference | |
| 1250 | /// determine the number of bytes needed based on how the return value gets used. For instance, | |
| 1251 | /// this function works well with functions like [`u64::from_le_bytes`] to turn an array of | |
| 1252 | /// bytes into an integer of the same size. | |
| 1253 | /// | |
| 1254 | /// Like `read_exact`, if this function encounters an "end of file" before reading the desired | |
| 1255 | /// number of bytes, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. | |
| 1256 | /// | |
| 1257 | /// ``` | |
| 1258 | /// #![feature(read_array)] | |
| 1259 | /// use std::io::Cursor; | |
| 1260 | /// use std::io::prelude::*; | |
| 1261 | /// | |
| 1262 | /// fn main() -> std::io::Result<()> { | |
| 1263 | /// let mut buf = Cursor::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2]); | |
| 1264 | /// let x = u64::from_le_bytes(buf.read_array()?); | |
| 1265 | /// let y = u32::from_be_bytes(buf.read_array()?); | |
| 1266 | /// let z = u16::from_be_bytes(buf.read_array()?); | |
| 1267 | /// assert_eq!(x, 0x807060504030201); | |
| 1268 | /// assert_eq!(y, 0x9080706); | |
| 1269 | /// assert_eq!(z, 0x504); | |
| 1270 | /// Ok(()) | |
| 1271 | /// } | |
| 1272 | /// ``` | |
| 1273 | #[unstable(feature = "read_array", issue = "148848")] | |
| 1274 | fn read_array<const N: usize>(&mut self) -> Result<[u8; N]> | |
| 1275 | where | |
| 1276 | Self: Sized, | |
| 1277 | { | |
| 1278 | let mut buf = [MaybeUninit::uninit(); N]; | |
| 1279 | let mut borrowed_buf = BorrowedBuf::from(buf.as_mut_slice()); | |
| 1280 | self.read_buf_exact(borrowed_buf.unfilled())?; | |
| 1281 | // Guard against incorrect `read_buf_exact` implementations. | |
| 1282 | assert_eq!(borrowed_buf.len(), N); | |
| 1283 | Ok(unsafe { MaybeUninit::array_assume_init(buf) }) | |
| 1284 | } | |
| 1245 | 1285 | } |
| 1246 | 1286 | |
| 1247 | 1287 | /// Reads all bytes from a [reader][Read] into a new [`String`]. |
library/std/src/lib.rs+1| ... | ... | @@ -348,6 +348,7 @@ |
| 348 | 348 | #![feature(int_from_ascii)] |
| 349 | 349 | #![feature(ip)] |
| 350 | 350 | #![feature(lazy_get)] |
| 351 | #![feature(maybe_uninit_array_assume_init)] | |
| 351 | 352 | #![feature(maybe_uninit_slice)] |
| 352 | 353 | #![feature(maybe_uninit_write_slice)] |
| 353 | 354 | #![feature(panic_can_unwind)] |
library/std/tests/run-time-detect.rs+1| ... | ... | @@ -16,6 +16,7 @@ |
| 16 | 16 | all(target_arch = "powerpc64", target_os = "linux"), |
| 17 | 17 | feature(stdarch_powerpc_feature_detection) |
| 18 | 18 | )] |
| 19 | #![cfg_attr(all(target_arch = "s390x", target_os = "linux"), feature(s390x_target_feature))] | |
| 19 | 20 | |
| 20 | 21 | #[test] |
| 21 | 22 | #[cfg(all(target_arch = "arm", any(target_os = "linux", target_os = "android")))] |
src/librustdoc/html/format.rs+15-1| ... | ... | @@ -838,9 +838,23 @@ fn print_higher_ranked_params_with_space( |
| 838 | 838 | pub(crate) fn print_anchor(did: DefId, text: Symbol, cx: &Context<'_>) -> impl Display { |
| 839 | 839 | fmt::from_fn(move |f| { |
| 840 | 840 | if let Ok(HrefInfo { url, kind, rust_path }) = href(did, cx) { |
| 841 | let tcx = cx.tcx(); | |
| 842 | let def_kind = tcx.def_kind(did); | |
| 843 | let anchor = if matches!( | |
| 844 | def_kind, | |
| 845 | DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst | DefKind::Variant | |
| 846 | ) { | |
| 847 | let parent_def_id = tcx.parent(did); | |
| 848 | let item_type = | |
| 849 | ItemType::from_def_kind(def_kind, Some(tcx.def_kind(parent_def_id))); | |
| 850 | format!("#{}.{}", item_type.as_str(), tcx.item_name(did)) | |
| 851 | } else { | |
| 852 | String::new() | |
| 853 | }; | |
| 854 | ||
| 841 | 855 | write!( |
| 842 | 856 | f, |
| 843 | r#"<a class="{kind}" href="{url}" title="{kind} {path}">{text}</a>"#, | |
| 857 | r#"<a class="{kind}" href="{url}{anchor}" title="{kind} {path}">{text}</a>"#, | |
| 844 | 858 | path = join_path_syms(rust_path), |
| 845 | 859 | text = EscapeBodyText(text.as_str()), |
| 846 | 860 | ) |
tests/assembly-llvm/tail-call-infinite-recursion.rs created+21| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | //@ add-minicore | |
| 2 | //@ assembly-output: emit-asm | |
| 3 | //@ compile-flags: --target x86_64-unknown-linux-gnu -Copt-level=0 -Cllvm-args=-x86-asm-syntax=intel | |
| 4 | //@ needs-llvm-components: x86 | |
| 5 | #![expect(incomplete_features)] | |
| 6 | #![feature(no_core, explicit_tail_calls)] | |
| 7 | #![crate_type = "lib"] | |
| 8 | #![no_core] | |
| 9 | ||
| 10 | extern crate minicore; | |
| 11 | use minicore::*; | |
| 12 | ||
| 13 | // Test that an infinite loop via guaranteed tail calls does not blow the stack. | |
| 14 | ||
| 15 | // CHECK-LABEL: inf | |
| 16 | // CHECK: mov rax, qword ptr [rip + inf@GOTPCREL] | |
| 17 | // CHECK: jmp rax | |
| 18 | #[unsafe(no_mangle)] | |
| 19 | fn inf() -> ! { | |
| 20 | become inf() | |
| 21 | } |
tests/mir-opt/uninhabited_enum.rs+3| ... | ... | @@ -1,4 +1,7 @@ |
| 1 | 1 | // skip-filecheck |
| 2 | // | |
| 3 | // check that we mark blocks with `!` locals as unreachable. | |
| 4 | // (and currently don't do the same for other uninhabited types) | |
| 2 | 5 | #![feature(never_type)] |
| 3 | 6 | |
| 4 | 7 | #[derive(Copy, Clone)] |
tests/mir-opt/uninhabited_not_read.rs+1-1| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | 1 | // skip-filecheck |
| 2 | 2 | |
| 3 | 3 | //@ edition: 2021 |
| 4 | // In ed 2021 and below, we don't fallback `!` to `()`. | |
| 4 | // In ed 2021 and below, we fallback `!` to `()`. | |
| 5 | 5 | // This would introduce a `! -> ()` coercion which would |
| 6 | 6 | // be UB if we didn't disallow this explicitly. |
| 7 | 7 |
tests/run-make/rustdoc-test-builder/rmake.rs+4-1| ... | ... | @@ -25,7 +25,10 @@ fn main() { |
| 25 | 25 | |
| 26 | 26 | // Some targets (for example wasm) cannot execute doctests directly even with a runner, |
| 27 | 27 | // so only exercise the success path when the target can run on the host. |
| 28 | if target().contains("wasm") || std::env::var_os("REMOTE_TEST_CLIENT").is_some() { | |
| 28 | if target().contains("wasm") | |
| 29 | || target().contains("sgx") | |
| 30 | || std::env::var_os("REMOTE_TEST_CLIENT").is_some() | |
| 31 | { | |
| 29 | 32 | return; |
| 30 | 33 | } |
| 31 | 34 |
tests/rustdoc/import_trait_associated_functions.rs created+19| ... | ... | @@ -0,0 +1,19 @@ |
| 1 | // This test ensures that reexports of associated items links to the associated items. | |
| 2 | // Regression test for <https://github.com/rust-lang/rust/issues/148008>. | |
| 3 | ||
| 4 | #![feature(import_trait_associated_functions)] | |
| 5 | ||
| 6 | #![crate_name = "foo"] | |
| 7 | ||
| 8 | //@ has 'foo/index.html' | |
| 9 | ||
| 10 | pub trait Test { | |
| 11 | fn method(); | |
| 12 | const CONST: u8; | |
| 13 | type Type; | |
| 14 | } | |
| 15 | ||
| 16 | //@ has - '//*[@id="reexport.method"]//a[@href="trait.Test.html#tymethod.method"]' 'method' | |
| 17 | //@ has - '//*[@id="reexport.CONST"]//a[@href="trait.Test.html#associatedconstant.CONST"]' 'CONST' | |
| 18 | //@ has - '//*[@id="reexport.Type"]//a[@href="trait.Test.html#associatedtype.Type"]' 'Type' | |
| 19 | pub use self::Test::{method, CONST, Type}; |
tests/ui/coercion/coerce-loop-issue-122561.rs+3-1| ... | ... | @@ -1,4 +1,6 @@ |
| 1 | // Regression test for #122561 | |
| 1 | // Regression test for <https://github.com/rust-lang/rust/issues/122561>. | |
| 2 | // | |
| 3 | // Tests suggestions for type mismatch of loop expressions. | |
| 2 | 4 | |
| 3 | 5 | fn for_infinite() -> bool { |
| 4 | 6 | for i in 0.. { |
tests/ui/coercion/coerce-loop-issue-122561.stderr+16-16| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | warning: denote infinite loops with `loop { ... }` |
| 2 | --> $DIR/coerce-loop-issue-122561.rs:47:5 | |
| 2 | --> $DIR/coerce-loop-issue-122561.rs:49:5 | |
| 3 | 3 | | |
| 4 | 4 | LL | while true { |
| 5 | 5 | | ^^^^^^^^^^ help: use `loop` |
| ... | ... | @@ -7,13 +7,13 @@ LL | while true { |
| 7 | 7 | = note: `#[warn(while_true)]` on by default |
| 8 | 8 | |
| 9 | 9 | warning: denote infinite loops with `loop { ... }` |
| 10 | --> $DIR/coerce-loop-issue-122561.rs:71:5 | |
| 10 | --> $DIR/coerce-loop-issue-122561.rs:73:5 | |
| 11 | 11 | | |
| 12 | 12 | LL | while true { |
| 13 | 13 | | ^^^^^^^^^^ help: use `loop` |
| 14 | 14 | |
| 15 | 15 | error[E0308]: mismatched types |
| 16 | --> $DIR/coerce-loop-issue-122561.rs:41:24 | |
| 16 | --> $DIR/coerce-loop-issue-122561.rs:43:24 | |
| 17 | 17 | | |
| 18 | 18 | LL | fn for_in_arg(a: &[(); for x in 0..2 {}]) -> bool { |
| 19 | 19 | | ^^^^^^^^^^^^^^^^ expected `usize`, found `()` |
| ... | ... | @@ -25,7 +25,7 @@ LL | fn for_in_arg(a: &[(); for x in 0..2 {} /* `usize` value */]) -> bool { |
| 25 | 25 | | +++++++++++++++++++ |
| 26 | 26 | |
| 27 | 27 | error[E0308]: mismatched types |
| 28 | --> $DIR/coerce-loop-issue-122561.rs:4:5 | |
| 28 | --> $DIR/coerce-loop-issue-122561.rs:6:5 | |
| 29 | 29 | | |
| 30 | 30 | LL | fn for_infinite() -> bool { |
| 31 | 31 | | ---- expected `bool` because of return type |
| ... | ... | @@ -43,7 +43,7 @@ LL + /* `bool` value */ |
| 43 | 43 | | |
| 44 | 44 | |
| 45 | 45 | error[E0308]: mismatched types |
| 46 | --> $DIR/coerce-loop-issue-122561.rs:11:5 | |
| 46 | --> $DIR/coerce-loop-issue-122561.rs:13:5 | |
| 47 | 47 | | |
| 48 | 48 | LL | fn for_finite() -> String { |
| 49 | 49 | | ------ expected `String` because of return type |
| ... | ... | @@ -61,7 +61,7 @@ LL + /* `String` value */ |
| 61 | 61 | | |
| 62 | 62 | |
| 63 | 63 | error[E0308]: mismatched types |
| 64 | --> $DIR/coerce-loop-issue-122561.rs:18:5 | |
| 64 | --> $DIR/coerce-loop-issue-122561.rs:20:5 | |
| 65 | 65 | | |
| 66 | 66 | LL | fn for_zero_times() -> bool { |
| 67 | 67 | | ---- expected `bool` because of return type |
| ... | ... | @@ -79,7 +79,7 @@ LL + /* `bool` value */ |
| 79 | 79 | | |
| 80 | 80 | |
| 81 | 81 | error[E0308]: mismatched types |
| 82 | --> $DIR/coerce-loop-issue-122561.rs:25:5 | |
| 82 | --> $DIR/coerce-loop-issue-122561.rs:27:5 | |
| 83 | 83 | | |
| 84 | 84 | LL | fn for_never_type() -> ! { |
| 85 | 85 | | - expected `!` because of return type |
| ... | ... | @@ -98,7 +98,7 @@ LL + /* `loop {}` or `panic!("...")` */ |
| 98 | 98 | | |
| 99 | 99 | |
| 100 | 100 | error[E0308]: mismatched types |
| 101 | --> $DIR/coerce-loop-issue-122561.rs:33:32 | |
| 101 | --> $DIR/coerce-loop-issue-122561.rs:35:32 | |
| 102 | 102 | | |
| 103 | 103 | LL | fn for_single_line() -> bool { for i in 0.. { return false; } } |
| 104 | 104 | | ---- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()` |
| ... | ... | @@ -112,7 +112,7 @@ LL | fn for_single_line() -> bool { for i in 0.. { return false; } /* `bool` val |
| 112 | 112 | | ++++++++++++++++++ |
| 113 | 113 | |
| 114 | 114 | error[E0308]: mismatched types |
| 115 | --> $DIR/coerce-loop-issue-122561.rs:47:5 | |
| 115 | --> $DIR/coerce-loop-issue-122561.rs:49:5 | |
| 116 | 116 | | |
| 117 | 117 | LL | fn while_inifinite() -> bool { |
| 118 | 118 | | ---- expected `bool` because of return type |
| ... | ... | @@ -131,7 +131,7 @@ LL + /* `bool` value */ |
| 131 | 131 | | |
| 132 | 132 | |
| 133 | 133 | error[E0308]: mismatched types |
| 134 | --> $DIR/coerce-loop-issue-122561.rs:56:5 | |
| 134 | --> $DIR/coerce-loop-issue-122561.rs:58:5 | |
| 135 | 135 | | |
| 136 | 136 | LL | fn while_finite() -> bool { |
| 137 | 137 | | ---- expected `bool` because of return type |
| ... | ... | @@ -151,7 +151,7 @@ LL + /* `bool` value */ |
| 151 | 151 | | |
| 152 | 152 | |
| 153 | 153 | error[E0308]: mismatched types |
| 154 | --> $DIR/coerce-loop-issue-122561.rs:64:5 | |
| 154 | --> $DIR/coerce-loop-issue-122561.rs:66:5 | |
| 155 | 155 | | |
| 156 | 156 | LL | fn while_zero_times() -> bool { |
| 157 | 157 | | ---- expected `bool` because of return type |
| ... | ... | @@ -169,7 +169,7 @@ LL + /* `bool` value */ |
| 169 | 169 | | |
| 170 | 170 | |
| 171 | 171 | error[E0308]: mismatched types |
| 172 | --> $DIR/coerce-loop-issue-122561.rs:71:5 | |
| 172 | --> $DIR/coerce-loop-issue-122561.rs:73:5 | |
| 173 | 173 | | |
| 174 | 174 | LL | fn while_never_type() -> ! { |
| 175 | 175 | | - expected `!` because of return type |
| ... | ... | @@ -188,7 +188,7 @@ LL + /* `loop {}` or `panic!("...")` */ |
| 188 | 188 | | |
| 189 | 189 | |
| 190 | 190 | error[E0308]: mismatched types |
| 191 | --> $DIR/coerce-loop-issue-122561.rs:85:5 | |
| 191 | --> $DIR/coerce-loop-issue-122561.rs:87:5 | |
| 192 | 192 | | |
| 193 | 193 | LL | / for i in 0.. { |
| 194 | 194 | LL | | |
| ... | ... | @@ -203,7 +203,7 @@ LL + /* `i32` value */ |
| 203 | 203 | | |
| 204 | 204 | |
| 205 | 205 | error[E0308]: mismatched types |
| 206 | --> $DIR/coerce-loop-issue-122561.rs:92:9 | |
| 206 | --> $DIR/coerce-loop-issue-122561.rs:94:9 | |
| 207 | 207 | | |
| 208 | 208 | LL | / for i in 0..5 { |
| 209 | 209 | LL | | |
| ... | ... | @@ -218,7 +218,7 @@ LL + /* `usize` value */ |
| 218 | 218 | | |
| 219 | 219 | |
| 220 | 220 | error[E0308]: mismatched types |
| 221 | --> $DIR/coerce-loop-issue-122561.rs:98:9 | |
| 221 | --> $DIR/coerce-loop-issue-122561.rs:100:9 | |
| 222 | 222 | | |
| 223 | 223 | LL | / while false { |
| 224 | 224 | LL | | |
| ... | ... | @@ -233,7 +233,7 @@ LL + /* `usize` value */ |
| 233 | 233 | | |
| 234 | 234 | |
| 235 | 235 | error[E0308]: mismatched types |
| 236 | --> $DIR/coerce-loop-issue-122561.rs:104:23 | |
| 236 | --> $DIR/coerce-loop-issue-122561.rs:106:23 | |
| 237 | 237 | | |
| 238 | 238 | LL | let _ = |a: &[(); for x in 0..2 {}]| {}; |
| 239 | 239 | | ^^^^^^^^^^^^^^^^ expected `usize`, found `()` |
tests/ui/consts/const-eval/raw-bytes.rs+1-1| ... | ... | @@ -34,7 +34,7 @@ const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; |
| 34 | 34 | #[derive(Copy, Clone)] |
| 35 | 35 | enum Never {} |
| 36 | 36 | |
| 37 | // An enum with 3 variants of which some are uninhabited -- so the uninhabited variants *do* | |
| 37 | // An enum with 4 variants of which only some are uninhabited -- so the uninhabited variants *do* | |
| 38 | 38 | // have a discriminant. |
| 39 | 39 | enum UninhDiscriminant { |
| 40 | 40 | A, |
tests/ui/consts/let-irrefutable-pattern-ice-120337.rs+9-1| ... | ... | @@ -1,10 +1,18 @@ |
| 1 | // Regression test for <https://github.com/rust-lang/rust/issues/120337>. | |
| 2 | // | |
| 3 | // This checks that const eval doesn't cause an ICE when reading an uninhabited | |
| 4 | // variant. (N.B. this is UB, but not currently detected by rustc) | |
| 5 | // | |
| 1 | 6 | //@ check-pass |
| 2 | 7 | #![feature(never_type)] |
| 8 | ||
| 3 | 9 | #[derive(Copy, Clone)] |
| 4 | 10 | pub enum E { A(!), } |
| 11 | ||
| 5 | 12 | pub union U { u: (), e: E, } |
| 13 | ||
| 6 | 14 | pub const C: () = { |
| 7 | let E::A(ref a) = unsafe { &(&U { u: () }).e}; | |
| 15 | let E::A(ref a) = unsafe { &(&U { u: () }).e }; | |
| 8 | 16 | }; |
| 9 | 17 | |
| 10 | 18 | fn main() {} |
tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-call.rs created+38| ... | ... | @@ -0,0 +1,38 @@ |
| 1 | //@ add-minicore | |
| 2 | //@ ignore-backends: gcc | |
| 3 | //@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib | |
| 4 | //@ needs-llvm-components: arm | |
| 5 | #![expect(incomplete_features)] | |
| 6 | #![feature(no_core, explicit_tail_calls, abi_cmse_nonsecure_call)] | |
| 7 | #![no_core] | |
| 8 | ||
| 9 | extern crate minicore; | |
| 10 | use minicore::*; | |
| 11 | ||
| 12 | unsafe extern "C" { | |
| 13 | safe fn magic() -> extern "cmse-nonsecure-call" fn(u32, u32) -> u32; | |
| 14 | } | |
| 15 | ||
| 16 | // The `cmse-nonsecure-call` ABI can only occur on function pointers: | |
| 17 | // | |
| 18 | // - a `cmse-nonsecure-call` definition throws an error | |
| 19 | // - a `cmse-nonsecure-call` become in a definition with any other ABI is an ABI mismatch | |
| 20 | #[no_mangle] | |
| 21 | extern "cmse-nonsecure-call" fn become_nonsecure_call_1(x: u32, y: u32) -> u32 { | |
| 22 | //~^ ERROR the `"cmse-nonsecure-call"` ABI is only allowed on function pointers | |
| 23 | unsafe { | |
| 24 | let f = magic(); | |
| 25 | become f(1, 2) | |
| 26 | //~^ ERROR ABI does not support guaranteed tail calls | |
| 27 | } | |
| 28 | } | |
| 29 | ||
| 30 | #[no_mangle] | |
| 31 | extern "C" fn become_nonsecure_call_2(x: u32, y: u32) -> u32 { | |
| 32 | unsafe { | |
| 33 | let f = magic(); | |
| 34 | become f(1, 2) | |
| 35 | //~^ ERROR mismatched function ABIs | |
| 36 | //~| ERROR ABI does not support guaranteed tail calls | |
| 37 | } | |
| 38 | } |
tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-call.stderr created+34| ... | ... | @@ -0,0 +1,34 @@ |
| 1 | error[E0781]: the `"cmse-nonsecure-call"` ABI is only allowed on function pointers | |
| 2 | --> $DIR/cmse-nonsecure-call.rs:21:1 | |
| 3 | | | |
| 4 | LL | extern "cmse-nonsecure-call" fn become_nonsecure_call_1(x: u32, y: u32) -> u32 { | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 6 | ||
| 7 | error: ABI does not support guaranteed tail calls | |
| 8 | --> $DIR/cmse-nonsecure-call.rs:25:9 | |
| 9 | | | |
| 10 | LL | become f(1, 2) | |
| 11 | | ^^^^^^^^^^^^^^ | |
| 12 | | | |
| 13 | = note: `become` is not supported for `extern "cmse-nonsecure-call"` functions | |
| 14 | ||
| 15 | error: mismatched function ABIs | |
| 16 | --> $DIR/cmse-nonsecure-call.rs:34:9 | |
| 17 | | | |
| 18 | LL | become f(1, 2) | |
| 19 | | ^^^^^^^^^^^^^^ | |
| 20 | | | |
| 21 | = note: `become` requires caller and callee to have the same ABI | |
| 22 | = note: caller ABI is `"C"`, while callee ABI is `"cmse-nonsecure-call"` | |
| 23 | ||
| 24 | error: ABI does not support guaranteed tail calls | |
| 25 | --> $DIR/cmse-nonsecure-call.rs:34:9 | |
| 26 | | | |
| 27 | LL | become f(1, 2) | |
| 28 | | ^^^^^^^^^^^^^^ | |
| 29 | | | |
| 30 | = note: `become` is not supported for `extern "cmse-nonsecure-call"` functions | |
| 31 | ||
| 32 | error: aborting due to 4 previous errors | |
| 33 | ||
| 34 | For more information about this error, try `rustc --explain E0781`. |
tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-entry.rs created+22| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | //@ add-minicore | |
| 2 | //@ ignore-backends: gcc | |
| 3 | //@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib | |
| 4 | //@ needs-llvm-components: arm | |
| 5 | #![expect(incomplete_features)] | |
| 6 | #![feature(no_core, explicit_tail_calls, cmse_nonsecure_entry)] | |
| 7 | #![no_core] | |
| 8 | ||
| 9 | extern crate minicore; | |
| 10 | use minicore::*; | |
| 11 | ||
| 12 | #[inline(never)] | |
| 13 | extern "cmse-nonsecure-entry" fn entry(c: bool, x: u32, y: u32) -> u32 { | |
| 14 | if c { x } else { y } | |
| 15 | } | |
| 16 | ||
| 17 | // A `cmse-nonsecure-entry` clears registers before returning, so a tail call cannot be guaranteed. | |
| 18 | #[unsafe(no_mangle)] | |
| 19 | extern "cmse-nonsecure-entry" fn become_nonsecure_entry(c: bool, x: u32, y: u32) -> u32 { | |
| 20 | become entry(c, x, y) | |
| 21 | //~^ ERROR ABI does not support guaranteed tail calls | |
| 22 | } |
tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-entry.stderr created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | error: ABI does not support guaranteed tail calls | |
| 2 | --> $DIR/cmse-nonsecure-entry.rs:20:5 | |
| 3 | | | |
| 4 | LL | become entry(c, x, y) | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^ | |
| 6 | | | |
| 7 | = note: `become` is not supported for `extern "cmse-nonsecure-entry"` functions | |
| 8 | ||
| 9 | error: aborting due to 1 previous error | |
| 10 |
tests/ui/never_type/dispatch_from_dyn_zst.rs deleted-51| ... | ... | @@ -1,51 +0,0 @@ |
| 1 | //@ run-pass | |
| 2 | ||
| 3 | #![feature(unsize, dispatch_from_dyn, never_type)] | |
| 4 | ||
| 5 | #![allow(dead_code)] | |
| 6 | ||
| 7 | use std::{ | |
| 8 | ops::DispatchFromDyn, | |
| 9 | marker::{Unsize, PhantomData}, | |
| 10 | }; | |
| 11 | ||
| 12 | struct Zst; | |
| 13 | struct NestedZst(PhantomData<()>, Zst); | |
| 14 | ||
| 15 | ||
| 16 | struct WithUnit<T: ?Sized>(Box<T>, ()); | |
| 17 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<WithUnit<U>> for WithUnit<T> | |
| 18 | where T: Unsize<U> {} | |
| 19 | ||
| 20 | struct WithPhantom<T: ?Sized>(Box<T>, PhantomData<()>); | |
| 21 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<WithPhantom<U>> for WithPhantom<T> | |
| 22 | where T: Unsize<U> {} | |
| 23 | ||
| 24 | struct WithNever<T: ?Sized>(Box<T>, !); | |
| 25 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<WithNever<U>> for WithNever<T> | |
| 26 | where T: Unsize<U> {} | |
| 27 | ||
| 28 | struct WithZst<T: ?Sized>(Box<T>, Zst); | |
| 29 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<WithZst<U>> for WithZst<T> | |
| 30 | where T: Unsize<U> {} | |
| 31 | ||
| 32 | struct WithNestedZst<T: ?Sized>(Box<T>, NestedZst); | |
| 33 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<WithNestedZst<U>> for WithNestedZst<T> | |
| 34 | where T: Unsize<U> {} | |
| 35 | ||
| 36 | ||
| 37 | struct Generic<T: ?Sized, A>(Box<T>, A); | |
| 38 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Generic<U, ()>> for Generic<T, ()> | |
| 39 | where T: Unsize<U> {} | |
| 40 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Generic<U, PhantomData<()>>> | |
| 41 | for Generic<T, PhantomData<()>> | |
| 42 | where T: Unsize<U> {} | |
| 43 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Generic<U, !>> for Generic<T, !> | |
| 44 | where T: Unsize<U> {} | |
| 45 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Generic<U, Zst>> for Generic<T, Zst> | |
| 46 | where T: Unsize<U> {} | |
| 47 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Generic<U, NestedZst>> for Generic<T, NestedZst> | |
| 48 | where T: Unsize<U> {} | |
| 49 | ||
| 50 | ||
| 51 | fn main() {} |
tests/ui/self/dispatch_from_dyn_zst.rs created+51| ... | ... | @@ -0,0 +1,51 @@ |
| 1 | //@ run-pass | |
| 2 | ||
| 3 | #![feature(unsize, dispatch_from_dyn, never_type)] | |
| 4 | ||
| 5 | #![allow(dead_code)] | |
| 6 | ||
| 7 | use std::{ | |
| 8 | ops::DispatchFromDyn, | |
| 9 | marker::{Unsize, PhantomData}, | |
| 10 | }; | |
| 11 | ||
| 12 | struct Zst; | |
| 13 | struct NestedZst(PhantomData<()>, Zst); | |
| 14 | ||
| 15 | ||
| 16 | struct WithUnit<T: ?Sized>(Box<T>, ()); | |
| 17 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<WithUnit<U>> for WithUnit<T> | |
| 18 | where T: Unsize<U> {} | |
| 19 | ||
| 20 | struct WithPhantom<T: ?Sized>(Box<T>, PhantomData<()>); | |
| 21 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<WithPhantom<U>> for WithPhantom<T> | |
| 22 | where T: Unsize<U> {} | |
| 23 | ||
| 24 | struct WithNever<T: ?Sized>(Box<T>, !); | |
| 25 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<WithNever<U>> for WithNever<T> | |
| 26 | where T: Unsize<U> {} | |
| 27 | ||
| 28 | struct WithZst<T: ?Sized>(Box<T>, Zst); | |
| 29 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<WithZst<U>> for WithZst<T> | |
| 30 | where T: Unsize<U> {} | |
| 31 | ||
| 32 | struct WithNestedZst<T: ?Sized>(Box<T>, NestedZst); | |
| 33 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<WithNestedZst<U>> for WithNestedZst<T> | |
| 34 | where T: Unsize<U> {} | |
| 35 | ||
| 36 | ||
| 37 | struct Generic<T: ?Sized, A>(Box<T>, A); | |
| 38 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Generic<U, ()>> for Generic<T, ()> | |
| 39 | where T: Unsize<U> {} | |
| 40 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Generic<U, PhantomData<()>>> | |
| 41 | for Generic<T, PhantomData<()>> | |
| 42 | where T: Unsize<U> {} | |
| 43 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Generic<U, !>> for Generic<T, !> | |
| 44 | where T: Unsize<U> {} | |
| 45 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Generic<U, Zst>> for Generic<T, Zst> | |
| 46 | where T: Unsize<U> {} | |
| 47 | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Generic<U, NestedZst>> for Generic<T, NestedZst> | |
| 48 | where T: Unsize<U> {} | |
| 49 | ||
| 50 | ||
| 51 | fn main() {} |
triagebot.toml-3| ... | ... | @@ -922,9 +922,6 @@ cc = ["@davidtwco", "@wesleywiser"] |
| 922 | 922 | [mentions."compiler/rustc_codegen_cranelift"] |
| 923 | 923 | cc = ["@bjorn3"] |
| 924 | 924 | |
| 925 | [mentions."compiler/rustc_codegen_ssa"] | |
| 926 | cc = ["@WaffleLapkin"] | |
| 927 | ||
| 928 | 925 | [mentions."compiler/rustc_codegen_gcc"] |
| 929 | 926 | cc = ["@antoyo", "@GuillaumeGomez"] |
| 930 | 927 |