diff --git a/Cargo.lock b/Cargo.lock index 75c5f78e2b6..e1b5ea30f50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,13 +37,14 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ "cfg-if", "once_cell", "version_check", + "zerocopy", ] [[package]] @@ -222,7 +223,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -525,7 +526,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -552,7 +553,7 @@ dependencies = [ "regex", "rustc_tools_util", "serde", - "syn 2.0.29", + "syn 2.0.32", "tempfile", "termize", "tester", @@ -964,7 +965,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -986,7 +987,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -1001,7 +1002,7 @@ version = "0.1.76" dependencies = [ "itertools", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -1068,7 +1069,7 @@ dependencies = [ "darling 0.20.3", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -1157,7 +1158,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -1489,7 +1490,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -1647,9 +1648,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", "allocator-api2", @@ -1912,7 +1913,7 @@ checksum = "2060258edfcfe32ca7058849bf0f146cb5c59aadbedf480333c0d0002f97bc99" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -2085,9 +2086,9 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ "libc", ] @@ -2675,7 +2676,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -2892,7 +2893,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -3868,7 +3869,7 @@ dependencies = [ "fluent-syntax", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", "unic-langid", ] @@ -3999,7 +4000,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", "synstructure", ] @@ -4145,7 +4146,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", "synstructure", ] @@ -4729,7 +4730,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -4892,7 +4893,7 @@ checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -5183,9 +5184,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.29" +version = "2.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" dependencies = [ "proc-macro2", "quote", @@ -5200,7 +5201,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", "unicode-xid", ] @@ -5379,7 +5380,7 @@ checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -5600,7 +5601,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -5994,7 +5995,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", "wasm-bindgen-shared", ] @@ -6028,7 +6029,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6097,7 +6098,7 @@ checksum = "970efb0b6849eb8a87a898f586af7cc167567b070014c7434514c0bde0ca341c" dependencies = [ "proc-macro2", "rayon", - "syn 2.0.29", + "syn 2.0.32", "windows-metadata", ] @@ -6339,10 +6340,30 @@ checksum = "d5e19fb6ed40002bab5403ffa37e53e0e56f914a4450c8765f533018db1db35f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", "synstructure", ] +[[package]] +name = "zerocopy" +version = "0.7.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d6f15f7ade05d2a4935e34a457b936c23dc70a05cc1d97133dc99e7a3fe0f0e" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbbad221e3f78500350ecbd7dfa4e63ef945c05f4c61cb7f4d3f84cd0bba649b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + [[package]] name = "zerofrom" version = "0.1.3" @@ -6360,7 +6381,7 @@ checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", "synstructure", ] @@ -6383,7 +6404,7 @@ checksum = "acabf549809064225ff8878baedc4ce3732ac3b07e7c7ce6e5c2ccdbc485c324" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 0ca19e5fe4a..3f2ed257d08 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -9,7 +9,7 @@ ar_archive_writer = "0.1.5" bitflags = "1.2.1" cc = "1.0.69" itertools = "0.11" -jobserver = "0.1.22" +jobserver = "0.1.27" pathdiff = "0.2.0" regex = "1.4" rustc_arena = { path = "../rustc_arena" } diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 6617f6f2ffb..221ff2b60e9 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -12,12 +12,12 @@ use rustc_middle::mir; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::def_id::DefId; -use rustc_target::abi::Size; +use rustc_target::abi::{Align, Size}; use rustc_target::spec::abi::Abi as CallAbi; use super::{ - AllocBytes, AllocId, AllocRange, Allocation, ConstAllocation, FnArg, Frame, ImmTy, InterpCx, - InterpResult, MPlaceTy, MemoryKind, OpTy, PlaceTy, Pointer, Provenance, + AllocBytes, AllocId, AllocKind, AllocRange, Allocation, ConstAllocation, FnArg, Frame, ImmTy, + InterpCx, InterpResult, MPlaceTy, MemoryKind, Misalignment, OpTy, PlaceTy, Pointer, Provenance, }; /// Data returned by Machine::stack_pop, @@ -143,11 +143,18 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// Whether memory accesses should be alignment-checked. fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; - /// Whether, when checking alignment, we should look at the actual address and thus support - /// custom alignment logic based on whatever the integer address happens to be. - /// - /// If this returns true, Provenance::OFFSET_IS_ADDR must be true. - fn use_addr_for_alignment_check(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; + /// Gives the machine a chance to detect more misalignment than the built-in checks would catch. + #[inline(always)] + fn alignment_check( + _ecx: &InterpCx<'mir, 'tcx, Self>, + _alloc_id: AllocId, + _alloc_align: Align, + _alloc_kind: AllocKind, + _offset: Size, + _align: Align, + ) -> Option { + None + } /// Whether to enforce the validity invariant for a specific layout. fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>, layout: TyAndLayout<'tcx>) -> bool; @@ -519,12 +526,6 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { type FrameExtra = (); type Bytes = Box<[u8]>; - #[inline(always)] - fn use_addr_for_alignment_check(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { - // We do not support `use_addr`. - false - } - #[inline(always)] fn ignore_optional_overflow_checks(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { false diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 1c1fd2a71ba..f865c0cc5fa 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -58,6 +58,7 @@ impl fmt::Display for MemoryKind { } /// The return value of `get_alloc_info` indicates the "kind" of the allocation. +#[derive(Copy, Clone, PartialEq, Debug)] pub enum AllocKind { /// A regular live data allocation. LiveData, @@ -473,8 +474,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { match self.ptr_try_get_alloc_id(ptr) { Err(addr) => offset_misalignment(addr, align), Ok((alloc_id, offset, _prov)) => { - let (_size, alloc_align, _kind) = self.get_alloc_info(alloc_id); - if M::use_addr_for_alignment_check(self) { + let (_size, alloc_align, kind) = self.get_alloc_info(alloc_id); + if let Some(misalign) = + M::alignment_check(self, alloc_id, alloc_align, kind, offset, align) + { + Some(misalign) + } else if M::Provenance::OFFSET_IS_ADDR { // `use_addr_for_alignment_check` can only be true if `OFFSET_IS_ADDR` is true. offset_misalignment(ptr.addr().bytes(), align) } else { diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 77d9e491748..4732783a12d 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -11,7 +11,7 @@ elsa = "=1.7.1" ena = "0.14.2" indexmap = { version = "2.0.0" } itertools = "0.11" -jobserver_crate = { version = "0.1.13", package = "jobserver" } +jobserver_crate = { version = "0.1.27", package = "jobserver" } libc = "0.2" measureme = "10.0.0" rustc-hash = "1.1.0" diff --git a/compiler/rustc_data_structures/src/jobserver.rs b/compiler/rustc_data_structures/src/jobserver.rs index 09baa3095a4..b777bfd4d3c 100644 --- a/compiler/rustc_data_structures/src/jobserver.rs +++ b/compiler/rustc_data_structures/src/jobserver.rs @@ -1,40 +1,78 @@ pub use jobserver_crate::Client; -use std::sync::LazyLock; -// We can only call `from_env` once per process +use jobserver_crate::{FromEnv, FromEnvErrorKind}; -// Note that this is unsafe because it may misinterpret file descriptors -// on Unix as jobserver file descriptors. We hopefully execute this near -// the beginning of the process though to ensure we don't get false -// positives, or in other words we try to execute this before we open -// any file descriptors ourselves. -// -// Pick a "reasonable maximum" if we don't otherwise have -// a jobserver in our environment, capping out at 32 so we -// don't take everything down by hogging the process run queue. -// The fixed number is used to have deterministic compilation -// across machines. -// -// Also note that we stick this in a global because there could be -// multiple rustc instances in this process, and the jobserver is -// per-process. -static GLOBAL_CLIENT: LazyLock = LazyLock::new(|| unsafe { - Client::from_env().unwrap_or_else(|| { - let client = Client::new(32).expect("failed to create jobserver"); - // Acquire a token for the main thread which we can release later - client.acquire_raw().ok(); - client - }) +use std::sync::{LazyLock, OnceLock}; + +// We can only call `from_env_ext` once per process + +// We stick this in a global because there could be multiple rustc instances +// in this process, and the jobserver is per-process. +static GLOBAL_CLIENT: LazyLock> = LazyLock::new(|| { + // Note that this is unsafe because it may misinterpret file descriptors + // on Unix as jobserver file descriptors. We hopefully execute this near + // the beginning of the process though to ensure we don't get false + // positives, or in other words we try to execute this before we open + // any file descriptors ourselves. + let FromEnv { client, var } = unsafe { Client::from_env_ext(true) }; + + let error = match client { + Ok(client) => return Ok(client), + Err(e) => e, + }; + + if matches!( + error.kind(), + FromEnvErrorKind::NoEnvVar | FromEnvErrorKind::NoJobserver | FromEnvErrorKind::Unsupported + ) { + return Ok(default_client()); + } + + // Environment specifies jobserver, but it looks incorrect. + // Safety: `error.kind()` should be `NoEnvVar` if `var == None`. + let (name, value) = var.unwrap(); + Err(format!( + "failed to connect to jobserver from environment variable `{name}={:?}`: {error}", + value + )) }); +// Create a new jobserver if there's no inherited one. +fn default_client() -> Client { + // Pick a "reasonable maximum" capping out at 32 + // so we don't take everything down by hogging the process run queue. + // The fixed number is used to have deterministic compilation across machines. + let client = Client::new(32).expect("failed to create jobserver"); + + // Acquire a token for the main thread which we can release later + client.acquire_raw().ok(); + + client +} + +static GLOBAL_CLIENT_CHECKED: OnceLock = OnceLock::new(); + +pub fn check(report_warning: impl FnOnce(&'static str)) { + let client_checked = match &*GLOBAL_CLIENT { + Ok(client) => client.clone(), + Err(e) => { + report_warning(e); + default_client() + } + }; + GLOBAL_CLIENT_CHECKED.set(client_checked).ok(); +} + +const ACCESS_ERROR: &str = "jobserver check should have been called earlier"; + pub fn client() -> Client { - GLOBAL_CLIENT.clone() + GLOBAL_CLIENT_CHECKED.get().expect(ACCESS_ERROR).clone() } pub fn acquire_thread() { - GLOBAL_CLIENT.acquire_raw().ok(); + GLOBAL_CLIENT_CHECKED.get().expect(ACCESS_ERROR).acquire_raw().ok(); } pub fn release_thread() { - GLOBAL_CLIENT.release_raw().ok(); + GLOBAL_CLIENT_CHECKED.get().expect(ACCESS_ERROR).release_raw().ok(); } diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index f39d795f0ed..7f56b3850dd 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -36,7 +36,9 @@ //! ``` use crate::FnCtxt; -use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan}; +use rustc_errors::{ + struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, +}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; @@ -53,8 +55,7 @@ use rustc_middle::ty::adjustment::{ use rustc_middle::ty::error::TypeError; use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::visit::TypeVisitableExt; -use rustc_middle::ty::GenericArgsRef; -use rustc_middle::ty::{self, Ty, TypeAndMut}; +use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeAndMut}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{self, DesugaringKind}; @@ -1639,12 +1640,15 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { None, Some(coercion_error), ); - } - - if visitor.ret_exprs.len() > 0 - && let Some(expr) = expression - { - self.note_unreachable_loop_return(&mut err, expr, &visitor.ret_exprs); + if visitor.ret_exprs.len() > 0 { + self.note_unreachable_loop_return( + &mut err, + fcx.tcx, + &expr, + &visitor.ret_exprs, + expected, + ); + } } let reported = err.emit_unless(unsized_return); @@ -1657,8 +1661,10 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fn note_unreachable_loop_return( &self, err: &mut Diagnostic, + tcx: TyCtxt<'tcx>, expr: &hir::Expr<'tcx>, ret_exprs: &Vec<&'tcx hir::Expr<'tcx>>, + ty: Ty<'tcx>, ) { let hir::ExprKind::Loop(_, _, _, loop_span) = expr.kind else { return; @@ -1683,10 +1689,77 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { ret_exprs.len() - MAXITER )); } - err.help( - "return a value for the case when the loop has zero elements to iterate on, or \ - consider changing the return type to account for that possibility", - ); + let hir = tcx.hir(); + let item = hir.get_parent_item(expr.hir_id); + let ret_msg = "return a value for the case when the loop has zero elements to iterate on"; + let ret_ty_msg = + "otherwise consider changing the return type to account for that possibility"; + if let Some(node) = hir.find(item.into()) + && let Some(body_id) = node.body_id() + && let Some(sig) = node.fn_sig() + && let hir::ExprKind::Block(block, _) = hir.body(body_id).value.kind + && !ty.is_never() + { + let indentation = if let None = block.expr + && let [.., last] = &block.stmts[..] + { + tcx.sess.source_map().indentation_before(last.span).unwrap_or_else(String::new) + } else if let Some(expr) = block.expr { + tcx.sess.source_map().indentation_before(expr.span).unwrap_or_else(String::new) + } else { + String::new() + }; + if let None = block.expr + && let [.., last] = &block.stmts[..] + { + err.span_suggestion_verbose( + last.span.shrink_to_hi(), + ret_msg, + format!("\n{indentation}/* `{ty}` value */"), + Applicability::MaybeIncorrect, + ); + } else if let Some(expr) = block.expr { + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + ret_msg, + format!("\n{indentation}/* `{ty}` value */"), + Applicability::MaybeIncorrect, + ); + } + let mut sugg = match sig.decl.output { + hir::FnRetTy::DefaultReturn(span) => { + vec![(span, " -> Option<()>".to_string())] + } + hir::FnRetTy::Return(ty) => { + vec![ + (ty.span.shrink_to_lo(), "Option<".to_string()), + (ty.span.shrink_to_hi(), ">".to_string()), + ] + } + }; + for ret_expr in ret_exprs { + match ret_expr.kind { + hir::ExprKind::Ret(Some(expr)) => { + sugg.push((expr.span.shrink_to_lo(), "Some(".to_string())); + sugg.push((expr.span.shrink_to_hi(), ")".to_string())); + } + hir::ExprKind::Ret(None) => { + sugg.push((ret_expr.span.shrink_to_hi(), " Some(())".to_string())); + } + _ => {} + } + } + if let None = block.expr + && let [.., last] = &block.stmts[..] + { + sugg.push((last.span.shrink_to_hi(), format!("\n{indentation}None"))); + } else if let Some(expr) = block.expr { + sugg.push((expr.span.shrink_to_hi(), format!("\n{indentation}None"))); + } + err.multipart_suggestion(ret_ty_msg, sugg, Applicability::MaybeIncorrect); + } else { + err.help(format!("{ret_msg}, {ret_ty_msg}")); + } } fn report_return_mismatched_types<'a>( diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 6189e5379ea..e1531f2c239 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -63,14 +63,14 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>( let statement_spans = data.statements.iter().filter_map(move |statement| { let expn_span = filtered_statement_span(statement)?; - let span = function_source_span(expn_span, body_span); + let span = unexpand_into_body_span(expn_span, body_span)?; Some(CoverageSpan::new(span, expn_span, bcb, is_closure(statement))) }); let terminator_span = Some(data.terminator()).into_iter().filter_map(move |terminator| { let expn_span = filtered_terminator_span(terminator)?; - let span = function_source_span(expn_span, body_span); + let span = unexpand_into_body_span(expn_span, body_span)?; Some(CoverageSpan::new(span, expn_span, bcb, false)) }); @@ -180,14 +180,16 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option { /// Returns an extrapolated span (pre-expansion[^1]) corresponding to a range /// within the function's body source. This span is guaranteed to be contained /// within, or equal to, the `body_span`. If the extrapolated span is not -/// contained within the `body_span`, the `body_span` is returned. +/// contained within the `body_span`, `None` is returned. /// /// [^1]Expansions result from Rust syntax including macros, syntactic sugar, /// etc.). #[inline] -fn function_source_span(span: Span, body_span: Span) -> Span { +fn unexpand_into_body_span(span: Span, body_span: Span) -> Option { use rustc_span::source_map::original_sp; + // FIXME(#118525): Consider switching from `original_sp` to `Span::find_ancestor_inside`, + // which is similar but gives slightly different results in some edge cases. let original_span = original_sp(span, body_span).with_ctxt(body_span.ctxt()); - if body_span.contains(original_span) { original_span } else { body_span } + body_span.contains(original_span).then_some(original_span) } diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs index e1eb58fecc7..2553df33cc7 100644 --- a/compiler/rustc_session/src/code_stats.rs +++ b/compiler/rustc_session/src/code_stats.rs @@ -132,6 +132,8 @@ impl CodeStats { pub fn print_type_sizes(&self) { let type_sizes = self.type_sizes.borrow(); + // We will soon sort, so the initial order does not matter. + #[allow(rustc::potential_query_instability)] let mut sorted: Vec<_> = type_sizes.iter().collect(); // Primary sort: large-to-small. @@ -227,6 +229,8 @@ impl CodeStats { } pub fn print_vtable_sizes(&self, crate_name: Symbol) { + // We will soon sort, so the initial order does not matter. + #[allow(rustc::potential_query_instability)] let mut infos = std::mem::take(&mut *self.vtable_sizes.lock()).into_values().collect::>(); diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 0b55af2f73b..805854bd5cf 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -6,7 +6,6 @@ #![feature(map_many_mut)] #![feature(iter_intersperse)] #![recursion_limit = "256"] -#![allow(rustc::potential_query_instability)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] #![allow(internal_features)] diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index f7b33cb598b..881e1de6755 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -51,6 +51,9 @@ impl GatedSpans { /// Prepend the given set of `spans` onto the set in `self`. pub fn merge(&self, mut spans: FxHashMap>) { let mut inner = self.spans.borrow_mut(); + // The entries will be moved to another map so the drain order does not + // matter. + #[allow(rustc::potential_query_instability)] for (gate, mut gate_spans) in inner.drain() { spans.entry(gate).or_default().append(&mut gate_spans); } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index c6f435a8f92..57a535d8c10 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -24,7 +24,7 @@ use rustc_errors::registry::Registry; use rustc_errors::{ error_code, fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, ErrorGuaranteed, FluentBundle, Handler, IntoDiagnostic, LazyFallbackBundle, MultiSpan, Noted, - TerminalUrl, + SubdiagnosticMessage, TerminalUrl, }; use rustc_macros::HashStable_Generic; pub use rustc_span::def_id::StableCrateId; @@ -1469,6 +1469,11 @@ pub fn build_session( let asm_arch = if target_cfg.allow_asm { InlineAsmArch::from_str(&target_cfg.arch).ok() } else { None }; + // Check jobserver before getting `jobserver::client`. + jobserver::check(|err| { + handler.early_warn_with_note(err, "the build environment is likely misconfigured") + }); + let sess = Session { target: target_cfg, host, @@ -1776,6 +1781,16 @@ impl EarlyErrorHandler { pub fn early_warn(&self, msg: impl Into) { self.handler.struct_warn(msg).emit() } + + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] + pub fn early_warn_with_note( + &self, + msg: impl Into, + note: impl Into, + ) { + self.handler.struct_warn(msg).note(note).emit() + } } fn mk_emitter(output: ErrorOutputType) -> Box { diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index d5e1efb9663..6b231a30ea7 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2112,7 +2112,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { && !expected_inputs.is_empty() && expected_inputs.len() == found_inputs.len() && let Some(typeck) = &self.typeck_results - && let Res::Def(_, fn_def_id) = typeck.qpath_res(&path, *arg_hir_id) + && let Res::Def(res_kind, fn_def_id) = typeck.qpath_res(&path, *arg_hir_id) + && res_kind.is_fn_like() { let closure: Vec<_> = self .tcx @@ -2155,7 +2156,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .map(|(name, ty)| { format!( "{name}{}", - if ty.has_infer_types() { String::new() } else { format!(": {ty}") } + if ty.has_infer_types() { + String::new() + } else if ty.references_error() { + ": /* type */".to_string() + } else { + format!(": {ty}") + } ) }) .collect(); diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index f25ca9e2b18..ab86e4e39ea 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -341,6 +341,9 @@ extern "rust-intrinsic" { /// [`Ordering::Relaxed`] as the `order`. For example, [`AtomicBool::load`]. #[rustc_nounwind] pub fn atomic_load_relaxed(src: *const T) -> T; + /// Do NOT use this intrinsic; "unordered" operations do not exist in our memory model! + /// In terms of the Rust Abstract Machine, this operation is equivalent to `src.read()`, + /// i.e., it performs a non-atomic read. #[rustc_nounwind] pub fn atomic_load_unordered(src: *const T) -> T; @@ -365,6 +368,9 @@ extern "rust-intrinsic" { /// [`Ordering::Relaxed`] as the `order`. For example, [`AtomicBool::store`]. #[rustc_nounwind] pub fn atomic_store_relaxed(dst: *mut T, val: T); + /// Do NOT use this intrinsic; "unordered" operations do not exist in our memory model! + /// In terms of the Rust Abstract Machine, this operation is equivalent to `dst.write(val)`, + /// i.e., it performs a non-atomic write. #[rustc_nounwind] pub fn atomic_store_unordered(dst: *mut T, val: T); @@ -2312,6 +2318,10 @@ extern "rust-intrinsic" { /// Emits a `!nontemporal` store according to LLVM (see their docs). /// Probably will never become stable. + /// + /// Do NOT use this intrinsic; "nontemporal" operations do not exist in our memory model! + /// It exists to support current stdarch, but the plan is to change stdarch and remove this intrinsic. + /// See for some more discussion. #[rustc_nounwind] pub fn nontemporal_store(ptr: *mut T, val: T); @@ -2849,3 +2859,28 @@ pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { write_bytes(dst, val, count) } } + +/// Inform Miri that a given pointer definitely has a certain alignment. +#[cfg(miri)] +pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize) { + extern "Rust" { + /// Miri-provided extern function to promise that a given pointer is properly aligned for + /// "symbolic" alignment checks. Will fail if the pointer is not actually aligned or `align` is + /// not a power of two. Has no effect when alignment checks are concrete (which is the default). + fn miri_promise_symbolic_alignment(ptr: *const (), align: usize); + } + + fn runtime(ptr: *const (), align: usize) { + // SAFETY: this call is always safe. + unsafe { + miri_promise_symbolic_alignment(ptr, align); + } + } + + const fn compiletime(_ptr: *const (), _align: usize) {} + + // SAFETY: the extra behavior at runtime is for UB checks only. + unsafe { + const_eval_select((ptr, align), compiletime, runtime); + } +} diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index c8a0eb4ffc2..2f47ca29ec5 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -186,10 +186,10 @@ impl *const T { /// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr]. /// /// If using those APIs is not possible because there is no way to preserve a pointer with the - /// required provenance, use [`expose_addr`][pointer::expose_addr] and - /// [`from_exposed_addr`][from_exposed_addr] instead. However, note that this makes - /// your code less portable and less amenable to tools that check for compliance with the Rust - /// memory model. + /// required provenance, then Strict Provenance might not be for you. Use pointer-integer casts + /// or [`expose_addr`][pointer::expose_addr] and [`from_exposed_addr`][from_exposed_addr] + /// instead. However, note that this makes your code less portable and less amenable to tools + /// that check for compliance with the Rust memory model. /// /// On most platforms this will produce a value with the same bytes as the original /// pointer, because all the bytes are dedicated to describing the address. @@ -219,7 +219,8 @@ impl *const T { /// later call [`from_exposed_addr`][] to reconstitute the original pointer including its /// provenance. (Reconstructing address space information, if required, is your responsibility.) /// - /// Using this method means that code is *not* following Strict Provenance rules. Supporting + /// Using this method means that code is *not* following [Strict + /// Provenance][../index.html#strict-provenance] rules. Supporting /// [`from_exposed_addr`][] complicates specification and reasoning and may not be supported by /// tools that help you to stay conformant with the Rust memory model, so it is recommended to /// use [`addr`][pointer::addr] wherever possible. @@ -230,13 +231,13 @@ impl *const T { /// side-effect which is required for [`from_exposed_addr`][] to work is typically not /// available. /// - /// This API and its claimed semantics are part of the Strict Provenance experiment, see the - /// [module documentation][crate::ptr] for details. + /// It is unclear whether this method can be given a satisfying unambiguous specification. This + /// API and its claimed semantics are part of [Exposed Provenance][../index.html#exposed-provenance]. /// /// [`from_exposed_addr`]: from_exposed_addr #[must_use] #[inline(always)] - #[unstable(feature = "strict_provenance", issue = "95228")] + #[unstable(feature = "exposed_provenance", issue = "95228")] pub fn expose_addr(self) -> usize { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. self.cast::<()>() as usize @@ -1367,10 +1368,16 @@ impl *const T { panic!("align_offset: align is not a power-of-two"); } - { - // SAFETY: `align` has been checked to be a power of 2 above - unsafe { align_offset(self, align) } + // SAFETY: `align` has been checked to be a power of 2 above + let ret = unsafe { align_offset(self, align) }; + + // Inform Miri that we want to consider the resulting pointer to be suitably aligned. + #[cfg(miri)] + if ret != usize::MAX { + intrinsics::miri_promise_symbolic_alignment(self.wrapping_add(ret).cast(), align); } + + ret } /// Returns whether the pointer is properly aligned for `T`. diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 2b21016c61d..50cf29227ca 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -312,22 +312,30 @@ //! For instance, ARM explicitly supports high-bit tagging, and so CHERI on ARM inherits //! that and should support it. //! -//! ## Pointer-usize-pointer roundtrips and 'exposed' provenance +//! ## Exposed Provenance //! -//! **This section is *non-normative* and is part of the [Strict Provenance] experiment.** +//! **This section is *non-normative* and is an extension to the [Strict Provenance] experiment.** //! //! As discussed above, pointer-usize-pointer roundtrips are not possible under [Strict Provenance]. -//! However, there exists legacy Rust code that is full of such roundtrips, and legacy platform APIs -//! regularly assume that `usize` can capture all the information that makes up a pointer. There -//! also might be code that cannot be ported to Strict Provenance (which is something we would [like -//! to hear about][Strict Provenance]). +//! This is by design: the goal of Strict Provenance is to provide a clear specification that we are +//! confident can be formalized unambiguously and can be subject to precise formal reasoning. //! -//! For situations like this, there is a fallback plan, a way to 'opt out' of Strict Provenance. -//! However, note that this makes your code a lot harder to specify, and the code will not work -//! (well) with tools like [Miri] and [CHERI]. +//! However, there exist situations where pointer-usize-pointer roundtrips cannot be avoided, or +//! where avoiding them would require major refactoring. Legacy platform APIs also regularly assume +//! that `usize` can capture all the information that makes up a pointer. The goal of Strict +//! Provenance is not to rule out such code; the goal is to put all the *other* pointer-manipulating +//! code onto a more solid foundation. Strict Provenance is about improving the situation where +//! possible (all the code that can be written with Strict Provenance) without making things worse +//! for situations where Strict Provenance is insufficient. //! -//! This fallback plan is provided by the [`expose_addr`] and [`from_exposed_addr`] methods (which -//! are equivalent to `as` casts between pointers and integers). [`expose_addr`] is a lot like +//! For these situations, there is a highly experimental extension to Strict Provenance called +//! *Exposed Provenance*. This extension permits pointer-usize-pointer roundtrips. However, its +//! semantics are on much less solid footing than Strict Provenance, and at this point it is not yet +//! clear where a satisfying unambiguous semantics can be defined for Exposed Provenance. +//! Furthermore, Exposed Provenance will not work (well) with tools like [Miri] and [CHERI]. +//! +//! Exposed Provenance is provided by the [`expose_addr`] and [`from_exposed_addr`] methods, which +//! are meant to replace `as` casts between pointers and integers. [`expose_addr`] is a lot like //! [`addr`], but additionally adds the provenance of the pointer to a global list of 'exposed' //! provenances. (This list is purely conceptual, it exists for the purpose of specifying Rust but //! is not materialized in actual executions, except in tools like [Miri].) [`from_exposed_addr`] @@ -341,10 +349,11 @@ //! there is *no* previously 'exposed' provenance that justifies the way the returned pointer will //! be used, the program has undefined behavior. //! -//! Using [`expose_addr`] or [`from_exposed_addr`] (or the equivalent `as` casts) means that code is +//! Using [`expose_addr`] or [`from_exposed_addr`] (or the `as` casts) means that code is //! *not* following Strict Provenance rules. The goal of the Strict Provenance experiment is to -//! determine whether it is possible to use Rust without [`expose_addr`] and [`from_exposed_addr`]. -//! If this is successful, it would be a major win for avoiding specification complexity and to +//! determine how far one can get in Rust without the use of [`expose_addr`] and +//! [`from_exposed_addr`], and to encourage code to be written with Strict Provenance APIs only. +//! Maximizing the amount of such code is a major win for avoiding specification complexity and to //! facilitate adoption of tools like [CHERI] and [Miri] that can be a big help in increasing the //! confidence in (unsafe) Rust code. //! @@ -619,12 +628,12 @@ pub const fn invalid_mut(addr: usize) -> *mut T { /// Convert an address back to a pointer, picking up a previously 'exposed' provenance. /// -/// This is equivalent to `addr as *const T`. The provenance of the returned pointer is that of *any* -/// pointer that was previously exposed by passing it to [`expose_addr`][pointer::expose_addr], -/// or a `ptr as usize` cast. In addition, memory which is outside the control of the Rust abstract -/// machine (MMIO registers, for example) is always considered to be exposed, so long as this memory -/// is disjoint from memory that will be used by the abstract machine such as the stack, heap, -/// and statics. +/// This is a more rigorously specified alternative to `addr as *const T`. The provenance of the +/// returned pointer is that of *any* pointer that was previously exposed by passing it to +/// [`expose_addr`][pointer::expose_addr], or a `ptr as usize` cast. In addition, memory which is +/// outside the control of the Rust abstract machine (MMIO registers, for example) is always +/// considered to be exposed, so long as this memory is disjoint from memory that will be used by +/// the abstract machine such as the stack, heap, and statics. /// /// If there is no 'exposed' provenance that justifies the way this pointer will be used, /// the program has undefined behavior. In particular, the aliasing rules still apply: pointers @@ -639,7 +648,8 @@ pub const fn invalid_mut(addr: usize) -> *mut T { /// On platforms with multiple address spaces, it is your responsibility to ensure that the /// address makes sense in the address space that this pointer will be used with. /// -/// Using this method means that code is *not* following strict provenance rules. "Guessing" a +/// Using this function means that code is *not* following [Strict +/// Provenance][../index.html#strict-provenance] rules. "Guessing" a /// suitable provenance complicates specification and reasoning and may not be supported by /// tools that help you to stay conformant with the Rust memory model, so it is recommended to /// use [`with_addr`][pointer::with_addr] wherever possible. @@ -649,13 +659,13 @@ pub const fn invalid_mut(addr: usize) -> *mut T { /// since it is generally not possible to actually *compute* which provenance the returned /// pointer has to pick up. /// -/// This API and its claimed semantics are part of the Strict Provenance experiment, see the -/// [module documentation][crate::ptr] for details. +/// It is unclear whether this function can be given a satisfying unambiguous specification. This +/// API and its claimed semantics are part of [Exposed Provenance][../index.html#exposed-provenance]. #[must_use] #[inline(always)] -#[unstable(feature = "strict_provenance", issue = "95228")] +#[unstable(feature = "exposed_provenance", issue = "95228")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -#[allow(fuzzy_provenance_casts)] // this *is* the strict provenance API one should use instead +#[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead pub fn from_exposed_addr(addr: usize) -> *const T where T: Sized, @@ -666,18 +676,20 @@ where /// Convert an address back to a mutable pointer, picking up a previously 'exposed' provenance. /// -/// This is equivalent to `addr as *mut T`. The provenance of the returned pointer is that of *any* -/// pointer that was previously passed to [`expose_addr`][pointer::expose_addr] or a `ptr as usize` -/// cast. If there is no previously 'exposed' provenance that justifies the way this pointer will be -/// used, the program has undefined behavior. Note that there is no algorithm that decides which -/// provenance will be used. You can think of this as "guessing" the right provenance, and the guess -/// will be "maximally in your favor", in the sense that if there is any way to avoid undefined -/// behavior, then that is the guess that will be taken. +/// This is a more rigorously specified alternative to `addr as *mut T`. The provenance of the +/// returned pointer is that of *any* pointer that was previously passed to +/// [`expose_addr`][pointer::expose_addr] or a `ptr as usize` cast. If there is no previously +/// 'exposed' provenance that justifies the way this pointer will be used, the program has undefined +/// behavior. Note that there is no algorithm that decides which provenance will be used. You can +/// think of this as "guessing" the right provenance, and the guess will be "maximally in your +/// favor", in the sense that if there is any way to avoid undefined behavior, then that is the +/// guess that will be taken. /// /// On platforms with multiple address spaces, it is your responsibility to ensure that the /// address makes sense in the address space that this pointer will be used with. /// -/// Using this method means that code is *not* following strict provenance rules. "Guessing" a +/// Using this function means that code is *not* following [Strict +/// Provenance][../index.html#strict-provenance] rules. "Guessing" a /// suitable provenance complicates specification and reasoning and may not be supported by /// tools that help you to stay conformant with the Rust memory model, so it is recommended to /// use [`with_addr`][pointer::with_addr] wherever possible. @@ -687,13 +699,13 @@ where /// since it is generally not possible to actually *compute* which provenance the returned /// pointer has to pick up. /// -/// This API and its claimed semantics are part of the Strict Provenance experiment, see the -/// [module documentation][crate::ptr] for details. +/// It is unclear whether this function can be given a satisfying unambiguous specification. This +/// API and its claimed semantics are part of [Exposed Provenance][../index.html#exposed-provenance]. #[must_use] #[inline(always)] -#[unstable(feature = "strict_provenance", issue = "95228")] +#[unstable(feature = "exposed_provenance", issue = "95228")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -#[allow(fuzzy_provenance_casts)] // this *is* the strict provenance API one should use instead +#[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead pub fn from_exposed_addr_mut(addr: usize) -> *mut T where T: Sized, diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index ce0e6d6f297..3aaae679a6f 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -193,10 +193,10 @@ impl *mut T { /// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr]. /// /// If using those APIs is not possible because there is no way to preserve a pointer with the - /// required provenance, use [`expose_addr`][pointer::expose_addr] and - /// [`from_exposed_addr_mut`][from_exposed_addr_mut] instead. However, note that this makes - /// your code less portable and less amenable to tools that check for compliance with the Rust - /// memory model. + /// required provenance, then Strict Provenance might not be for you. Use pointer-integer casts + /// or [`expose_addr`][pointer::expose_addr] and [`from_exposed_addr`][from_exposed_addr] + /// instead. However, note that this makes your code less portable and less amenable to tools + /// that check for compliance with the Rust memory model. /// /// On most platforms this will produce a value with the same bytes as the original /// pointer, because all the bytes are dedicated to describing the address. @@ -226,7 +226,8 @@ impl *mut T { /// later call [`from_exposed_addr_mut`][] to reconstitute the original pointer including its /// provenance. (Reconstructing address space information, if required, is your responsibility.) /// - /// Using this method means that code is *not* following Strict Provenance rules. Supporting + /// Using this method means that code is *not* following [Strict + /// Provenance][../index.html#strict-provenance] rules. Supporting /// [`from_exposed_addr_mut`][] complicates specification and reasoning and may not be supported /// by tools that help you to stay conformant with the Rust memory model, so it is recommended /// to use [`addr`][pointer::addr] wherever possible. @@ -237,13 +238,13 @@ impl *mut T { /// side-effect which is required for [`from_exposed_addr_mut`][] to work is typically not /// available. /// - /// This API and its claimed semantics are part of the Strict Provenance experiment, see the - /// [module documentation][crate::ptr] for details. + /// It is unclear whether this method can be given a satisfying unambiguous specification. This + /// API and its claimed semantics are part of [Exposed Provenance][../index.html#exposed-provenance]. /// /// [`from_exposed_addr_mut`]: from_exposed_addr_mut #[must_use] #[inline(always)] - #[unstable(feature = "strict_provenance", issue = "95228")] + #[unstable(feature = "exposed_provenance", issue = "95228")] pub fn expose_addr(self) -> usize { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. self.cast::<()>() as usize @@ -259,7 +260,7 @@ impl *mut T { /// This is equivalent to using [`wrapping_offset`][pointer::wrapping_offset] to offset /// `self` to the given address, and therefore has all the same capabilities and restrictions. /// - /// This API and its claimed semantics are part of the Strict Provenance experiment, + /// This API and its claimed semantics are an extension to the Strict Provenance experiment, /// see the [module documentation][crate::ptr] for details. #[must_use] #[inline] @@ -1634,10 +1635,19 @@ impl *mut T { panic!("align_offset: align is not a power-of-two"); } - { - // SAFETY: `align` has been checked to be a power of 2 above - unsafe { align_offset(self, align) } + // SAFETY: `align` has been checked to be a power of 2 above + let ret = unsafe { align_offset(self, align) }; + + // Inform Miri that we want to consider the resulting pointer to be suitably aligned. + #[cfg(miri)] + if ret != usize::MAX { + intrinsics::miri_promise_symbolic_alignment( + self.wrapping_add(ret).cast_const().cast(), + align, + ); } + + ret } /// Returns whether the pointer is properly aligned for `T`. diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index a7b36fe7d29..5957f9fd443 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -3868,6 +3868,12 @@ impl [T] { } else { let (left, rest) = self.split_at(offset); let (us_len, ts_len) = rest.align_to_offsets::(); + // Inform Miri that we want to consider the "middle" pointer to be suitably aligned. + #[cfg(miri)] + crate::intrinsics::miri_promise_symbolic_alignment( + rest.as_ptr().cast(), + mem::align_of::(), + ); // SAFETY: now `rest` is definitely aligned, so `from_raw_parts` below is okay, // since the caller guarantees that we can transmute `T` to `U` safely. unsafe { @@ -3938,6 +3944,12 @@ impl [T] { let (us_len, ts_len) = rest.align_to_offsets::(); let rest_len = rest.len(); let mut_ptr = rest.as_mut_ptr(); + // Inform Miri that we want to consider the "middle" pointer to be suitably aligned. + #[cfg(miri)] + crate::intrinsics::miri_promise_symbolic_alignment( + mut_ptr.cast() as *const (), + mem::align_of::(), + ); // We can't use `rest` again after this, that would invalidate its alias `mut_ptr`! // SAFETY: see comments for `align_to`. unsafe { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 6b4ae943dd6..52b1fe822d6 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -317,6 +317,7 @@ #![feature(error_iter)] #![feature(exact_size_is_empty)] #![feature(exclusive_wrapper)] +#![feature(exposed_provenance)] #![feature(extend_one)] #![feature(float_gamma)] #![feature(float_minimum_maximum)] diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index e4309c782f6..131b1d608e6 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1509,7 +1509,7 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>( matches!(*field.kind, clean::StrippedItem(box clean::StructFieldItem(..))) }) { - return f.write_str("/* private fields */"); + return f.write_str("/* private fields */"); } for (i, ty) in s.iter().enumerate() { @@ -1666,7 +1666,7 @@ fn render_enum_fields( } if variants_stripped && !is_non_exhaustive { - w.write_str(" // some variants omitted\n"); + w.write_str(" // some variants omitted\n"); } if toggle { toggle_close(&mut w); @@ -1811,7 +1811,8 @@ fn item_proc_macro( let name = it.name.expect("proc-macros always have names"); match m.kind { MacroKind::Bang => { - write!(buffer, "{name}!() {{ /* proc-macro */ }}").unwrap(); + write!(buffer, "{name}!() {{ /* proc-macro */ }}") + .unwrap(); } MacroKind::Attr => { write!(buffer, "#[{name}]").unwrap(); @@ -1819,7 +1820,12 @@ fn item_proc_macro( MacroKind::Derive => { write!(buffer, "#[derive({name})]").unwrap(); if !m.helpers.is_empty() { - buffer.write_str("\n{\n // Attributes available to this derive:\n").unwrap(); + buffer + .write_str( + "\n{\n \ + // Attributes available to this derive:\n", + ) + .unwrap(); for attr in &m.helpers { writeln!(buffer, " #[{attr}]").unwrap(); } @@ -2181,7 +2187,7 @@ fn render_union<'a, 'cx: 'a>( } if it.has_stripped_entries().unwrap() { - write!(f, " /* private fields */\n")?; + write!(f, " /* private fields */\n")?; } if toggle { toggle_close(&mut f); @@ -2267,11 +2273,11 @@ fn render_struct_fields( if has_visible_fields { if has_stripped_entries { - write!(w, "\n{tab} /* private fields */"); + write!(w, "\n{tab} /* private fields */"); } write!(w, "\n{tab}"); } else if has_stripped_entries { - write!(w, " /* private fields */ "); + write!(w, " /* private fields */ "); } if toggle { toggle_close(&mut w); @@ -2285,7 +2291,7 @@ fn render_struct_fields( matches!(*field.kind, clean::StrippedItem(box clean::StructFieldItem(..))) }) { - write!(w, "/* private fields */"); + write!(w, "/* private fields */"); } else { for (i, field) in fields.iter().enumerate() { if i > 0 { diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index d655df6d994..a7412f90c85 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -501,6 +501,14 @@ pub fn phase_runner(mut binary_args: impl Iterator, phase: Runner // Set missing env vars. We prefer build-time env vars over run-time ones; see // for the kind of issue that fixes. for (name, val) in info.env { + // `CARGO_MAKEFLAGS` contains information about how to reach the + // jobserver, but by the time the program is being run, that jobserver + // no longer exists. Hence we shouldn't forward this. + // FIXME: Miri builds the final crate without a jobserver. + // This may be fixed with github.com/rust-lang/cargo/issues/12597. + if name == "CARGO_MAKEFLAGS" { + continue; + } if let Some(old_val) = env::var_os(&name) { if old_val == val { // This one did not actually change, no need to re-set it. diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index f066ac1e3ff..243dc5e779b 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -2,7 +2,7 @@ //! `Machine` trait. use std::borrow::Cow; -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::fmt; use std::path::Path; use std::process; @@ -309,11 +309,20 @@ pub struct AllocExtra<'tcx> { /// if this allocation is leakable. The backtrace is not /// pruned yet; that should be done before printing it. pub backtrace: Option>>, + /// An offset inside this allocation that was deemed aligned even for symbolic alignment checks. + /// Invariant: the promised alignment will never be less than the native alignment of this allocation. + pub symbolic_alignment: Cell>, } impl VisitProvenance for AllocExtra<'_> { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { - let AllocExtra { borrow_tracker, data_race, weak_memory, backtrace: _ } = self; + let AllocExtra { + borrow_tracker, + data_race, + weak_memory, + backtrace: _, + symbolic_alignment: _, + } = self; borrow_tracker.visit_provenance(visit); data_race.visit_provenance(visit); @@ -902,8 +911,45 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { } #[inline(always)] - fn use_addr_for_alignment_check(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool { - ecx.machine.check_alignment == AlignmentCheck::Int + fn alignment_check( + ecx: &MiriInterpCx<'mir, 'tcx>, + alloc_id: AllocId, + alloc_align: Align, + alloc_kind: AllocKind, + offset: Size, + align: Align, + ) -> Option { + if ecx.machine.check_alignment != AlignmentCheck::Symbolic { + // Just use the built-in check. + return None; + } + if alloc_kind != AllocKind::LiveData { + // Can't have any extra info here. + return None; + } + // Let's see which alignment we have been promised for this allocation. + let alloc_info = ecx.get_alloc_extra(alloc_id).unwrap(); // cannot fail since the allocation is live + let (promised_offset, promised_align) = + alloc_info.symbolic_alignment.get().unwrap_or((Size::ZERO, alloc_align)); + if promised_align < align { + // Definitely not enough. + Some(Misalignment { has: promised_align, required: align }) + } else { + // What's the offset between us and the promised alignment? + let distance = offset.bytes().wrapping_sub(promised_offset.bytes()); + // That must also be aligned. + if distance % align.bytes() == 0 { + // All looking good! + None + } else { + // The biggest power of two through which `distance` is divisible. + let distance_pow2 = 1 << distance.trailing_zeros(); + Some(Misalignment { + has: Align::from_bytes(distance_pow2).unwrap(), + required: align, + }) + } + } } #[inline(always)] @@ -1107,6 +1153,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { data_race: race_alloc, weak_memory: buffer_alloc, backtrace, + symbolic_alignment: Cell::new(None), }, |ptr| ecx.global_base_pointer(ptr), )?; diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 0957e72ee91..3a0ff7a5567 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -467,7 +467,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let ptr = this.read_pointer(ptr)?; let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr).map_err(|_e| { err_machine_stop!(TerminationInfo::Abort(format!( - "pointer passed to miri_get_alloc_id must not be dangling, got {ptr:?}" + "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}" ))) })?; this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?; @@ -499,7 +499,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr)?; if offset != Size::ZERO { throw_unsup_format!( - "pointer passed to miri_static_root must point to beginning of an allocated block" + "pointer passed to `miri_static_root` must point to beginning of an allocated block" ); } this.machine.static_roots.push(alloc_id); @@ -556,6 +556,39 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; } + // Promises that a pointer has a given symbolic alignment. + "miri_promise_symbolic_alignment" => { + let [ptr, align] = this.check_shim(abi, Abi::Rust, link_name, args)?; + let ptr = this.read_pointer(ptr)?; + let align = this.read_target_usize(align)?; + let Ok(align) = Align::from_bytes(align) else { + throw_unsup_format!( + "`miri_promise_symbolic_alignment`: alignment must be a power of 2" + ); + }; + let (_, addr) = ptr.into_parts(); // we know the offset is absolute + if addr.bytes() % align.bytes() != 0 { + throw_unsup_format!( + "`miri_promise_symbolic_alignment`: pointer is not actually aligned" + ); + } + if let Ok((alloc_id, offset, ..)) = this.ptr_try_get_alloc_id(ptr) { + let (_size, alloc_align, _kind) = this.get_alloc_info(alloc_id); + // Not `get_alloc_extra_mut`, need to handle read-only allocations! + let alloc_extra = this.get_alloc_extra(alloc_id)?; + // If the newly promised alignment is bigger than the native alignment of this + // allocation, and bigger than the previously promised alignment, then set it. + if align > alloc_align + && !alloc_extra + .symbolic_alignment + .get() + .is_some_and(|(_, old_align)| align <= old_align) + { + alloc_extra.symbolic_alignment.set(Some((offset, align))); + } + } + } + // Standard C allocation "malloc" => { let [size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs index a031a2a25c9..1e9d927e1a9 100644 --- a/src/tools/miri/src/shims/mod.rs +++ b/src/tools/miri/src/shims/mod.rs @@ -23,7 +23,6 @@ use rustc_middle::{mir, ty}; use rustc_target::spec::abi::Abi; use crate::*; -use helpers::check_arg_count; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { @@ -39,16 +38,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); trace!("eval_fn_call: {:#?}, {:?}", instance, dest); - // There are some more lang items we want to hook that CTFE does not hook (yet). - if this.tcx.lang_items().align_offset_fn() == Some(instance.def.def_id()) { - let args = this.copy_fn_args(args)?; - let [ptr, align] = check_arg_count(&args)?; - if this.align_offset(ptr, align, dest, ret, unwind)? { - return Ok(None); - } - } - - // Try to see if we can do something about foreign items. + // For foreign items, try to see if we can emulate them. if this.tcx.is_foreign_item(instance.def_id()) { // An external function call that does not have a MIR body. We either find MIR elsewhere // or emulate its effect. @@ -64,53 +54,4 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Otherwise, load the MIR. Ok(Some((this.load_mir(instance.def, None)?, instance))) } - - /// Returns `true` if the computation was performed, and `false` if we should just evaluate - /// the actual MIR of `align_offset`. - fn align_offset( - &mut self, - ptr_op: &OpTy<'tcx, Provenance>, - align_op: &OpTy<'tcx, Provenance>, - dest: &PlaceTy<'tcx, Provenance>, - ret: Option, - unwind: mir::UnwindAction, - ) -> InterpResult<'tcx, bool> { - let this = self.eval_context_mut(); - let ret = ret.unwrap(); - - if this.machine.check_alignment != AlignmentCheck::Symbolic { - // Just use actual implementation. - return Ok(false); - } - - let req_align = this.read_target_usize(align_op)?; - - // Stop if the alignment is not a power of two. - if !req_align.is_power_of_two() { - this.start_panic("align_offset: align is not a power-of-two", unwind)?; - return Ok(true); // nothing left to do - } - - let ptr = this.read_pointer(ptr_op)?; - // If this carries no provenance, treat it like an integer. - if ptr.provenance.is_none() { - // Use actual implementation. - return Ok(false); - } - - if let Ok((alloc_id, _offset, _)) = this.ptr_try_get_alloc_id(ptr) { - // Only do anything if we can identify the allocation this goes to. - let (_size, cur_align, _kind) = this.get_alloc_info(alloc_id); - if cur_align.bytes() >= req_align { - // If the allocation alignment is at least the required alignment we use the - // real implementation. - return Ok(false); - } - } - - // Return error result (usize::MAX), and jump to caller. - this.write_scalar(Scalar::from_target_usize(this.target_usize_max(), this), dest)?; - this.go_to_block(ret); - Ok(true) - } } diff --git a/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs b/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs index 07de41d10a0..20fd3306998 100644 --- a/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs +++ b/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-permissive-provenance -#![feature(strict_provenance)] +#![feature(strict_provenance, exposed_provenance)] fn main() { let x: i32 = 3; diff --git a/src/tools/miri/tests/fail/provenance/ptr_invalid.rs b/src/tools/miri/tests/fail/provenance/ptr_invalid.rs index d7d32d83e07..5d44928d1d2 100644 --- a/src/tools/miri/tests/fail/provenance/ptr_invalid.rs +++ b/src/tools/miri/tests/fail/provenance/ptr_invalid.rs @@ -1,4 +1,4 @@ -#![feature(strict_provenance)] +#![feature(strict_provenance, exposed_provenance)] // Ensure that a `ptr::invalid` ptr is truly invalid. fn main() { diff --git a/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs b/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs index 04552d0c332..106cf4d804b 100644 --- a/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs +++ b/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-strict-provenance -#![feature(strict_provenance)] +#![feature(exposed_provenance)] fn main() { let addr = &0 as *const i32 as usize; diff --git a/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs b/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs index 0b4fb0ccd33..b0e4cceb98f 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs +++ b/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-permissive-provenance -#![feature(strict_provenance)] +#![feature(exposed_provenance)] // If we have only exposed read-only pointers, doing a write through a wildcard ptr should fail. diff --git a/src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.call_unaligned_ptr.stderr b/src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.call_unaligned_ptr.stderr new file mode 100644 index 00000000000..e23ac5ac2fc --- /dev/null +++ b/src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.call_unaligned_ptr.stderr @@ -0,0 +1,14 @@ +error: unsupported operation: `miri_promise_symbolic_alignment`: pointer is not actually aligned + --> $DIR/promise_alignment.rs:LL:CC + | +LL | unsafe { utils::miri_promise_symbolic_alignment(align8.add(1).cast(), 8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `miri_promise_symbolic_alignment`: pointer is not actually aligned + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = note: BACKTRACE: + = note: inside `main` at $DIR/promise_alignment.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.read_unaligned_ptr.stderr b/src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.read_unaligned_ptr.stderr new file mode 100644 index 00000000000..0842ccd6d5b --- /dev/null +++ b/src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.read_unaligned_ptr.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: accessing memory based on pointer with alignment ALIGN, but alignment ALIGN is required + --> $DIR/promise_alignment.rs:LL:CC + | +LL | let _val = unsafe { align8.cast::().read() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ accessing memory based on pointer with alignment ALIGN, but alignment ALIGN is required + | + = help: this usually indicates that your program performed an invalid operation and caused Undefined Behavior + = help: but due to `-Zmiri-symbolic-alignment-check`, alignment errors can also be false positives + = note: BACKTRACE: + = note: inside `main` at $DIR/promise_alignment.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.rs b/src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.rs new file mode 100644 index 00000000000..d2d49c604af --- /dev/null +++ b/src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.rs @@ -0,0 +1,54 @@ +//@compile-flags: -Zmiri-symbolic-alignment-check +//@revisions: call_unaligned_ptr read_unaligned_ptr +#![feature(strict_provenance)] + +#[path = "../../utils/mod.rs"] +mod utils; + +#[repr(align(8))] +#[derive(Copy, Clone)] +struct Align8(u64); + +fn main() { + let buffer = [0u32; 128]; // get some 4-aligned memory + let buffer = buffer.as_ptr(); + // "Promising" the alignment down to 1 must not hurt. + unsafe { utils::miri_promise_symbolic_alignment(buffer.cast(), 1) }; + let _val = unsafe { buffer.read() }; + + // Let's find a place to promise alignment 8. + let align8 = if buffer.addr() % 8 == 0 { + buffer + } else { + buffer.wrapping_add(1) + }; + assert!(align8.addr() % 8 == 0); + unsafe { utils::miri_promise_symbolic_alignment(align8.cast(), 8) }; + // Promising the alignment down to 1 *again* still must not hurt. + unsafe { utils::miri_promise_symbolic_alignment(buffer.cast(), 1) }; + // Now we can do 8-aligned reads here. + let _val = unsafe { align8.cast::().read() }; + + // Make sure we error if the pointer is not actually aligned. + if cfg!(call_unaligned_ptr) { + unsafe { utils::miri_promise_symbolic_alignment(align8.add(1).cast(), 8) }; + //~[call_unaligned_ptr]^ ERROR: pointer is not actually aligned + } + + // Also don't accept even higher-aligned reads. + if cfg!(read_unaligned_ptr) { + #[repr(align(16))] + #[derive(Copy, Clone)] + struct Align16(u128); + + let align16 = if align8.addr() % 16 == 0 { + align8 + } else { + align8.wrapping_add(2) + }; + assert!(align16.addr() % 16 == 0); + + let _val = unsafe { align8.cast::().read() }; + //~[read_unaligned_ptr]^ ERROR: accessing memory based on pointer with alignment 8, but alignment 16 is required + } +} diff --git a/src/tools/miri/tests/pass/align_offset_symbolic.rs b/src/tools/miri/tests/pass/align_offset_symbolic.rs index 4e1584b8387..292858ebc2e 100644 --- a/src/tools/miri/tests/pass/align_offset_symbolic.rs +++ b/src/tools/miri/tests/pass/align_offset_symbolic.rs @@ -1,29 +1,6 @@ //@compile-flags: -Zmiri-symbolic-alignment-check #![feature(strict_provenance)] -use std::ptr; - -fn test_align_offset() { - let d = Box::new([0u32; 4]); - // Get u8 pointer to base - let raw = d.as_ptr() as *const u8; - - assert_eq!(raw.align_offset(2), 0); - assert_eq!(raw.align_offset(4), 0); - assert_eq!(raw.align_offset(8), usize::MAX); // requested alignment higher than allocation alignment - - assert_eq!(raw.wrapping_offset(1).align_offset(2), 1); - assert_eq!(raw.wrapping_offset(1).align_offset(4), 3); - assert_eq!(raw.wrapping_offset(1).align_offset(8), usize::MAX); // requested alignment higher than allocation alignment - - assert_eq!(raw.wrapping_offset(2).align_offset(2), 0); - assert_eq!(raw.wrapping_offset(2).align_offset(4), 2); - assert_eq!(raw.wrapping_offset(2).align_offset(8), usize::MAX); // requested alignment higher than allocation alignment - - let p = ptr::invalid::<()>(1); - assert_eq!(p.align_offset(1), 0); -} - fn test_align_to() { const N: usize = 4; let d = Box::new([0u32; N]); @@ -31,6 +8,8 @@ fn test_align_to() { let s = unsafe { std::slice::from_raw_parts(d.as_ptr() as *const u8, 4 * N) }; let raw = s.as_ptr(); + // Cases where we get the expected "middle" part without any fuzz, since the allocation is + // 4-aligned. { let (l, m, r) = unsafe { s.align_to::() }; assert_eq!(l.len(), 0); @@ -63,18 +42,21 @@ fn test_align_to() { assert_eq!(raw.wrapping_offset(4), m.as_ptr() as *const u8); } + // Cases where we request more alignment than the allocation has. { #[repr(align(8))] + #[derive(Copy, Clone)] struct Align8(u64); - let (l, m, r) = unsafe { s.align_to::() }; // requested alignment higher than allocation alignment - assert_eq!(l.len(), 4 * N); - assert_eq!(r.len(), 0); - assert_eq!(m.len(), 0); + let (_l, m, _r) = unsafe { s.align_to::() }; + assert!(m.len() > 0); + // Ensure the symbolic alignment check has been informed that this is okay now. + let _val = m[0]; } } fn test_from_utf8() { + // uses `align_offset` internally const N: usize = 10; let vec = vec![0x4141414141414141u64; N]; let content = unsafe { std::slice::from_raw_parts(vec.as_ptr() as *const u8, 8 * N) }; @@ -103,9 +85,14 @@ fn test_u64_array() { example(&Data::default()); } +fn test_cstr() { + // uses `align_offset` internally + std::ffi::CStr::from_bytes_with_nul(b"this is a test that is longer than 16 bytes\0").unwrap(); +} + fn main() { - test_align_offset(); test_align_to(); test_from_utf8(); test_u64_array(); + test_cstr(); } diff --git a/src/tools/miri/tests/pass/panic/catch_panic.rs b/src/tools/miri/tests/pass/panic/catch_panic.rs index f5b4eaf685d..b83902a8b19 100644 --- a/src/tools/miri/tests/pass/panic/catch_panic.rs +++ b/src/tools/miri/tests/pass/panic/catch_panic.rs @@ -1,5 +1,3 @@ -// We test the `align_offset` panic below, make sure we test the interpreter impl and not the "real" one. -//@compile-flags: -Zmiri-symbolic-alignment-check #![feature(never_type)] #![allow(unconditional_panic, non_fmt_panics)] @@ -70,6 +68,7 @@ fn main() { process::abort() }); + // Panic somewhere in the standard library. test(Some("align_offset: align is not a power-of-two"), |_old_val| { let _ = std::ptr::null::().align_offset(3); process::abort() diff --git a/src/tools/miri/tests/pass/ptr_int_from_exposed.rs b/src/tools/miri/tests/pass/ptr_int_from_exposed.rs index 35a52d0220b..d8d57679e6b 100644 --- a/src/tools/miri/tests/pass/ptr_int_from_exposed.rs +++ b/src/tools/miri/tests/pass/ptr_int_from_exposed.rs @@ -1,7 +1,7 @@ //@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows //@compile-flags: -Zmiri-permissive-provenance -#![feature(strict_provenance)] +#![feature(strict_provenance, exposed_provenance)] use std::ptr; diff --git a/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs b/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs index 9e604f9abb8..e467356dd04 100644 --- a/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs +++ b/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-permissive-provenance -#![feature(strict_provenance)] +#![feature(exposed_provenance)] use std::ptr; // Just to make sure that casting a ref to raw, to int and back to raw diff --git a/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs b/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs index e62ee528686..5bb4e879c3e 100644 --- a/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs +++ b/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-permissive-provenance -#![feature(strict_provenance)] +#![feature(exposed_provenance)] use std::ptr; diff --git a/src/tools/miri/tests/utils/miri_extern.rs b/src/tools/miri/tests/utils/miri_extern.rs index 7363c189840..ff7990561f2 100644 --- a/src/tools/miri/tests/utils/miri_extern.rs +++ b/src/tools/miri/tests/utils/miri_extern.rs @@ -142,4 +142,9 @@ extern "Rust" { /// but in tests we want to for sure run it at certain points to check /// that it doesn't break anything. pub fn miri_run_provenance_gc(); + + /// Miri-provided extern function to promise that a given pointer is properly aligned for + /// "symbolic" alignment checks. Will fail if the pointer is not actually aligned or `align` is + /// not a power of two. Has no effect when alignment checks are concrete (which is the default). + pub fn miri_promise_symbolic_alignment(ptr: *const (), align: usize); } diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 1f8edd7937b..6baf2e21604 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -16,6 +16,7 @@ const LICENSES: &[&str] = &[ "Apache-2.0 OR MIT", "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", // wasi license "Apache-2.0/MIT", + "BSD-2-Clause OR Apache-2.0 OR MIT", // zerocopy "ISC", "MIT / Apache-2.0", "MIT OR Apache-2.0 OR LGPL-2.1-or-later", // r-efi, r-efi-alloc @@ -392,6 +393,8 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "yansi-term", // this is a false-positive: it's only used by rustfmt, but because it's enabled through a feature, tidy thinks it's used by rustc as well. "yoke", "yoke-derive", + "zerocopy", + "zerocopy-derive", "zerofrom", "zerofrom-derive", "zerovec", diff --git a/tests/coverage/async.cov-map b/tests/coverage/async.cov-map index 857e0a536a7..e4354a1af87 100644 --- a/tests/coverage/async.cov-map +++ b/tests/coverage/async.cov-map @@ -74,28 +74,28 @@ Number of file 0 mappings: 6 = ((c0 + c1) - c1) Function name: async::executor::block_on::VTABLE::{closure#0} -Raw bytes (9): 0x[01, 01, 00, 01, 01, 72, 11, 00, 33] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 72, 11, 00, 31] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 114, 17) to (start + 0, 51) +- Code(Counter(0)) at (prev + 114, 17) to (start + 0, 49) Function name: async::executor::block_on::VTABLE::{closure#1} -Raw bytes (9): 0x[01, 01, 00, 01, 01, 73, 11, 00, 33] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 73, 11, 00, 31] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 115, 17) to (start + 0, 51) +- Code(Counter(0)) at (prev + 115, 17) to (start + 0, 49) Function name: async::executor::block_on::VTABLE::{closure#2} -Raw bytes (9): 0x[01, 01, 00, 01, 01, 74, 11, 00, 33] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 74, 11, 00, 31] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 116, 17) to (start + 0, 51) +- Code(Counter(0)) at (prev + 116, 17) to (start + 0, 49) Function name: async::executor::block_on::VTABLE::{closure#3} Raw bytes (9): 0x[01, 01, 00, 01, 01, 75, 11, 00, 13] diff --git a/tests/coverage/async2.cov-map b/tests/coverage/async2.cov-map index cc7aed9aee3..23f26ee4e5f 100644 --- a/tests/coverage/async2.cov-map +++ b/tests/coverage/async2.cov-map @@ -78,28 +78,28 @@ Number of file 0 mappings: 6 = ((c0 + c1) - c1) Function name: async2::executor::block_on::VTABLE::{closure#0} -Raw bytes (9): 0x[01, 01, 00, 01, 01, 2b, 11, 00, 33] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 2b, 11, 00, 31] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 43, 17) to (start + 0, 51) +- Code(Counter(0)) at (prev + 43, 17) to (start + 0, 49) Function name: async2::executor::block_on::VTABLE::{closure#1} -Raw bytes (9): 0x[01, 01, 00, 01, 01, 2c, 11, 00, 33] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 2c, 11, 00, 31] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 44, 17) to (start + 0, 51) +- Code(Counter(0)) at (prev + 44, 17) to (start + 0, 49) Function name: async2::executor::block_on::VTABLE::{closure#2} -Raw bytes (9): 0x[01, 01, 00, 01, 01, 2d, 11, 00, 33] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 2d, 11, 00, 31] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 45, 17) to (start + 0, 51) +- Code(Counter(0)) at (prev + 45, 17) to (start + 0, 49) Function name: async2::executor::block_on::VTABLE::{closure#3} Raw bytes (9): 0x[01, 01, 00, 01, 01, 2e, 11, 00, 13] diff --git a/tests/coverage/inline.cov-map b/tests/coverage/inline.cov-map index 72b10fd0cc2..001c333ae6d 100644 --- a/tests/coverage/inline.cov-map +++ b/tests/coverage/inline.cov-map @@ -15,12 +15,12 @@ Number of file 0 mappings: 5 = ((c0 + c1) - c1) Function name: inline::error -Raw bytes (9): 0x[01, 01, 00, 01, 01, 31, 01, 02, 02] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 31, 01, 01, 14] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 49, 1) to (start + 2, 2) +- Code(Counter(0)) at (prev + 49, 1) to (start + 1, 20) Function name: inline::length:: Raw bytes (9): 0x[01, 01, 00, 01, 01, 1e, 01, 02, 02] diff --git a/tests/coverage/inline.coverage b/tests/coverage/inline.coverage index 6efd9a0830b..68a2e408306 100644 --- a/tests/coverage/inline.coverage +++ b/tests/coverage/inline.coverage @@ -50,5 +50,5 @@ LL| |#[inline(always)] LL| 0|fn error() { LL| 0| panic!("error"); - LL| 0|} + LL| |} diff --git a/tests/coverage/unreachable.cov-map b/tests/coverage/unreachable.cov-map index 495419820c1..55d124a16f5 100644 --- a/tests/coverage/unreachable.cov-map +++ b/tests/coverage/unreachable.cov-map @@ -1,24 +1,24 @@ Function name: unreachable::UNREACHABLE_CLOSURE::{closure#0} -Raw bytes (9): 0x[01, 01, 00, 01, 01, 0f, 27, 00, 49] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 0f, 27, 00, 47] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 15, 39) to (start + 0, 73) +- Code(Counter(0)) at (prev + 15, 39) to (start + 0, 71) Function name: unreachable::unreachable_function -Raw bytes (9): 0x[01, 01, 00, 01, 01, 11, 01, 02, 02] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 11, 01, 01, 25] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 17, 1) to (start + 2, 2) +- Code(Counter(0)) at (prev + 17, 1) to (start + 1, 37) Function name: unreachable::unreachable_intrinsic -Raw bytes (9): 0x[01, 01, 00, 01, 01, 16, 01, 02, 02] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 16, 01, 01, 2c] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 22, 1) to (start + 2, 2) +- Code(Counter(0)) at (prev + 22, 1) to (start + 1, 44) diff --git a/tests/coverage/unreachable.coverage b/tests/coverage/unreachable.coverage index fa0ac9ccfa1..7015bb90aa3 100644 --- a/tests/coverage/unreachable.coverage +++ b/tests/coverage/unreachable.coverage @@ -16,12 +16,12 @@ LL| | LL| 0|fn unreachable_function() { LL| 0| unsafe { unreachable_unchecked() } - LL| 0|} + LL| |} LL| | LL| |// Use an intrinsic to more reliably trigger unreachable-propagation. LL| 0|fn unreachable_intrinsic() { LL| 0| unsafe { std::intrinsics::unreachable() } - LL| 0|} + LL| |} LL| | LL| |#[coverage(off)] LL| |fn main() { diff --git a/tests/run-make/jobserver-error/Makefile b/tests/run-make/jobserver-error/Makefile index 4a1699cc740..a7601b86715 100644 --- a/tests/run-make/jobserver-error/Makefile +++ b/tests/run-make/jobserver-error/Makefile @@ -1,9 +1,15 @@ include ../tools.mk # only-linux -# ignore-test: This test randomly fails, see https://github.com/rust-lang/rust/issues/110321 +# ignore-cross-compile -# Test compiler behavior in case: `jobserver-auth` points to correct pipe which is not jobserver. +# Test compiler behavior in case environment specifies wrong jobserver. all: - bash -c 'echo "fn main() {}" | MAKEFLAGS="--jobserver-auth=3,3" $(RUSTC) - 3&1 | diff jobserver.stderr - + bash -c 'echo "fn main() {}" | MAKEFLAGS="--jobserver-auth=3,3" $(RUSTC)' 2>&1 | diff cannot_open_fd.stderr - + bash -c 'echo "fn main() {}" | MAKEFLAGS="--jobserver-auth=3,3" $(RUSTC) - 3&1 | diff not_a_pipe.stderr - + +# This test randomly fails, see https://github.com/rust-lang/rust/issues/110321 +disabled: + bash -c 'echo "fn main() {}" | MAKEFLAGS="--jobserver-auth=3,3" $(RUSTC) - 3< <(cat /dev/null)' 2>&1 | diff poisoned_pipe.stderr - + diff --git a/tests/run-make/jobserver-error/cannot_open_fd.stderr b/tests/run-make/jobserver-error/cannot_open_fd.stderr new file mode 100644 index 00000000000..343de5cd52c --- /dev/null +++ b/tests/run-make/jobserver-error/cannot_open_fd.stderr @@ -0,0 +1,6 @@ +warning: failed to connect to jobserver from environment variable `MAKEFLAGS="--jobserver-auth=3,3"`: cannot open file descriptor 3 from the jobserver environment variable value: Bad file descriptor (os error 9) + | + = note: the build environment is likely misconfigured + +error: no input filename given + diff --git a/tests/run-make/jobserver-error/not_a_pipe.stderr b/tests/run-make/jobserver-error/not_a_pipe.stderr new file mode 100644 index 00000000000..536c04576b9 --- /dev/null +++ b/tests/run-make/jobserver-error/not_a_pipe.stderr @@ -0,0 +1,4 @@ +warning: failed to connect to jobserver from environment variable `MAKEFLAGS="--jobserver-auth=3,3"`: file descriptor 3 from the jobserver environment variable value is not a pipe + | + = note: the build environment is likely misconfigured + diff --git a/tests/run-make/jobserver-error/jobserver.stderr b/tests/run-make/jobserver-error/poisoned_pipe.stderr similarity index 100% rename from tests/run-make/jobserver-error/jobserver.stderr rename to tests/run-make/jobserver-error/poisoned_pipe.stderr diff --git a/tests/rustdoc-gui/item-decl-comment-highlighting.goml b/tests/rustdoc-gui/item-decl-comment-highlighting.goml new file mode 100644 index 00000000000..60772693d6c --- /dev/null +++ b/tests/rustdoc-gui/item-decl-comment-highlighting.goml @@ -0,0 +1,73 @@ +// This test checks that comments in item declarations are highlighted. +go-to: "file://" + |DOC_PATH| + "/test_docs/private/enum.Enum.html" +show-text: true + +define-function: ( + "check-item-decl-comment", + (theme, url, comment_color), + block { + go-to: |url| + set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} + reload: + assert-css: (".item-decl .comment", {"color": |comment_color|}, ALL) + } +) + +define-function: ( + "check-items-for-theme", + (theme, comment_color), + block { + call-function: ("check-item-decl-comment", { + "theme": |theme|, + "url": "file://" + |DOC_PATH| + "/test_docs/private/enum.Enum.html", + "comment_color": |comment_color|, + }) + call-function: ("check-item-decl-comment", { + "theme": |theme|, + "url": "file://" + |DOC_PATH| + "/test_docs/private/struct.Struct.html", + "comment_color": |comment_color|, + }) + call-function: ("check-item-decl-comment", { + "theme": |theme|, + "url": "file://" + |DOC_PATH| + "/test_docs/private/struct.Tuple.html", + "comment_color": |comment_color|, + }) + call-function: ("check-item-decl-comment", { + "theme": |theme|, + "url": "file://" + |DOC_PATH| + "/test_docs/private/union.Union.html", + "comment_color": |comment_color|, + }) + call-function: ("check-item-decl-comment", { + "theme": |theme|, + "url": "file://" + |DOC_PATH| + "/proc_macro_test/macro.make_answer.html", + "comment_color": |comment_color|, + }) + call-function: ("check-item-decl-comment", { + "theme": |theme|, + "url": "file://" + |DOC_PATH| + "/proc_macro_test/derive.HelperAttr.html", + "comment_color": |comment_color|, + }) + } +) + +call-function: ( + "check-items-for-theme", + { + "theme": "ayu", + "comment_color": "#788797", + } +) +call-function: ( + "check-items-for-theme", + { + "theme": "dark", + "comment_color": "#8d8d8b", + } +) +call-function: ( + "check-items-for-theme", + { + "theme": "light", + "comment_color": "#8e908c", + } +) diff --git a/tests/rustdoc-gui/sidebar-source-code.goml b/tests/rustdoc-gui/sidebar-source-code.goml index 92b9045b734..0d72e670cf4 100644 --- a/tests/rustdoc-gui/sidebar-source-code.goml +++ b/tests/rustdoc-gui/sidebar-source-code.goml @@ -73,7 +73,7 @@ assert: "//*[@class='dir-entry' and @open]/*[text()='sub_mod']" // Only "another_folder" should be "open" in "lib2". assert: "//*[@class='dir-entry' and not(@open)]/*[text()='another_mod']" // All other trees should be collapsed. -assert-count: ("//*[@id='src-sidebar']/details[not(text()='lib2') and not(@open)]", 10) +assert-count: ("//*[@id='src-sidebar']/details[not(text()='lib2') and not(@open)]", 11) // We now switch to mobile mode. set-window-size: (600, 600) diff --git a/tests/rustdoc-gui/src/proc_macro_test/Cargo.lock b/tests/rustdoc-gui/src/proc_macro_test/Cargo.lock new file mode 100644 index 00000000000..eae9d75367f --- /dev/null +++ b/tests/rustdoc-gui/src/proc_macro_test/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "proc_macro_test" +version = "0.1.0" diff --git a/tests/rustdoc-gui/src/proc_macro_test/Cargo.toml b/tests/rustdoc-gui/src/proc_macro_test/Cargo.toml new file mode 100644 index 00000000000..768ced65184 --- /dev/null +++ b/tests/rustdoc-gui/src/proc_macro_test/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "proc_macro_test" +version = "0.1.0" +edition = "2021" + +[lib] +path = "lib.rs" +proc-macro = true diff --git a/tests/rustdoc-gui/src/proc_macro_test/lib.rs b/tests/rustdoc-gui/src/proc_macro_test/lib.rs new file mode 100644 index 00000000000..8a6c62df87c --- /dev/null +++ b/tests/rustdoc-gui/src/proc_macro_test/lib.rs @@ -0,0 +1,11 @@ +use proc_macro::TokenStream; + +#[proc_macro] +pub fn make_answer(_item: TokenStream) -> TokenStream { + "fn answer() -> u32 { 42 }".parse().unwrap() +} + +#[proc_macro_derive(HelperAttr, attributes(helper))] +pub fn derive_helper_attr(_item: TokenStream) -> TokenStream { + TokenStream::new() +} diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs index c7d115bdb98..0bc777230bf 100644 --- a/tests/rustdoc-gui/src/test_docs/lib.rs +++ b/tests/rustdoc-gui/src/test_docs/lib.rs @@ -593,3 +593,21 @@ pub mod foreign_impl_order { fn f(&mut self, fg: [u8; 3]) {} } } + +pub mod private { + pub struct Tuple(u32, u8); + pub struct Struct { + a: u8, + } + + pub union Union { + a: u8, + b: u16, + } + + pub enum Enum { + A, + #[doc(hidden)] + B, + } +} diff --git a/tests/rustdoc/where.SWhere_Simd_item-decl.html b/tests/rustdoc/where.SWhere_Simd_item-decl.html index 46708b9e4e9..1987b1d59f5 100644 --- a/tests/rustdoc/where.SWhere_Simd_item-decl.html +++ b/tests/rustdoc/where.SWhere_Simd_item-decl.html @@ -1,3 +1,3 @@ -
pub struct Simd<T>(/* private fields */)
+
pub struct Simd<T>(/* private fields */)
 where
-    T: MyTrait;
+ T: MyTrait;
\ No newline at end of file diff --git a/tests/rustdoc/where.alpha_trait_decl.html b/tests/rustdoc/where.alpha_trait_decl.html index 0c0b2d1ceca..2c010ca7c2d 100644 --- a/tests/rustdoc/where.alpha_trait_decl.html +++ b/tests/rustdoc/where.alpha_trait_decl.html @@ -1,3 +1,3 @@ -pub struct Alpha<A>(/* private fields */) +pub struct Alpha<A>(/* private fields */) where A: MyTrait; \ No newline at end of file diff --git a/tests/rustdoc/whitespace-after-where-clause.union.html b/tests/rustdoc/whitespace-after-where-clause.union.html index 7e0d5f8717a..e63374760d9 100644 --- a/tests/rustdoc/whitespace-after-where-clause.union.html +++ b/tests/rustdoc/whitespace-after-where-clause.union.html @@ -1,4 +1,4 @@
pub union Union<'a, B>
where B: ToOwned<()> + ?Sized + 'a,
{ - /* private fields */ + /* private fields */ }
\ No newline at end of file diff --git a/tests/rustdoc/whitespace-after-where-clause.union2.html b/tests/rustdoc/whitespace-after-where-clause.union2.html index 177a161b83a..da984343daa 100644 --- a/tests/rustdoc/whitespace-after-where-clause.union2.html +++ b/tests/rustdoc/whitespace-after-where-clause.union2.html @@ -1,3 +1,3 @@
pub union Union2<'a, B: ?Sized + ToOwned<()> + 'a> {
-    /* private fields */
+    /* private fields */
 }
\ No newline at end of file diff --git a/tests/ui/for-loop-while/break-while-condition.stderr b/tests/ui/for-loop-while/break-while-condition.stderr index e79f6a75fde..48b29f44fa1 100644 --- a/tests/ui/for-loop-while/break-while-condition.stderr +++ b/tests/ui/for-loop-while/break-while-condition.stderr @@ -38,7 +38,7 @@ LL | while false { | ^^^^^^^^^^^ this might have zero elements to iterate on LL | return | ------ if the loop doesn't execute, this value would never get returned - = help: return a value for the case when the loop has zero elements to iterate on, or consider changing the return type to account for that possibility + = help: return a value for the case when the loop has zero elements to iterate on, otherwise consider changing the return type to account for that possibility error: aborting due to 3 previous errors diff --git a/tests/ui/mismatched_types/issue-118510.rs b/tests/ui/mismatched_types/issue-118510.rs new file mode 100644 index 00000000000..86b9d44a373 --- /dev/null +++ b/tests/ui/mismatched_types/issue-118510.rs @@ -0,0 +1,10 @@ +pub enum Sexpr<'a, S> { + Ident(&'a mut S), +} + +fn map T>(f: F) {} + +fn main() { + map(Sexpr::Ident); + //~^ ERROR type mismatch in function arguments +} diff --git a/tests/ui/mismatched_types/issue-118510.stderr b/tests/ui/mismatched_types/issue-118510.stderr new file mode 100644 index 00000000000..e8bf92d9047 --- /dev/null +++ b/tests/ui/mismatched_types/issue-118510.stderr @@ -0,0 +1,26 @@ +error[E0631]: type mismatch in function arguments + --> $DIR/issue-118510.rs:8:9 + | +LL | Ident(&'a mut S), + | ----- found signature defined here +... +LL | map(Sexpr::Ident); + | --- ^^^^^^^^^^^^ expected due to this + | | + | required by a bound introduced by this call + | + = note: expected function signature `for<'a> fn(&'a _) -> _` + found function signature `fn(&mut _) -> _` +note: required by a bound in `map` + --> $DIR/issue-118510.rs:5:19 + | +LL | fn map T>(f: F) {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `map` +help: consider wrapping the function in a closure + | +LL | map(|arg0| Sexpr::Ident(&mut *arg0)); + | ++++++ ++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0631`. diff --git a/tests/ui/typeck/issue-100285.rs b/tests/ui/typeck/issue-100285.rs index e206469b85f..460e0457105 100644 --- a/tests/ui/typeck/issue-100285.rs +++ b/tests/ui/typeck/issue-100285.rs @@ -1,6 +1,5 @@ -fn foo(n: i32) -> i32 { - for i in 0..0 { - //~^ ERROR: mismatched types [E0308] +fn foo(n: i32) -> i32 { //~ HELP otherwise consider changing the return type to account for that possibility + for i in 0..0 { //~ ERROR mismatched types [E0308] if n < 0 { return i; } else if n < 10 { @@ -15,8 +14,7 @@ fn foo(n: i32) -> i32 { return 5; } - } - //~| help: return a value for the case when the loop has zero elements to iterate on, or consider changing the return type to account for that possibility + } //~ HELP return a value for the case when the loop has zero elements to iterate on } fn main() {} diff --git a/tests/ui/typeck/issue-100285.stderr b/tests/ui/typeck/issue-100285.stderr index 9a1d5d964fa..9c8685a7712 100644 --- a/tests/ui/typeck/issue-100285.stderr +++ b/tests/ui/typeck/issue-100285.stderr @@ -4,9 +4,9 @@ error[E0308]: mismatched types LL | fn foo(n: i32) -> i32 { | --- expected `i32` because of return type LL | / for i in 0..0 { -LL | | LL | | if n < 0 { LL | | return i; +LL | | } else if n < 10 { ... | LL | | LL | | } @@ -17,7 +17,7 @@ note: the function expects a value to always be returned, but loops might run ze | LL | for i in 0..0 { | ^^^^^^^^^^^^^ this might have zero elements to iterate on -... +LL | if n < 0 { LL | return i; | -------- if the loop doesn't execute, this value would never get returned LL | } else if n < 10 { @@ -27,7 +27,32 @@ LL | } else if n < 20 { LL | return 2; | -------- if the loop doesn't execute, this value would never get returned = note: if the loop doesn't execute, 3 other values would never get returned - = help: return a value for the case when the loop has zero elements to iterate on, or consider changing the return type to account for that possibility +help: return a value for the case when the loop has zero elements to iterate on + | +LL ~ } +LL ~ /* `i32` value */ + | +help: otherwise consider changing the return type to account for that possibility + | +LL ~ fn foo(n: i32) -> Option { +LL | for i in 0..0 { +LL | if n < 0 { +LL ~ return Some(i); +LL | } else if n < 10 { +LL ~ return Some(1); +LL | } else if n < 20 { +LL ~ return Some(2); +LL | } else if n < 30 { +LL ~ return Some(3); +LL | } else if n < 40 { +LL ~ return Some(4); +LL | } else { +LL ~ return Some(5); +LL | } +LL | +LL ~ } +LL ~ None + | error: aborting due to 1 previous error diff --git a/tests/ui/typeck/issue-98982.rs b/tests/ui/typeck/issue-98982.rs index 2553824bbfe..f875d20fa4c 100644 --- a/tests/ui/typeck/issue-98982.rs +++ b/tests/ui/typeck/issue-98982.rs @@ -1,9 +1,7 @@ -fn foo() -> i32 { - for i in 0..0 { - //~^ ERROR: mismatched types [E0308] +fn foo() -> i32 { //~ HELP otherwise consider changing the return type to account for that possibility + for i in 0..0 { //~ ERROR mismatched types [E0308] return i; - } - //~| help: return a value for the case when the loop has zero elements to iterate on, or consider changing the return type to account for that possibility + } //~ HELP return a value for the case when the loop has zero elements to iterate on } fn main() {} diff --git a/tests/ui/typeck/issue-98982.stderr b/tests/ui/typeck/issue-98982.stderr index c854460c34c..d8d5a86b157 100644 --- a/tests/ui/typeck/issue-98982.stderr +++ b/tests/ui/typeck/issue-98982.stderr @@ -4,7 +4,6 @@ error[E0308]: mismatched types LL | fn foo() -> i32 { | --- expected `i32` because of return type LL | / for i in 0..0 { -LL | | LL | | return i; LL | | } | |_____^ expected `i32`, found `()` @@ -14,10 +13,21 @@ note: the function expects a value to always be returned, but loops might run ze | LL | for i in 0..0 { | ^^^^^^^^^^^^^ this might have zero elements to iterate on -LL | LL | return i; | -------- if the loop doesn't execute, this value would never get returned - = help: return a value for the case when the loop has zero elements to iterate on, or consider changing the return type to account for that possibility +help: return a value for the case when the loop has zero elements to iterate on + | +LL ~ } +LL ~ /* `i32` value */ + | +help: otherwise consider changing the return type to account for that possibility + | +LL ~ fn foo() -> Option { +LL | for i in 0..0 { +LL ~ return Some(i); +LL ~ } +LL ~ None + | error: aborting due to 1 previous error