diff --git a/Cargo.lock b/Cargo.lock index 75c6ab37bd14f..7765353092553 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,7 +43,7 @@ dependencies = [ "cfg-if", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -567,7 +567,7 @@ dependencies = [ "termize", "tokio", "toml 0.7.8", - "ui_test", + "ui_test 0.26.5", "walkdir", ] @@ -1442,7 +1442,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", ] [[package]] @@ -2342,18 +2354,18 @@ dependencies = [ "chrono-tz", "colored", "directories", - "getrandom", + "getrandom 0.3.1", "libc", "libffi", "libloading", "measureme", - "rand", + "rand 0.9.0", "regex", "rustc_version", "smallvec", "tempfile", "tikv-jemalloc-sys", - "ui_test", + "ui_test 0.28.0", "windows-sys 0.52.0", ] @@ -2782,7 +2794,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" dependencies = [ "phf_shared 0.10.0", - "rand", + "rand 0.8.5", ] [[package]] @@ -2792,7 +2804,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared 0.11.3", - "rand", + "rand 0.8.5", ] [[package]] @@ -2860,7 +2872,7 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -2978,8 +2990,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.0", + "zerocopy 0.8.14", ] [[package]] @@ -2989,7 +3012,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.0", ] [[package]] @@ -2998,7 +3031,17 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff" +dependencies = [ + "getrandom 0.3.1", + "zerocopy 0.8.14", ] [[package]] @@ -3007,7 +3050,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" dependencies = [ - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -3045,7 +3088,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", "thiserror 1.0.69", ] @@ -3283,7 +3326,7 @@ name = "rustc_abi" version = "0.0.0" dependencies = [ "bitflags", - "rand", + "rand 0.8.5", "rand_xoshiro", "rustc_data_structures", "rustc_feature", @@ -3897,7 +3940,7 @@ dependencies = [ name = "rustc_incremental" version = "0.0.0" dependencies = [ - "rand", + "rand 0.8.5", "rustc_ast", "rustc_data_structures", "rustc_errors", @@ -5218,7 +5261,7 @@ checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", "fastrand", - "getrandom", + "getrandom 0.2.15", "once_cell", "rustix", "windows-sys 0.59.0", @@ -5281,8 +5324,8 @@ version = "0.1.0" dependencies = [ "indicatif", "num", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "rayon", ] @@ -5602,7 +5645,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", - "rand", + "rand 0.8.5", "static_assertions", ] @@ -5662,6 +5705,32 @@ dependencies = [ "spanned", ] +[[package]] +name = "ui_test" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7484683d60d50ca1d1b6433c3dbf6c5ad71d20387acdcfb16fe79573f3fba576" +dependencies = [ + "annotate-snippets 0.11.5", + "anyhow", + "bstr", + "cargo-platform", + "cargo_metadata 0.18.1", + "color-eyre", + "colored", + "comma", + "crossbeam-channel", + "indicatif", + "levenshtein", + "prettydiff", + "regex", + "rustc_version", + "rustfix", + "serde", + "serde_json", + "spanned", +] + [[package]] name = "unic-langid" version = "0.9.5" @@ -5843,7 +5912,7 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] @@ -5880,6 +5949,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasi-preview1-component-adapter-provider" version = "29.0.1" @@ -6475,6 +6553,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ab703352da6a72f35c39a533526393725640575bb211f61987a2748323ad956" +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + [[package]] name = "wit-component" version = "0.223.0" @@ -6584,7 +6671,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468" +dependencies = [ + "zerocopy-derive 0.8.14", ] [[package]] @@ -6598,6 +6694,17 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "zerocopy-derive" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "zerofrom" version = "0.1.5" diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 771ebf2057f9a..c6855dd42e531 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -420,8 +420,14 @@ impl<'ll> CodegenCx<'ll, '_> { let g = if val_llty == llty { g } else { - // If we created the global with the wrong type, - // correct the type. + // codegen_static_initializer creates the global value just from the + // `Allocation` data by generating one big struct value that is just + // all the bytes and pointers after each other. This will almost never + // match the type that the static was declared with. Unfortunately + // we can't just LLVMConstBitCast our way out of it because that has very + // specific rules on what can be cast. So instead of adding a new way to + // generate static initializers that match the static's type, we picked + // the easier option and retroactively change the type of the static item itself. let name = llvm::get_value_name(g).to_vec(); llvm::set_value_name(g, b""); diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index d75df1ad4425b..8e54479874297 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -1240,6 +1240,17 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, self.visit_field(val, 0, &self.ecx.project_index(val, 0)?)?; } } + ty::Pat(base, pat) => { + // First check that the base type is valid + self.visit_value(&val.transmute(self.ecx.layout_of(*base)?, self.ecx)?)?; + // When you extend this match, make sure to also add tests to + // tests/ui/type/pattern_types/validity.rs(( + match **pat { + // Range patterns are precisely reflected into `valid_range` and thus + // handled fully by `visit_scalar` (called below). + ty::PatternKind::Range { .. } => {}, + } + } _ => { // default handler try_validation!( diff --git a/compiler/rustc_driver_impl/src/args.rs b/compiler/rustc_driver_impl/src/args.rs index 2fc767b375086..b0970144c421e 100644 --- a/compiler/rustc_driver_impl/src/args.rs +++ b/compiler/rustc_driver_impl/src/args.rs @@ -1,7 +1,6 @@ use std::{env, error, fmt, fs, io}; use rustc_session::EarlyDiagCtxt; -use rustc_span::ErrorGuaranteed; /// Expands argfiles in command line arguments. #[derive(Default)] @@ -118,22 +117,22 @@ pub fn arg_expand_all(early_dcx: &EarlyDiagCtxt, at_args: &[String]) -> Vec Result, ErrorGuaranteed> { - let mut res = Ok(Vec::new()); +pub fn raw_args(early_dcx: &EarlyDiagCtxt) -> Vec { + let mut args = Vec::new(); + let mut guar = Ok(()); for (i, arg) in env::args_os().enumerate() { match arg.into_string() { - Ok(arg) => { - if let Ok(args) = &mut res { - args.push(arg); - } - } + Ok(arg) => args.push(arg), Err(arg) => { - res = + guar = Err(early_dcx.early_err(format!("argument {i} is not valid Unicode: {arg:?}"))) } } } - res + if let Err(guar) = guar { + guar.raise_fatal(); + } + args } #[derive(Debug)] diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index d489926148795..6ea14d15c1440 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -1212,9 +1212,9 @@ pub fn catch_fatal_errors R, R>(f: F) -> Result { /// Variant of `catch_fatal_errors` for the `interface::Result` return type /// that also computes the exit code. -pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 { +pub fn catch_with_exit_code(f: impl FnOnce()) -> i32 { match catch_fatal_errors(f) { - Ok(Ok(())) => EXIT_SUCCESS, + Ok(()) => EXIT_SUCCESS, _ => EXIT_FAILURE, } } @@ -1499,10 +1499,8 @@ pub fn main() -> ! { install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ()); install_ctrlc_handler(); - let exit_code = catch_with_exit_code(|| { - run_compiler(&args::raw_args(&early_dcx)?, &mut callbacks); - Ok(()) - }); + let exit_code = + catch_with_exit_code(|| run_compiler(&args::raw_args(&early_dcx), &mut callbacks)); if let Some(format) = callbacks.time_passes { let end_rss = get_resident_set_size(); diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 991dfa1821a9b..d0b4211c351c9 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -35,8 +35,8 @@ use crate::snippet::{ use crate::styled_buffer::StyledBuffer; use crate::translation::{Translate, to_fluent_args}; use crate::{ - CodeSuggestion, DiagCtxt, DiagInner, DiagMessage, ErrCode, FluentBundle, LazyFallbackBundle, - Level, MultiSpan, Subdiag, SubstitutionHighlight, SuggestionStyle, TerminalUrl, + CodeSuggestion, DiagInner, DiagMessage, ErrCode, FluentBundle, LazyFallbackBundle, Level, + MultiSpan, Subdiag, SubstitutionHighlight, SuggestionStyle, TerminalUrl, }; /// Default column width, used in tests and when terminal dimensions cannot be determined. @@ -537,11 +537,10 @@ impl Emitter for HumanEmitter { } /// An emitter that does nothing when emitting a non-fatal diagnostic. -/// Fatal diagnostics are forwarded to `fatal_dcx` to avoid silent +/// Fatal diagnostics are forwarded to `fatal_emitter` to avoid silent /// failures of rustc, as witnessed e.g. in issue #89358. pub struct SilentEmitter { - pub fallback_bundle: LazyFallbackBundle, - pub fatal_dcx: DiagCtxt, + pub fatal_emitter: Box, pub fatal_note: Option, pub emit_fatal_diagnostic: bool, } @@ -552,9 +551,7 @@ impl Translate for SilentEmitter { } fn fallback_fluent_bundle(&self) -> &FluentBundle { - // Ideally this field wouldn't be necessary and the fallback bundle in `fatal_dcx` would be - // used but the lock prevents this. - &self.fallback_bundle + self.fatal_emitter.fallback_fluent_bundle() } } @@ -563,12 +560,12 @@ impl Emitter for SilentEmitter { None } - fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) { + fn emit_diagnostic(&mut self, mut diag: DiagInner, registry: &Registry) { if self.emit_fatal_diagnostic && diag.level == Level::Fatal { if let Some(fatal_note) = &self.fatal_note { diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new()); } - self.fatal_dcx.handle().emit_diagnostic(diag); + self.fatal_emitter.emit_diagnostic(diag, registry); } } } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index a6458c9ffdcdb..7a02e0dd2f093 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -59,7 +59,7 @@ use emitter::{DynEmitter, Emitter, is_case_difference, is_different}; use rustc_data_structures::AtomicRef; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::stable_hasher::{Hash128, StableHasher}; -use rustc_data_structures::sync::Lock; +use rustc_data_structures::sync::{DynSend, Lock}; pub use rustc_error_messages::{ DiagMessage, FluentBundle, LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, SubdiagMessage, fallback_fluent_bundle, fluent_bundle, @@ -676,57 +676,44 @@ impl DiagCtxt { Self { inner: Lock::new(DiagCtxtInner::new(emitter)) } } - pub fn make_silent( - &self, - fallback_bundle: LazyFallbackBundle, - fatal_note: Option, - emit_fatal_diagnostic: bool, - ) { - self.wrap_emitter(|old_dcx| { - Box::new(emitter::SilentEmitter { - fallback_bundle, - fatal_dcx: DiagCtxt { inner: Lock::new(old_dcx) }, - fatal_note, - emit_fatal_diagnostic, - }) - }); - } - - fn wrap_emitter(&self, f: F) - where - F: FnOnce(DiagCtxtInner) -> Box, - { - // A empty type that implements `Emitter` so that a `DiagCtxtInner` can be constructed - // to temporarily swap in place of the real one, which will be used in constructing - // its replacement. + pub fn make_silent(&self, fatal_note: Option, emit_fatal_diagnostic: bool) { + // An empty type that implements `Emitter` to temporarily swap in place of the real one, + // which will be used in constructing its replacement. struct FalseEmitter; impl Emitter for FalseEmitter { fn emit_diagnostic(&mut self, _: DiagInner, _: &Registry) { - unimplemented!("false emitter must only used during `wrap_emitter`") + unimplemented!("false emitter must only used during `make_silent`") } fn source_map(&self) -> Option<&SourceMap> { - unimplemented!("false emitter must only used during `wrap_emitter`") + unimplemented!("false emitter must only used during `make_silent`") } } impl translation::Translate for FalseEmitter { fn fluent_bundle(&self) -> Option<&FluentBundle> { - unimplemented!("false emitter must only used during `wrap_emitter`") + unimplemented!("false emitter must only used during `make_silent`") } fn fallback_fluent_bundle(&self) -> &FluentBundle { - unimplemented!("false emitter must only used during `wrap_emitter`") + unimplemented!("false emitter must only used during `make_silent`") } } let mut inner = self.inner.borrow_mut(); - let mut prev_dcx = DiagCtxtInner::new(Box::new(FalseEmitter)); - std::mem::swap(&mut *inner, &mut prev_dcx); - let new_emitter = f(prev_dcx); - let mut new_dcx = DiagCtxtInner::new(new_emitter); - std::mem::swap(&mut *inner, &mut new_dcx); + let mut prev_emitter = Box::new(FalseEmitter) as Box; + std::mem::swap(&mut inner.emitter, &mut prev_emitter); + let new_emitter = Box::new(emitter::SilentEmitter { + fatal_emitter: prev_emitter, + fatal_note, + emit_fatal_diagnostic, + }); + inner.emitter = new_emitter; + } + + pub fn set_emitter(&self, emitter: Box) { + self.inner.borrow_mut().emitter = emitter; } /// Translate `message` eagerly with `args` to `SubdiagMessage::Eager`. diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index be4004f5904cc..a4b5a87361ef3 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -234,6 +234,9 @@ hir_analysis_inherent_ty_outside_relevant = cannot define inherent `impl` for a .help = consider moving this inherent impl into the crate defining the type if possible .span_help = alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items +hir_analysis_invalid_base_type = `{$ty}` is not a valid base type for range patterns + .note = range patterns only support integers + hir_analysis_invalid_generic_receiver_ty = invalid generic `self` parameter type: `{$receiver_ty}` .note = type of `self` must not be a method generic parameter type @@ -438,7 +441,6 @@ hir_analysis_pattern_type_wild_pat = wildcard patterns are not permitted for pat .label = this type is the same as the inner type without a pattern hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind} .label = not allowed in type signatures - hir_analysis_precise_capture_self_alias = `Self` can't be captured in `use<...>` precise captures list, since it is an alias .label = `Self` is not a generic argument, but an alias to the type of the {$what} diff --git a/compiler/rustc_hir_analysis/src/errors/pattern_types.rs b/compiler/rustc_hir_analysis/src/errors/pattern_types.rs index bb771d6ea170e..272edbe841b96 100644 --- a/compiler/rustc_hir_analysis/src/errors/pattern_types.rs +++ b/compiler/rustc_hir_analysis/src/errors/pattern_types.rs @@ -1,4 +1,5 @@ use rustc_macros::Diagnostic; +use rustc_middle::ty::Ty; use rustc_span::Span; #[derive(Diagnostic)] @@ -7,3 +8,14 @@ pub(crate) struct WildPatTy { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_invalid_base_type)] +pub(crate) struct InvalidBaseType<'tcx> { + pub ty: Ty<'tcx>, + #[primary_span] + pub ty_span: Span, + pub pat: &'static str, + #[note] + pub pat_span: Span, +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index b4cab3330afaf..5813fdeae7645 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -53,7 +53,7 @@ use tracing::{debug, instrument}; use crate::bounds::Bounds; use crate::check::check_abi_fn_ptr; -use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, WildPatTy}; +use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, InvalidBaseType, WildPatTy}; use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; @@ -2432,6 +2432,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.ty_infer(None, hir_ty.span) } hir::TyKind::Pat(ty, pat) => { + let ty_span = ty.span; let ty = self.lower_ty(ty); let pat_ty = match pat.kind { hir::PatKind::Wild => { @@ -2439,6 +2440,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Ty::new_error(tcx, err) } hir::PatKind::Range(start, end, include_end) => { + let ty = match ty.kind() { + ty::Int(_) | ty::Uint(_) | ty::Char => ty, + _ => Ty::new_error( + tcx, + self.dcx().emit_err(InvalidBaseType { + ty, + pat: "range", + ty_span, + pat_span: pat.span, + }), + ), + }; let expr_to_const = |expr: &'tcx hir::PatExpr<'tcx>| -> ty::Const<'tcx> { let (c, c_ty) = match expr.kind { hir::PatExprKind::Lit { lit, negated } => { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 515c9c3409856..c2513a1af1988 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -26,7 +26,6 @@ use rustc_macros::extension; pub use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::bug; use rustc_middle::infer::canonical::{CanonicalQueryInput, CanonicalVarValues}; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::select; pub use rustc_middle::ty::IntVarValue; @@ -46,6 +45,7 @@ use tracing::{debug, instrument}; use type_variable::TypeVariableOrigin; use crate::infer::region_constraints::UndoLog; +use crate::infer::unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; use crate::traits::{ self, ObligationCause, ObligationInspector, PredicateObligations, TraitEngine, }; @@ -64,6 +64,7 @@ pub mod relate; pub mod resolve; pub(crate) mod snapshot; mod type_variable; +mod unify_key; /// `InferOk<'tcx, ()>` is used a lot. It may seem like a useless wrapper /// around `PredicateObligations<'tcx>`, but it has one important property: diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 6496f38269a5e..57555db37abd5 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -8,7 +8,6 @@ use rustc_data_structures::undo_log::UndoLogs; use rustc_data_structures::unify as ut; use rustc_index::IndexVec; use rustc_macros::{TypeFoldable, TypeVisitable}; -use rustc_middle::infer::unify_key::{RegionVariableValue, RegionVidKey}; use rustc_middle::ty::{self, ReBound, ReStatic, ReVar, Region, RegionVid, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use tracing::{debug, instrument}; @@ -17,6 +16,7 @@ use self::CombineMapType::*; use self::UndoLog::*; use super::{MiscVariable, RegionVariableOrigin, Rollback, SubregionOrigin}; use crate::infer::snapshot::undo_log::{InferCtxtUndoLogs, Snapshot}; +use crate::infer::unify_key::{RegionVariableValue, RegionVidKey}; mod leak_check; diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 21c47967eada9..ce2d07f4af996 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -4,7 +4,6 @@ use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def_id::DefId; use rustc_middle::bug; -use rustc_middle::infer::unify_key::ConstVariableValue; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::visit::MaxUniverse; use rustc_middle::ty::{ @@ -18,6 +17,7 @@ use super::{ PredicateEmittingRelation, Relate, RelateResult, StructurallyRelateAliases, TypeRelation, }; use crate::infer::type_variable::TypeVariableValue; +use crate::infer::unify_key::ConstVariableValue; use crate::infer::{InferCtxt, RegionVariableOrigin, relate}; impl<'tcx> InferCtxt<'tcx> { diff --git a/compiler/rustc_infer/src/infer/snapshot/fudge.rs b/compiler/rustc_infer/src/infer/snapshot/fudge.rs index 394e07a81e7da..3a47b13665d45 100644 --- a/compiler/rustc_infer/src/infer/snapshot/fudge.rs +++ b/compiler/rustc_infer/src/infer/snapshot/fudge.rs @@ -1,7 +1,6 @@ use std::ops::Range; use rustc_data_structures::{snapshot_vec as sv, unify as ut}; -use rustc_middle::infer::unify_key::{ConstVariableValue, ConstVidKey}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::{self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid}; use rustc_type_ir::visit::TypeVisitableExt; @@ -10,6 +9,7 @@ use ut::UnifyKey; use super::VariableLengths; use crate::infer::type_variable::TypeVariableOrigin; +use crate::infer::unify_key::{ConstVariableValue, ConstVidKey}; use crate::infer::{ConstVariableOrigin, InferCtxt, RegionVariableOrigin, UnificationTable}; fn vars_since_snapshot<'tcx, T>( diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index 713389f461836..ba7d8f588e68e 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -2,10 +2,10 @@ use std::marker::PhantomData; use rustc_data_structures::undo_log::{Rollback, UndoLogs}; use rustc_data_structures::{snapshot_vec as sv, unify as ut}; -use rustc_middle::infer::unify_key::{ConstVidKey, RegionVidKey}; use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey}; use tracing::debug; +use crate::infer::unify_key::{ConstVidKey, RegionVidKey}; use crate::infer::{InferCtxtInner, region_constraints, type_variable}; use crate::traits; diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_infer/src/infer/unify_key.rs similarity index 94% rename from compiler/rustc_middle/src/infer/unify_key.rs rename to compiler/rustc_infer/src/infer/unify_key.rs index 7f9211043d690..3ba8aea1d3a05 100644 --- a/compiler/rustc_middle/src/infer/unify_key.rs +++ b/compiler/rustc_infer/src/infer/unify_key.rs @@ -2,23 +2,18 @@ use std::cmp; use std::marker::PhantomData; use rustc_data_structures::unify::{NoError, UnifyKey, UnifyValue}; +use rustc_middle::{bug, ty}; use rustc_span::Span; use rustc_span::def_id::DefId; -use crate::ty::{self, Ty, TyCtxt}; - -pub trait ToType { - fn to_type<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>; -} - #[derive(Copy, Clone, Debug)] -pub enum RegionVariableValue<'tcx> { +pub(crate) enum RegionVariableValue<'tcx> { Known { value: ty::Region<'tcx> }, Unknown { universe: ty::UniverseIndex }, } #[derive(PartialEq, Copy, Clone, Debug)] -pub struct RegionVidKey<'tcx> { +pub(crate) struct RegionVidKey<'tcx> { pub vid: ty::RegionVid, pub phantom: PhantomData>, } @@ -44,7 +39,8 @@ impl<'tcx> UnifyKey for RegionVidKey<'tcx> { } } -pub struct RegionUnificationError; +pub(crate) struct RegionUnificationError; + impl<'tcx> UnifyValue for RegionVariableValue<'tcx> { type Error = RegionUnificationError; @@ -100,7 +96,7 @@ pub struct ConstVariableOrigin { } #[derive(Copy, Clone, Debug)] -pub enum ConstVariableValue<'tcx> { +pub(crate) enum ConstVariableValue<'tcx> { Known { value: ty::Const<'tcx> }, Unknown { origin: ConstVariableOrigin, universe: ty::UniverseIndex }, } @@ -108,7 +104,7 @@ pub enum ConstVariableValue<'tcx> { impl<'tcx> ConstVariableValue<'tcx> { /// If this value is known, returns the const it is known to be. /// Otherwise, `None`. - pub fn known(&self) -> Option> { + pub(crate) fn known(&self) -> Option> { match *self { ConstVariableValue::Unknown { .. } => None, ConstVariableValue::Known { value } => Some(value), @@ -117,7 +113,7 @@ impl<'tcx> ConstVariableValue<'tcx> { } #[derive(PartialEq, Copy, Clone, Debug)] -pub struct ConstVidKey<'tcx> { +pub(crate) struct ConstVidKey<'tcx> { pub vid: ty::ConstVid, pub phantom: PhantomData>, } diff --git a/compiler/rustc_middle/src/infer/mod.rs b/compiler/rustc_middle/src/infer/mod.rs index 3dfcf90cb9356..73e8971c3bdea 100644 --- a/compiler/rustc_middle/src/infer/mod.rs +++ b/compiler/rustc_middle/src/infer/mod.rs @@ -1,2 +1 @@ pub mod canonical; -pub mod unify_key; diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index c8a811985d5bf..67e314cc6850c 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1819,7 +1819,7 @@ pub fn parse_error_format( ErrorOutputType::HumanReadable(HumanReadableErrorType::Unicode, color) } Some(arg) => { - early_dcx.abort_if_error_and_set_error_format(ErrorOutputType::HumanReadable( + early_dcx.set_error_format(ErrorOutputType::HumanReadable( HumanReadableErrorType::Default, color, )); @@ -2360,7 +2360,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let error_format = parse_error_format(early_dcx, matches, color, json_color, json_rendered); - early_dcx.abort_if_error_and_set_error_format(error_format); + early_dcx.set_error_format(error_format); let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_else(|_| { early_dcx.early_fatal("`--diagnostic-width` must be an positive integer"); @@ -2770,6 +2770,7 @@ pub mod nightly_options { "the option `{}` is only accepted on the nightly compiler", opt.name ); + // The non-zero nightly_options_on_stable will force an early_fatal eventually. let _ = early_dcx.early_err(msg); } OptionStability::Stable => {} diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 90361efed844b..81ae06602cdb9 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -277,14 +277,10 @@ impl ParseSess { ) -> Self { let fallback_bundle = fallback_fluent_bundle(locale_resources, false); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let emitter = Box::new(HumanEmitter::new( - stderr_destination(ColorConfig::Auto), - Lrc::clone(&fallback_bundle), - )); - let fatal_dcx = DiagCtxt::new(emitter); + let fatal_emitter = + Box::new(HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle)); let dcx = DiagCtxt::new(Box::new(SilentEmitter { - fallback_bundle, - fatal_dcx, + fatal_emitter, fatal_note: Some(fatal_note), emit_fatal_diagnostic, })) @@ -341,8 +337,4 @@ impl ParseSess { pub fn dcx(&self) -> DiagCtxtHandle<'_> { self.dcx.handle() } - - pub fn set_dcx(&mut self, dcx: DiagCtxt) { - self.dcx = dcx; - } } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index c0f5f0d4a9e9e..2b79081a26e72 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1362,12 +1362,6 @@ pub struct EarlyDiagCtxt { dcx: DiagCtxt, } -impl Default for EarlyDiagCtxt { - fn default() -> Self { - Self::new(ErrorOutputType::default()) - } -} - impl EarlyDiagCtxt { pub fn new(output: ErrorOutputType) -> Self { let emitter = mk_emitter(output); @@ -1375,10 +1369,9 @@ impl EarlyDiagCtxt { } /// Swap out the underlying dcx once we acquire the user's preference on error emission - /// format. Any errors prior to that will cause an abort and all stashed diagnostics of the - /// previous dcx will be emitted. - pub fn abort_if_error_and_set_error_format(&mut self, output: ErrorOutputType) { - self.dcx.handle().abort_if_errors(); + /// format. If `early_err` was previously called this will panic. + pub fn set_error_format(&mut self, output: ErrorOutputType) { + assert!(self.dcx.handle().has_errors().is_none()); let emitter = mk_emitter(output); self.dcx = DiagCtxt::new(emitter); @@ -1398,7 +1391,7 @@ impl EarlyDiagCtxt { #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] - #[must_use = "ErrorGuaranteed must be returned from `run_compiler` in order to exit with a non-zero status code"] + #[must_use = "raise_fatal must be called on the returned ErrorGuaranteed in order to exit with a non-zero status code"] pub fn early_err(&self, msg: impl Into) -> ErrorGuaranteed { self.dcx.handle().err(msg) } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index d0a193e8bd524..87dcdcfd6652c 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1504,7 +1504,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { with_forced_trimmed_paths! { if self.tcx.is_lang_item(projection_term.def_id, LangItem::FnOnceOutput) { - let fn_kind = self_ty.prefix_string(self.tcx); let (span, closure_span) = if let ty::Closure(def_id, _) = self_ty.kind() { let def_span = self.tcx.def_span(def_id); if let Some(local_def_id) = def_id.as_local() @@ -1541,8 +1540,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { _ => self.tcx.short_string(self_ty, file), }; Some((format!( - "expected `{item}` to be a {fn_kind} that returns `{expected_ty}`, but it \ - returns `{normalized_ty}`", + "expected `{item}` to return `{expected_ty}`, but it returns `{normalized_ty}`", ), span, closure_span)) } else if self.tcx.is_lang_item(trait_def_id, LangItem::Future) { Some((format!( diff --git a/src/doc/rustc-dev-guide/examples/rustc-interface-getting-diagnostics.rs b/src/doc/rustc-dev-guide/examples/rustc-interface-getting-diagnostics.rs index be37dd867b256..2355cb85ab3dd 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-interface-getting-diagnostics.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-interface-getting-diagnostics.rs @@ -10,6 +10,8 @@ extern crate rustc_interface; extern crate rustc_session; extern crate rustc_span; +use std::sync::{Arc, Mutex}; + use rustc_errors::emitter::Emitter; use rustc_errors::registry::{self, Registry}; use rustc_errors::translation::Translate; @@ -17,8 +19,6 @@ use rustc_errors::{DiagCtxt, DiagInner, FluentBundle}; use rustc_session::config; use rustc_span::source_map::SourceMap; -use std::sync::{Arc, Mutex}; - struct DebugEmitter { source_map: Arc, diagnostics: Arc>>, @@ -67,10 +67,10 @@ fn main() { locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES.to_owned(), lint_caps: rustc_hash::FxHashMap::default(), psess_created: Some(Box::new(|parse_sess| { - parse_sess.set_dcx(DiagCtxt::new(Box::new(DebugEmitter { + parse_sess.dcx().set_emitter(Box::new(DebugEmitter { source_map: parse_sess.clone_source_map(), diagnostics, - }))); + })); })), register_lints: None, override_queries: None, diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 44adf92ff0eef..af225c9d68dd9 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -177,9 +177,8 @@ pub fn main() { rustc_driver::init_logger(&early_dcx, rustc_log::LoggerConfig::from_env("RUSTDOC_LOG")); let exit_code = rustc_driver::catch_with_exit_code(|| { - let at_args = rustc_driver::args::raw_args(&early_dcx)?; + let at_args = rustc_driver::args::raw_args(&early_dcx); main_args(&mut early_dcx, &at_args); - Ok(()) }); process::exit(exit_code); } diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index c548f262a92fe..e4092bcd10564 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -197,7 +197,7 @@ pub fn main() { }); exit(rustc_driver::catch_with_exit_code(move || { - let mut orig_args = rustc_driver::args::raw_args(&early_dcx)?; + let mut orig_args = rustc_driver::args::raw_args(&early_dcx); let has_sysroot_arg = |args: &mut [String]| -> bool { if has_arg(args, "--sysroot") { @@ -239,7 +239,7 @@ pub fn main() { pass_sysroot_env_if_given(&mut args, sys_root_env); rustc_driver::run_compiler(&args, &mut DefaultCallbacks); - return Ok(()); + return; } if orig_args.iter().any(|a| a == "--version" || a == "-V") { @@ -301,7 +301,6 @@ pub fn main() { } else { rustc_driver::run_compiler(&args, &mut RustcCallbacks { clippy_args_var }); } - Ok(()) })) } diff --git a/src/tools/compiletest/src/directive-list.rs b/src/tools/compiletest/src/directive-list.rs index 71496444660f5..a7ac875d0a36c 100644 --- a/src/tools/compiletest/src/directive-list.rs +++ b/src/tools/compiletest/src/directive-list.rs @@ -39,6 +39,10 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-android", "ignore-apple", "ignore-arm", + "ignore-arm-unknown-linux-gnueabi", + "ignore-arm-unknown-linux-gnueabihf", + "ignore-arm-unknown-linux-musleabi", + "ignore-arm-unknown-linux-musleabihf", "ignore-avr", "ignore-beta", "ignore-cdb", diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index 363b96fdff1f6..57a757f9085ca 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -351,7 +351,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", ] [[package]] @@ -529,12 +541,12 @@ dependencies = [ "chrono-tz", "colored", "directories", - "getrandom", + "getrandom 0.3.1", "libc", "libffi", "libloading", "measureme", - "rand", + "rand 0.9.0", "regex", "rustc_version", "smallvec", @@ -662,7 +674,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ "phf_shared", - "rand", + "rand 0.8.5", ] [[package]] @@ -692,7 +704,7 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -729,19 +741,28 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "libc", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ "rand_chacha", - "rand_core", + "rand_core 0.9.0", + "zerocopy 0.8.14", ] [[package]] name = "rand_chacha" -version = "0.3.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.0", ] [[package]] @@ -749,8 +770,15 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff" dependencies = [ - "getrandom", + "getrandom 0.3.1", + "zerocopy 0.8.14", ] [[package]] @@ -768,7 +796,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", "thiserror", ] @@ -1051,9 +1079,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ui_test" -version = "0.26.5" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ee4c40e5a5f9fa6864ff976473e5d6a6e9884b6ce68b40690d9f87e1994c83" +checksum = "7484683d60d50ca1d1b6433c3dbf6c5ad71d20387acdcfb16fe79573f3fba576" dependencies = [ "annotate-snippets", "anyhow", @@ -1105,6 +1133,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -1244,6 +1281,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -1251,7 +1297,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468" +dependencies = [ + "zerocopy-derive 0.8.14", ] [[package]] @@ -1264,3 +1319,14 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zerocopy-derive" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index 6e8e270985e07..de80722fc3df1 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -18,8 +18,8 @@ test = false # we have no unit tests doctest = false # and no doc tests [dependencies] -getrandom = { version = "0.2", features = ["std"] } -rand = "0.8" +getrandom = { version = "0.3", features = ["std"] } +rand = "0.9" smallvec = { version = "1.7", features = ["drain_filter"] } aes = { version = "0.8.3", features = ["hazmat"] } measureme = "11" @@ -47,8 +47,8 @@ windows-sys = { version = "0.52", features = [ ] } [dev-dependencies] +ui_test = "0.28.0" colored = "2" -ui_test = "0.26.5" rustc_version = "0.4" regex = "1.5.5" tempfile = "3" diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index fb3fc621565e3..5583030b490ae 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -14,9 +14,7 @@ function endgroup { begingroup "Building Miri" # Global configuration -# We are getting some odd linker warnings on macOS, make sure they do not fail the build. -# (See .) -export RUSTFLAGS="-D warnings -A linker-messages" +export RUSTFLAGS="-D warnings" export CARGO_INCREMENTAL=0 export CARGO_EXTRA_FLAGS="--locked" diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 0d405f532fcd8..6e84524c8aa86 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -2f0ad2a71e4a4528bb80bcb24bf8fa4e50cb87c2 +6dd75f0d6802f56564f5f9c947a85ded286d3986 diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index 3d0fc5590eb81..a4f2a117b181c 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -217,7 +217,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // We have to pick a fresh address. // Leave some space to the previous allocation, to give it some chance to be less aligned. // We ensure that `(global_state.next_base_addr + slack) % 16` is uniformly distributed. - let slack = rng.gen_range(0..16); + let slack = rng.random_range(0..16); // From next_base_addr + slack, round up to adjust for alignment. let base_addr = global_state .next_base_addr diff --git a/src/tools/miri/src/alloc_addresses/reuse_pool.rs b/src/tools/miri/src/alloc_addresses/reuse_pool.rs index b0c7ee7dff562..c0d24a9fbbcf9 100644 --- a/src/tools/miri/src/alloc_addresses/reuse_pool.rs +++ b/src/tools/miri/src/alloc_addresses/reuse_pool.rs @@ -58,7 +58,7 @@ impl ReusePool { // We don't remember stack addresses: there's a lot of them (so the perf impact is big), // and we only want to reuse stack slots within the same thread or else we'll add a lot of // undesired synchronization. - if kind == MemoryKind::Stack || !rng.gen_bool(self.address_reuse_rate) { + if kind == MemoryKind::Stack || !rng.random_bool(self.address_reuse_rate) { return; } let clock = clock(); @@ -88,10 +88,10 @@ impl ReusePool { thread: ThreadId, ) -> Option<(u64, Option)> { // Determine whether we'll even attempt a reuse. As above, we don't do reuse for stack addresses. - if kind == MemoryKind::Stack || !rng.gen_bool(self.address_reuse_rate) { + if kind == MemoryKind::Stack || !rng.random_bool(self.address_reuse_rate) { return None; } - let cross_thread_reuse = rng.gen_bool(self.address_reuse_cross_thread_rate); + let cross_thread_reuse = rng.random_bool(self.address_reuse_cross_thread_rate); // Determine the pool to take this from. let subpool = self.subpool(align); // Let's see if we can find something of the right size. We want to find the full range of @@ -118,7 +118,7 @@ impl ReusePool { return None; } // Pick a random element with the desired size. - let idx = rng.gen_range(begin..end); + let idx = rng.random_range(begin..end); // Remove it from the pool and return. let (chosen_addr, chosen_size, chosen_thread, clock) = subpool.remove(idx); debug_assert!(chosen_size >= size && chosen_addr % align.bytes() == 0); diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 988a0be632774..685f5670ab4e7 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -379,10 +379,8 @@ fn run_compiler_and_exit( callbacks: &mut (dyn rustc_driver::Callbacks + Send), ) -> ! { // Invoke compiler, and handle return code. - let exit_code = rustc_driver::catch_with_exit_code(move || { - rustc_driver::run_compiler(args, callbacks); - Ok(()) - }); + let exit_code = + rustc_driver::catch_with_exit_code(move || rustc_driver::run_compiler(args, callbacks)); std::process::exit(exit_code) } @@ -461,7 +459,7 @@ fn main() { // (`install_ice_hook` might change `RUST_BACKTRACE`.) let env_snapshot = env::vars_os().collect::>(); - let args = rustc_driver::args::raw_args(&early_dcx) + let args = rustc_driver::catch_fatal_errors(|| rustc_driver::args::raw_args(&early_dcx)) .unwrap_or_else(|_| std::process::exit(rustc_driver::EXIT_FAILURE)); // Install the ctrlc handler that sets `rustc_const_eval::CTRL_C_RECEIVED`, even if @@ -723,8 +721,8 @@ fn main() { // Ensure we have parallelism for many-seeds mode. if many_seeds.is_some() && !rustc_args.iter().any(|arg| arg.starts_with("-Zthreads=")) { - // Clamp to 8 threads; things get a lot less efficient beyond that due to lock contention. - let threads = std::thread::available_parallelism().map_or(1, |n| n.get()).min(8); + // Clamp to 10 threads; things get a lot less efficient beyond that due to lock contention. + let threads = std::thread::available_parallelism().map_or(1, |n| n.get()).min(10); rustc_args.push(format!("-Zthreads={threads}")); } let many_seeds = diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index bcc8668dbc122..18a5a0612bb06 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -865,7 +865,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let new_perm = NewPermission::from_ref_ty(val.layout.ty, kind, this); let cause = match kind { - RetagKind::TwoPhase { .. } => RetagCause::TwoPhase, + RetagKind::TwoPhase => RetagCause::TwoPhase, RetagKind::FnEntry => unreachable!(), RetagKind::Raw | RetagKind::Default => RetagCause::Normal, }; @@ -880,7 +880,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let retag_fields = this.machine.borrow_tracker.as_mut().unwrap().get_mut().retag_fields; let retag_cause = match kind { - RetagKind::TwoPhase { .. } => unreachable!(), // can only happen in `retag_ptr_value` + RetagKind::TwoPhase => unreachable!(), // can only happen in `retag_ptr_value` RetagKind::FnEntry => RetagCause::FnEntry, RetagKind::Default | RetagKind::Raw => RetagCause::Normal, }; @@ -904,10 +904,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { new_perm: NewPermission, ) -> InterpResult<'tcx> { let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?; - let val = self.ecx.sb_retag_reference(&val, new_perm, RetagInfo { - cause: self.retag_cause, - in_field: self.in_field, - })?; + let val = self.ecx.sb_retag_reference( + &val, + new_perm, + RetagInfo { cause: self.retag_cause, in_field: self.in_field }, + )?; self.ecx.write_immediate(*val, place)?; interp_ok(()) } @@ -996,10 +997,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { access: Some(AccessKind::Write), protector: Some(ProtectorKind::StrongProtector), }; - this.sb_retag_place(place, new_perm, RetagInfo { - cause: RetagCause::InPlaceFnPassing, - in_field: false, - }) + this.sb_retag_place( + place, + new_perm, + RetagInfo { cause: RetagCause::InPlaceFnPassing, in_field: false }, + ) } /// Mark the given tag as exposed. It was found on a pointer with the given AllocId. diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs index 5d7c3d8c219f9..5c12ce39d10da 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs @@ -379,14 +379,18 @@ pub mod diagnostics { use super::*; impl fmt::Display for PermissionPriv { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", match self { - ReservedFrz { conflicted: false } => "Reserved", - ReservedFrz { conflicted: true } => "Reserved (conflicted)", - ReservedIM => "Reserved (interior mutable)", - Active => "Active", - Frozen => "Frozen", - Disabled => "Disabled", - }) + write!( + f, + "{}", + match self { + ReservedFrz { conflicted: false } => "Reserved", + ReservedFrz { conflicted: true } => "Reserved (conflicted)", + ReservedIM => "Reserved (interior mutable)", + Active => "Active", + Frozen => "Frozen", + Disabled => "Disabled", + } + ) } } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index fd69278f20a7f..3389b1c602c33 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -581,15 +581,18 @@ impl Tree { let mut debug_info = NodeDebugInfo::new(root_tag, root_default_perm, span); // name the root so that all allocations contain one named pointer debug_info.add_name("root of the allocation"); - nodes.insert(root_idx, Node { - tag: root_tag, - parent: None, - children: SmallVec::default(), - default_initial_perm: root_default_perm, - // The root may never be skipped, all accesses will be local. - default_initial_idempotent_foreign_access: IdempotentForeignAccess::None, - debug_info, - }); + nodes.insert( + root_idx, + Node { + tag: root_tag, + parent: None, + children: SmallVec::default(), + default_initial_perm: root_default_perm, + // The root may never be skipped, all accesses will be local. + default_initial_idempotent_foreign_access: IdempotentForeignAccess::None, + debug_info, + }, + ); nodes }; let rperms = { @@ -624,14 +627,17 @@ impl<'tcx> Tree { let parent_idx = self.tag_mapping.get(&parent_tag).unwrap(); let strongest_idempotent = default_initial_perm.strongest_idempotent_foreign_access(prot); // Create the node - self.nodes.insert(idx, Node { - tag: new_tag, - parent: Some(parent_idx), - children: SmallVec::default(), - default_initial_perm, - default_initial_idempotent_foreign_access: strongest_idempotent, - debug_info: NodeDebugInfo::new(new_tag, default_initial_perm, span), - }); + self.nodes.insert( + idx, + Node { + tag: new_tag, + parent: Some(parent_idx), + children: SmallVec::default(), + default_initial_perm, + default_initial_idempotent_foreign_access: strongest_idempotent, + debug_info: NodeDebugInfo::new(new_tag, default_initial_perm, span), + }, + ); // Register new_tag as a child of parent_tag self.nodes.get_mut(parent_idx).unwrap().children.push(idx); // Initialize perms diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 4cdc9348dc9f8..b1ca434361b4a 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -830,7 +830,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { let success_rate = 1.0 - this.machine.cmpxchg_weak_failure_rate; let cmpxchg_success = eq.to_scalar().to_bool()? && if can_fail_spuriously { - this.machine.rng.get_mut().gen_bool(success_rate) + this.machine.rng.get_mut().random_bool(success_rate) } else { true }; diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index 14c72e9398add..268268848ed2f 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -128,7 +128,7 @@ struct Condvar { /// The futex state. #[derive(Default, Debug)] struct Futex { - waiters: VecDeque, + waiters: Vec, /// Tracks the happens-before relationship /// between a futex-wake and a futex-wait /// during a non-spurious wake event. @@ -140,6 +140,12 @@ struct Futex { #[derive(Default, Clone)] pub struct FutexRef(Rc>); +impl FutexRef { + pub fn waiters(&self) -> usize { + self.0.borrow().waiters.len() + } +} + impl VisitProvenance for FutexRef { fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { // No provenance in `Futex`. @@ -728,25 +734,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(true) } - /// Wait for the futex to be signaled, or a timeout. - /// On a signal, `retval_succ` is written to `dest`. - /// On a timeout, `retval_timeout` is written to `dest` and `errno_timeout` is set as the last error. + /// Wait for the futex to be signaled, or a timeout. Once the thread is + /// unblocked, `callback` is called with the unblock reason. fn futex_wait( &mut self, futex_ref: FutexRef, bitset: u32, timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>, - retval_succ: Scalar, - retval_timeout: Scalar, - dest: MPlaceTy<'tcx>, - errno_timeout: IoError, + callback: DynUnblockCallback<'tcx>, ) { let this = self.eval_context_mut(); let thread = this.active_thread(); let mut futex = futex_ref.0.borrow_mut(); let waiters = &mut futex.waiters; assert!(waiters.iter().all(|waiter| waiter.thread != thread), "thread is already waiting"); - waiters.push_back(FutexWaiter { thread, bitset }); + waiters.push(FutexWaiter { thread, bitset }); drop(futex); this.block_thread( @@ -755,10 +757,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { callback!( @capture<'tcx> { futex_ref: FutexRef, - retval_succ: Scalar, - retval_timeout: Scalar, - dest: MPlaceTy<'tcx>, - errno_timeout: IoError, + callback: DynUnblockCallback<'tcx>, } |this, unblock: UnblockKind| { match unblock { @@ -768,29 +767,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let Some(data_race) = &this.machine.data_race { data_race.acquire_clock(&futex.clock, &this.machine.threads); } - // Write the return value. - this.write_scalar(retval_succ, &dest)?; - interp_ok(()) }, UnblockKind::TimedOut => { // Remove the waiter from the futex. let thread = this.active_thread(); let mut futex = futex_ref.0.borrow_mut(); futex.waiters.retain(|waiter| waiter.thread != thread); - // Set errno and write return value. - this.set_last_error(errno_timeout)?; - this.write_scalar(retval_timeout, &dest)?; - interp_ok(()) }, } + + callback.call(this, unblock) } ), ); } - /// Wake up the first thread in the queue that matches any of the bits in the bitset. - /// Returns whether anything was woken. - fn futex_wake(&mut self, futex_ref: &FutexRef, bitset: u32) -> InterpResult<'tcx, bool> { + /// Wake up `count` of the threads in the queue that match any of the bits + /// in the bitset. Returns how many threads were woken. + fn futex_wake( + &mut self, + futex_ref: &FutexRef, + bitset: u32, + count: usize, + ) -> InterpResult<'tcx, usize> { let this = self.eval_context_mut(); let mut futex = futex_ref.0.borrow_mut(); let data_race = &this.machine.data_race; @@ -800,13 +799,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { data_race.release_clock(&this.machine.threads, |clock| futex.clock.clone_from(clock)); } - // Wake up the first thread in the queue that matches any of the bits in the bitset. - let Some(i) = futex.waiters.iter().position(|w| w.bitset & bitset != 0) else { - return interp_ok(false); - }; - let waiter = futex.waiters.remove(i).unwrap(); + // Remove `count` of the threads in the queue that match any of the bits in the bitset. + // We collect all of them before unblocking because the unblock callback may access the + // futex state to retrieve the remaining number of waiters on macOS. + let waiters: Vec<_> = + futex.waiters.extract_if(.., |w| w.bitset & bitset != 0).take(count).collect(); drop(futex); - this.unblock_thread(waiter.thread, BlockReason::Futex)?; - interp_ok(true) + + let woken = waiters.len(); + for waiter in waiters { + this.unblock_thread(waiter.thread, BlockReason::Futex)?; + } + + interp_ok(woken) } } diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 6d22dd8d68d93..a8a2491304dd1 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -1138,7 +1138,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { use rand::Rng as _; let this = self.eval_context_mut(); - if this.machine.rng.get_mut().gen_bool(this.machine.preemption_rate) { + if this.machine.rng.get_mut().random_bool(this.machine.preemption_rate) { this.yield_active_thread(); } } diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index c8f04e252072e..36b15dbf623da 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -558,15 +558,15 @@ where match chars.next() { Some('"') => { - cmd.extend(iter::repeat('\\').take(nslashes * 2 + 1)); + cmd.extend(iter::repeat_n('\\', nslashes * 2 + 1)); cmd.push('"'); } Some(c) => { - cmd.extend(iter::repeat('\\').take(nslashes)); + cmd.extend(iter::repeat_n('\\', nslashes)); cmd.push(c); } None => { - cmd.extend(iter::repeat('\\').take(nslashes * 2)); + cmd.extend(iter::repeat_n('\\', nslashes * 2)); break; } } diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index c5538351d7dd1..a26f12cdfb1e2 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -421,7 +421,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if this.machine.communicate() { // Fill the buffer using the host's rng. - getrandom::getrandom(&mut data) + getrandom::fill(&mut data) .map_err(|err| err_unsup_format!("host getrandom failed: {}", err))?; } else { let rng = this.machine.rng.get_mut(); @@ -678,6 +678,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) } + /// Helper function used inside shims of foreign functions to check that the target OS + /// is one of `target_oses`. It returns an error containing the `name` of the foreign function + /// in a message if this is not the case. + fn check_target_os(&self, target_oses: &[&str], name: Symbol) -> InterpResult<'tcx> { + let target_os = self.eval_context_ref().tcx.sess.target.os.as_ref(); + if !target_oses.contains(&target_os) { + throw_unsup_format!("`{name}` is not supported on {target_os}"); + } + interp_ok(()) + } + /// Helper function used inside the shims of foreign functions to assert that the target OS /// is part of the UNIX family. It panics showing a message with the `name` of the foreign function /// if this is not the case. @@ -991,6 +1002,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { check_arg_count(args) } + /// Check shim for variadic function. + /// Returns a tuple that consisting of an array of fixed args, and a slice of varargs. + fn check_shim_variadic<'a, const N: usize>( + &mut self, + abi: &FnAbi<'tcx, Ty<'tcx>>, + exp_abi: Conv, + link_name: Symbol, + args: &'a [OpTy<'tcx>], + ) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], &'a [OpTy<'tcx>])> + where + &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>, + { + self.check_abi_and_shim_symbol_clash(abi, exp_abi, link_name)?; + check_vargarg_fixed_arg_count(link_name, abi, args) + } + /// Mark a machine allocation that was just created as immutable. fn mark_immutable(&mut self, mplace: &MPlaceTy<'tcx>) { let this = self.eval_context_mut(); @@ -1184,8 +1211,10 @@ where throw_ub_format!("incorrect number of arguments: got {}, expected {}", args.len(), N) } -/// Check that the number of args is at least the minumim what we expect. -pub fn check_min_arg_count<'a, 'tcx, const N: usize>( +/// Check that the number of varargs is at least the minimum what we expect. +/// Fixed args should not be included. +/// Use `check_vararg_fixed_arg_count` to extract the varargs slice from full function arguments. +pub fn check_min_vararg_count<'a, 'tcx, const N: usize>( name: &'a str, args: &'a [OpTy<'tcx>], ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> { @@ -1193,7 +1222,35 @@ pub fn check_min_arg_count<'a, 'tcx, const N: usize>( return interp_ok(ops); } throw_ub_format!( - "incorrect number of arguments for `{name}`: got {}, expected at least {}", + "not enough variadic arguments for `{name}`: got {}, expected at least {}", + args.len(), + N + ) +} + +/// Check the number of fixed args of a vararg function. +/// Returns a tuple that consisting of an array of fixed args, and a slice of varargs. +fn check_vargarg_fixed_arg_count<'a, 'tcx, const N: usize>( + link_name: Symbol, + abi: &FnAbi<'tcx, Ty<'tcx>>, + args: &'a [OpTy<'tcx>], +) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], &'a [OpTy<'tcx>])> { + if !abi.c_variadic { + throw_ub_format!("calling a variadic function with a non-variadic caller-side signature"); + } + if abi.fixed_count != u32::try_from(N).unwrap() { + throw_ub_format!( + "incorrect number of fixed arguments for variadic function `{}`: got {}, expected {N}", + link_name.as_str(), + abi.fixed_count + ) + } + if let Some(args) = args.split_first_chunk() { + return interp_ok(args); + } + throw_ub_format!( + "incorrect number of arguments for `{}`: got {}, expected at least {}", + link_name.as_str(), args.len(), N ) diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 9eebbc5d3631e..bce78adcaea45 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -141,7 +141,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // FIXME: should we check for validity here? It's tricky because we do not have a // place. Codegen does not seem to set any attributes like `noundef` for intrinsic // calls, so we don't *have* to do anything. - let branch: bool = this.machine.rng.get_mut().gen(); + let branch: bool = this.machine.rng.get_mut().random(); this.write_scalar(Scalar::from_bool(branch), dest)?; } @@ -289,7 +289,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let a = this.read_scalar(a)?.to_f32()?; let b = this.read_scalar(b)?.to_f32()?; let c = this.read_scalar(c)?.to_f32()?; - let fuse: bool = this.machine.rng.get_mut().gen(); + let fuse: bool = this.machine.rng.get_mut().random(); let res = if fuse { // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11 a.to_host().mul_add(b.to_host(), c.to_host()).to_soft() @@ -304,7 +304,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let a = this.read_scalar(a)?.to_f64()?; let b = this.read_scalar(b)?.to_f64()?; let c = this.read_scalar(c)?.to_f64()?; - let fuse: bool = this.machine.rng.get_mut().gen(); + let fuse: bool = this.machine.rng.get_mut().random(); let res = if fuse { // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11 a.to_host().mul_add(b.to_host(), c.to_host()).to_soft() diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index 63a61dcd14878..45e316b190a68 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -304,7 +304,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let c = this.read_scalar(&this.project_index(&c, i)?)?; let dest = this.project_index(&dest, i)?; - let fuse: bool = intrinsic_name == "fma" || this.machine.rng.get_mut().gen(); + let fuse: bool = intrinsic_name == "fma" || this.machine.rng.get_mut().random(); // Works for f32 and f64. // FIXME: using host floats to work around https://github.com/rust-lang/miri/issues/2468. @@ -639,8 +639,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let (right, right_len) = this.project_to_simd(right)?; let (dest, dest_len) = this.project_to_simd(dest)?; - let index = - generic_args[2].expect_const().to_value().valtree.unwrap_branch(); + let index = generic_args[2].expect_const().to_value().valtree.unwrap_branch(); let index_len = index.len(); assert_eq!(left_len, right_len); diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 3ec35763a7d05..45054c37c40e9 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -15,6 +15,8 @@ #![feature(unqualified_local_imports)] #![feature(derive_coerce_pointee)] #![feature(arbitrary_self_types)] +#![feature(unsigned_is_multiple_of)] +#![feature(extract_if)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, @@ -36,6 +38,7 @@ clippy::needless_question_mark, clippy::needless_lifetimes, clippy::too_long_first_doc_paragraph, + // We don't use translatable diagnostics rustc::diagnostic_outside_of_impl, // We are not implementing queries here so it's fine rustc::potential_query_instability, diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 3727b5f4cae4a..4735db48e81f8 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1112,10 +1112,13 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { // Call the lang item. let panic = ecx.tcx.lang_items().get(reason.lang_item()).unwrap(); let panic = ty::Instance::mono(ecx.tcx.tcx, panic); - ecx.call_function(panic, ExternAbi::Rust, &[], None, StackPopCleanup::Goto { - ret: None, - unwind: mir::UnwindAction::Unreachable, - })?; + ecx.call_function( + panic, + ExternAbi::Rust, + &[], + None, + StackPopCleanup::Goto { ret: None, unwind: mir::UnwindAction::Unreachable }, + )?; interp_ok(()) } @@ -1501,7 +1504,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { catch_unwind: None, timing, is_user_relevant: ecx.machine.is_user_relevant(&frame), - salt: ecx.machine.rng.borrow_mut().gen::() % ADDRS_PER_ANON_GLOBAL, + salt: ecx.machine.rng.borrow_mut().random_range(0..ADDRS_PER_ANON_GLOBAL), data_race: ecx.machine.data_race.as_ref().map(|_| data_race::FrameState::default()), }; @@ -1716,7 +1719,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { if unique { CTFE_ALLOC_SALT } else { - ecx.machine.rng.borrow_mut().gen::() % ADDRS_PER_ANON_GLOBAL + ecx.machine.rng.borrow_mut().random_range(0..ADDRS_PER_ANON_GLOBAL) } } diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs index ed3d2d55678e2..7117f722fee89 100644 --- a/src/tools/miri/src/math.rs +++ b/src/tools/miri/src/math.rs @@ -1,9 +1,12 @@ use rand::Rng as _; -use rand::distributions::Distribution as _; use rustc_apfloat::Float as _; use rustc_apfloat::ieee::IeeeFloat; -/// Disturbes a floating-point result by a relative error on the order of (-2^scale, 2^scale). +/// Disturbes a floating-point result by a relative error in the range (-2^scale, 2^scale). +/// +/// For a 2^N ULP error, you can use an `err_scale` of `-(F::PRECISION - 1 - N)`. +/// In other words, a 1 ULP (absolute) error is the same as a `2^-(F::PRECISION-1)` relative error. +/// (Subtracting 1 compensates for the integer bit.) pub(crate) fn apply_random_float_error( ecx: &mut crate::MiriInterpCx<'_>, val: F, @@ -11,12 +14,15 @@ pub(crate) fn apply_random_float_error( ) -> F { let rng = ecx.machine.rng.get_mut(); // Generate a random integer in the range [0, 2^PREC). - let dist = rand::distributions::Uniform::new(0, 1 << F::PRECISION); - let err = F::from_u128(dist.sample(rng)) - .value - .scalbn(err_scale.strict_sub(F::PRECISION.try_into().unwrap())); + // (When read as binary, the position of the first `1` determines the exponent, + // and the remaining bits fill the mantissa. `PREC` is one plus the size of the mantissa, + // so this all works out.) + let r = F::from_u128(rng.random_range(0..(1 << F::PRECISION))).value; + // Multiply this with 2^(scale - PREC). The result is between 0 and + // 2^PREC * 2^(scale - PREC) = 2^scale. + let err = r.scalbn(err_scale.strict_sub(F::PRECISION.try_into().unwrap())); // give it a random sign - let err = if rng.gen::() { -err } else { err }; + let err = if rng.random() { -err } else { err }; // multiple the value with (1+err) (val * (F::from_u128(1).value + err).value).value } diff --git a/src/tools/miri/src/operator.rs b/src/tools/miri/src/operator.rs index 43c628d66d590..c588b6fc7f159 100644 --- a/src/tools/miri/src/operator.rs +++ b/src/tools/miri/src/operator.rs @@ -108,7 +108,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Pick one of the NaNs. let nan = nans.choose(&mut *rand).unwrap(); // Non-deterministically flip the sign. - if rand.gen() { + if rand.random() { // This will properly flip even for NaN. -nan } else { @@ -120,6 +120,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_ref(); // Return one side non-deterministically. let mut rand = this.machine.rng.borrow_mut(); - if rand.gen() { a } else { b } + if rand.random() { a } else { b } } } diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index 0fda13e061600..323b95d5f5f23 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -81,7 +81,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn malloc(&mut self, size: u64, init: AllocInit) -> InterpResult<'tcx, Pointer> { let this = self.eval_context_mut(); let align = this.malloc_align(size); - let ptr = this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into(), init)?; + let ptr = + this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into(), init)?; interp_ok(ptr.into()) } @@ -92,7 +93,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { size: &OpTy<'tcx>, ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let memptr = this.deref_pointer(memptr)?; + let memptr = this.deref_pointer_as(memptr, this.machine.layouts.mut_raw_ptr)?; let align = this.read_target_usize(align)?; let size = this.read_target_usize(size)?; @@ -105,7 +106,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::C.into(), - AllocInit::Uninit + AllocInit::Uninit, )?; this.write_pointer(ptr, &memptr)?; interp_ok(Scalar::from_i32(0)) @@ -138,7 +139,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(new_size), new_align, MiriMemoryKind::C.into(), - AllocInit::Uninit + AllocInit::Uninit, )?; interp_ok(new_ptr.into()) } @@ -179,7 +180,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::C.into(), - AllocInit::Uninit + AllocInit::Uninit, )?; interp_ok(ptr.into()) } diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index 1622ef280d25c..7e667e70a1721 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -4,7 +4,6 @@ use rustc_middle::ty::{self, Instance, Ty}; use rustc_span::{BytePos, Loc, Symbol, hygiene}; use rustc_target::callconv::{Conv, FnAbi}; -use crate::helpers::check_min_arg_count; use crate::*; impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -34,13 +33,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { abi: &FnAbi<'tcx, Ty<'tcx>>, link_name: Symbol, args: &[OpTy<'tcx>], - dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let tcx = this.tcx; + let ptr_ty = this.machine.layouts.mut_raw_ptr.ty; + let ptr_layout = this.layout_of(ptr_ty)?; + + let [flags, buf] = this.check_shim(abi, Conv::Rust, link_name, args)?; - let [flags] = check_min_arg_count("miri_get_backtrace", args)?; let flags = this.read_scalar(flags)?.to_u64()?; + let buf_place = this.deref_pointer_as(buf, ptr_layout)?; let mut data = Vec::new(); for frame in this.active_thread_stack().iter().rev() { @@ -63,44 +64,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }) .collect(); - let len: u64 = ptrs.len().try_into().unwrap(); - - let ptr_ty = this.machine.layouts.mut_raw_ptr.ty; - let array_layout = this.layout_of(Ty::new_array(tcx.tcx, ptr_ty, len)).unwrap(); - match flags { - // storage for pointers is allocated by miri - // deallocating the slice is undefined behavior with a custom global allocator 0 => { - let [_flags] = this.check_shim(abi, Conv::Rust, link_name, args)?; - - let alloc = this.allocate(array_layout, MiriMemoryKind::Rust.into())?; - - // Write pointers into array - for (i, ptr) in ptrs.into_iter().enumerate() { - let place = this.project_index(&alloc, i as u64)?; - - this.write_pointer(ptr, &place)?; - } - - this.write_immediate(Immediate::new_slice(alloc.ptr(), len, this), dest)?; + throw_unsup_format!("miri_get_backtrace: v0 is not supported any more"); } - // storage for pointers is allocated by the caller - 1 => { - let [_flags, buf] = this.check_shim(abi, Conv::Rust, link_name, args)?; - - let buf_place = this.deref_pointer(buf)?; - - let ptr_layout = this.layout_of(ptr_ty)?; - + 1 => for (i, ptr) in ptrs.into_iter().enumerate() { let offset = ptr_layout.size.checked_mul(i.try_into().unwrap(), this).unwrap(); let op_place = buf_place.offset(offset, ptr_layout, this)?; this.write_pointer(ptr, &op_place)?; - } - } + }, _ => throw_unsup_format!("unknown `miri_get_backtrace` flags {}", flags), }; diff --git a/src/tools/miri/src/shims/extern_static.rs b/src/tools/miri/src/shims/extern_static.rs index f0aebfe169378..20dd3c8db2a3e 100644 --- a/src/tools/miri/src/shims/extern_static.rs +++ b/src/tools/miri/src/shims/extern_static.rs @@ -60,10 +60,10 @@ impl<'tcx> MiriMachine<'tcx> { match ecx.tcx.sess.target.os.as_ref() { "linux" => { - Self::null_ptr_extern_statics(ecx, &[ - "__cxa_thread_atexit_impl", - "__clock_gettime64", - ])?; + Self::null_ptr_extern_statics( + ecx, + &["__cxa_thread_atexit_impl", "__clock_gettime64"], + )?; Self::weak_symbol_extern_statics(ecx, &["getrandom", "statx"])?; } "freebsd" => { diff --git a/src/tools/miri/src/shims/files.rs b/src/tools/miri/src/shims/files.rs index 73425eee51569..6b4f4cdc922a0 100644 --- a/src/tools/miri/src/shims/files.rs +++ b/src/tools/miri/src/shims/files.rs @@ -1,6 +1,6 @@ use std::any::Any; use std::collections::BTreeMap; -use std::io::{IsTerminal, Read, SeekFrom, Write}; +use std::io::{IsTerminal, SeekFrom, Write}; use std::marker::CoercePointee; use std::ops::Deref; use std::rc::{Rc, Weak}; @@ -140,8 +140,8 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt { _communicate_allowed: bool, _ptr: Pointer, _len: usize, - _dest: &MPlaceTy<'tcx>, _ecx: &mut MiriInterpCx<'tcx>, + _finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { throw_unsup_format!("cannot read from {}", self.name()); } @@ -154,8 +154,8 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt { _communicate_allowed: bool, _ptr: Pointer, _len: usize, - _dest: &MPlaceTy<'tcx>, _ecx: &mut MiriInterpCx<'tcx>, + _finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { throw_unsup_format!("cannot write to {}", self.name()); } @@ -207,19 +207,16 @@ impl FileDescription for io::Stdin { communicate_allowed: bool, ptr: Pointer, len: usize, - dest: &MPlaceTy<'tcx>, ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { - let mut bytes = vec![0; len]; if !communicate_allowed { // We want isolation mode to be deterministic, so we have to disallow all reads, even stdin. helpers::isolation_abort_error("`read` from stdin")?; } - let result = Read::read(&mut &*self, &mut bytes); - match result { - Ok(read_size) => ecx.return_read_success(ptr, &bytes, read_size, dest), - Err(e) => ecx.set_last_error_and_return(e, dest), - } + + let result = ecx.read_from_host(&*self, len, ptr)?; + finish.call(ecx, result) } fn is_tty(&self, communicate_allowed: bool) -> bool { @@ -237,22 +234,19 @@ impl FileDescription for io::Stdout { _communicate_allowed: bool, ptr: Pointer, len: usize, - dest: &MPlaceTy<'tcx>, ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { - let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?; - // We allow writing to stderr even with isolation enabled. - let result = Write::write(&mut &*self, bytes); + // We allow writing to stdout even with isolation enabled. + let result = ecx.write_to_host(&*self, len, ptr)?; // Stdout is buffered, flush to make sure it appears on the // screen. This is the write() syscall of the interpreted // program, we want it to correspond to a write() syscall on // the host -- there is no good in adding extra buffering // here. io::stdout().flush().unwrap(); - match result { - Ok(write_size) => ecx.return_write_success(write_size, dest), - Err(e) => ecx.set_last_error_and_return(e, dest), - } + + finish.call(ecx, result) } fn is_tty(&self, communicate_allowed: bool) -> bool { @@ -270,17 +264,13 @@ impl FileDescription for io::Stderr { _communicate_allowed: bool, ptr: Pointer, len: usize, - dest: &MPlaceTy<'tcx>, ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { - let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?; // We allow writing to stderr even with isolation enabled. + let result = ecx.write_to_host(&*self, len, ptr)?; // No need to flush, stderr is not buffered. - let result = Write::write(&mut &*self, bytes); - match result { - Ok(write_size) => ecx.return_write_success(write_size, dest), - Err(e) => ecx.set_last_error_and_return(e, dest), - } + finish.call(ecx, result) } fn is_tty(&self, communicate_allowed: bool) -> bool { @@ -302,11 +292,11 @@ impl FileDescription for NullOutput { _communicate_allowed: bool, _ptr: Pointer, len: usize, - dest: &MPlaceTy<'tcx>, ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { // We just don't write anything, but report to the user that we did. - ecx.return_write_success(len, dest) + finish.call(ecx, Ok(len)) } } @@ -405,40 +395,41 @@ impl FdTable { impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { - /// Helper to implement `FileDescription::read`: - /// This is only used when `read` is successful. - /// `actual_read_size` should be the return value of some underlying `read` call that used - /// `bytes` as its output buffer. - /// The length of `bytes` must not exceed either the host's or the target's `isize`. - /// `bytes` is written to `buf` and the size is written to `dest`. - fn return_read_success( + /// Read data from a host `Read` type, store the result into machine memory, + /// and return whether that worked. + fn read_from_host( &mut self, - buf: Pointer, - bytes: &[u8], - actual_read_size: usize, - dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx> { + mut file: impl io::Read, + len: usize, + ptr: Pointer, + ) -> InterpResult<'tcx, Result> { let this = self.eval_context_mut(); - // If reading to `bytes` did not fail, we write those bytes to the buffer. - // Crucially, if fewer than `bytes.len()` bytes were read, only write - // that much into the output buffer! - this.write_bytes_ptr(buf, bytes[..actual_read_size].iter().copied())?; - // The actual read size is always less than what got originally requested so this cannot fail. - this.write_int(u64::try_from(actual_read_size).unwrap(), dest)?; - interp_ok(()) + let mut bytes = vec![0; len]; + let result = file.read(&mut bytes); + match result { + Ok(read_size) => { + // If reading to `bytes` did not fail, we write those bytes to the buffer. + // Crucially, if fewer than `bytes.len()` bytes were read, only write + // that much into the output buffer! + this.write_bytes_ptr(ptr, bytes[..read_size].iter().copied())?; + interp_ok(Ok(read_size)) + } + Err(e) => interp_ok(Err(IoError::HostError(e))), + } } - /// Helper to implement `FileDescription::write`: - /// This function is only used when `write` is successful, and writes `actual_write_size` to `dest` - fn return_write_success( + /// Write data to a host `Write` type, withthe bytes taken from machine memory. + fn write_to_host( &mut self, - actual_write_size: usize, - dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx> { + mut file: impl io::Write, + len: usize, + ptr: Pointer, + ) -> InterpResult<'tcx, Result> { let this = self.eval_context_mut(); - // The actual write size is always less than what got originally requested so this cannot fail. - this.write_int(u64::try_from(actual_write_size).unwrap(), dest)?; - interp_ok(()) + + let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?; + let result = file.write(bytes); + interp_ok(result.map_err(IoError::HostError)) } } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 1ce0c209de9e2..97bfb04f1f471 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -357,7 +357,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Obtains a Miri backtrace. See the README for details. "miri_get_backtrace" => { // `check_shim` happens inside `handle_miri_get_backtrace`. - this.handle_miri_get_backtrace(abi, link_name, args, dest)?; + this.handle_miri_get_backtrace(abi, link_name, args)?; } // Resolves a Miri backtrace frame. See the README for details. "miri_resolve_frame" => { @@ -509,7 +509,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), memory_kind.into(), - AllocInit::Uninit + AllocInit::Uninit, )?; ecx.write_pointer(ptr, dest) @@ -538,7 +538,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::Rust.into(), - AllocInit::Zero + AllocInit::Zero, )?; this.write_pointer(ptr, dest) }); @@ -599,7 +599,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(new_size), align, MiriMemoryKind::Rust.into(), - AllocInit::Uninit + AllocInit::Uninit, )?; this.write_pointer(new_ptr, dest) }); @@ -861,7 +861,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { "lgammaf_r" => { let [x, signp] = this.check_shim(abi, Conv::C, link_name, args)?; let x = this.read_scalar(x)?.to_f32()?; - let signp = this.deref_pointer(signp)?; + let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?; // Using host floats (but it's fine, these operations do not have guaranteed precision). let (res, sign) = x.to_host().ln_gamma(); @@ -872,7 +872,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { "lgamma_r" => { let [x, signp] = this.check_shim(abi, Conv::C, link_name, args)?; let x = this.read_scalar(x)?.to_f64()?; - let signp = this.deref_pointer(signp)?; + let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?; // Using host floats (but it's fine, these operations do not have guaranteed precision). let (res, sign) = x.to_host().ln_gamma(); diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs index 93479540009ea..83f331bb173db 100644 --- a/src/tools/miri/src/shims/panic.rs +++ b/src/tools/miri/src/shims/panic.rs @@ -247,10 +247,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Call the lang item associated with this message. let fn_item = this.tcx.require_lang_item(msg.panic_function(), None); let instance = ty::Instance::mono(this.tcx.tcx, fn_item); - this.call_function(instance, ExternAbi::Rust, &[], None, StackPopCleanup::Goto { - ret: None, - unwind, - })?; + this.call_function( + instance, + ExternAbi::Rust, + &[], + None, + StackPopCleanup::Goto { ret: None, unwind }, + )?; } } interp_ok(()) diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index d6c77d9c4d9a0..64b3ce6b4e494 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -132,16 +132,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.assert_target_os_is_unix("localtime_r"); this.check_no_isolation("`localtime_r`")?; - let timep = this.deref_pointer(timep)?; + let time_layout = this.libc_ty_layout("time_t"); + let timep = this.deref_pointer_as(timep, time_layout)?; let result = this.deref_pointer_as(result_op, this.libc_ty_layout("tm"))?; // The input "represents the number of seconds elapsed since the Epoch, // 1970-01-01 00:00:00 +0000 (UTC)". - let sec_since_epoch: i64 = this - .read_scalar(&timep)? - .to_int(this.libc_ty_layout("time_t").size)? - .try_into() - .unwrap(); + let sec_since_epoch: i64 = + this.read_scalar(&timep)?.to_int(time_layout.size)?.try_into().unwrap(); let dt_utc: DateTime = DateTime::from_timestamp(sec_since_epoch, 0).expect("Invalid timestamp"); @@ -254,7 +252,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let qpc = i64::try_from(duration.as_nanos()).map_err(|_| { err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported") })?; - this.write_scalar(Scalar::from_i64(qpc), &this.deref_pointer(lpPerformanceCount_op)?)?; + this.write_scalar( + Scalar::from_i64(qpc), + &this.deref_pointer_as(lpPerformanceCount_op, this.machine.layouts.i64)?, + )?; interp_ok(Scalar::from_i32(-1)) // return non-zero on success } diff --git a/src/tools/miri/src/shims/unix/android/thread.rs b/src/tools/miri/src/shims/unix/android/thread.rs index 8d5d4a52b6efe..c7e2c4d507b22 100644 --- a/src/tools/miri/src/shims/unix/android/thread.rs +++ b/src/tools/miri/src/shims/unix/android/thread.rs @@ -3,7 +3,7 @@ use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::{Conv, FnAbi}; -use crate::helpers::check_min_arg_count; +use crate::helpers::check_min_vararg_count; use crate::shims::unix::thread::{EvalContextExt as _, ThreadNameResult}; use crate::*; @@ -16,18 +16,15 @@ pub fn prctl<'tcx>( args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { - // We do not use `check_shim` here because `prctl` is variadic. The argument - // count is checked bellow. - ecx.check_abi_and_shim_symbol_clash(abi, Conv::C, link_name)?; + let ([op], varargs) = ecx.check_shim_variadic(abi, Conv::C, link_name, args)?; // FIXME: Use constants once https://github.com/rust-lang/libc/pull/3941 backported to the 0.2 branch. let pr_set_name = 15; let pr_get_name = 16; - let [op] = check_min_arg_count("prctl", args)?; let res = match ecx.read_scalar(op)?.to_i32()? { op if op == pr_set_name => { - let [_, name] = check_min_arg_count("prctl(PR_SET_NAME, ...)", args)?; + let [name] = check_min_vararg_count("prctl(PR_SET_NAME, ...)", varargs)?; let name = ecx.read_scalar(name)?; let thread = ecx.pthread_self()?; // The Linux kernel silently truncates long names. @@ -38,7 +35,7 @@ pub fn prctl<'tcx>( Scalar::from_u32(0) } op if op == pr_get_name => { - let [_, name] = check_min_arg_count("prctl(PR_GET_NAME, ...)", args)?; + let [name] = check_min_vararg_count("prctl(PR_GET_NAME, ...)", varargs)?; let name = ecx.read_scalar(name)?; let thread = ecx.pthread_self()?; let len = Scalar::from_target_usize(TASK_COMM_LEN as u64, ecx); diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index 0b59490308b44..3f85b9ae9bd94 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -6,7 +6,7 @@ use std::io::ErrorKind; use rustc_abi::Size; -use crate::helpers::check_min_arg_count; +use crate::helpers::check_min_vararg_count; use crate::shims::files::FileDescription; use crate::shims::unix::linux_like::epoll::EpollReadyEvents; use crate::shims::unix::*; @@ -30,8 +30,8 @@ pub trait UnixFileDescription: FileDescription { _offset: u64, _ptr: Pointer, _len: usize, - _dest: &MPlaceTy<'tcx>, _ecx: &mut MiriInterpCx<'tcx>, + _finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { throw_unsup_format!("cannot pread from {}", self.name()); } @@ -46,8 +46,8 @@ pub trait UnixFileDescription: FileDescription { _ptr: Pointer, _len: usize, _offset: u64, - _dest: &MPlaceTy<'tcx>, _ecx: &mut MiriInterpCx<'tcx>, + _finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { throw_unsup_format!("cannot pwrite to {}", self.name()); } @@ -127,11 +127,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } - fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> { + fn fcntl( + &mut self, + fd_num: &OpTy<'tcx>, + cmd: &OpTy<'tcx>, + varargs: &[OpTy<'tcx>], + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let [fd_num, cmd] = check_min_arg_count("fcntl", args)?; - let fd_num = this.read_scalar(fd_num)?.to_i32()?; let cmd = this.read_scalar(cmd)?.to_i32()?; @@ -163,7 +166,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "fcntl(fd, F_DUPFD_CLOEXEC, ...)" }; - let [_, _, start] = check_min_arg_count(cmd_name, args)?; + let [start] = check_min_vararg_count(cmd_name, varargs)?; let start = this.read_scalar(start)?.to_i32()?; if let Some(fd) = this.machine.fds.get(fd_num) { @@ -233,7 +236,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let count = usize::try_from(count).unwrap(); // now it fits in a `usize` let communicate = this.machine.communicate(); - // We temporarily dup the FD to be able to retain mutable access to `this`. + // Get the FD. let Some(fd) = this.machine.fds.get(fd_num) else { trace!("read: FD not found"); return this.set_last_error_and_return(LibcError("EBADF"), dest); @@ -244,13 +247,33 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // because it was a target's `usize`. Also we are sure that its smaller than // `usize::MAX` because it is bounded by the host's `isize`. + let finish = { + let dest = dest.clone(); + callback!( + @capture<'tcx> { + count: usize, + dest: MPlaceTy<'tcx>, + } + |this, result: Result| { + match result { + Ok(read_size) => { + assert!(read_size <= count); + // This must fit since `count` fits. + this.write_int(u64::try_from(read_size).unwrap(), &dest) + } + Err(e) => { + this.set_last_error_and_return(e, &dest) + } + }} + ) + }; match offset { - None => fd.read(communicate, buf, count, dest, this)?, + None => fd.read(communicate, buf, count, this, finish)?, Some(offset) => { let Ok(offset) = u64::try_from(offset) else { return this.set_last_error_and_return(LibcError("EINVAL"), dest); }; - fd.as_unix().pread(communicate, offset, buf, count, dest, this)? + fd.as_unix().pread(communicate, offset, buf, count, this, finish)? } }; interp_ok(()) @@ -284,13 +307,33 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return(LibcError("EBADF"), dest); }; + let finish = { + let dest = dest.clone(); + callback!( + @capture<'tcx> { + count: usize, + dest: MPlaceTy<'tcx>, + } + |this, result: Result| { + match result { + Ok(write_size) => { + assert!(write_size <= count); + // This must fit since `count` fits. + this.write_int(u64::try_from(write_size).unwrap(), &dest) + } + Err(e) => { + this.set_last_error_and_return(e, &dest) + } + }} + ) + }; match offset { - None => fd.write(communicate, buf, count, dest, this)?, + None => fd.write(communicate, buf, count, this, finish)?, Some(offset) => { let Ok(offset) = u64::try_from(offset) else { return this.set_last_error_and_return(LibcError("EINVAL"), dest); }; - fd.as_unix().pwrite(communicate, buf, count, offset, dest, this)? + fd.as_unix().pwrite(communicate, buf, count, offset, this, finish)? } }; interp_ok(()) diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 3353cf2cc59d2..d459ec7cb774c 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -205,10 +205,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "fcntl" => { - // `fcntl` is variadic. The argument count is checked based on the first argument - // in `this.fcntl()`, so we do not use `check_shim` here. - this.check_abi_and_shim_symbol_clash(abi, Conv::C, link_name)?; - let result = this.fcntl(args)?; + let ([fd_num, cmd], varargs) = + this.check_shim_variadic(abi, Conv::C, link_name, args)?; + let result = this.fcntl(fd_num, cmd, varargs)?; this.write_scalar(result, dest)?; } "dup" => { @@ -236,8 +235,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "open" | "open64" => { // `open` is variadic, the third argument is only present when the second argument // has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set - this.check_abi_and_shim_symbol_clash(abi, Conv::C, link_name)?; - let result = this.open(args)?; + let ([path_raw, flag], varargs) = + this.check_shim_variadic(abi, Conv::C, link_name, args)?; + let result = this.open(path_raw, flag, varargs)?; this.write_scalar(result, dest)?; } "unlink" => { @@ -354,10 +354,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "pipe2" => { // Currently this function does not exist on all Unixes, e.g. on macOS. - if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "solaris" | "illumos") - { - throw_unsup_format!("`pipe2` is not supported on {}", this.tcx.sess.target.os); - } + this.check_target_os(&["linux", "freebsd", "solaris", "illumos"], link_name)?; let [pipefd, flags] = this.check_shim(abi, Conv::C, link_name, args)?; let result = this.pipe2(pipefd, Some(flags))?; this.write_scalar(result, dest)?; @@ -402,12 +399,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "reallocarray" => { // Currently this function does not exist on all Unixes, e.g. on macOS. - if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "android") { - throw_unsup_format!( - "`reallocarray` is not supported on {}", - this.tcx.sess.target.os - ); - } + this.check_target_os(&["linux", "freebsd", "android"], link_name)?; let [ptr, nmemb, size] = this.check_shim(abi, Conv::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; let nmemb = this.read_target_usize(nmemb)?; @@ -656,13 +648,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "sched_getaffinity" => { // Currently this function does not exist on all Unixes, e.g. on macOS. - if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "android") { - throw_unsup_format!( - "`sched_getaffinity` is not supported on {}", - this.tcx.sess.target.os - ); - } - + this.check_target_os(&["linux", "freebsd", "android"], link_name)?; let [pid, cpusetsize, mask] = this.check_shim(abi, Conv::C, link_name, args)?; let pid = this.read_scalar(pid)?.to_u32()?; let cpusetsize = this.read_target_usize(cpusetsize)?; @@ -699,13 +685,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "sched_setaffinity" => { // Currently this function does not exist on all Unixes, e.g. on macOS. - if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "android") { - throw_unsup_format!( - "`sched_setaffinity` is not supported on {}", - this.tcx.sess.target.os - ); - } - + this.check_target_os(&["linux", "freebsd", "android"], link_name)?; let [pid, cpusetsize, mask] = this.check_shim(abi, Conv::C, link_name, args)?; let pid = this.read_scalar(pid)?.to_u32()?; let cpusetsize = this.read_target_usize(cpusetsize)?; @@ -761,16 +741,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "getentropy" => { // This function is non-standard but exists with the same signature and behavior on // Linux, macOS, FreeBSD and Solaris/Illumos. - if !matches!( - &*this.tcx.sess.target.os, - "linux" | "macos" | "freebsd" | "illumos" | "solaris" | "android" - ) { - throw_unsup_format!( - "`getentropy` is not supported on {}", - this.tcx.sess.target.os - ); - } - + this.check_target_os( + &["linux", "macos", "freebsd", "illumos", "solaris", "android"], + link_name, + )?; let [buf, bufsize] = this.check_shim(abi, Conv::C, link_name, args)?; let buf = this.read_pointer(buf)?; let bufsize = this.read_target_usize(bufsize)?; @@ -797,15 +771,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "getrandom" => { // This function is non-standard but exists with the same signature and behavior on // Linux, FreeBSD and Solaris/Illumos. - if !matches!( - &*this.tcx.sess.target.os, - "linux" | "freebsd" | "illumos" | "solaris" | "android" - ) { - throw_unsup_format!( - "`getrandom` is not supported on {}", - this.tcx.sess.target.os - ); - } + this.check_target_os( + &["linux", "freebsd", "illumos", "solaris", "android"], + link_name, + )?; let [ptr, len, flags] = this.check_shim(abi, Conv::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; let len = this.read_target_usize(len)?; @@ -817,12 +786,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "arc4random_buf" => { // This function is non-standard but exists with the same signature and // same behavior (eg never fails) on FreeBSD and Solaris/Illumos. - if !matches!(&*this.tcx.sess.target.os, "freebsd" | "illumos" | "solaris") { - throw_unsup_format!( - "`arc4random_buf` is not supported on {}", - this.tcx.sess.target.os - ); - } + this.check_target_os(&["freebsd", "illumos", "solaris"], link_name)?; let [ptr, len] = this.check_shim(abi, Conv::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; let len = this.read_target_usize(len)?; @@ -842,15 +806,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // For arm32 they did something custom, but similar enough that the same // `_Unwind_RaiseException` impl in miri should work: // https://github.com/ARM-software/abi-aa/blob/main/ehabi32/ehabi32.rst - if !matches!( - &*this.tcx.sess.target.os, - "linux" | "freebsd" | "illumos" | "solaris" | "android" | "macos" - ) { - throw_unsup_format!( - "`_Unwind_RaiseException` is not supported on {}", - this.tcx.sess.target.os - ); - } + this.check_target_os( + &["linux", "freebsd", "illumos", "solaris", "android", "macos"], + link_name, + )?; // This function looks and behaves excatly like miri_start_unwind. let [payload] = this.check_shim(abi, Conv::C, link_name, args)?; this.handle_miri_start_unwind(payload)?; @@ -866,8 +825,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // These shims are enabled only when the caller is in the standard library. "pthread_attr_getguardsize" if this.frame_in_std() => { let [_attr, guard_size] = this.check_shim(abi, Conv::C, link_name, args)?; - let guard_size = this.deref_pointer(guard_size)?; let guard_size_layout = this.libc_ty_layout("size_t"); + let guard_size = this.deref_pointer_as(guard_size, guard_size_layout)?; this.write_scalar( Scalar::from_uint(this.machine.page_size, guard_size_layout.size), &guard_size, @@ -893,8 +852,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.check_shim(abi, Conv::C, link_name, args)?; let _attr_place = this.deref_pointer_as(attr_place, this.libc_ty_layout("pthread_attr_t"))?; - let addr_place = this.deref_pointer(addr_place)?; - let size_place = this.deref_pointer(size_place)?; + let addr_place = this.deref_pointer_as(addr_place, this.machine.layouts.usize)?; + let size_place = this.deref_pointer_as(size_place, this.machine.layouts.usize)?; this.write_scalar( Scalar::from_uint(this.machine.stack_addr, this.pointer_size()), @@ -928,7 +887,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let pwd = this.deref_pointer_as(pwd, this.libc_ty_layout("passwd"))?; let buf = this.read_pointer(buf)?; let buflen = this.read_target_usize(buflen)?; - let result = this.deref_pointer(result)?; + let result = this.deref_pointer_as(result, this.machine.layouts.mut_raw_ptr)?; // Must be for "us". if uid != UID { diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index 03dbd931329c7..08d06fe5d4c61 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -60,17 +60,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // since freebsd 12 the former form can be expected. "stat" | "stat@FBSD_1.0" => { let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?; - let result = this.macos_fbsd_solaris_stat(path, buf)?; + let result = this.macos_fbsd_solarish_stat(path, buf)?; this.write_scalar(result, dest)?; } "lstat" | "lstat@FBSD_1.0" => { let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?; - let result = this.macos_fbsd_solaris_lstat(path, buf)?; + let result = this.macos_fbsd_solarish_lstat(path, buf)?; this.write_scalar(result, dest)?; } "fstat" | "fstat@FBSD_1.0" => { let [fd, buf] = this.check_shim(abi, Conv::C, link_name, args)?; - let result = this.macos_fbsd_solaris_fstat(fd, buf)?; + let result = this.macos_fbsd_solarish_fstat(fd, buf)?; this.write_scalar(result, dest)?; } "readdir_r" | "readdir_r@FBSD_1.0" => { diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index cafce62cfedbe..c7399b00d3fe2 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -13,7 +13,7 @@ use rustc_abi::Size; use rustc_data_structures::fx::FxHashMap; use self::shims::time::system_time_to_duration; -use crate::helpers::check_min_arg_count; +use crate::helpers::check_min_vararg_count; use crate::shims::files::{EvalContextExt as _, FileDescription, FileDescriptionRef}; use crate::shims::os_str::bytes_to_os_str; use crate::shims::unix::fd::{FlockOp, UnixFileDescription}; @@ -35,16 +35,13 @@ impl FileDescription for FileHandle { communicate_allowed: bool, ptr: Pointer, len: usize, - dest: &MPlaceTy<'tcx>, ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { assert!(communicate_allowed, "isolation should have prevented even opening a file"); - let mut bytes = vec![0; len]; - let result = (&mut &self.file).read(&mut bytes); - match result { - Ok(read_size) => ecx.return_read_success(ptr, &bytes, read_size, dest), - Err(e) => ecx.set_last_error_and_return(e, dest), - } + + let result = ecx.read_from_host(&self.file, len, ptr)?; + finish.call(ecx, result) } fn write<'tcx>( @@ -52,16 +49,13 @@ impl FileDescription for FileHandle { communicate_allowed: bool, ptr: Pointer, len: usize, - dest: &MPlaceTy<'tcx>, ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { assert!(communicate_allowed, "isolation should have prevented even opening a file"); - let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?; - let result = (&mut &self.file).write(bytes); - match result { - Ok(write_size) => ecx.return_write_success(write_size, dest), - Err(e) => ecx.set_last_error_and_return(e, dest), - } + + let result = ecx.write_to_host(&self.file, len, ptr)?; + finish.call(ecx, result) } fn seek<'tcx>( @@ -119,8 +113,8 @@ impl UnixFileDescription for FileHandle { offset: u64, ptr: Pointer, len: usize, - dest: &MPlaceTy<'tcx>, ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { assert!(communicate_allowed, "isolation should have prevented even opening a file"); let mut bytes = vec![0; len]; @@ -137,11 +131,17 @@ impl UnixFileDescription for FileHandle { .expect("failed to restore file position, this shouldn't be possible"); res }; - let result = f(); - match result { - Ok(read_size) => ecx.return_read_success(ptr, &bytes, read_size, dest), - Err(e) => ecx.set_last_error_and_return(e, dest), - } + let result = match f() { + Ok(read_size) => { + // If reading to `bytes` did not fail, we write those bytes to the buffer. + // Crucially, if fewer than `bytes.len()` bytes were read, only write + // that much into the output buffer! + ecx.write_bytes_ptr(ptr, bytes[..read_size].iter().copied())?; + Ok(read_size) + } + Err(e) => Err(IoError::HostError(e)), + }; + finish.call(ecx, result) } fn pwrite<'tcx>( @@ -150,8 +150,8 @@ impl UnixFileDescription for FileHandle { ptr: Pointer, len: usize, offset: u64, - dest: &MPlaceTy<'tcx>, ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { assert!(communicate_allowed, "isolation should have prevented even opening a file"); // Emulates pwrite using seek + write + seek to restore cursor position. @@ -169,10 +169,7 @@ impl UnixFileDescription for FileHandle { res }; let result = f(); - match result { - Ok(write_size) => ecx.return_write_success(write_size, dest), - Err(e) => ecx.set_last_error_and_return(e, dest), - } + finish.call(ecx, result.map_err(IoError::HostError)) } fn flock<'tcx>( @@ -273,7 +270,7 @@ impl UnixFileDescription for FileHandle { impl<'tcx> EvalContextExtPrivate<'tcx> for crate::MiriInterpCx<'tcx> {} trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { - fn macos_fbsd_solaris_write_buf( + fn macos_fbsd_solarish_write_stat_buf( &mut self, metadata: FileMetadata, buf_op: &OpTy<'tcx>, @@ -321,9 +318,9 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { } if matches!(&*this.tcx.sess.target.os, "solaris" | "illumos") { - // FIXME: write st_fstype field once libc is updated. - // https://github.com/rust-lang/libc/pull/4145 - //this.write_int_fields_named(&[("st_fstype", 0)], &buf)?; + let st_fstype = this.project_field_named(&buf, "st_fstype")?; + // This is an array; write 0 into first element so that it encodes the empty string. + this.write_int(0, &this.project_index(&st_fstype, 0)?)?; } interp_ok(0) @@ -452,9 +449,12 @@ fn maybe_sync_file( impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { - fn open(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> { - let [path_raw, flag] = check_min_arg_count("open", args)?; - + fn open( + &mut self, + path_raw: &OpTy<'tcx>, + flag: &OpTy<'tcx>, + varargs: &[OpTy<'tcx>], + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let path_raw = this.read_pointer(path_raw)?; @@ -507,7 +507,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Get the mode. On macOS, the argument type `mode_t` is actually `u16`, but // C integer promotion rules mean that on the ABI level, it gets passed as `u32` // (see https://github.com/rust-lang/rust/issues/71915). - let [_, _, mode] = check_min_arg_count("open(pathname, O_CREAT, ...)", args)?; + let [mode] = check_min_vararg_count("open(pathname, O_CREAT, ...)", varargs)?; let mode = this.read_scalar(mode)?.to_u32()?; #[cfg(unix)] @@ -668,7 +668,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } - fn macos_fbsd_solaris_stat( + fn macos_fbsd_solarish_stat( &mut self, path_op: &OpTy<'tcx>, buf_op: &OpTy<'tcx>, @@ -694,11 +694,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Err(err) => return this.set_last_error_and_return_i32(err), }; - interp_ok(Scalar::from_i32(this.macos_fbsd_solaris_write_buf(metadata, buf_op)?)) + interp_ok(Scalar::from_i32(this.macos_fbsd_solarish_write_stat_buf(metadata, buf_op)?)) } // `lstat` is used to get symlink metadata. - fn macos_fbsd_solaris_lstat( + fn macos_fbsd_solarish_lstat( &mut self, path_op: &OpTy<'tcx>, buf_op: &OpTy<'tcx>, @@ -726,10 +726,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Err(err) => return this.set_last_error_and_return_i32(err), }; - interp_ok(Scalar::from_i32(this.macos_fbsd_solaris_write_buf(metadata, buf_op)?)) + interp_ok(Scalar::from_i32(this.macos_fbsd_solarish_write_stat_buf(metadata, buf_op)?)) } - fn macos_fbsd_solaris_fstat( + fn macos_fbsd_solarish_fstat( &mut self, fd_op: &OpTy<'tcx>, buf_op: &OpTy<'tcx>, @@ -756,7 +756,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(metadata) => metadata, Err(err) => return this.set_last_error_and_return_i32(err), }; - interp_ok(Scalar::from_i32(this.macos_fbsd_solaris_write_buf(metadata, buf_op)?)) + interp_ok(Scalar::from_i32(this.macos_fbsd_solarish_write_stat_buf(metadata, buf_op)?)) } fn linux_statx( @@ -1109,7 +1109,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), dirent_layout.align.abi, MiriMemoryKind::Runtime.into(), - AllocInit::Uninit + AllocInit::Uninit, )?; let entry: Pointer = entry.into(); @@ -1169,6 +1169,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } let dirp = this.read_target_usize(dirp_op)?; + let result_place = this.deref_pointer_as(result_op, this.machine.layouts.mut_raw_ptr)?; // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { @@ -1254,15 +1255,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } _ => unreachable!(), } - - let result_place = this.deref_pointer(result_op)?; this.write_scalar(this.read_scalar(entry_op)?, &result_place)?; Scalar::from_i32(0) } None => { // end of stream: return 0, assign *result=NULL - this.write_null(&this.deref_pointer(result_op)?)?; + this.write_null(&result_place)?; Scalar::from_i32(0) } Some(Err(e)) => { @@ -1548,7 +1547,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } fn mkstemp(&mut self, template_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { - use rand::seq::SliceRandom; + use rand::seq::IndexedRandom; // POSIX defines the template string. const TEMPFILE_TEMPLATE_STR: &str = "XXXXXX"; diff --git a/src/tools/miri/src/shims/unix/linux/mem.rs b/src/tools/miri/src/shims/unix/linux/mem.rs index 6418d749d3d9d..8e5a3021b1c03 100644 --- a/src/tools/miri/src/shims/unix/linux/mem.rs +++ b/src/tools/miri/src/shims/unix/linux/mem.rs @@ -49,7 +49,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(new_size), align, MiriMemoryKind::Mmap.into(), - AllocInit::Zero + AllocInit::Zero, )?; interp_ok(Scalar::from_pointer(ptr, this)) diff --git a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs index 4b76bbb2b4de4..936d436bd82d6 100644 --- a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs @@ -51,20 +51,20 @@ impl FileDescription for EventFd { _communicate_allowed: bool, ptr: Pointer, len: usize, - dest: &MPlaceTy<'tcx>, ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { // We're treating the buffer as a `u64`. let ty = ecx.machine.layouts.u64; // Check the size of slice, and return error only if the size of the slice < 8. if len < ty.size.bytes_usize() { - return ecx.set_last_error_and_return(ErrorKind::InvalidInput, dest); + return finish.call(ecx, Err(ErrorKind::InvalidInput.into())); } // Turn the pointer into a place at the right type. let buf_place = ecx.ptr_to_mplace_unaligned(ptr, ty); - eventfd_read(buf_place, dest, self, ecx) + eventfd_read(buf_place, self, ecx, finish) } /// A write call adds the 8-byte integer value supplied in @@ -84,20 +84,20 @@ impl FileDescription for EventFd { _communicate_allowed: bool, ptr: Pointer, len: usize, - dest: &MPlaceTy<'tcx>, ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { // We're treating the buffer as a `u64`. let ty = ecx.machine.layouts.u64; // Check the size of slice, and return error only if the size of the slice < 8. if len < ty.layout.size.bytes_usize() { - return ecx.set_last_error_and_return(ErrorKind::InvalidInput, dest); + return finish.call(ecx, Err(ErrorKind::InvalidInput.into())); } // Turn the pointer into a place at the right type. let buf_place = ecx.ptr_to_mplace_unaligned(ptr, ty); - eventfd_write(buf_place, dest, self, ecx) + eventfd_write(buf_place, self, ecx, finish) } fn as_unix(&self) -> &dyn UnixFileDescription { @@ -183,15 +183,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// else just add the user-supplied value to current counter. fn eventfd_write<'tcx>( buf_place: MPlaceTy<'tcx>, - dest: &MPlaceTy<'tcx>, eventfd: FileDescriptionRef, ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { // Figure out which value we should add. let num = ecx.read_scalar(&buf_place)?.to_u64()?; // u64::MAX as input is invalid because the maximum value of counter is u64::MAX - 1. if num == u64::MAX { - return ecx.set_last_error_and_return(ErrorKind::InvalidInput, dest); + return finish.call(ecx, Err(ErrorKind::InvalidInput.into())); } match eventfd.counter.get().checked_add(num) { @@ -219,16 +219,14 @@ fn eventfd_write<'tcx>( ecx.check_and_update_readiness(eventfd)?; // Return how many bytes we consumed from the user-provided buffer. - return ecx.write_int(buf_place.layout.size.bytes(), dest); + return finish.call(ecx, Ok(buf_place.layout.size.bytes_usize())); } None | Some(u64::MAX) => { // We can't update the state, so we have to block. if eventfd.is_nonblock { - return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest); + return finish.call(ecx, Err(ErrorKind::WouldBlock.into())); } - let dest = dest.clone(); - eventfd.blocked_write_tid.borrow_mut().push(ecx.active_thread()); let weak_eventfd = FileDescriptionRef::downgrade(&eventfd); @@ -239,7 +237,7 @@ fn eventfd_write<'tcx>( @capture<'tcx> { num: u64, buf_place: MPlaceTy<'tcx>, - dest: MPlaceTy<'tcx>, + finish: DynMachineCallback<'tcx, Result>, weak_eventfd: WeakFileDescriptionRef, } |this, unblock: UnblockKind| { @@ -247,7 +245,7 @@ fn eventfd_write<'tcx>( // When we get unblocked, try again. We know the ref is still valid, // otherwise there couldn't be a `write` that unblocks us. let eventfd_ref = weak_eventfd.upgrade().unwrap(); - eventfd_write(buf_place, &dest, eventfd_ref, this) + eventfd_write(buf_place, eventfd_ref, this, finish) } ), ); @@ -260,9 +258,9 @@ fn eventfd_write<'tcx>( /// else just return the current counter value to the caller and set the counter to 0. fn eventfd_read<'tcx>( buf_place: MPlaceTy<'tcx>, - dest: &MPlaceTy<'tcx>, eventfd: FileDescriptionRef, ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { // Set counter to 0, get old value. let counter = eventfd.counter.replace(0); @@ -270,9 +268,8 @@ fn eventfd_read<'tcx>( // Block when counter == 0. if counter == 0 { if eventfd.is_nonblock { - return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest); + return finish.call(ecx, Err(ErrorKind::WouldBlock.into())); } - let dest = dest.clone(); eventfd.blocked_read_tid.borrow_mut().push(ecx.active_thread()); @@ -283,7 +280,7 @@ fn eventfd_read<'tcx>( callback!( @capture<'tcx> { buf_place: MPlaceTy<'tcx>, - dest: MPlaceTy<'tcx>, + finish: DynMachineCallback<'tcx, Result>, weak_eventfd: WeakFileDescriptionRef, } |this, unblock: UnblockKind| { @@ -291,7 +288,7 @@ fn eventfd_read<'tcx>( // When we get unblocked, try again. We know the ref is still valid, // otherwise there couldn't be a `write` that unblocks us. let eventfd_ref = weak_eventfd.upgrade().unwrap(); - eventfd_read(buf_place, &dest, eventfd_ref, this) + eventfd_read(buf_place, eventfd_ref, this, finish) } ), ); @@ -317,7 +314,7 @@ fn eventfd_read<'tcx>( ecx.check_and_update_readiness(eventfd)?; // Tell userspace how many bytes we put into the buffer. - return ecx.write_int(buf_place.layout.size.bytes(), dest); + return finish.call(ecx, Ok(buf_place.layout.size.bytes_usize())); } interp_ok(()) } diff --git a/src/tools/miri/src/shims/unix/linux_like/sync.rs b/src/tools/miri/src/shims/unix/linux_like/sync.rs index 51124fb2a003b..280bee4800fe5 100644 --- a/src/tools/miri/src/shims/unix/linux_like/sync.rs +++ b/src/tools/miri/src/shims/unix/linux_like/sync.rs @@ -1,5 +1,5 @@ use crate::concurrency::sync::FutexRef; -use crate::helpers::check_min_arg_count; +use crate::helpers::check_min_vararg_count; use crate::*; struct LinuxFutex { @@ -10,7 +10,7 @@ struct LinuxFutex { /// `args` is the arguments *including* the syscall number. pub fn futex<'tcx>( ecx: &mut MiriInterpCx<'tcx>, - args: &[OpTy<'tcx>], + varargs: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { // The amount of arguments used depends on the type of futex operation. @@ -21,7 +21,7 @@ pub fn futex<'tcx>( // may or may not be left out from the `syscall()` call. // Therefore we don't use `check_arg_count` here, but only check for the // number of arguments to fall within a range. - let [_, addr, op, val] = check_min_arg_count("`syscall(SYS_futex, ...)`", args)?; + let [addr, op, val] = check_min_vararg_count("`syscall(SYS_futex, ...)`", varargs)?; // The first three arguments (after the syscall number itself) are the same to all futex operations: // (int *addr, int op, int val). @@ -55,14 +55,16 @@ pub fn futex<'tcx>( let wait_bitset = op & !futex_realtime == futex_wait_bitset; let (timeout, bitset) = if wait_bitset { - let [_, _, _, _, timeout, uaddr2, bitset] = - check_min_arg_count("`syscall(SYS_futex, FUTEX_WAIT_BITSET, ...)`", args)?; + let [_, _, _, timeout, uaddr2, bitset] = check_min_vararg_count( + "`syscall(SYS_futex, FUTEX_WAIT_BITSET, ...)`", + varargs, + )?; let _timeout = ecx.read_pointer(timeout)?; let _uaddr2 = ecx.read_pointer(uaddr2)?; (timeout, ecx.read_scalar(bitset)?.to_u32()?) } else { - let [_, _, _, _, timeout] = - check_min_arg_count("`syscall(SYS_futex, FUTEX_WAIT, ...)`", args)?; + let [_, _, _, timeout] = + check_min_vararg_count("`syscall(SYS_futex, FUTEX_WAIT, ...)`", varargs)?; (timeout, u32::MAX) }; @@ -156,14 +158,24 @@ pub fn futex<'tcx>( .futex .clone(); + let dest = dest.clone(); ecx.futex_wait( futex_ref, bitset, timeout, - Scalar::from_target_isize(0, ecx), // retval_succ - Scalar::from_target_isize(-1, ecx), // retval_timeout - dest.clone(), - LibcError("ETIMEDOUT"), // errno_timeout + callback!( + @capture<'tcx> { + dest: MPlaceTy<'tcx>, + } + |ecx, unblock: UnblockKind| match unblock { + UnblockKind::Ready => { + ecx.write_int(0, &dest) + } + UnblockKind::TimedOut => { + ecx.set_last_error_and_return(LibcError("ETIMEDOUT"), &dest) + } + } + ), ); } else { // The futex value doesn't match the expected value, so we return failure @@ -190,8 +202,10 @@ pub fn futex<'tcx>( let futex_ref = futex_ref.futex.clone(); let bitset = if op == futex_wake_bitset { - let [_, _, _, _, timeout, uaddr2, bitset] = - check_min_arg_count("`syscall(SYS_futex, FUTEX_WAKE_BITSET, ...)`", args)?; + let [_, _, _, timeout, uaddr2, bitset] = check_min_vararg_count( + "`syscall(SYS_futex, FUTEX_WAKE_BITSET, ...)`", + varargs, + )?; let _timeout = ecx.read_pointer(timeout)?; let _uaddr2 = ecx.read_pointer(uaddr2)?; ecx.read_scalar(bitset)?.to_u32()? @@ -205,16 +219,8 @@ pub fn futex<'tcx>( // will see the latest value on addr which could be changed by our caller // before doing the syscall. ecx.atomic_fence(AtomicFenceOrd::SeqCst)?; - let mut n = 0; - #[expect(clippy::arithmetic_side_effects)] - for _ in 0..val { - if ecx.futex_wake(&futex_ref, bitset)? { - n += 1; - } else { - break; - } - } - ecx.write_scalar(Scalar::from_target_isize(n, ecx), dest)?; + let woken = ecx.futex_wake(&futex_ref, bitset, val.try_into().unwrap())?; + ecx.write_scalar(Scalar::from_target_isize(woken.try_into().unwrap(), ecx), dest)?; } op => throw_unsup_format!("Miri does not support `futex` syscall with op={}", op), } diff --git a/src/tools/miri/src/shims/unix/linux_like/syscall.rs b/src/tools/miri/src/shims/unix/linux_like/syscall.rs index 5fb262e176f0b..22c6dc975070b 100644 --- a/src/tools/miri/src/shims/unix/linux_like/syscall.rs +++ b/src/tools/miri/src/shims/unix/linux_like/syscall.rs @@ -2,7 +2,7 @@ use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::{Conv, FnAbi}; -use crate::helpers::check_min_arg_count; +use crate::helpers::check_min_vararg_count; use crate::shims::unix::linux_like::eventfd::EvalContextExt as _; use crate::shims::unix::linux_like::sync::futex; use crate::*; @@ -14,9 +14,7 @@ pub fn syscall<'tcx>( args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { - // We do not use `check_shim` here because `syscall` is variadic. The argument - // count is checked bellow. - ecx.check_abi_and_shim_symbol_clash(abi, Conv::C, link_name)?; + let ([op], varargs) = ecx.check_shim_variadic(abi, Conv::C, link_name, args)?; // The syscall variadic function is legal to call with more arguments than needed, // extra arguments are simply ignored. The important check is that when we use an // argument, we have to also check all arguments *before* it to ensure that they @@ -26,14 +24,13 @@ pub fn syscall<'tcx>( let sys_futex = ecx.eval_libc("SYS_futex").to_target_usize(ecx)?; let sys_eventfd2 = ecx.eval_libc("SYS_eventfd2").to_target_usize(ecx)?; - let [op] = check_min_arg_count("syscall", args)?; match ecx.read_target_usize(op)? { // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)` // is called if a `HashMap` is created the regular way (e.g. HashMap). num if num == sys_getrandom => { // Used by getrandom 0.1 // The first argument is the syscall id, so skip over it. - let [_, ptr, len, flags] = check_min_arg_count("syscall(SYS_getrandom, ...)", args)?; + let [ptr, len, flags] = check_min_vararg_count("syscall(SYS_getrandom, ...)", varargs)?; let ptr = ecx.read_pointer(ptr)?; let len = ecx.read_target_usize(len)?; @@ -47,10 +44,10 @@ pub fn syscall<'tcx>( } // `futex` is used by some synchronization primitives. num if num == sys_futex => { - futex(ecx, args, dest)?; + futex(ecx, varargs, dest)?; } num if num == sys_eventfd2 => { - let [_, initval, flags] = check_min_arg_count("syscall(SYS_evetfd2, ...)", args)?; + let [initval, flags] = check_min_vararg_count("syscall(SYS_evetfd2, ...)", varargs)?; let result = ecx.eventfd(initval, flags)?; ecx.write_int(result.to_i32()?, dest)?; diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index 85c963774a159..918fd8dd52dfd 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -2,13 +2,20 @@ use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::{Conv, FnAbi}; -use super::sync::EvalContextExt as _; -use crate::helpers::check_min_arg_count; +use super::sync::{EvalContextExt as _, MacOsFutexTimeout}; use crate::shims::unix::*; use crate::*; -pub fn is_dyn_sym(_name: &str) -> bool { - false +pub fn is_dyn_sym(name: &str) -> bool { + match name { + // These only became available with macOS 11.0, so std looks them up dynamically. + "os_sync_wait_on_address" + | "os_sync_wait_on_address_with_deadline" + | "os_sync_wait_on_address_with_timeout" + | "os_sync_wake_by_address_any" + | "os_sync_wake_by_address_all" => true, + _ => false, + } } impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -40,17 +47,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "stat" | "stat64" | "stat$INODE64" => { let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?; - let result = this.macos_fbsd_solaris_stat(path, buf)?; + let result = this.macos_fbsd_solarish_stat(path, buf)?; this.write_scalar(result, dest)?; } "lstat" | "lstat64" | "lstat$INODE64" => { let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?; - let result = this.macos_fbsd_solaris_lstat(path, buf)?; + let result = this.macos_fbsd_solarish_lstat(path, buf)?; this.write_scalar(result, dest)?; } "fstat" | "fstat64" | "fstat$INODE64" => { let [fd, buf] = this.check_shim(abi, Conv::C, link_name, args)?; - let result = this.macos_fbsd_solaris_fstat(fd, buf)?; + let result = this.macos_fbsd_solarish_fstat(fd, buf)?; this.write_scalar(result, dest)?; } "opendir$INODE64" => { @@ -69,10 +76,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "ioctl" => { - // `ioctl` is variadic. The argument count is checked based on the first argument - // in `this.ioctl()`, so we do not use `check_shim` here. - this.check_abi_and_shim_symbol_clash(abi, Conv::C, link_name)?; - let result = this.ioctl(args)?; + let ([fd_num, cmd], varargs) = + this.check_shim_variadic(abi, Conv::C, link_name, args)?; + let result = this.ioctl(fd_num, cmd, varargs)?; this.write_scalar(result, dest)?; } @@ -216,6 +222,58 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } + // Futex primitives + "os_sync_wait_on_address" => { + let [addr_op, value_op, size_op, flags_op] = + this.check_shim(abi, Conv::C, link_name, args)?; + this.os_sync_wait_on_address( + addr_op, + value_op, + size_op, + flags_op, + MacOsFutexTimeout::None, + dest, + )?; + } + "os_sync_wait_on_address_with_deadline" => { + let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] = + this.check_shim(abi, Conv::C, link_name, args)?; + this.os_sync_wait_on_address( + addr_op, + value_op, + size_op, + flags_op, + MacOsFutexTimeout::Absolute { clock_op, timeout_op }, + dest, + )?; + } + "os_sync_wait_on_address_with_timeout" => { + let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] = + this.check_shim(abi, Conv::C, link_name, args)?; + this.os_sync_wait_on_address( + addr_op, + value_op, + size_op, + flags_op, + MacOsFutexTimeout::Relative { clock_op, timeout_op }, + dest, + )?; + } + "os_sync_wake_by_address_any" => { + let [addr_op, size_op, flags_op] = + this.check_shim(abi, Conv::C, link_name, args)?; + this.os_sync_wake_by_address( + addr_op, size_op, flags_op, /* all */ false, dest, + )?; + } + "os_sync_wake_by_address_all" => { + let [addr_op, size_op, flags_op] = + this.check_shim(abi, Conv::C, link_name, args)?; + this.os_sync_wake_by_address( + addr_op, size_op, flags_op, /* all */ true, dest, + )?; + } + "os_unfair_lock_lock" => { let [lock_op] = this.check_shim(abi, Conv::C, link_name, args)?; this.os_unfair_lock_lock(lock_op)?; @@ -243,12 +301,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(EmulateItemResult::NeedsReturn) } - fn ioctl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> { + fn ioctl( + &mut self, + fd_num: &OpTy<'tcx>, + cmd: &OpTy<'tcx>, + _varargs: &[OpTy<'tcx>], + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let fioclex = this.eval_libc_u64("FIOCLEX"); - let [fd_num, cmd] = check_min_arg_count("ioctl", args)?; let fd_num = this.read_scalar(fd_num)?.to_i32()?; let cmd = this.read_scalar(cmd)?.to_u64()?; diff --git a/src/tools/miri/src/shims/unix/macos/sync.rs b/src/tools/miri/src/shims/unix/macos/sync.rs index 330c64f06a3e6..6ba52f2f57e40 100644 --- a/src/tools/miri/src/shims/unix/macos/sync.rs +++ b/src/tools/miri/src/shims/unix/macos/sync.rs @@ -10,8 +10,12 @@ //! and we do not detect copying of the lock, but macOS doesn't guarantee anything //! in that case either. +use std::cell::Cell; +use std::time::Duration; + use rustc_abi::Size; +use crate::concurrency::sync::FutexRef; use crate::*; #[derive(Clone)] @@ -20,6 +24,26 @@ enum MacOsUnfairLock { Active { mutex_ref: MutexRef }, } +pub enum MacOsFutexTimeout<'a, 'tcx> { + None, + Relative { clock_op: &'a OpTy<'tcx>, timeout_op: &'a OpTy<'tcx> }, + Absolute { clock_op: &'a OpTy<'tcx>, timeout_op: &'a OpTy<'tcx> }, +} + +/// Metadata for a macOS futex. +/// +/// Since macOS 11.0, Apple has exposed the previously private futex API consisting +/// of `os_sync_wait_on_address` (and friends) and `os_sync_wake_by_address_{any, all}`. +/// These work with different value sizes and flags, which are validated to be consistent. +/// This structure keeps track of both the futex queue and these values. +struct MacOsFutex { + futex: FutexRef, + /// The size in bytes of the atomic primitive underlying this futex. + size: Cell, + /// Whether the futex is shared across process boundaries. + shared: Cell, +} + impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { fn os_unfair_lock_get_data<'a>( @@ -30,7 +54,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { 'tcx: 'a, { let this = self.eval_context_mut(); - let lock = this.deref_pointer(lock_ptr)?; + let lock = this.deref_pointer_as(lock_ptr, this.libc_ty_layout("os_unfair_lock_s"))?; this.lazy_sync_get_data( &lock, Size::ZERO, // offset for init tracking @@ -54,6 +78,198 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// Implements [`os_sync_wait_on_address`], [`os_sync_wait_on_address_with_deadline`] + /// and [`os_sync_wait_on_address_with_timeout`]. + /// + /// [`os_sync_wait_on_address`]: https://developer.apple.com/documentation/os/os_sync_wait_on_address?language=objc + /// [`os_sync_wait_on_address_with_deadline`]: https://developer.apple.com/documentation/os/os_sync_wait_on_address_with_deadline?language=objc + /// [`os_sync_wait_on_address_with_timeout`]: https://developer.apple.com/documentation/os/os_sync_wait_on_address_with_timeout?language=objc + fn os_sync_wait_on_address( + &mut self, + addr_op: &OpTy<'tcx>, + value_op: &OpTy<'tcx>, + size_op: &OpTy<'tcx>, + flags_op: &OpTy<'tcx>, + timeout: MacOsFutexTimeout<'_, 'tcx>, + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + let none = this.eval_libc_u32("OS_SYNC_WAIT_ON_ADDRESS_NONE"); + let shared = this.eval_libc_u32("OS_SYNC_WAIT_ON_ADDRESS_SHARED"); + let absolute_clock = this.eval_libc_u32("OS_CLOCK_MACH_ABSOLUTE_TIME"); + + let ptr = this.read_pointer(addr_op)?; + let value = this.read_scalar(value_op)?.to_u64()?; + let size = this.read_target_usize(size_op)?; + let flags = this.read_scalar(flags_op)?.to_u32()?; + + let clock_timeout = match timeout { + MacOsFutexTimeout::None => None, + MacOsFutexTimeout::Relative { clock_op, timeout_op } => { + let clock = this.read_scalar(clock_op)?.to_u32()?; + let timeout = this.read_scalar(timeout_op)?.to_u64()?; + Some((clock, TimeoutAnchor::Relative, timeout)) + } + MacOsFutexTimeout::Absolute { clock_op, timeout_op } => { + let clock = this.read_scalar(clock_op)?.to_u32()?; + let timeout = this.read_scalar(timeout_op)?.to_u64()?; + Some((clock, TimeoutAnchor::Absolute, timeout)) + } + }; + + // Perform validation of the arguments. + let addr = ptr.addr().bytes(); + if addr == 0 + || !matches!(size, 4 | 8) + || !addr.is_multiple_of(size) + || (flags != none && flags != shared) + || clock_timeout + .is_some_and(|(clock, _, timeout)| clock != absolute_clock || timeout == 0) + { + this.set_last_error_and_return(LibcError("EINVAL"), dest)?; + return interp_ok(()); + } + + let is_shared = flags == shared; + let timeout = clock_timeout.map(|(_, anchor, timeout)| { + // The only clock that is currenlty supported is the monotonic clock. + // While the deadline argument of `os_sync_wait_on_address_with_deadline` + // is actually not in nanoseconds but in the units of `mach_current_time`, + // the two are equivalent in miri. + (TimeoutClock::Monotonic, anchor, Duration::from_nanos(timeout)) + }); + + // See the Linux futex implementation for why this fence exists. + this.atomic_fence(AtomicFenceOrd::SeqCst)?; + + let layout = this.machine.layouts.uint(Size::from_bytes(size)).unwrap(); + let futex_val = this + .read_scalar_atomic(&this.ptr_to_mplace(ptr, layout), AtomicReadOrd::Acquire)? + .to_bits(Size::from_bytes(size))?; + + let futex = this + .get_sync_or_init(ptr, |_| { + MacOsFutex { + futex: Default::default(), + size: Cell::new(size), + shared: Cell::new(is_shared), + } + }) + .unwrap(); + + // Detect mismatches between the flags and sizes used on this address + // by comparing it with the parameters used by the other waiters in + // the current list. If the list is currently empty, update those + // parameters. + if futex.futex.waiters() == 0 { + futex.size.set(size); + futex.shared.set(is_shared); + } else if futex.size.get() != size || futex.shared.get() != is_shared { + this.set_last_error_and_return(LibcError("EINVAL"), dest)?; + return interp_ok(()); + } + + if futex_val == value.into() { + // If the values are the same, we have to block. + let futex_ref = futex.futex.clone(); + let dest = dest.clone(); + this.futex_wait( + futex_ref.clone(), + u32::MAX, // bitset + timeout, + callback!( + @capture<'tcx> { + dest: MPlaceTy<'tcx>, + futex_ref: FutexRef, + } + |this, unblock: UnblockKind| { + match unblock { + UnblockKind::Ready => { + let remaining = futex_ref.waiters().try_into().unwrap(); + this.write_scalar(Scalar::from_i32(remaining), &dest) + } + UnblockKind::TimedOut => { + this.set_last_error_and_return(LibcError("ETIMEDOUT"), &dest) + } + } + } + ), + ); + } else { + // else retrieve the current number of waiters. + let waiters = futex.futex.waiters().try_into().unwrap(); + this.write_scalar(Scalar::from_i32(waiters), dest)?; + } + + interp_ok(()) + } + + /// Implements [`os_sync_wake_by_address_all`] and [`os_sync_wake_by_address_any`]. + /// + /// [`os_sync_wake_by_address_all`]: https://developer.apple.com/documentation/os/os_sync_wake_by_address_all?language=objc + /// [`os_sync_wake_by_address_any`]: https://developer.apple.com/documentation/os/os_sync_wake_by_address_any?language=objc + fn os_sync_wake_by_address( + &mut self, + addr_op: &OpTy<'tcx>, + size_op: &OpTy<'tcx>, + flags_op: &OpTy<'tcx>, + all: bool, + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + let none = this.eval_libc_u32("OS_SYNC_WAKE_BY_ADDRESS_NONE"); + let shared = this.eval_libc_u32("OS_SYNC_WAKE_BY_ADDRESS_SHARED"); + + let ptr = this.read_pointer(addr_op)?; + let size = this.read_target_usize(size_op)?; + let flags = this.read_scalar(flags_op)?.to_u32()?; + + // Perform validation of the arguments. + let addr = ptr.addr().bytes(); + if addr == 0 || !matches!(size, 4 | 8) || (flags != none && flags != shared) { + this.set_last_error_and_return(LibcError("EINVAL"), dest)?; + return interp_ok(()); + } + + let is_shared = flags == shared; + + let Some(futex) = this.get_sync_or_init(ptr, |_| { + MacOsFutex { + futex: Default::default(), + size: Cell::new(size), + shared: Cell::new(is_shared), + } + }) else { + // No AllocId, or no live allocation at that AllocId. Return an + // error code. (That seems nicer than silently doing something + // non-intuitive.) This means that if an address gets reused by a + // new allocation, we'll use an independent futex queue for this... + // that seems acceptable. + this.set_last_error_and_return(LibcError("ENOENT"), dest)?; + return interp_ok(()); + }; + + if futex.futex.waiters() == 0 { + this.set_last_error_and_return(LibcError("ENOENT"), dest)?; + return interp_ok(()); + // If there are waiters in the queue, they have all used the parameters + // stored in `futex` (we check this in `os_sync_wait_on_address` above). + // Detect mismatches between "our" parameters and the parameters used by + // the waiters and return an error in that case. + } else if futex.size.get() != size || futex.shared.get() != is_shared { + this.set_last_error_and_return(LibcError("EINVAL"), dest)?; + return interp_ok(()); + } + + let futex_ref = futex.futex.clone(); + + // See the Linux futex implementation for why this fence exists. + this.atomic_fence(AtomicFenceOrd::SeqCst)?; + this.futex_wake(&futex_ref, u32::MAX, if all { usize::MAX } else { 1 })?; + this.write_scalar(Scalar::from_i32(0), dest)?; + interp_ok(()) + } + fn os_unfair_lock_lock(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index 2d5d3a6471ab9..aefeee6f7a3a3 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -116,7 +116,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { align, MiriMemoryKind::Mmap.into(), // mmap guarantees new mappings are zero-init. - AllocInit::Zero + AllocInit::Zero, )?; interp_ok(Scalar::from_pointer(ptr, this)) diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs index f94783a390722..21d4f41f48529 100644 --- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs @@ -87,17 +87,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // File related shims "stat" | "stat64" => { let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?; - let result = this.macos_fbsd_solaris_stat(path, buf)?; + let result = this.macos_fbsd_solarish_stat(path, buf)?; this.write_scalar(result, dest)?; } "lstat" | "lstat64" => { let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?; - let result = this.macos_fbsd_solaris_lstat(path, buf)?; + let result = this.macos_fbsd_solarish_lstat(path, buf)?; this.write_scalar(result, dest)?; } "fstat" | "fstat64" => { let [fd, buf] = this.check_shim(abi, Conv::C, link_name, args)?; - let result = this.macos_fbsd_solaris_fstat(fd, buf)?; + let result = this.macos_fbsd_solarish_fstat(fd, buf)?; this.write_scalar(result, dest)?; } "readdir" => { @@ -163,7 +163,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_unsup_format!("pset_info is only supported with list==NULL"); } - let cpus = this.deref_pointer(cpus)?; + let cpus = this.deref_pointer_as(cpus, this.machine.layouts.u32)?; this.write_scalar(Scalar::from_u32(this.machine.num_cpus), &cpus)?; this.write_null(dest)?; } diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 5b0a9398b4b6a..9f1fabfbf6494 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -170,7 +170,7 @@ fn mutex_create<'tcx>( mutex_ptr: &OpTy<'tcx>, kind: MutexKind, ) -> InterpResult<'tcx, PthreadMutex> { - let mutex = ecx.deref_pointer(mutex_ptr)?; + let mutex = ecx.deref_pointer_as(mutex_ptr, ecx.libc_ty_layout("pthread_mutex_t"))?; let id = ecx.machine.sync.mutex_create(); let data = PthreadMutex { mutex_ref: id, kind }; ecx.lazy_sync_init(&mutex, mutex_init_offset(ecx)?, data.clone())?; @@ -186,7 +186,7 @@ fn mutex_get_data<'tcx, 'a>( where 'tcx: 'a, { - let mutex = ecx.deref_pointer(mutex_ptr)?; + let mutex = ecx.deref_pointer_as(mutex_ptr, ecx.libc_ty_layout("pthread_mutex_t"))?; ecx.lazy_sync_get_data( &mutex, mutex_init_offset(ecx)?, @@ -265,7 +265,7 @@ fn rwlock_get_data<'tcx, 'a>( where 'tcx: 'a, { - let rwlock = ecx.deref_pointer(rwlock_ptr)?; + let rwlock = ecx.deref_pointer_as(rwlock_ptr, ecx.libc_ty_layout("pthread_rwlock_t"))?; ecx.lazy_sync_get_data( &rwlock, rwlock_init_offset(ecx)?, @@ -383,7 +383,7 @@ fn cond_create<'tcx>( cond_ptr: &OpTy<'tcx>, clock: ClockId, ) -> InterpResult<'tcx, PthreadCondvar> { - let cond = ecx.deref_pointer(cond_ptr)?; + let cond = ecx.deref_pointer_as(cond_ptr, ecx.libc_ty_layout("pthread_cond_t"))?; let id = ecx.machine.sync.condvar_create(); let data = PthreadCondvar { id, clock }; ecx.lazy_sync_init(&cond, cond_init_offset(ecx)?, data)?; @@ -397,7 +397,7 @@ fn cond_get_data<'tcx, 'a>( where 'tcx: 'a, { - let cond = ecx.deref_pointer(cond_ptr)?; + let cond = ecx.deref_pointer_as(cond_ptr, ecx.libc_ty_layout("pthread_cond_t"))?; ecx.lazy_sync_get_data( &cond, cond_init_offset(ecx)?, @@ -760,7 +760,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let clock_id = condattr_get_clock_id(this, attr_op)?; - this.write_scalar(Scalar::from_i32(clock_id), &this.deref_pointer(clk_id_op)?)?; + this.write_scalar( + Scalar::from_i32(clock_id), + &this.deref_pointer_as(clk_id_op, this.libc_ty_layout("clockid_t"))?, + )?; interp_ok(()) } diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs index 08515b815a900..e183bfdf0e137 100644 --- a/src/tools/miri/src/shims/unix/unnamed_socket.rs +++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs @@ -5,9 +5,7 @@ use std::cell::{Cell, OnceCell, RefCell}; use std::collections::VecDeque; use std::io; -use std::io::{ErrorKind, Read}; - -use rustc_abi::Size; +use std::io::ErrorKind; use crate::concurrency::VClock; use crate::shims::files::{ @@ -92,10 +90,10 @@ impl FileDescription for AnonSocket { _communicate_allowed: bool, ptr: Pointer, len: usize, - dest: &MPlaceTy<'tcx>, ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { - anonsocket_read(self, len, ptr, dest, ecx) + anonsocket_read(self, ptr, len, ecx, finish) } fn write<'tcx>( @@ -103,10 +101,10 @@ impl FileDescription for AnonSocket { _communicate_allowed: bool, ptr: Pointer, len: usize, - dest: &MPlaceTy<'tcx>, ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { - anonsocket_write(self, ptr, len, dest, ecx) + anonsocket_write(self, ptr, len, ecx, finish) } fn as_unix(&self) -> &dyn UnixFileDescription { @@ -119,25 +117,25 @@ fn anonsocket_write<'tcx>( self_ref: FileDescriptionRef, ptr: Pointer, len: usize, - dest: &MPlaceTy<'tcx>, ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { // Always succeed on write size 0. // ("If count is zero and fd refers to a file other than a regular file, the results are not specified.") if len == 0 { - return ecx.return_write_success(0, dest); + return finish.call(ecx, Ok(0)); } // We are writing to our peer's readbuf. let Some(peer_fd) = self_ref.peer_fd().upgrade() else { // If the upgrade from Weak to Rc fails, it indicates that all read ends have been // closed. It is an error to write even if there would be space. - return ecx.set_last_error_and_return(ErrorKind::BrokenPipe, dest); + return finish.call(ecx, Err(ErrorKind::BrokenPipe.into())); }; let Some(writebuf) = &peer_fd.readbuf else { // Writing to the read end of a pipe. - return ecx.set_last_error_and_return(IoError::LibcError("EBADF"), dest); + return finish.call(ecx, Err(IoError::LibcError("EBADF"))); }; // Let's see if we can write. @@ -145,13 +143,12 @@ fn anonsocket_write<'tcx>( if available_space == 0 { if self_ref.is_nonblock { // Non-blocking socketpair with a full buffer. - return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest); + return finish.call(ecx, Err(ErrorKind::WouldBlock.into())); } else { self_ref.blocked_write_tid.borrow_mut().push(ecx.active_thread()); // Blocking socketpair with a full buffer. // Block the current thread; only keep a weak ref for this. let weak_self_ref = FileDescriptionRef::downgrade(&self_ref); - let dest = dest.clone(); ecx.block_thread( BlockReason::UnnamedSocket, None, @@ -160,14 +157,14 @@ fn anonsocket_write<'tcx>( weak_self_ref: WeakFileDescriptionRef, ptr: Pointer, len: usize, - dest: MPlaceTy<'tcx>, + finish: DynMachineCallback<'tcx, Result>, } |this, unblock: UnblockKind| { assert_eq!(unblock, UnblockKind::Ready); // If we got unblocked, then our peer successfully upgraded its weak // ref to us. That means we can also upgrade our weak ref. let self_ref = weak_self_ref.upgrade().unwrap(); - anonsocket_write(self_ref, ptr, len, &dest, this) + anonsocket_write(self_ref, ptr, len, this, finish) } ), ); @@ -180,9 +177,9 @@ fn anonsocket_write<'tcx>( writebuf.clock.join(clock); }); // Do full write / partial write based on the space available. - let actual_write_size = len.min(available_space); - let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?; - writebuf.buf.extend(&bytes[..actual_write_size]); + let write_size = len.min(available_space); + let actual_write_size = ecx.write_to_host(&mut writebuf.buf, write_size, ptr)?.unwrap(); + assert_eq!(actual_write_size, write_size); // Need to stop accessing peer_fd so that it can be notified. drop(writebuf); @@ -197,7 +194,7 @@ fn anonsocket_write<'tcx>( // The kernel does this even if the fd was already readable before, so we follow suit. ecx.check_and_update_readiness(peer_fd)?; - return ecx.return_write_success(actual_write_size, dest); + return finish.call(ecx, Ok(write_size)); } interp_ok(()) } @@ -205,14 +202,14 @@ fn anonsocket_write<'tcx>( /// Read from AnonSocket and return the number of bytes read. fn anonsocket_read<'tcx>( self_ref: FileDescriptionRef, - len: usize, ptr: Pointer, - dest: &MPlaceTy<'tcx>, + len: usize, ecx: &mut MiriInterpCx<'tcx>, + finish: DynMachineCallback<'tcx, Result>, ) -> InterpResult<'tcx> { // Always succeed on read size 0. if len == 0 { - return ecx.return_read_success(ptr, &[], 0, dest); + return finish.call(ecx, Ok(0)); } let Some(readbuf) = &self_ref.readbuf else { @@ -225,43 +222,41 @@ fn anonsocket_read<'tcx>( if self_ref.peer_fd().upgrade().is_none() { // Socketpair with no peer and empty buffer. // 0 bytes successfully read indicates end-of-file. - return ecx.return_read_success(ptr, &[], 0, dest); + return finish.call(ecx, Ok(0)); } else if self_ref.is_nonblock { // Non-blocking socketpair with writer and empty buffer. // https://linux.die.net/man/2/read // EAGAIN or EWOULDBLOCK can be returned for socket, // POSIX.1-2001 allows either error to be returned for this case. // Since there is no ErrorKind for EAGAIN, WouldBlock is used. - return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest); + return finish.call(ecx, Err(ErrorKind::WouldBlock.into())); } else { self_ref.blocked_read_tid.borrow_mut().push(ecx.active_thread()); // Blocking socketpair with writer and empty buffer. // Block the current thread; only keep a weak ref for this. let weak_self_ref = FileDescriptionRef::downgrade(&self_ref); - let dest = dest.clone(); ecx.block_thread( BlockReason::UnnamedSocket, None, callback!( @capture<'tcx> { weak_self_ref: WeakFileDescriptionRef, - len: usize, ptr: Pointer, - dest: MPlaceTy<'tcx>, + len: usize, + finish: DynMachineCallback<'tcx, Result>, } |this, unblock: UnblockKind| { assert_eq!(unblock, UnblockKind::Ready); // If we got unblocked, then our peer successfully upgraded its weak // ref to us. That means we can also upgrade our weak ref. let self_ref = weak_self_ref.upgrade().unwrap(); - anonsocket_read(self_ref, len, ptr, &dest, this) + anonsocket_read(self_ref, ptr, len, this, finish) } ), ); } } else { // There's data to be read! - let mut bytes = vec![0; len]; let mut readbuf = readbuf.borrow_mut(); // Synchronize with all previous writes to this buffer. // FIXME: this over-synchronizes; a more precise approach would be to @@ -270,7 +265,7 @@ fn anonsocket_read<'tcx>( // Do full read / partial read based on the space available. // Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior. - let actual_read_size = readbuf.buf.read(&mut bytes[..]).unwrap(); + let read_size = ecx.read_from_host(&mut readbuf.buf, len, ptr)?.unwrap(); // Need to drop before others can access the readbuf again. drop(readbuf); @@ -293,7 +288,7 @@ fn anonsocket_read<'tcx>( ecx.check_and_update_readiness(peer_fd)?; }; - return ecx.return_read_success(ptr, &bytes, actual_read_size, dest); + return finish.call(ecx, Ok(read_size)); } interp_ok(()) } @@ -362,7 +357,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let domain = this.read_scalar(domain)?.to_i32()?; let mut flags = this.read_scalar(type_)?.to_i32()?; let protocol = this.read_scalar(protocol)?.to_i32()?; - let sv = this.deref_pointer(sv)?; + // This is really a pointer to `[i32; 2]` but we use a ptr-to-first-element representation. + let sv = this.deref_pointer_as(sv, this.machine.layouts.i32)?; let mut is_sock_nonblock = false; diff --git a/src/tools/miri/src/shims/windows/env.rs b/src/tools/miri/src/shims/windows/env.rs index 72c1fb58023a8..1b2ccd99ef9f4 100644 --- a/src/tools/miri/src/shims/windows/env.rs +++ b/src/tools/miri/src/shims/windows/env.rs @@ -218,7 +218,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let token = this.read_target_isize(token)?; let buf = this.read_pointer(buf)?; - let size = this.deref_pointer(size)?; + let size = this.deref_pointer_as(size, this.machine.layouts.u32)?; if token != -4 { throw_unsup_format!( diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 4462d025beada..fae6170a9e72c 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -266,7 +266,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::WinHeap.into(), - init + init, )?; this.write_pointer(ptr, dest)?; } @@ -299,7 +299,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::WinHeap.into(), - AllocInit::Uninit + AllocInit::Uninit, )?; this.write_pointer(new_ptr, dest)?; } @@ -335,7 +335,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Initialize with `0`. this.write_bytes_ptr( system_info.ptr(), - iter::repeat(0u8).take(system_info.layout.size.bytes_usize()), + iter::repeat_n(0u8, system_info.layout.size.bytes_usize()), )?; // Set selected fields. this.write_int_fields_named( @@ -523,7 +523,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [handle, name_ptr] = this.check_shim(abi, sys_conv, link_name, args)?; let handle = this.read_scalar(handle)?; - let name_ptr = this.deref_pointer(name_ptr)?; // the pointer where we should store the ptr to the name + let name_ptr = this.deref_pointer_as(name_ptr, this.machine.layouts.mut_raw_ptr)?; // the pointer where we should store the ptr to the name let thread = match Handle::try_from_scalar(handle, this)? { Ok(Handle::Thread(thread)) => Ok(thread), @@ -725,7 +725,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "GetConsoleMode" if this.frame_in_std() => { let [console, mode] = this.check_shim(abi, sys_conv, link_name, args)?; this.read_target_isize(console)?; - this.deref_pointer(mode)?; + this.deref_pointer_as(mode, this.machine.layouts.u32)?; // Indicate an error. this.write_null(dest)?; } diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index 4001201bf678a..8d5ea7db9e496 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -29,7 +29,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { { let this = self.eval_context_mut(); - let init_once = this.deref_pointer(init_once_ptr)?; + let init_once = + this.deref_pointer_as(init_once_ptr, this.windows_ty_layout("INIT_ONCE"))?; let init_offset = Size::ZERO; this.lazy_sync_get_data( @@ -85,7 +86,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let id = this.init_once_get_data(init_once_op)?.id; let flags = this.read_scalar(flags_op)?.to_u32()?; - let pending_place = this.deref_pointer(pending_op)?; + // PBOOL is int* + let pending_place = this.deref_pointer_as(pending_op, this.machine.layouts.i32)?; let context = this.read_pointer(context_op)?; if flags != 0 { @@ -210,14 +212,27 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .futex .clone(); + let dest = dest.clone(); this.futex_wait( futex_ref, u32::MAX, // bitset timeout, - Scalar::from_i32(1), // retval_succ - Scalar::from_i32(0), // retval_timeout - dest.clone(), - IoError::WindowsError("ERROR_TIMEOUT"), // errno_timeout + callback!( + @capture<'tcx> { + dest: MPlaceTy<'tcx> + } + |this, unblock: UnblockKind| { + match unblock { + UnblockKind::Ready => { + this.write_int(1, &dest) + } + UnblockKind::TimedOut => { + this.set_last_error(IoError::WindowsError("ERROR_TIMEOUT"))?; + this.write_int(0, &dest) + } + } + } + ), ); } @@ -242,7 +257,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let futex_ref = futex_ref.futex.clone(); - this.futex_wake(&futex_ref, u32::MAX)?; + this.futex_wake(&futex_ref, u32::MAX, 1)?; interp_ok(()) } @@ -262,7 +277,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let futex_ref = futex_ref.futex.clone(); - while this.futex_wake(&futex_ref, u32::MAX)? {} + this.futex_wake(&futex_ref, u32::MAX, usize::MAX)?; interp_ok(()) } diff --git a/src/tools/miri/src/shims/windows/thread.rs b/src/tools/miri/src/shims/windows/thread.rs index efc1c2286bcbc..5db554044227c 100644 --- a/src/tools/miri/src/shims/windows/thread.rs +++ b/src/tools/miri/src/shims/windows/thread.rs @@ -29,7 +29,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let thread = if this.ptr_is_null(this.read_pointer(thread_op)?)? { None } else { - let thread_info_place = this.deref_pointer(thread_op)?; + let thread_info_place = this.deref_pointer_as(thread_op, this.machine.layouts.u32)?; Some(thread_info_place) }; diff --git a/src/tools/miri/test_dependencies/Cargo.lock b/src/tools/miri/test_dependencies/Cargo.lock index 0a5e9f62dd9fb..af92f9d0dec49 100644 --- a/src/tools/miri/test_dependencies/Cargo.lock +++ b/src/tools/miri/test_dependencies/Cargo.lock @@ -105,6 +105,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets", +] + [[package]] name = "gimli" version = "0.29.0" @@ -178,6 +190,7 @@ dependencies = [ "cfg-if", "getrandom 0.1.16", "getrandom 0.2.15", + "getrandom 0.3.1", "libc", "num_cpus", "page_size", @@ -359,6 +372,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.92" @@ -507,3 +529,12 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] diff --git a/src/tools/miri/test_dependencies/Cargo.toml b/src/tools/miri/test_dependencies/Cargo.toml index e7eff46afca52..78dddaf11dff5 100644 --- a/src/tools/miri/test_dependencies/Cargo.toml +++ b/src/tools/miri/test_dependencies/Cargo.toml @@ -8,13 +8,14 @@ version = "0.1.0" edition = "2021" [dependencies] -# all dependencies (and their transitive ones) listed here can be used in `tests/`. +# all dependencies (and their transitive ones) listed here can be used in `tests/*-dep`. libc = "0.2" num_cpus = "1.10.1" cfg-if = "1" getrandom_01 = { package = "getrandom", version = "0.1" } getrandom_02 = { package = "getrandom", version = "0.2", features = ["js"] } +getrandom_03 = { package = "getrandom", version = "0.3" } [target.'cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))'.dependencies] tempfile = "3" diff --git a/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs b/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs index 4b6f344a78e28..457f32e55446e 100644 --- a/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs +++ b/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs @@ -8,5 +8,5 @@ fn main() { fn test_file_open_missing_needed_mode() { let name = b"missing_arg.txt\0"; let name_ptr = name.as_ptr().cast::(); - let _fd = unsafe { libc::open(name_ptr, libc::O_CREAT) }; //~ ERROR: Undefined Behavior: incorrect number of arguments for `open(pathname, O_CREAT, ...)`: got 2, expected at least 3 + let _fd = unsafe { libc::open(name_ptr, libc::O_CREAT) }; //~ ERROR: Undefined Behavior: not enough variadic arguments } diff --git a/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.stderr b/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.stderr index ca9e3c6c4be40..f48b75460d4f9 100644 --- a/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.stderr +++ b/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: incorrect number of arguments for `open(pathname, O_CREAT, ...)`: got 2, expected at least 3 +error: Undefined Behavior: not enough variadic arguments for `open(pathname, O_CREAT, ...)`: got 0, expected at least 1 --> tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs:LL:CC | -LL | ... { libc::open(name_ptr, libc::O_CREAT) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of arguments for `open(pathname, O_CREAT, ...)`: got 2, expected at least 3 +LL | let _fd = unsafe { libc::open(name_ptr, libc::O_CREAT) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not enough variadic arguments for `open(pathname, O_CREAT, ...)`: got 0, expected at least 1 | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-decl.rs b/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-decl.rs index a20539ee7c708..c557c35c9dea8 100644 --- a/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-decl.rs +++ b/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-decl.rs @@ -1,10 +1,13 @@ extern "Rust" { - fn miri_get_backtrace(flags: u64) -> Box<[*mut ()]>; + fn miri_backtrace_size(flags: u64) -> usize; + fn miri_get_backtrace(flags: u64, buf: *mut *mut ()); fn miri_resolve_frame(ptr: *mut (), flags: u64); } fn main() { - let frames = unsafe { miri_get_backtrace(0) }; + let size = unsafe { miri_backtrace_size(0) }; + let mut frames = vec![std::ptr::null_mut(); size]; + unsafe { miri_get_backtrace(1, frames.as_mut_ptr()) }; for frame in frames.iter() { unsafe { miri_resolve_frame(*frame, 0); //~ ERROR: Undefined Behavior: bad declaration of miri_resolve_frame - should return a struct with 5 fields diff --git a/src/tools/miri/tests/fail/shims/non_vararg_signature_mismatch.rs b/src/tools/miri/tests/fail/shims/non_vararg_signature_mismatch.rs new file mode 100644 index 0000000000000..b920e6795f90d --- /dev/null +++ b/src/tools/miri/tests/fail/shims/non_vararg_signature_mismatch.rs @@ -0,0 +1,17 @@ +//@ignore-target: windows # File handling is not implemented yet +//@compile-flags: -Zmiri-disable-isolation +use std::ffi::{CString, OsStr, c_char, c_int}; +use std::os::unix::ffi::OsStrExt; + +// Declare a variadic function as non-variadic. +extern "C" { + fn open(path: *const c_char, oflag: c_int) -> c_int; +} + +fn main() { + let c_path = CString::new(OsStr::new("./text").as_bytes()).expect("CString::new failed"); + let _fd = unsafe { + open(c_path.as_ptr(), /* value does not matter */ 0) + //~^ ERROR: calling a variadic function with a non-variadic caller-side signature + }; +} diff --git a/src/tools/miri/tests/fail/shims/non_vararg_signature_mismatch.stderr b/src/tools/miri/tests/fail/shims/non_vararg_signature_mismatch.stderr new file mode 100644 index 0000000000000..43813af9a3c61 --- /dev/null +++ b/src/tools/miri/tests/fail/shims/non_vararg_signature_mismatch.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: calling a variadic function with a non-variadic caller-side signature + --> tests/fail/shims/non_vararg_signature_mismatch.rs:LL:CC + | +LL | open(c_path.as_ptr(), /* value does not matter */ 0) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling a variadic function with a non-variadic caller-side signature + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/fail/shims/non_vararg_signature_mismatch.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/shims/wrong_fixed_arg_count.rs b/src/tools/miri/tests/fail/shims/wrong_fixed_arg_count.rs new file mode 100644 index 0000000000000..e9cb69418d22f --- /dev/null +++ b/src/tools/miri/tests/fail/shims/wrong_fixed_arg_count.rs @@ -0,0 +1,16 @@ +//@ignore-target: windows # File handling is not implemented yet +//@compile-flags: -Zmiri-disable-isolation +use std::ffi::{CString, OsStr, c_char, c_int}; +use std::os::unix::ffi::OsStrExt; + +extern "C" { + fn open(path: *const c_char, ...) -> c_int; +} + +fn main() { + let c_path = CString::new(OsStr::new("./text").as_bytes()).expect("CString::new failed"); + let _fd = unsafe { + open(c_path.as_ptr(), /* value does not matter */ 0) + //~^ ERROR: incorrect number of fixed arguments for variadic function + }; +} diff --git a/src/tools/miri/tests/fail/shims/wrong_fixed_arg_count.stderr b/src/tools/miri/tests/fail/shims/wrong_fixed_arg_count.stderr new file mode 100644 index 0000000000000..12e464813cfff --- /dev/null +++ b/src/tools/miri/tests/fail/shims/wrong_fixed_arg_count.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: incorrect number of fixed arguments for variadic function `open`: got 1, expected 2 + --> tests/fail/shims/wrong_fixed_arg_count.rs:LL:CC + | +LL | open(c_path.as_ptr(), /* value does not matter */ 0) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of fixed arguments for variadic function `open`: got 1, expected 2 + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/fail/shims/wrong_fixed_arg_count.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/pass-dep/concurrency/apple-futex.rs b/src/tools/miri/tests/pass-dep/concurrency/apple-futex.rs new file mode 100644 index 0000000000000..becb90eb92307 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/concurrency/apple-futex.rs @@ -0,0 +1,276 @@ +//@only-target: darwin +//@compile-flags: -Zmiri-preemption-rate=0 + +use std::time::{Duration, Instant}; +use std::{io, ptr, thread}; + +fn wake_nobody() { + let futex = 0; + + // Wake 1 waiter. Expect ENOENT as nobody is waiting. + unsafe { + assert_eq!( + libc::os_sync_wake_by_address_any( + ptr::from_ref(&futex).cast_mut().cast(), + size_of::(), + libc::OS_SYNC_WAKE_BY_ADDRESS_NONE + ), + -1 + ); + assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::ENOENT); + } +} + +fn wake_dangling() { + let futex = Box::new(0); + let ptr = ptr::from_ref(&futex).cast_mut().cast(); + drop(futex); + + // Expect error since this is now "unmapped" memory. + unsafe { + assert_eq!( + libc::os_sync_wake_by_address_any( + ptr, + size_of::(), + libc::OS_SYNC_WAKE_BY_ADDRESS_NONE + ), + -1 + ); + assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::ENOENT); + } +} + +fn wait_wrong_val() { + let futex: i32 = 123; + + // Only wait if the futex value is 456. + unsafe { + assert_eq!( + libc::os_sync_wait_on_address( + ptr::from_ref(&futex).cast_mut().cast(), + 456, + size_of::(), + libc::OS_SYNC_WAIT_ON_ADDRESS_NONE + ), + 0, + ); + } +} + +fn wait_timeout() { + let start = Instant::now(); + + let futex: i32 = 123; + + // Wait for 200ms, with nobody waking us up early. + unsafe { + assert_eq!( + libc::os_sync_wait_on_address_with_timeout( + ptr::from_ref(&futex).cast_mut().cast(), + 123, + size_of::(), + libc::OS_SYNC_WAIT_ON_ADDRESS_NONE, + libc::OS_CLOCK_MACH_ABSOLUTE_TIME, + 200_000_000, + ), + -1, + ); + assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::ETIMEDOUT); + } + + assert!((200..1000).contains(&start.elapsed().as_millis())); +} + +fn wait_absolute_timeout() { + let start = Instant::now(); + + // Get the current monotonic timestamp. + #[allow(deprecated)] + let mut deadline = unsafe { libc::mach_absolute_time() }; + + // Add 200ms. + // What we should be doing here is call `mach_timebase_info` to determine the + // unit used for `deadline`, but we know what Miri returns for that function: + // the unit is nanoseconds. + deadline += 200_000_000; + + let futex: i32 = 123; + + // Wait for 200ms from now, with nobody waking us up early. + unsafe { + assert_eq!( + libc::os_sync_wait_on_address_with_deadline( + ptr::from_ref(&futex).cast_mut().cast(), + 123, + size_of::(), + libc::OS_SYNC_WAIT_ON_ADDRESS_NONE, + libc::OS_CLOCK_MACH_ABSOLUTE_TIME, + deadline, + ), + -1, + ); + assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::ETIMEDOUT); + } + + assert!((200..1000).contains(&start.elapsed().as_millis())); +} + +fn wait_wake() { + let start = Instant::now(); + + static mut FUTEX: i32 = 0; + + let t = thread::spawn(move || { + thread::sleep(Duration::from_millis(200)); + unsafe { + assert_eq!( + libc::os_sync_wake_by_address_any( + (&raw const FUTEX).cast_mut().cast(), + size_of::(), + libc::OS_SYNC_WAKE_BY_ADDRESS_NONE, + ), + 0, + ); + } + }); + + unsafe { + assert_eq!( + libc::os_sync_wait_on_address( + (&raw const FUTEX).cast_mut().cast(), + 0, + size_of::(), + libc::OS_SYNC_WAIT_ON_ADDRESS_NONE, + ), + 0, + ); + } + + // When running this in stress-gc mode, things can take quite long. + // So the timeout is 3000 ms. + assert!((200..3000).contains(&start.elapsed().as_millis())); + t.join().unwrap(); +} + +fn wait_wake_multiple() { + let val = 0i32; + let futex = &val; + + thread::scope(|s| { + // Spawn some threads and make them wait on the futex. + for i in 0..4 { + s.spawn(move || unsafe { + assert_eq!( + libc::os_sync_wait_on_address( + ptr::from_ref(futex).cast_mut().cast(), + 0, + size_of::(), + libc::OS_SYNC_WAIT_ON_ADDRESS_NONE, + ), + // The last two threads will be woken at the same time, + // but for the first two threads the remaining number + // of waiters should be strictly decreasing. + if i < 2 { 3 - i } else { 0 }, + ); + }); + + thread::yield_now(); + } + + // Wake the threads up again. + unsafe { + assert_eq!( + libc::os_sync_wake_by_address_any( + ptr::from_ref(futex).cast_mut().cast(), + size_of::(), + libc::OS_SYNC_WAKE_BY_ADDRESS_NONE, + ), + 0 + ); + + assert_eq!( + libc::os_sync_wake_by_address_any( + ptr::from_ref(futex).cast_mut().cast(), + size_of::(), + libc::OS_SYNC_WAKE_BY_ADDRESS_NONE, + ), + 0 + ); + + // Wake both remaining threads at the same time. + assert_eq!( + libc::os_sync_wake_by_address_all( + ptr::from_ref(futex).cast_mut().cast(), + size_of::(), + libc::OS_SYNC_WAKE_BY_ADDRESS_NONE, + ), + 0 + ); + } + }) +} + +fn param_mismatch() { + let futex = 0; + thread::scope(|s| { + s.spawn(|| { + unsafe { + assert_eq!( + libc::os_sync_wait_on_address_with_timeout( + ptr::from_ref(&futex).cast_mut().cast(), + 0, + size_of::(), + libc::OS_SYNC_WAIT_ON_ADDRESS_NONE, + libc::OS_CLOCK_MACH_ABSOLUTE_TIME, + 400_000_000, + ), + -1, + ); + assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::ETIMEDOUT); + } + }); + + s.spawn(|| { + thread::yield_now(); + unsafe { + assert_eq!( + libc::os_sync_wait_on_address( + ptr::from_ref(&futex).cast_mut().cast(), + 0, + size_of::(), + libc::OS_SYNC_WAIT_ON_ADDRESS_SHARED, + ), + -1, + ); + // This call fails because it uses the shared flag whereas the first waiter didn't. + assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); + } + }); + + thread::yield_now(); + + unsafe { + assert_eq!( + libc::os_sync_wake_by_address_any( + ptr::from_ref(&futex).cast_mut().cast(), + size_of::(), + libc::OS_SYNC_WAIT_ON_ADDRESS_SHARED, + ), + -1, + ); + // This call fails because it uses the shared flag whereas the waiter didn't. + assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); + } + }); +} + +fn main() { + wake_nobody(); + wake_dangling(); + wait_wrong_val(); + wait_timeout(); + wait_absolute_timeout(); + wait_wake(); + wait_wake_multiple(); + param_mismatch(); +} diff --git a/src/tools/miri/tests/pass-dep/getrandom.rs b/src/tools/miri/tests/pass-dep/getrandom.rs index a5bc5ec7079be..d359730e7f971 100644 --- a/src/tools/miri/tests/pass-dep/getrandom.rs +++ b/src/tools/miri/tests/pass-dep/getrandom.rs @@ -3,7 +3,7 @@ //@revisions: isolation no_isolation //@[no_isolation]compile-flags: -Zmiri-disable-isolation -/// Test direct calls of getrandom 0.1 and 0.2. +/// Test direct calls of getrandom 0.1, 0.2 and 0.3. fn main() { let mut data = vec![0; 16]; @@ -13,4 +13,6 @@ fn main() { getrandom_01::getrandom(&mut data).unwrap(); getrandom_02::getrandom(&mut data).unwrap(); + + getrandom_03::fill(&mut data).unwrap(); } diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs index 23e2122ee50fa..dc3ab2828faa2 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs @@ -230,10 +230,10 @@ fn test_two_same_fd_in_same_epoll_instance() { //Two notification should be received. let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap(); let expected_value = 5 as u64; - check_epoll_wait::<8>(epfd, &[ - (expected_event, expected_value), - (expected_event, expected_value), - ]); + check_epoll_wait::<8>( + epfd, + &[(expected_event, expected_value), (expected_event, expected_value)], + ); } fn test_epoll_eventfd() { @@ -290,10 +290,10 @@ fn test_epoll_socketpair_both_sides() { let expected_value0 = fds[0] as u64; let expected_event1 = u32::try_from(libc::EPOLLOUT).unwrap(); let expected_value1 = fds[1] as u64; - check_epoll_wait::<8>(epfd, &[ - (expected_event0, expected_value0), - (expected_event1, expected_value1), - ]); + check_epoll_wait::<8>( + epfd, + &[(expected_event0, expected_value0), (expected_event1, expected_value1)], + ); // Read from fds[0]. let mut buf: [u8; 5] = [0; 5]; @@ -453,10 +453,10 @@ fn test_socketpair_read() { let expected_value0 = fds[0] as u64; let expected_event1 = u32::try_from(libc::EPOLLOUT).unwrap(); let expected_value1 = fds[1] as u64; - check_epoll_wait::<8>(epfd, &[ - (expected_event0, expected_value0), - (expected_event1, expected_value1), - ]); + check_epoll_wait::<8>( + epfd, + &[(expected_event0, expected_value0), (expected_event1, expected_value1)], + ); // Read 3 bytes from fds[0]. let mut buf: [u8; 3] = [0; 3]; diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index 9b9542b88a962..3bc953c3a5fbf 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -13,7 +13,7 @@ use ui_test::custom_flags::edition::Edition; use ui_test::dependencies::DependencyBuilder; use ui_test::per_test_config::TestConfig; use ui_test::spanned::Spanned; -use ui_test::{CommandBuilder, Config, Format, Match, OutputConflictHandling, status_emitter}; +use ui_test::{CommandBuilder, Config, Format, Match, ignore_output_conflict, status_emitter}; #[derive(Copy, Clone, Debug)] enum Mode { @@ -82,9 +82,18 @@ fn build_native_lib() -> PathBuf { native_lib_path } +struct WithDependencies { + bless: bool, +} + /// Does *not* set any args or env vars, since it is shared between the test runner and /// run_dep_mode. -fn miri_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> Config { +fn miri_config( + target: &str, + path: &str, + mode: Mode, + with_dependencies: Option, +) -> Config { // Miri is rustc-like, so we create a default builder for rustc and modify it let mut program = CommandBuilder::rustc(); program.program = miri_path(); @@ -119,22 +128,26 @@ fn miri_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> // keep in sync with `./miri run` config.comment_defaults.base().add_custom("edition", Edition("2021".into())); - if with_dependencies { - config.comment_defaults.base().set_custom("dependencies", DependencyBuilder { - program: CommandBuilder { - // Set the `cargo-miri` binary, which we expect to be in the same folder as the `miri` binary. - // (It's a separate crate, so we don't get an env var from cargo.) - program: miri_path() - .with_file_name(format!("cargo-miri{}", env::consts::EXE_SUFFIX)), - // There is no `cargo miri build` so we just use `cargo miri run`. - args: ["miri", "run"].into_iter().map(Into::into).collect(), - // Reset `RUSTFLAGS` to work around . - envs: vec![("RUSTFLAGS".into(), None)], - ..CommandBuilder::cargo() + if let Some(WithDependencies { bless }) = with_dependencies { + config.comment_defaults.base().set_custom( + "dependencies", + DependencyBuilder { + program: CommandBuilder { + // Set the `cargo-miri` binary, which we expect to be in the same folder as the `miri` binary. + // (It's a separate crate, so we don't get an env var from cargo.) + program: miri_path() + .with_file_name(format!("cargo-miri{}", env::consts::EXE_SUFFIX)), + // There is no `cargo miri build` so we just use `cargo miri run`. + args: ["miri", "run"].into_iter().map(Into::into).collect(), + // Reset `RUSTFLAGS` to work around . + envs: vec![("RUSTFLAGS".into(), None)], + ..CommandBuilder::cargo() + }, + crate_manifest_path: Path::new("test_dependencies").join("Cargo.toml"), + build_std: None, + bless_lockfile: bless, }, - crate_manifest_path: Path::new("test_dependencies").join("Cargo.toml"), - build_std: None, - }); + ); } config } @@ -146,7 +159,20 @@ fn run_tests( with_dependencies: bool, tmpdir: &Path, ) -> Result<()> { + // Handle command-line arguments. + let mut args = ui_test::Args::test()?; + args.bless |= env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0"); + + let with_dependencies = with_dependencies.then_some(WithDependencies { bless: args.bless }); + let mut config = miri_config(target, path, mode, with_dependencies); + config.with_args(&args); + config.bless_command = Some("./miri test --bless".into()); + + if env::var_os("MIRI_SKIP_UI_CHECKS").is_some() { + assert!(!args.bless, "cannot use RUSTC_BLESS and MIRI_SKIP_UI_CHECKS at the same time"); + config.output_conflict_handling = ignore_output_conflict; + } // Add a test env var to do environment communication tests. config.program.envs.push(("MIRI_ENV_VAR_TEST".into(), Some("0".into()))); @@ -182,16 +208,6 @@ fn run_tests( config.program.args.push(flag); } - // Handle command-line arguments. - let mut args = ui_test::Args::test()?; - args.bless |= env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0"); - config.with_args(&args); - config.bless_command = Some("./miri test --bless".into()); - - if env::var_os("MIRI_SKIP_UI_CHECKS").is_some() { - assert!(!args.bless, "cannot use RUSTC_BLESS and MIRI_SKIP_UI_CHECKS at the same time"); - config.output_conflict_handling = OutputConflictHandling::Ignore; - } eprintln!(" Compiler: {}", config.program.display()); ui_test::run_tests_generic( // Only run one test suite. In the future we can add all test suites to one `Vec` and run @@ -327,7 +343,8 @@ fn main() -> Result<()> { } fn run_dep_mode(target: String, args: impl Iterator) -> Result<()> { - let mut config = miri_config(&target, "", Mode::RunDep, /* with dependencies */ true); + let mut config = + miri_config(&target, "", Mode::RunDep, Some(WithDependencies { bless: false })); config.comment_defaults.base().custom.remove("edition"); // `./miri` adds an `--edition` in `args`, so don't set it twice config.fill_host_and_target()?; config.program.args = args.collect(); diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index 63cc8794ceaec..34077c5f86658 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -114,14 +114,13 @@ fn default_dcx( false, ); let emitter = Box::new( - HumanEmitter::new(stderr_destination(emit_color), fallback_bundle.clone()) + HumanEmitter::new(stderr_destination(emit_color), fallback_bundle) .sm(Some(source_map.clone())), ); let emitter: Box = if !show_parse_errors { Box::new(SilentEmitter { - fallback_bundle, - fatal_dcx: DiagCtxt::new(emitter), + fatal_emitter: emitter, fatal_note: None, emit_fatal_diagnostic: false, }) @@ -205,16 +204,7 @@ impl ParseSess { } pub(crate) fn set_silent_emitter(&mut self) { - // Ideally this invocation wouldn't be necessary and the fallback bundle in - // `self.parse_sess.dcx` could be used, but the lock in `DiagCtxt` prevents this. - // See `::fallback_fluent_bundle`. - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), - false, - ); - self.raw_psess - .dcx() - .make_silent(fallback_bundle, None, false); + self.raw_psess.dcx().make_silent(None, false); } pub(crate) fn span_to_filename(&self, span: Span) -> FileName { diff --git a/tests/ui-fulldeps/obtain-borrowck.rs b/tests/ui-fulldeps/obtain-borrowck.rs index f8064c245a872..c6bec4f77a062 100644 --- a/tests/ui-fulldeps/obtain-borrowck.rs +++ b/tests/ui-fulldeps/obtain-borrowck.rs @@ -48,7 +48,6 @@ fn main() { let mut callbacks = CompilerCalls::default(); // Call the Rust compiler with our callbacks. rustc_driver::run_compiler(&rustc_args, &mut callbacks); - Ok(()) }); std::process::exit(exit_code); } diff --git a/tests/ui/async-await/async-closures/is-not-fn.current.stderr b/tests/ui/async-await/async-closures/is-not-fn.current.stderr index e7be1d5b10efe..0fab1c15f27df 100644 --- a/tests/ui/async-await/async-closures/is-not-fn.current.stderr +++ b/tests/ui/async-await/async-closures/is-not-fn.current.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `{async closure@is-not-fn.rs:8:14}` to be a closure that returns `()`, but it returns `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25}` +error[E0271]: expected `{async closure@is-not-fn.rs:8:14}` to return `()`, but it returns `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25}` --> $DIR/is-not-fn.rs:8:14 | LL | needs_fn(async || {}); diff --git a/tests/ui/async-await/async-closures/is-not-fn.next.stderr b/tests/ui/async-await/async-closures/is-not-fn.next.stderr index e7be1d5b10efe..0fab1c15f27df 100644 --- a/tests/ui/async-await/async-closures/is-not-fn.next.stderr +++ b/tests/ui/async-await/async-closures/is-not-fn.next.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `{async closure@is-not-fn.rs:8:14}` to be a closure that returns `()`, but it returns `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25}` +error[E0271]: expected `{async closure@is-not-fn.rs:8:14}` to return `()`, but it returns `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25}` --> $DIR/is-not-fn.rs:8:14 | LL | needs_fn(async || {}); diff --git a/tests/ui/async-await/async-closures/is-not-fn.rs b/tests/ui/async-await/async-closures/is-not-fn.rs index eacd07b7cdd86..e5ab4742daba1 100644 --- a/tests/ui/async-await/async-closures/is-not-fn.rs +++ b/tests/ui/async-await/async-closures/is-not-fn.rs @@ -6,5 +6,5 @@ fn main() { fn needs_fn(x: impl FnOnce()) {} needs_fn(async || {}); - //~^ ERROR expected `{async closure@is-not-fn.rs:8:14}` to be a closure that returns `()` + //~^ ERROR expected `{async closure@is-not-fn.rs:8:14}` to return `()` } diff --git a/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr b/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr index f478d55d10e35..ce023397db90a 100644 --- a/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr +++ b/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr @@ -20,7 +20,7 @@ LL | true = note: expected enum `Option<()>` found type `bool` -error[E0271]: expected `{closure@dont-ice-for-type-mismatch-in-closure-in-async.rs:6:10}` to be a closure that returns `bool`, but it returns `Option<()>` +error[E0271]: expected `{closure@dont-ice-for-type-mismatch-in-closure-in-async.rs:6:10}` to return `bool`, but it returns `Option<()>` --> $DIR/dont-ice-for-type-mismatch-in-closure-in-async.rs:6:16 | LL | call(|| -> Option<()> { diff --git a/tests/ui/async-await/issue-98634.rs b/tests/ui/async-await/issue-98634.rs index 02e869f4325f3..3bfc2bf8a7ca7 100644 --- a/tests/ui/async-await/issue-98634.rs +++ b/tests/ui/async-await/issue-98634.rs @@ -43,8 +43,8 @@ impl Runtime { fn main() { Runtime.block_on(async { StructAsync { callback }.await; - //~^ ERROR expected `callback` to be a fn item that returns `Pin>>`, but it returns `impl Future` - //~| ERROR expected `callback` to be a fn item that returns `Pin>>`, but it returns `impl Future` - //~| ERROR expected `callback` to be a fn item that returns `Pin>>`, but it returns `impl Future` + //~^ ERROR expected `callback` to return `Pin>>`, but it returns `impl Future` + //~| ERROR expected `callback` to return `Pin>>`, but it returns `impl Future` + //~| ERROR expected `callback` to return `Pin>>`, but it returns `impl Future` }); } diff --git a/tests/ui/async-await/issue-98634.stderr b/tests/ui/async-await/issue-98634.stderr index 574904ceafada..e0739ae3a9b87 100644 --- a/tests/ui/async-await/issue-98634.stderr +++ b/tests/ui/async-await/issue-98634.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `callback` to be a fn item that returns `Pin>>`, but it returns `impl Future` +error[E0271]: expected `callback` to return `Pin>>`, but it returns `impl Future` --> $DIR/issue-98634.rs:45:23 | LL | StructAsync { callback }.await; @@ -10,7 +10,7 @@ note: required by a bound in `StructAsync` LL | pub struct StructAsync Pin>>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `StructAsync` -error[E0271]: expected `callback` to be a fn item that returns `Pin>>`, but it returns `impl Future` +error[E0271]: expected `callback` to return `Pin>>`, but it returns `impl Future` --> $DIR/issue-98634.rs:45:9 | LL | StructAsync { callback }.await; @@ -22,7 +22,7 @@ note: required by a bound in `StructAsync` LL | pub struct StructAsync Pin>>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `StructAsync` -error[E0271]: expected `callback` to be a fn item that returns `Pin>>`, but it returns `impl Future` +error[E0271]: expected `callback` to return `Pin>>`, but it returns `impl Future` --> $DIR/issue-98634.rs:45:34 | LL | StructAsync { callback }.await; diff --git a/tests/ui/closures/return-type-doesnt-match-bound.rs b/tests/ui/closures/return-type-doesnt-match-bound.rs index f9098d0cb5cca..af15385d4b07f 100644 --- a/tests/ui/closures/return-type-doesnt-match-bound.rs +++ b/tests/ui/closures/return-type-doesnt-match-bound.rs @@ -5,7 +5,7 @@ fn foo(f: F) -> () where F: FnOnce() -> Result<(), Box>, { - f().or_else(|e| -> ! { //~ ERROR to be a closure that returns + f().or_else(|e| -> ! { //~ ERROR to return eprintln!("{:?}", e); exit(1) }); @@ -15,7 +15,7 @@ fn bar(f: F) -> () where F: FnOnce() -> Result<(), Box>, { - let c = |e| -> ! { //~ ERROR to be a closure that returns + let c = |e| -> ! { //~ ERROR to return eprintln!("{:?}", e); exit(1) }; diff --git a/tests/ui/closures/return-type-doesnt-match-bound.stderr b/tests/ui/closures/return-type-doesnt-match-bound.stderr index f796a5552ac0c..c0bc2f6084c9c 100644 --- a/tests/ui/closures/return-type-doesnt-match-bound.stderr +++ b/tests/ui/closures/return-type-doesnt-match-bound.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `{closure@return-type-doesnt-match-bound.rs:8:17}` to be a closure that returns `Result<(), _>`, but it returns `!` +error[E0271]: expected `{closure@return-type-doesnt-match-bound.rs:8:17}` to return `Result<(), _>`, but it returns `!` --> $DIR/return-type-doesnt-match-bound.rs:8:24 | LL | f().or_else(|e| -> ! { @@ -13,7 +13,7 @@ LL | f().or_else(|e| -> ! { note: required by a bound in `Result::::or_else` --> $SRC_DIR/core/src/result.rs:LL:COL -error[E0271]: expected `{closure@return-type-doesnt-match-bound.rs:18:13}` to be a closure that returns `Result<(), _>`, but it returns `!` +error[E0271]: expected `{closure@return-type-doesnt-match-bound.rs:18:13}` to return `Result<(), _>`, but it returns `!` --> $DIR/return-type-doesnt-match-bound.rs:18:20 | LL | let c = |e| -> ! { diff --git a/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.rs b/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.rs index f2b73d6926189..c8963e078f08a 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.rs +++ b/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.rs @@ -39,7 +39,7 @@ fn main() { L { //~ ERROR type mismatch f: |x| { drop(x); - Unit4 //~ ERROR to be a closure that returns `Unit3`, but it returns `Unit4` + Unit4 //~ ERROR to return `Unit3`, but it returns `Unit4` }, }, ); diff --git a/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.stderr b/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.stderr index d0675d5020c80..4302570cdbdef 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.stderr +++ b/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.stderr @@ -29,7 +29,7 @@ LL | where LL | F: for<'r> T0<'r, (>::V,), O = >::V>, | ^^^^^^^^^^^^^^^^^^^^ required by this bound in `T1::m` -error[E0271]: expected `{closure@issue-62203-hrtb-ice.rs:40:16}` to be a closure that returns `Unit3`, but it returns `Unit4` +error[E0271]: expected `{closure@issue-62203-hrtb-ice.rs:40:16}` to return `Unit3`, but it returns `Unit4` --> $DIR/issue-62203-hrtb-ice.rs:42:17 | LL | let v = Unit2.m( diff --git a/tests/ui/intrinsics/const-eval-select-bad.rs b/tests/ui/intrinsics/const-eval-select-bad.rs index 7e75de88d2926..3365d57af7ce4 100644 --- a/tests/ui/intrinsics/const-eval-select-bad.rs +++ b/tests/ui/intrinsics/const-eval-select-bad.rs @@ -30,7 +30,7 @@ fn baz(n: bool) -> i32 { const fn return_ty_mismatch() { const_eval_select((1,), foo, bar); - //~^ ERROR expected `bar` to be a fn item that returns `i32`, but it returns `bool` + //~^ ERROR expected `bar` to return `i32`, but it returns `bool` } const fn args_ty_mismatch() { diff --git a/tests/ui/intrinsics/const-eval-select-bad.stderr b/tests/ui/intrinsics/const-eval-select-bad.stderr index e317ed23ab103..bb159bed28224 100644 --- a/tests/ui/intrinsics/const-eval-select-bad.stderr +++ b/tests/ui/intrinsics/const-eval-select-bad.stderr @@ -60,7 +60,7 @@ LL | const_eval_select((), 42, 0xDEADBEEF); = note: expected a function item, found {integer} = help: consult the documentation on `const_eval_select` for more information -error[E0271]: expected `bar` to be a fn item that returns `i32`, but it returns `bool` +error[E0271]: expected `bar` to return `i32`, but it returns `bool` --> $DIR/const-eval-select-bad.rs:32:34 | LL | const_eval_select((1,), foo, bar); diff --git a/tests/ui/lint/issue-106991.rs b/tests/ui/lint/issue-106991.rs index e4d7f765b4a8a..b70f751195a5f 100644 --- a/tests/ui/lint/issue-106991.rs +++ b/tests/ui/lint/issue-106991.rs @@ -3,7 +3,7 @@ fn foo(items: &mut Vec) { } fn bar() -> impl Iterator { - //~^ ERROR expected `foo` to be a fn item that returns `i32`, but it returns `()` [E0271] + //~^ ERROR expected `foo` to return `i32`, but it returns `()` [E0271] let mut x: Vec> = vec![vec![0, 2, 1], vec![5, 4, 3]]; x.iter_mut().map(foo) } diff --git a/tests/ui/lint/issue-106991.stderr b/tests/ui/lint/issue-106991.stderr index 0441a6377d088..15e0ba5337f14 100644 --- a/tests/ui/lint/issue-106991.stderr +++ b/tests/ui/lint/issue-106991.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `foo` to be a fn item that returns `i32`, but it returns `()` +error[E0271]: expected `foo` to return `i32`, but it returns `()` --> $DIR/issue-106991.rs:5:13 | LL | fn bar() -> impl Iterator { diff --git a/tests/ui/never_type/fallback-closure-wrap.fallback.stderr b/tests/ui/never_type/fallback-closure-wrap.fallback.stderr index a559a95a334ad..ac99a1fc24cf0 100644 --- a/tests/ui/never_type/fallback-closure-wrap.fallback.stderr +++ b/tests/ui/never_type/fallback-closure-wrap.fallback.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `{closure@fallback-closure-wrap.rs:18:40}` to be a closure that returns `()`, but it returns `!` +error[E0271]: expected `{closure@fallback-closure-wrap.rs:18:40}` to return `()`, but it returns `!` --> $DIR/fallback-closure-wrap.rs:19:9 | LL | let error = Closure::wrap(Box::new(move || { diff --git a/tests/ui/never_type/fallback-closure-wrap.rs b/tests/ui/never_type/fallback-closure-wrap.rs index e97505b6bc0c3..e7f7d5aae3f8b 100644 --- a/tests/ui/never_type/fallback-closure-wrap.rs +++ b/tests/ui/never_type/fallback-closure-wrap.rs @@ -17,7 +17,7 @@ use std::marker::PhantomData; fn main() { let error = Closure::wrap(Box::new(move || { panic!("Can't connect to server."); - //[fallback]~^ to be a closure that returns `()`, but it returns `!` + //[fallback]~^ to return `()`, but it returns `!` }) as Box); } diff --git a/tests/ui/suggestions/dont-suggest-boxing-async-closure-body.rs b/tests/ui/suggestions/dont-suggest-boxing-async-closure-body.rs index 47a590668dde2..e03c43d15ee0a 100644 --- a/tests/ui/suggestions/dont-suggest-boxing-async-closure-body.rs +++ b/tests/ui/suggestions/dont-suggest-boxing-async-closure-body.rs @@ -7,6 +7,6 @@ fn bar(x: Box X>) {} fn main() { foo(async move || {}); - //~^ ERROR expected `{async closure@dont-suggest-boxing-async-closure-body.rs:9:9}` to be a closure that returns `Box<_>` + //~^ ERROR expected `{async closure@dont-suggest-boxing-async-closure-body.rs:9:9}` to return `Box<_>` bar(async move || {}); //~ ERROR mismatched types } diff --git a/tests/ui/suggestions/dont-suggest-boxing-async-closure-body.stderr b/tests/ui/suggestions/dont-suggest-boxing-async-closure-body.stderr index db2a3b9a9c15f..abf8e2d824b5d 100644 --- a/tests/ui/suggestions/dont-suggest-boxing-async-closure-body.stderr +++ b/tests/ui/suggestions/dont-suggest-boxing-async-closure-body.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `{async closure@dont-suggest-boxing-async-closure-body.rs:9:9}` to be a closure that returns `Box<_>`, but it returns `{async closure body@$DIR/dont-suggest-boxing-async-closure-body.rs:9:23: 9:25}` +error[E0271]: expected `{async closure@dont-suggest-boxing-async-closure-body.rs:9:9}` to return `Box<_>`, but it returns `{async closure body@$DIR/dont-suggest-boxing-async-closure-body.rs:9:23: 9:25}` --> $DIR/dont-suggest-boxing-async-closure-body.rs:9:9 | LL | foo(async move || {}); diff --git a/tests/ui/type-alias-impl-trait/issue-98604.rs b/tests/ui/type-alias-impl-trait/issue-98604.rs index 9231e82d9f43b..35c25c7f7267c 100644 --- a/tests/ui/type-alias-impl-trait/issue-98604.rs +++ b/tests/ui/type-alias-impl-trait/issue-98604.rs @@ -7,5 +7,5 @@ async fn test() {} #[allow(unused_must_use)] fn main() { Box::new(test) as AsyncFnPtr; - //~^ ERROR expected `test` to be a fn item that returns `Pin>>`, but it returns `impl Future + //~^ ERROR expected `test` to return `Pin>>`, but it returns `impl Future } diff --git a/tests/ui/type-alias-impl-trait/issue-98604.stderr b/tests/ui/type-alias-impl-trait/issue-98604.stderr index 2390b72535625..77c6ba3003f0d 100644 --- a/tests/ui/type-alias-impl-trait/issue-98604.stderr +++ b/tests/ui/type-alias-impl-trait/issue-98604.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `test` to be a fn item that returns `Pin>>`, but it returns `impl Future` +error[E0271]: expected `test` to return `Pin>>`, but it returns `impl Future` --> $DIR/issue-98604.rs:9:5 | LL | Box::new(test) as AsyncFnPtr; diff --git a/tests/ui/type-alias-impl-trait/issue-98608.rs b/tests/ui/type-alias-impl-trait/issue-98608.rs index 5e026ea4096c3..5612ccd6caed2 100644 --- a/tests/ui/type-alias-impl-trait/issue-98608.rs +++ b/tests/ui/type-alias-impl-trait/issue-98608.rs @@ -4,7 +4,7 @@ fn hi() -> impl Sized { fn main() { let b: Box Box> = Box::new(hi); - //~^ ERROR expected `hi` to be a fn item that returns `Box`, but it returns `impl Sized` + //~^ ERROR expected `hi` to return `Box`, but it returns `impl Sized` let boxed = b(); let null = *boxed; println!("{null:?}"); diff --git a/tests/ui/type-alias-impl-trait/issue-98608.stderr b/tests/ui/type-alias-impl-trait/issue-98608.stderr index d5c56636f66e4..872a6ed435001 100644 --- a/tests/ui/type-alias-impl-trait/issue-98608.stderr +++ b/tests/ui/type-alias-impl-trait/issue-98608.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `hi` to be a fn item that returns `Box`, but it returns `impl Sized` +error[E0271]: expected `hi` to return `Box`, but it returns `impl Sized` --> $DIR/issue-98608.rs:6:39 | LL | fn hi() -> impl Sized { diff --git a/tests/ui/type/pattern_types/chars.rs b/tests/ui/type/pattern_types/chars.rs new file mode 100644 index 0000000000000..9073998b387b7 --- /dev/null +++ b/tests/ui/type/pattern_types/chars.rs @@ -0,0 +1,12 @@ +//! Check that chars can be used in ranges + +//@ check-pass + +#![feature(pattern_types)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +const LOWERCASE: pattern_type!(char is 'a'..='z') = unsafe { std::mem::transmute('b') }; + +fn main() {} diff --git a/tests/ui/type/pattern_types/nested.rs b/tests/ui/type/pattern_types/nested.rs new file mode 100644 index 0000000000000..519fb3f05b483 --- /dev/null +++ b/tests/ui/type/pattern_types/nested.rs @@ -0,0 +1,26 @@ +//! Check that pattern types can only have specific base types + +#![feature(pattern_types)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +// Undoing an inner pattern type's restrictions should either be forbidden, +// or still validate correctly. +const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); +//~^ ERROR: not a valid base type for range patterns + +// We want to get the most narrowest version that a pattern could be +const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); +//~^ ERROR: not a valid base type for range patterns + +const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); +//~^ ERROR: not a valid base type for range patterns + +const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); +//~^ ERROR: not a valid base type for range patterns + +const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!(); +//~^ ERROR: not a valid base type for range patterns + +fn main() {} diff --git a/tests/ui/type/pattern_types/nested.stderr b/tests/ui/type/pattern_types/nested.stderr new file mode 100644 index 0000000000000..99d3979e98c49 --- /dev/null +++ b/tests/ui/type/pattern_types/nested.stderr @@ -0,0 +1,62 @@ +error: `(u32) is 1..=` is not a valid base type for range patterns + --> $DIR/nested.rs:10:34 + | +LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: range patterns only support integers + --> $DIR/nested.rs:10:63 + | +LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); + | ^^^ + +error: `(i32) is 1..=` is not a valid base type for range patterns + --> $DIR/nested.rs:14:35 + | +LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: range patterns only support integers + --> $DIR/nested.rs:14:64 + | +LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); + | ^^^^^ + +error: `(i32) is 1..=` is not a valid base type for range patterns + --> $DIR/nested.rs:17:35 + | +LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: range patterns only support integers + --> $DIR/nested.rs:17:64 + | +LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); + | ^^^ + +error: `()` is not a valid base type for range patterns + --> $DIR/nested.rs:20:35 + | +LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); + | ^^ + | +note: range patterns only support integers + --> $DIR/nested.rs:20:41 + | +LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); + | ^^^ + +error: `f32` is not a valid base type for range patterns + --> $DIR/nested.rs:23:35 + | +LL | const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!(); + | ^^^ + | +note: range patterns only support integers + --> $DIR/nested.rs:23:42 + | +LL | const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!(); + | ^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/type/pattern_types/pattern_type_mismatch.rs b/tests/ui/type/pattern_types/pattern_type_mismatch.rs new file mode 100644 index 0000000000000..8d375d7932bc6 --- /dev/null +++ b/tests/ui/type/pattern_types/pattern_type_mismatch.rs @@ -0,0 +1,20 @@ +//! Check that pattern types patterns must be of the type of the base type + +//@ known-bug: unknown +//@ failure-status: 101 +//@ normalize-stderr: "note: .*\n\n" -> "" +//@ normalize-stderr: "thread 'rustc' panicked.*\n" -> "" +//@ normalize-stderr: "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " +//@ normalize-stderr: "(delayed at compiler/rustc_mir_transform/src/lib.rs:)\d+:\d+" -> "$1:LL:CC" +//@ rustc-env:RUST_BACKTRACE=0 + +#![feature(pattern_types)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +const BAD_NESTING4: pattern_type!(u8 is 'a'..='a') = todo!(); + +const BAD_NESTING5: pattern_type!(char is 1..=1) = todo!(); + +fn main() {} diff --git a/tests/ui/type/pattern_types/pattern_type_mismatch.stderr b/tests/ui/type/pattern_types/pattern_type_mismatch.stderr new file mode 100644 index 0000000000000..ee413133ab317 --- /dev/null +++ b/tests/ui/type/pattern_types/pattern_type_mismatch.stderr @@ -0,0 +1,31 @@ +error: internal compiler error: ty::ConstKind::Error constructed but no error reported + | + = error: internal compiler error: ty::ConstKind::Error constructed but no error reported + | + = note: delayed at compiler/rustc_mir_build/src/thir/constant.rs:72:21 - disabled backtrace + = error: internal compiler error: mir_const_qualif: MIR had errors + --> $DIR/pattern_type_mismatch.rs:16:1 + | +LL | const BAD_NESTING4: pattern_type!(u8 is 'a'..='a') = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: delayed at compiler/rustc_mir_transform/src/lib.rs::LL:CC - disabled backtrace + --> $DIR/pattern_type_mismatch.rs:16:1 + | +LL | const BAD_NESTING4: pattern_type!(u8 is 'a'..='a') = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: internal compiler error: mir_const_qualif: MIR had errors + --> $DIR/pattern_type_mismatch.rs:18:1 + | +LL | const BAD_NESTING5: pattern_type!(char is 1..=1) = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: delayed at compiler/rustc_mir_transform/src/lib.rs::LL:CC - disabled backtrace + --> $DIR/pattern_type_mismatch.rs:18:1 + | +LL | const BAD_NESTING5: pattern_type!(char is 1..=1) = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +query stack during panic: +end of query stack diff --git a/tests/ui/type/pattern_types/reverse_range.rs b/tests/ui/type/pattern_types/reverse_range.rs new file mode 100644 index 0000000000000..6a245615f1ac0 --- /dev/null +++ b/tests/ui/type/pattern_types/reverse_range.rs @@ -0,0 +1,14 @@ +//! Check that the range start must be smaller than the range end +//@ known-bug: unknown +//@ failure-status: 101 +//@ normalize-stderr: "note: .*\n\n" -> "" +//@ normalize-stderr: "thread 'rustc' panicked.*\n" -> "" +//@ normalize-stderr: "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " +//@ rustc-env:RUST_BACKTRACE=0 + +#![feature(pattern_types)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +const NONE: pattern_type!(u8 is 1..0) = unsafe { std::mem::transmute(3_u8) }; diff --git a/tests/ui/type/pattern_types/reverse_range.stderr b/tests/ui/type/pattern_types/reverse_range.stderr new file mode 100644 index 0000000000000..b714ca7d9ab96 --- /dev/null +++ b/tests/ui/type/pattern_types/reverse_range.stderr @@ -0,0 +1,17 @@ +error[E0601]: `main` function not found in crate `reverse_range` + --> $DIR/reverse_range.rs:14:78 + | +LL | const NONE: pattern_type!(u8 is 1..0) = unsafe { std::mem::transmute(3_u8) }; + | ^ consider adding a `main` function to `$DIR/reverse_range.rs` + + +assertion failed: end <= max_value +error: the compiler unexpectedly panicked. this is a bug. + +query stack during panic: +#0 [eval_to_allocation_raw] const-evaluating + checking `NONE` +#1 [eval_to_const_value_raw] simplifying constant for the type system `NONE` +... and 1 other queries... use `env RUST_BACKTRACE=1` to see the full query stack +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0601`. diff --git a/tests/ui/type/pattern_types/validity.rs b/tests/ui/type/pattern_types/validity.rs new file mode 100644 index 0000000000000..77a4e72f6755a --- /dev/null +++ b/tests/ui/type/pattern_types/validity.rs @@ -0,0 +1,37 @@ +//! Check that pattern types have their validity checked + +#![feature(pattern_types)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +const BAD: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(0) }; +//~^ ERROR: it is undefined behavior to use this value + +const BAD_UNINIT: pattern_type!(u32 is 1..) = + //~^ ERROR: evaluation of constant value failed + unsafe { std::mem::transmute(std::mem::MaybeUninit::::uninit()) }; + +const BAD_PTR: pattern_type!(usize is 1..) = unsafe { std::mem::transmute(&42) }; +//~^ ERROR: evaluation of constant value failed + +const BAD_AGGREGATE: (pattern_type!(u32 is 1..), u32) = (unsafe { std::mem::transmute(0) }, 0); +//~^ ERROR: it is undefined behavior to use this value + +struct Foo(Bar); +struct Bar(pattern_type!(u32 is 1..)); + +const BAD_FOO: Foo = Foo(Bar(unsafe { std::mem::transmute(0) })); +//~^ ERROR: it is undefined behavior to use this value + +const CHAR_UNINIT: pattern_type!(char is 'A'..'Z') = + //~^ ERROR: evaluation of constant value failed + unsafe { std::mem::transmute(std::mem::MaybeUninit::::uninit()) }; + +const CHAR_OOB_PAT: pattern_type!(char is 'A'..'Z') = unsafe { std::mem::transmute('a') }; +//~^ ERROR: it is undefined behavior to use this value + +const CHAR_OOB: pattern_type!(char is 'A'..'Z') = unsafe { std::mem::transmute(u32::MAX) }; +//~^ ERROR: it is undefined behavior to use this value + +fn main() {} diff --git a/tests/ui/type/pattern_types/validity.stderr b/tests/ui/type/pattern_types/validity.stderr new file mode 100644 index 0000000000000..5bc18cfba3f7d --- /dev/null +++ b/tests/ui/type/pattern_types/validity.stderr @@ -0,0 +1,79 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/validity.rs:8:1 + | +LL | const BAD: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0, but expected something greater or equal to 1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 00 00 00 00 │ .... + } + +error[E0080]: evaluation of constant value failed + --> $DIR/validity.rs:11:1 + | +LL | const BAD_UNINIT: pattern_type!(u32 is 1..) = + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory + +error[E0080]: evaluation of constant value failed + --> $DIR/validity.rs:15:1 + | +LL | const BAD_PTR: pattern_type!(usize is 1..) = unsafe { std::mem::transmute(&42) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer + | + = help: this code performed an operation that depends on the underlying bytes representing a pointer + = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported + +error[E0080]: it is undefined behavior to use this value + --> $DIR/validity.rs:18:1 + | +LL | const BAD_AGGREGATE: (pattern_type!(u32 is 1..), u32) = (unsafe { std::mem::transmute(0) }, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered 0, but expected something greater or equal to 1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + 00 00 00 00 00 00 00 00 │ ........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/validity.rs:24:1 + | +LL | const BAD_FOO: Foo = Foo(Bar(unsafe { std::mem::transmute(0) })); + | ^^^^^^^^^^^^^^^^^^ constructing invalid value at .0.0: encountered 0, but expected something greater or equal to 1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 00 00 00 00 │ .... + } + +error[E0080]: evaluation of constant value failed + --> $DIR/validity.rs:27:1 + | +LL | const CHAR_UNINIT: pattern_type!(char is 'A'..'Z') = + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory + +error[E0080]: it is undefined behavior to use this value + --> $DIR/validity.rs:31:1 + | +LL | const CHAR_OOB_PAT: pattern_type!(char is 'A'..'Z') = unsafe { std::mem::transmute('a') }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 97, but expected something in the range 65..=89 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 61 00 00 00 │ a... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/validity.rs:34:1 + | +LL | const CHAR_OOB: pattern_type!(char is 'A'..'Z') = unsafe { std::mem::transmute(u32::MAX) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0xffffffff, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ff ff ff ff │ .... + } + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/typeck/path-to-method-sugg-unresolved-expr.rs b/tests/ui/typeck/path-to-method-sugg-unresolved-expr.rs index 7b4f62fea0c81..e095850879cde 100644 --- a/tests/ui/typeck/path-to-method-sugg-unresolved-expr.rs +++ b/tests/ui/typeck/path-to-method-sugg-unresolved-expr.rs @@ -5,6 +5,6 @@ fn main() { let page_size = page_size::get(); //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `page_size` //~| NOTE use of unresolved module or unlinked crate `page_size` - //@[cargo-invoked]~^^^ HELP if you wanted to use a crate named `page_size`, use `cargo add - //@[only-rustc]~^^^^ HELP you might be missing a crate named `page_size` + //[cargo-invoked]~^^^ HELP if you wanted to use a crate named `page_size`, use `cargo add + //[only-rustc]~^^^^ HELP you might be missing a crate named `page_size` }