| author | bors <bors@rust-lang.org> 2025-12-06 21:42:15 UTC |
| committer | bors <bors@rust-lang.org> 2025-12-06 21:42:15 UTC |
| log | d427ddfe90367eaa6d2ed7bb8a16559f0230f47a |
| tree | 747fd920bd2bd4cbebe012a620441f35871fba47 |
| parent | ba86c0460b0233319e01fd789a42a7276eade805 |
| parent | 874b7c2e0bbe4396afb6fe1f46489094f9f063e7 |
Rollup of 5 pull requests
Successful merges:
- rust-lang/rust#149659 (Look for typos when reporting an unknown nightly feature)
- rust-lang/rust#149699 (Implement `Vec::from_fn`)
- rust-lang/rust#149700 (rustdoc: fix bugs with search aliases and merging)
- rust-lang/rust#149713 (Update windows-gnullvm platform support doc)
- rust-lang/rust#149716 (miri subtree update)
r? `@ghost`
`@rustbot` modify labels: rollup105 files changed, 2751 insertions(+), 977 deletions(-)
compiler/rustc_parse/src/parser/diagnostics.rs+10-14| ... | @@ -16,7 +16,6 @@ use rustc_errors::{ | ... | @@ -16,7 +16,6 @@ use rustc_errors::{ |
| 16 | pluralize, | 16 | pluralize, |
| 17 | }; | 17 | }; |
| 18 | use rustc_session::errors::ExprParenthesesNeeded; | 18 | use rustc_session::errors::ExprParenthesesNeeded; |
| 19 | use rustc_span::edit_distance::find_best_match_for_name; | ||
| 20 | use rustc_span::source_map::Spanned; | 19 | use rustc_span::source_map::Spanned; |
| 21 | use rustc_span::symbol::used_keywords; | 20 | use rustc_span::symbol::used_keywords; |
| 22 | use rustc_span::{BytePos, DUMMY_SP, Ident, Span, SpanSnippetError, Symbol, kw, sym}; | 21 | use rustc_span::{BytePos, DUMMY_SP, Ident, Span, SpanSnippetError, Symbol, kw, sym}; |
| ... | @@ -222,6 +221,8 @@ impl std::fmt::Display for UnaryFixity { | ... | @@ -222,6 +221,8 @@ impl std::fmt::Display for UnaryFixity { |
| 222 | style = "verbose" | 221 | style = "verbose" |
| 223 | )] | 222 | )] |
| 224 | struct MisspelledKw { | 223 | struct MisspelledKw { |
| 224 | // We use a String here because `Symbol::into_diag_arg` calls `Symbol::to_ident_string`, which | ||
| 225 | // prefix the keyword with a `r#` because it aims to print the symbol as an identifier. | ||
| 225 | similar_kw: String, | 226 | similar_kw: String, |
| 226 | #[primary_span] | 227 | #[primary_span] |
| 227 | span: Span, | 228 | span: Span, |
| ... | @@ -229,20 +230,15 @@ struct MisspelledKw { | ... | @@ -229,20 +230,15 @@ struct MisspelledKw { |
| 229 | } | 230 | } |
| 230 | 231 | ||
| 231 | /// Checks if the given `lookup` identifier is similar to any keyword symbol in `candidates`. | 232 | /// Checks if the given `lookup` identifier is similar to any keyword symbol in `candidates`. |
| 233 | /// | ||
| 234 | /// This is a specialized version of [`Symbol::find_similar`] that constructs an error when a | ||
| 235 | /// candidate is found. | ||
| 232 | fn find_similar_kw(lookup: Ident, candidates: &[Symbol]) -> Option<MisspelledKw> { | 236 | fn find_similar_kw(lookup: Ident, candidates: &[Symbol]) -> Option<MisspelledKw> { |
| 233 | let lowercase = lookup.name.as_str().to_lowercase(); | 237 | lookup.name.find_similar(candidates).map(|(similar_kw, is_incorrect_case)| MisspelledKw { |
| 234 | let lowercase_sym = Symbol::intern(&lowercase); | 238 | similar_kw: similar_kw.to_string(), |
| 235 | if candidates.contains(&lowercase_sym) { | 239 | is_incorrect_case, |
| 236 | Some(MisspelledKw { similar_kw: lowercase, span: lookup.span, is_incorrect_case: true }) | 240 | span: lookup.span, |
| 237 | } else if let Some(similar_sym) = find_best_match_for_name(candidates, lookup.name, None) { | 241 | }) |
| 238 | Some(MisspelledKw { | ||
| 239 | similar_kw: similar_sym.to_string(), | ||
| 240 | span: lookup.span, | ||
| 241 | is_incorrect_case: false, | ||
| 242 | }) | ||
| 243 | } else { | ||
| 244 | None | ||
| 245 | } | ||
| 246 | } | 242 | } |
| 247 | 243 | ||
| 248 | struct MultiSugg { | 244 | struct MultiSugg { |
compiler/rustc_passes/messages.ftl+2| ... | @@ -421,6 +421,8 @@ passes_missing_panic_handler = | ... | @@ -421,6 +421,8 @@ passes_missing_panic_handler = |
| 421 | passes_missing_stability_attr = | 421 | passes_missing_stability_attr = |
| 422 | {$descr} has missing stability attribute | 422 | {$descr} has missing stability attribute |
| 423 | 423 | ||
| 424 | passes_misspelled_feature = there is a feature with a similar name: `{$actual_name}` | ||
| 425 | |||
| 424 | passes_mixed_export_name_and_no_mangle = `{$no_mangle_attr}` attribute may not be used in combination with `{$export_name_attr}` | 426 | passes_mixed_export_name_and_no_mangle = `{$no_mangle_attr}` attribute may not be used in combination with `{$export_name_attr}` |
| 425 | .label = `{$no_mangle_attr}` is ignored | 427 | .label = `{$no_mangle_attr}` is ignored |
| 426 | .note = `{$export_name_attr}` takes precedence | 428 | .note = `{$export_name_attr}` takes precedence |
compiler/rustc_passes/src/errors.rs+15| ... | @@ -1183,6 +1183,21 @@ pub(crate) struct UnknownFeature { | ... | @@ -1183,6 +1183,21 @@ pub(crate) struct UnknownFeature { |
| 1183 | #[primary_span] | 1183 | #[primary_span] |
| 1184 | pub span: Span, | 1184 | pub span: Span, |
| 1185 | pub feature: Symbol, | 1185 | pub feature: Symbol, |
| 1186 | #[subdiagnostic] | ||
| 1187 | pub suggestion: Option<MisspelledFeature>, | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | #[derive(Subdiagnostic)] | ||
| 1191 | #[suggestion( | ||
| 1192 | passes_misspelled_feature, | ||
| 1193 | style = "verbose", | ||
| 1194 | code = "{actual_name}", | ||
| 1195 | applicability = "maybe-incorrect" | ||
| 1196 | )] | ||
| 1197 | pub(crate) struct MisspelledFeature { | ||
| 1198 | #[primary_span] | ||
| 1199 | pub span: Span, | ||
| 1200 | pub actual_name: Symbol, | ||
| 1186 | } | 1201 | } |
| 1187 | 1202 | ||
| 1188 | #[derive(Diagnostic)] | 1203 | #[derive(Diagnostic)] |
compiler/rustc_passes/src/stability.rs+24-6| ... | @@ -6,7 +6,7 @@ use std::num::NonZero; | ... | @@ -6,7 +6,7 @@ use std::num::NonZero; |
| 6 | use rustc_ast_lowering::stability::extern_abi_stability; | 6 | use rustc_ast_lowering::stability::extern_abi_stability; |
| 7 | use rustc_data_structures::fx::FxIndexMap; | 7 | use rustc_data_structures::fx::FxIndexMap; |
| 8 | use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet}; | 8 | use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet}; |
| 9 | use rustc_feature::{EnabledLangFeature, EnabledLibFeature}; | 9 | use rustc_feature::{EnabledLangFeature, EnabledLibFeature, UNSTABLE_LANG_FEATURES}; |
| 10 | use rustc_hir::attrs::{AttributeKind, DeprecatedSince}; | 10 | use rustc_hir::attrs::{AttributeKind, DeprecatedSince}; |
| 11 | use rustc_hir::def::{DefKind, Res}; | 11 | use rustc_hir::def::{DefKind, Res}; |
| 12 | use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModDefId}; | 12 | use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModDefId}; |
| ... | @@ -1062,11 +1062,13 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { | ... | @@ -1062,11 +1062,13 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { |
| 1062 | // no unknown features, because the collection also does feature attribute validation. | 1062 | // no unknown features, because the collection also does feature attribute validation. |
| 1063 | let local_defined_features = tcx.lib_features(LOCAL_CRATE); | 1063 | let local_defined_features = tcx.lib_features(LOCAL_CRATE); |
| 1064 | if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() { | 1064 | if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() { |
| 1065 | let crates = tcx.crates(()); | ||
| 1066 | |||
| 1065 | // Loading the implications of all crates is unavoidable to be able to emit the partial | 1067 | // Loading the implications of all crates is unavoidable to be able to emit the partial |
| 1066 | // stabilization diagnostic, but it can be avoided when there are no | 1068 | // stabilization diagnostic, but it can be avoided when there are no |
| 1067 | // `remaining_lib_features`. | 1069 | // `remaining_lib_features`. |
| 1068 | let mut all_implications = remaining_implications.clone(); | 1070 | let mut all_implications = remaining_implications.clone(); |
| 1069 | for &cnum in tcx.crates(()) { | 1071 | for &cnum in crates { |
| 1070 | all_implications | 1072 | all_implications |
| 1071 | .extend_unord(tcx.stability_implications(cnum).items().map(|(k, v)| (*k, *v))); | 1073 | .extend_unord(tcx.stability_implications(cnum).items().map(|(k, v)| (*k, *v))); |
| 1072 | } | 1074 | } |
| ... | @@ -1079,7 +1081,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { | ... | @@ -1079,7 +1081,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { |
| 1079 | &all_implications, | 1081 | &all_implications, |
| 1080 | ); | 1082 | ); |
| 1081 | 1083 | ||
| 1082 | for &cnum in tcx.crates(()) { | 1084 | for &cnum in crates { |
| 1083 | if remaining_lib_features.is_empty() && remaining_implications.is_empty() { | 1085 | if remaining_lib_features.is_empty() && remaining_implications.is_empty() { |
| 1084 | break; | 1086 | break; |
| 1085 | } | 1087 | } |
| ... | @@ -1091,10 +1093,26 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { | ... | @@ -1091,10 +1093,26 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { |
| 1091 | &all_implications, | 1093 | &all_implications, |
| 1092 | ); | 1094 | ); |
| 1093 | } | 1095 | } |
| 1094 | } | ||
| 1095 | 1096 | ||
| 1096 | for (feature, span) in remaining_lib_features { | 1097 | if !remaining_lib_features.is_empty() { |
| 1097 | tcx.dcx().emit_err(errors::UnknownFeature { span, feature }); | 1098 | let lang_features = |
| 1099 | UNSTABLE_LANG_FEATURES.iter().map(|feature| feature.name).collect::<Vec<_>>(); | ||
| 1100 | let lib_features = crates | ||
| 1101 | .into_iter() | ||
| 1102 | .flat_map(|&cnum| { | ||
| 1103 | tcx.lib_features(cnum).stability.keys().copied().into_sorted_stable_ord() | ||
| 1104 | }) | ||
| 1105 | .collect::<Vec<_>>(); | ||
| 1106 | |||
| 1107 | let valid_feature_names = [lang_features, lib_features].concat(); | ||
| 1108 | |||
| 1109 | for (feature, span) in remaining_lib_features { | ||
| 1110 | let suggestion = feature | ||
| 1111 | .find_similar(&valid_feature_names) | ||
| 1112 | .map(|(actual_name, _)| errors::MisspelledFeature { span, actual_name }); | ||
| 1113 | tcx.dcx().emit_err(errors::UnknownFeature { span, feature, suggestion }); | ||
| 1114 | } | ||
| 1115 | } | ||
| 1098 | } | 1116 | } |
| 1099 | 1117 | ||
| 1100 | for (&implied_by, &feature) in remaining_implications.to_sorted_stable_ord() { | 1118 | for (&implied_by, &feature) in remaining_implications.to_sorted_stable_ord() { |
compiler/rustc_span/src/symbol.rs+22| ... | @@ -14,6 +14,7 @@ use rustc_data_structures::stable_hasher::{ | ... | @@ -14,6 +14,7 @@ use rustc_data_structures::stable_hasher::{ |
| 14 | use rustc_data_structures::sync::Lock; | 14 | use rustc_data_structures::sync::Lock; |
| 15 | use rustc_macros::{Decodable, Encodable, HashStable_Generic, symbols}; | 15 | use rustc_macros::{Decodable, Encodable, HashStable_Generic, symbols}; |
| 16 | 16 | ||
| 17 | use crate::edit_distance::find_best_match_for_name; | ||
| 17 | use crate::{DUMMY_SP, Edition, Span, with_session_globals}; | 18 | use crate::{DUMMY_SP, Edition, Span, with_session_globals}; |
| 18 | 19 | ||
| 19 | #[cfg(test)] | 20 | #[cfg(test)] |
| ... | @@ -2843,6 +2844,27 @@ impl Symbol { | ... | @@ -2843,6 +2844,27 @@ impl Symbol { |
| 2843 | // Avoid creating an empty identifier, because that asserts in debug builds. | 2844 | // Avoid creating an empty identifier, because that asserts in debug builds. |
| 2844 | if self == sym::empty { String::new() } else { Ident::with_dummy_span(self).to_string() } | 2845 | if self == sym::empty { String::new() } else { Ident::with_dummy_span(self).to_string() } |
| 2845 | } | 2846 | } |
| 2847 | |||
| 2848 | /// Checks if `self` is similar to any symbol in `candidates`. | ||
| 2849 | /// | ||
| 2850 | /// The returned boolean represents whether the candidate is the same symbol with a different | ||
| 2851 | /// casing. | ||
| 2852 | /// | ||
| 2853 | /// All the candidates are assumed to be lowercase. | ||
| 2854 | pub fn find_similar( | ||
| 2855 | self, | ||
| 2856 | candidates: &[Symbol], | ||
| 2857 | ) -> Option<(Symbol, /* is incorrect case */ bool)> { | ||
| 2858 | let lowercase = self.as_str().to_lowercase(); | ||
| 2859 | let lowercase_sym = Symbol::intern(&lowercase); | ||
| 2860 | if candidates.contains(&lowercase_sym) { | ||
| 2861 | Some((lowercase_sym, true)) | ||
| 2862 | } else if let Some(similar_sym) = find_best_match_for_name(candidates, self, None) { | ||
| 2863 | Some((similar_sym, false)) | ||
| 2864 | } else { | ||
| 2865 | None | ||
| 2866 | } | ||
| 2867 | } | ||
| 2846 | } | 2868 | } |
| 2847 | 2869 | ||
| 2848 | impl fmt::Debug for Symbol { | 2870 | impl fmt::Debug for Symbol { |
library/alloc/src/vec/mod.rs+53| ... | @@ -745,6 +745,59 @@ impl<T> Vec<T> { | ... | @@ -745,6 +745,59 @@ impl<T> Vec<T> { |
| 745 | unsafe { Self::from_parts_in(ptr, length, capacity, Global) } | 745 | unsafe { Self::from_parts_in(ptr, length, capacity, Global) } |
| 746 | } | 746 | } |
| 747 | 747 | ||
| 748 | /// Creates a `Vec<T>` where each element is produced by calling `f` with | ||
| 749 | /// that element's index while walking forward through the `Vec<T>`. | ||
| 750 | /// | ||
| 751 | /// This is essentially the same as writing | ||
| 752 | /// | ||
| 753 | /// ```text | ||
| 754 | /// vec![f(0), f(1), f(2), …, f(length - 2), f(length - 1)] | ||
| 755 | /// ``` | ||
| 756 | /// and is similar to `(0..i).map(f)`, just for `Vec<T>`s not iterators. | ||
| 757 | /// | ||
| 758 | /// If `length == 0`, this produces an empty `Vec<T>` without ever calling `f`. | ||
| 759 | /// | ||
| 760 | /// # Example | ||
| 761 | /// | ||
| 762 | /// ```rust | ||
| 763 | /// #![feature(vec_from_fn)] | ||
| 764 | /// | ||
| 765 | /// let vec = Vec::from_fn(5, |i| i); | ||
| 766 | /// | ||
| 767 | /// // indexes are: 0 1 2 3 4 | ||
| 768 | /// assert_eq!(vec, [0, 1, 2, 3, 4]); | ||
| 769 | /// | ||
| 770 | /// let vec2 = Vec::from_fn(8, |i| i * 2); | ||
| 771 | /// | ||
| 772 | /// // indexes are: 0 1 2 3 4 5 6 7 | ||
| 773 | /// assert_eq!(vec2, [0, 2, 4, 6, 8, 10, 12, 14]); | ||
| 774 | /// | ||
| 775 | /// let bool_vec = Vec::from_fn(5, |i| i % 2 == 0); | ||
| 776 | /// | ||
| 777 | /// // indexes are: 0 1 2 3 4 | ||
| 778 | /// assert_eq!(bool_vec, [true, false, true, false, true]); | ||
| 779 | /// ``` | ||
| 780 | /// | ||
| 781 | /// The `Vec<T>` is generated in ascending index order, starting from the front | ||
| 782 | /// and going towards the back, so you can use closures with mutable state: | ||
| 783 | /// ``` | ||
| 784 | /// #![feature(vec_from_fn)] | ||
| 785 | /// | ||
| 786 | /// let mut state = 1; | ||
| 787 | /// let a = Vec::from_fn(6, |_| { let x = state; state *= 2; x }); | ||
| 788 | /// | ||
| 789 | /// assert_eq!(a, [1, 2, 4, 8, 16, 32]); | ||
| 790 | /// ``` | ||
| 791 | #[cfg(not(no_global_oom_handling))] | ||
| 792 | #[inline] | ||
| 793 | #[unstable(feature = "vec_from_fn", reason = "new API", issue = "149698")] | ||
| 794 | pub fn from_fn<F>(length: usize, f: F) -> Self | ||
| 795 | where | ||
| 796 | F: FnMut(usize) -> T, | ||
| 797 | { | ||
| 798 | (0..length).map(f).collect() | ||
| 799 | } | ||
| 800 | |||
| 748 | /// Decomposes a `Vec<T>` into its raw components: `(pointer, length, capacity)`. | 801 | /// Decomposes a `Vec<T>` into its raw components: `(pointer, length, capacity)`. |
| 749 | /// | 802 | /// |
| 750 | /// Returns the raw pointer to the underlying data, the length of | 803 | /// Returns the raw pointer to the underlying data, the length of |
src/doc/rustc/src/platform-support/windows-gnullvm.md+26-28| ... | @@ -2,9 +2,11 @@ | ... | @@ -2,9 +2,11 @@ |
| 2 | 2 | ||
| 3 | **Tier: 2 (with host tools)** | 3 | **Tier: 2 (with host tools)** |
| 4 | 4 | ||
| 5 | Windows targets similar to `*-windows-gnu` but using UCRT as the runtime and various LLVM tools/libraries instead of GCC/Binutils. | 5 | Windows targets similar to `*-windows-gnu` but using UCRT as the runtime and various LLVM tools/libraries instead of |
| 6 | GCC/Binutils. | ||
| 6 | 7 | ||
| 7 | Target triples available so far: | 8 | Target triples available so far: |
| 9 | |||
| 8 | - `aarch64-pc-windows-gnullvm` | 10 | - `aarch64-pc-windows-gnullvm` |
| 9 | - `i686-pc-windows-gnullvm` | 11 | - `i686-pc-windows-gnullvm` |
| 10 | - `x86_64-pc-windows-gnullvm` | 12 | - `x86_64-pc-windows-gnullvm` |
| ... | @@ -16,10 +18,11 @@ Target triples available so far: | ... | @@ -16,10 +18,11 @@ Target triples available so far: |
| 16 | 18 | ||
| 17 | ## Requirements | 19 | ## Requirements |
| 18 | 20 | ||
| 19 | The easiest way to obtain these targets is cross-compilation, but native build from `x86_64-pc-windows-gnu` is possible with few hacks which I don't recommend. | 21 | Building those targets requires an LLVM-based C toolchain, for example, [llvm-mingw][1] or [MSYS2][2] with CLANG* |
| 20 | Std support is expected to be on par with `*-windows-gnu`. | 22 | environment. |
| 21 | 23 | ||
| 22 | Binaries for this target should be at least on par with `*-windows-gnu` in terms of requirements and functionality. | 24 | Binaries for this target should be at least on par with `*-windows-gnu` in terms of requirements and functionality, |
| 25 | except for implicit self-contained mode (explained in [the section below](#building-rust-programs)). | ||
| 23 | 26 | ||
| 24 | Those targets follow Windows calling convention for `extern "C"`. | 27 | Those targets follow Windows calling convention for `extern "C"`. |
| 25 | 28 | ||
| ... | @@ -27,37 +30,32 @@ Like with any other Windows target, created binaries are in PE format. | ... | @@ -27,37 +30,32 @@ Like with any other Windows target, created binaries are in PE format. |
| 27 | 30 | ||
| 28 | ## Building the target | 31 | ## Building the target |
| 29 | 32 | ||
| 30 | These targets can be easily cross-compiled | 33 | Both native and cross-compilation builds are supported and function similarly to other Rust targets. |
| 31 | using [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) toolchain or [MSYS2 CLANG*](https://www.msys2.org/docs/environments/) environments. | ||
| 32 | Just fill `[target.*]` sections for both build and resulting compiler and set installation prefix in `bootstrap.toml`. | ||
| 33 | Then run `./x.py install`. | ||
| 34 | In my case I had ran `./x.py install --host x86_64-pc-windows-gnullvm --target x86_64-pc-windows-gnullvm` inside MSYS2 MINGW64 shell | ||
| 35 | so `x86_64-pc-windows-gnu` was my build toolchain. | ||
| 36 | |||
| 37 | Native bootstrapping is doable in two ways: | ||
| 38 | - cross-compile gnullvm host toolchain and use it as build toolchain for the next build, | ||
| 39 | - copy libunwind libraries and rename them to mimic libgcc like here: https://github.com/msys2/MINGW-packages/blob/68e640756df2df6df6afa60f025e3f936e7b977c/mingw-w64-rust/PKGBUILD#L108-L109, stage0 compiler will be mostly broken but good enough to build the next stage. | ||
| 40 | |||
| 41 | The second option might stop working anytime, so it's not recommended. | ||
| 42 | 34 | ||
| 43 | ## Building Rust programs | 35 | ## Building Rust programs |
| 44 | 36 | ||
| 45 | Rust does ship a pre-compiled std library for those targets. | 37 | Rust ships both std and host tools for those targets. That allows using them as both the host and the target. |
| 46 | That means one can easily cross-compile for those targets from other hosts if C proper toolchain is installed. | ||
| 47 | 38 | ||
| 48 | Alternatively full toolchain can be built as described in the previous section. | 39 | When used as the host and building pure Rust programs, no additional C toolchain is required. |
| 40 | The only requirements are to install `rust-mingw` component and to set `rust-lld` as the linker. | ||
| 41 | Otherwise, you will need to install the C toolchain mentioned previously. | ||
| 42 | There is no automatic fallback to `rust-lld` when the C toolchain is missing yet, but it may be added in the future. | ||
| 49 | 43 | ||
| 50 | ## Testing | 44 | ## Testing |
| 51 | 45 | ||
| 52 | Created binaries work fine on Windows or Wine using native hardware. Testing AArch64 on x86_64 is problematic though and requires spending some time with QEMU. | 46 | Created binaries work fine on Windows and Linux with Wine using native hardware. |
| 53 | Most of x86_64 testsuite does pass when cross-compiling, | 47 | Testing AArch64 on x86_64 is problematic, though, and requires launching a whole AArch64 system with QEMU. |
| 54 | with exception for `rustdoc` and `ui-fulldeps` that fail with and error regarding a missing library, | 48 | |
| 55 | they do pass in native builds though. | 49 | Most of the x86_64 testsuite does pass, but because it isn't run on CI, different failures are expected over time. |
| 56 | The only failing test is std's `process::tests::test_proc_thread_attributes` for unknown reason. | ||
| 57 | 50 | ||
| 58 | ## Cross-compilation toolchains and C code | 51 | ## Cross-compilation toolchains and C code |
| 59 | 52 | ||
| 60 | Compatible C code can be built with Clang's `aarch64-pc-windows-gnu`, `i686-pc-windows-gnullvm` and `x86_64-pc-windows-gnu` targets as long as LLVM-based C toolchains are used. | 53 | Compatible C code can be built with Clang's `aarch64-pc-windows-gnu`, `i686-pc-windows-gnullvm` and |
| 61 | Those include: | 54 | `x86_64-pc-windows-gnu` targets as long as LLVM-based C toolchains are used. Those include: |
| 62 | - [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) | 55 | |
| 63 | - [MSYS2 with CLANG* environment](https://www.msys2.org/docs/environments) | 56 | - [llvm-mingw][1] |
| 57 | - [MSYS2][2] with CLANG* environment | ||
| 58 | |||
| 59 | [1]: https://github.com/mstorsjo/llvm-mingw | ||
| 60 | |||
| 61 | [2]: https://www.msys2.org/docs/environments |
src/librustdoc/html/render/search_index.rs+10-2| ... | @@ -24,6 +24,7 @@ use tracing::instrument; | ... | @@ -24,6 +24,7 @@ use tracing::instrument; |
| 24 | 24 | ||
| 25 | use crate::clean::types::{Function, Generics, ItemId, Type, WherePredicate}; | 25 | use crate::clean::types::{Function, Generics, ItemId, Type, WherePredicate}; |
| 26 | use crate::clean::{self, utils}; | 26 | use crate::clean::{self, utils}; |
| 27 | use crate::config::ShouldMerge; | ||
| 27 | use crate::error::Error; | 28 | use crate::error::Error; |
| 28 | use crate::formats::cache::{Cache, OrphanImplItem}; | 29 | use crate::formats::cache::{Cache, OrphanImplItem}; |
| 29 | use crate::formats::item_type::ItemType; | 30 | use crate::formats::item_type::ItemType; |
| ... | @@ -721,7 +722,9 @@ impl SerializedSearchIndex { | ... | @@ -721,7 +722,9 @@ impl SerializedSearchIndex { |
| 721 | } | 722 | } |
| 722 | }, | 723 | }, |
| 723 | ), | 724 | ), |
| 724 | self.alias_pointers[id].and_then(|alias| map.get(&alias).copied()), | 725 | self.alias_pointers[id].and_then(|alias| { |
| 726 | if self.names[alias].is_empty() { None } else { map.get(&alias).copied() } | ||
| 727 | }), | ||
| 725 | ); | 728 | ); |
| 726 | } | 729 | } |
| 727 | new.generic_inverted_index = self | 730 | new.generic_inverted_index = self |
| ... | @@ -1248,6 +1251,7 @@ pub(crate) fn build_index( | ... | @@ -1248,6 +1251,7 @@ pub(crate) fn build_index( |
| 1248 | tcx: TyCtxt<'_>, | 1251 | tcx: TyCtxt<'_>, |
| 1249 | doc_root: &Path, | 1252 | doc_root: &Path, |
| 1250 | resource_suffix: &str, | 1253 | resource_suffix: &str, |
| 1254 | should_merge: &ShouldMerge, | ||
| 1251 | ) -> Result<SerializedSearchIndex, Error> { | 1255 | ) -> Result<SerializedSearchIndex, Error> { |
| 1252 | let mut search_index = std::mem::take(&mut cache.search_index); | 1256 | let mut search_index = std::mem::take(&mut cache.search_index); |
| 1253 | 1257 | ||
| ... | @@ -1298,7 +1302,11 @@ pub(crate) fn build_index( | ... | @@ -1298,7 +1302,11 @@ pub(crate) fn build_index( |
| 1298 | // | 1302 | // |
| 1299 | // if there's already a search index, load it into memory and add the new entries to it | 1303 | // if there's already a search index, load it into memory and add the new entries to it |
| 1300 | // otherwise, do nothing | 1304 | // otherwise, do nothing |
| 1301 | let mut serialized_index = SerializedSearchIndex::load(doc_root, resource_suffix)?; | 1305 | let mut serialized_index = if should_merge.read_rendered_cci { |
| 1306 | SerializedSearchIndex::load(doc_root, resource_suffix)? | ||
| 1307 | } else { | ||
| 1308 | SerializedSearchIndex::default() | ||
| 1309 | }; | ||
| 1302 | 1310 | ||
| 1303 | // The crate always goes first in this list | 1311 | // The crate always goes first in this list |
| 1304 | let crate_name = krate.name(tcx); | 1312 | let crate_name = krate.name(tcx); |
src/librustdoc/html/render/write_shared.rs+8-2| ... | @@ -66,8 +66,14 @@ pub(crate) fn write_shared( | ... | @@ -66,8 +66,14 @@ pub(crate) fn write_shared( |
| 66 | // Write shared runs within a flock; disable thread dispatching of IO temporarily. | 66 | // Write shared runs within a flock; disable thread dispatching of IO temporarily. |
| 67 | let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file); | 67 | let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file); |
| 68 | 68 | ||
| 69 | let search_index = | 69 | let search_index = build_index( |
| 70 | build_index(krate, &mut cx.shared.cache, tcx, &cx.dst, &cx.shared.resource_suffix)?; | 70 | krate, |
| 71 | &mut cx.shared.cache, | ||
| 72 | tcx, | ||
| 73 | &cx.dst, | ||
| 74 | &cx.shared.resource_suffix, | ||
| 75 | &opt.should_merge, | ||
| 76 | )?; | ||
| 71 | 77 | ||
| 72 | let crate_name = krate.name(cx.tcx()); | 78 | let crate_name = krate.name(cx.tcx()); |
| 73 | let crate_name = crate_name.as_str(); // rand | 79 | let crate_name = crate_name.as_str(); // rand |
src/tools/miri/.github/workflows/ci.yml+5-5| ... | @@ -31,13 +31,13 @@ jobs: | ... | @@ -31,13 +31,13 @@ jobs: |
| 31 | os: ubuntu-24.04-arm | 31 | os: ubuntu-24.04-arm |
| 32 | multiarch: armhf | 32 | multiarch: armhf |
| 33 | gcc_cross: arm-linux-gnueabihf | 33 | gcc_cross: arm-linux-gnueabihf |
| 34 | - host_target: riscv64gc-unknown-linux-gnu | ||
| 35 | os: ubuntu-latest | ||
| 36 | multiarch: riscv64 | ||
| 37 | gcc_cross: riscv64-linux-gnu | ||
| 38 | qemu: true | ||
| 39 | # Ubuntu mirrors are not reliable enough for these architectures | 34 | # Ubuntu mirrors are not reliable enough for these architectures |
| 40 | # (see <https://bugs.launchpad.net/ubuntu/+bug/2130309>). | 35 | # (see <https://bugs.launchpad.net/ubuntu/+bug/2130309>). |
| 36 | # - host_target: riscv64gc-unknown-linux-gnu | ||
| 37 | # os: ubuntu-latest | ||
| 38 | # multiarch: riscv64 | ||
| 39 | # gcc_cross: riscv64-linux-gnu | ||
| 40 | # qemu: true | ||
| 41 | # - host_target: s390x-unknown-linux-gnu | 41 | # - host_target: s390x-unknown-linux-gnu |
| 42 | # os: ubuntu-latest | 42 | # os: ubuntu-latest |
| 43 | # multiarch: s390x | 43 | # multiarch: s390x |
src/tools/miri/README.md+1| ... | @@ -624,6 +624,7 @@ Definite bugs found: | ... | @@ -624,6 +624,7 @@ Definite bugs found: |
| 624 | * [Mockall reading uninitialized memory when mocking `std::io::Read::read`, even if all expectations are satisfied](https://github.com/asomers/mockall/issues/647) (caught by Miri running Tokio's test suite) | 624 | * [Mockall reading uninitialized memory when mocking `std::io::Read::read`, even if all expectations are satisfied](https://github.com/asomers/mockall/issues/647) (caught by Miri running Tokio's test suite) |
| 625 | * [`ReentrantLock` not correctly dealing with reuse of addresses for TLS storage of different threads](https://github.com/rust-lang/rust/pull/141248) | 625 | * [`ReentrantLock` not correctly dealing with reuse of addresses for TLS storage of different threads](https://github.com/rust-lang/rust/pull/141248) |
| 626 | * [Rare Deadlock in the thread (un)parking example code](https://github.com/rust-lang/rust/issues/145816) | 626 | * [Rare Deadlock in the thread (un)parking example code](https://github.com/rust-lang/rust/issues/145816) |
| 627 | * [`winit` registering a global constructor with the wrong ABI on Windows](https://github.com/rust-windowing/winit/issues/4435) | ||
| 627 | 628 | ||
| 628 | Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment): | 629 | Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment): |
| 629 | 630 |
src/tools/miri/etc/rust_analyzer_helix.toml+2| ... | @@ -28,5 +28,7 @@ overrideCommand = [ | ... | @@ -28,5 +28,7 @@ overrideCommand = [ |
| 28 | "./miri", | 28 | "./miri", |
| 29 | "check", | 29 | "check", |
| 30 | "--no-default-features", | 30 | "--no-default-features", |
| 31 | "-Zunstable-options", | ||
| 32 | "--compile-time-deps", | ||
| 31 | "--message-format=json", | 33 | "--message-format=json", |
| 32 | ] | 34 | ] |
src/tools/miri/etc/rust_analyzer_vscode.json+2| ... | @@ -22,6 +22,8 @@ | ... | @@ -22,6 +22,8 @@ |
| 22 | "./miri", | 22 | "./miri", |
| 23 | "check", | 23 | "check", |
| 24 | "--no-default-features", | 24 | "--no-default-features", |
| 25 | "-Zunstable-options", | ||
| 26 | "--compile-time-deps", | ||
| 25 | "--message-format=json", | 27 | "--message-format=json", |
| 26 | ], | 28 | ], |
| 27 | } | 29 | } |
src/tools/miri/etc/rust_analyzer_zed.json+2| ... | @@ -31,6 +31,8 @@ | ... | @@ -31,6 +31,8 @@ |
| 31 | "./miri", | 31 | "./miri", |
| 32 | "check", | 32 | "check", |
| 33 | "--no-default-features", | 33 | "--no-default-features", |
| 34 | "-Zunstable-options", | ||
| 35 | "--compile-time-deps", | ||
| 34 | "--message-format=json" | 36 | "--message-format=json" |
| 35 | ] | 37 | ] |
| 36 | } | 38 | } |
src/tools/miri/rust-version+1-1| ... | @@ -1 +1 @@ | ... | @@ -1 +1 @@ |
| 1 | 1eb0657f78777f0b4d6bcc49c126d5d35212cae5 | 1 | 36b2369c91d32c2659887ed6fe3d570640f44fd2 |
src/tools/miri/src/bin/miri.rs+25-38| ... | @@ -272,18 +272,13 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { | ... | @@ -272,18 +272,13 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { |
| 272 | } | 272 | } |
| 273 | } | 273 | } |
| 274 | 274 | ||
| 275 | struct MiriBeRustCompilerCalls { | 275 | /// This compiler produces rlibs that are meant for later consumption by Miri. |
| 276 | target_crate: bool, | 276 | struct MiriDepCompilerCalls; |
| 277 | } | ||
| 278 | 277 | ||
| 279 | impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { | 278 | impl rustc_driver::Callbacks for MiriDepCompilerCalls { |
| 280 | #[allow(rustc::potential_query_instability)] // rustc_codegen_ssa (where this code is copied from) also allows this lint | 279 | #[allow(rustc::potential_query_instability)] // rustc_codegen_ssa (where this code is copied from) also allows this lint |
| 281 | fn config(&mut self, config: &mut Config) { | 280 | fn config(&mut self, config: &mut Config) { |
| 282 | if !self.target_crate { | 281 | // We don't need actual codegen, we just emit an rlib that Miri can later consume. |
| 283 | // For a host crate, we fully behave like rustc. | ||
| 284 | return; | ||
| 285 | } | ||
| 286 | // For a target crate, we emit an rlib that Miri can later consume. | ||
| 287 | config.make_codegen_backend = Some(Box::new(make_miri_codegen_backend)); | 282 | config.make_codegen_backend = Some(Box::new(make_miri_codegen_backend)); |
| 288 | 283 | ||
| 289 | // Avoid warnings about unsupported crate types. However, only do that we we are *not* being | 284 | // Avoid warnings about unsupported crate types. However, only do that we we are *not* being |
| ... | @@ -367,16 +362,12 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { | ... | @@ -367,16 +362,12 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { |
| 367 | _: &rustc_interface::interface::Compiler, | 362 | _: &rustc_interface::interface::Compiler, |
| 368 | tcx: TyCtxt<'tcx>, | 363 | tcx: TyCtxt<'tcx>, |
| 369 | ) -> Compilation { | 364 | ) -> Compilation { |
| 370 | if self.target_crate { | 365 | // While the dummy codegen backend doesn't do any codegen, we are still emulating |
| 371 | // cargo-miri has patched the compiler flags to make these into check-only builds, | 366 | // regular rustc builds, which would perform post-mono const-eval during collection. |
| 372 | // but we are still emulating regular rustc builds, which would perform post-mono | 367 | // So let's also do that here. In particular this is needed to make `compile_fail` |
| 373 | // const-eval during collection. So let's also do that here, even if we might be | 368 | // doc tests trigger post-mono errors. |
| 374 | // running with `--emit=metadata`. In particular this is needed to make | 369 | let _ = tcx.collect_and_partition_mono_items(()); |
| 375 | // `compile_fail` doc tests trigger post-mono errors. | 370 | |
| 376 | // In general `collect_and_partition_mono_items` is not safe to call in check-only | ||
| 377 | // builds, but we are setting `-Zalways-encode-mir` which avoids those issues. | ||
| 378 | let _ = tcx.collect_and_partition_mono_items(()); | ||
| 379 | } | ||
| 380 | Compilation::Continue | 371 | Compilation::Continue |
| 381 | } | 372 | } |
| 382 | } | 373 | } |
| ... | @@ -457,32 +448,28 @@ fn main() { | ... | @@ -457,32 +448,28 @@ fn main() { |
| 457 | 448 | ||
| 458 | // If the environment asks us to actually be rustc, then do that. | 449 | // If the environment asks us to actually be rustc, then do that. |
| 459 | if let Some(crate_kind) = env::var_os("MIRI_BE_RUSTC") { | 450 | if let Some(crate_kind) = env::var_os("MIRI_BE_RUSTC") { |
| 451 | if crate_kind == "host" { | ||
| 452 | // For host crates like proc macros and build scripts, we are an entirely normal rustc. | ||
| 453 | // These eventually produce actual binaries and never run in Miri. | ||
| 454 | match rustc_driver::main() { | ||
| 455 | // Empty match proves this function will never return. | ||
| 456 | } | ||
| 457 | } else if crate_kind != "target" { | ||
| 458 | panic!("invalid `MIRI_BE_RUSTC` value: {crate_kind:?}") | ||
| 459 | }; | ||
| 460 | |||
| 460 | // Earliest rustc setup. | 461 | // Earliest rustc setup. |
| 461 | rustc_driver::install_ice_hook(rustc_driver::DEFAULT_BUG_REPORT_URL, |_| ()); | 462 | rustc_driver::install_ice_hook(rustc_driver::DEFAULT_BUG_REPORT_URL, |_| ()); |
| 462 | rustc_driver::init_rustc_env_logger(&early_dcx); | 463 | rustc_driver::init_rustc_env_logger(&early_dcx); |
| 463 | 464 | ||
| 464 | let target_crate = if crate_kind == "target" { | ||
| 465 | true | ||
| 466 | } else if crate_kind == "host" { | ||
| 467 | false | ||
| 468 | } else { | ||
| 469 | panic!("invalid `MIRI_BE_RUSTC` value: {crate_kind:?}") | ||
| 470 | }; | ||
| 471 | |||
| 472 | let mut args = args; | 465 | let mut args = args; |
| 473 | // Don't insert `MIRI_DEFAULT_ARGS`, in particular, `--cfg=miri`, if we are building | 466 | // Splice in the default arguments after the program name. |
| 474 | // a "host" crate. That may cause procedural macros (and probably build scripts) to | 467 | // Some options have different defaults in Miri than in plain rustc; apply those by making |
| 475 | // depend on Miri-only symbols, such as `miri_resolve_frame`: | 468 | // them the first arguments after the binary name (but later arguments can overwrite them). |
| 476 | // https://github.com/rust-lang/miri/issues/1760 | 469 | args.splice(1..1, miri::MIRI_DEFAULT_ARGS.iter().map(ToString::to_string)); |
| 477 | if target_crate { | ||
| 478 | // Splice in the default arguments after the program name. | ||
| 479 | // Some options have different defaults in Miri than in plain rustc; apply those by making | ||
| 480 | // them the first arguments after the binary name (but later arguments can overwrite them). | ||
| 481 | args.splice(1..1, miri::MIRI_DEFAULT_ARGS.iter().map(ToString::to_string)); | ||
| 482 | } | ||
| 483 | 470 | ||
| 484 | // We cannot use `rustc_driver::main` as we want it to use `args` as the CLI arguments. | 471 | // We cannot use `rustc_driver::main` as we want it to use `args` as the CLI arguments. |
| 485 | run_compiler_and_exit(&args, &mut MiriBeRustCompilerCalls { target_crate }) | 472 | run_compiler_and_exit(&args, &mut MiriDepCompilerCalls) |
| 486 | } | 473 | } |
| 487 | 474 | ||
| 488 | // Add an ICE bug report hook. | 475 | // Add an ICE bug report hook. |
src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs+84-25| ... | @@ -488,6 +488,8 @@ struct DisplayFmtPadding { | ... | @@ -488,6 +488,8 @@ struct DisplayFmtPadding { |
| 488 | indent_middle: S, | 488 | indent_middle: S, |
| 489 | /// Indentation for the last child. | 489 | /// Indentation for the last child. |
| 490 | indent_last: S, | 490 | indent_last: S, |
| 491 | /// Replaces `join_last` for a wildcard root. | ||
| 492 | wildcard_root: S, | ||
| 491 | } | 493 | } |
| 492 | /// How to show whether a location has been accessed | 494 | /// How to show whether a location has been accessed |
| 493 | /// | 495 | /// |
| ... | @@ -561,6 +563,11 @@ impl DisplayFmt { | ... | @@ -561,6 +563,11 @@ impl DisplayFmt { |
| 561 | }) | 563 | }) |
| 562 | .unwrap_or("") | 564 | .unwrap_or("") |
| 563 | } | 565 | } |
| 566 | |||
| 567 | /// Print extra text if the tag is exposed. | ||
| 568 | fn print_exposed(&self, exposed: bool) -> S { | ||
| 569 | if exposed { " (exposed)" } else { "" } | ||
| 570 | } | ||
| 564 | } | 571 | } |
| 565 | 572 | ||
| 566 | /// Track the indentation of the tree. | 573 | /// Track the indentation of the tree. |
| ... | @@ -607,23 +614,21 @@ fn char_repeat(c: char, n: usize) -> String { | ... | @@ -607,23 +614,21 @@ fn char_repeat(c: char, n: usize) -> String { |
| 607 | struct DisplayRepr { | 614 | struct DisplayRepr { |
| 608 | tag: BorTag, | 615 | tag: BorTag, |
| 609 | name: Option<String>, | 616 | name: Option<String>, |
| 617 | exposed: bool, | ||
| 610 | rperm: Vec<Option<LocationState>>, | 618 | rperm: Vec<Option<LocationState>>, |
| 611 | children: Vec<DisplayRepr>, | 619 | children: Vec<DisplayRepr>, |
| 612 | } | 620 | } |
| 613 | 621 | ||
| 614 | impl DisplayRepr { | 622 | impl DisplayRepr { |
| 615 | fn from(tree: &Tree, show_unnamed: bool) -> Option<Self> { | 623 | fn from(tree: &Tree, root: UniIndex, show_unnamed: bool) -> Option<Self> { |
| 616 | let mut v = Vec::new(); | 624 | let mut v = Vec::new(); |
| 617 | extraction_aux(tree, tree.root, show_unnamed, &mut v); | 625 | extraction_aux(tree, root, show_unnamed, &mut v); |
| 618 | let Some(root) = v.pop() else { | 626 | let Some(root) = v.pop() else { |
| 619 | if show_unnamed { | 627 | if show_unnamed { |
| 620 | unreachable!( | 628 | unreachable!( |
| 621 | "This allocation contains no tags, not even a root. This should not happen." | 629 | "This allocation contains no tags, not even a root. This should not happen." |
| 622 | ); | 630 | ); |
| 623 | } | 631 | } |
| 624 | eprintln!( | ||
| 625 | "This allocation does not contain named tags. Use `miri_print_borrow_state(_, true)` to also print unnamed tags." | ||
| 626 | ); | ||
| 627 | return None; | 632 | return None; |
| 628 | }; | 633 | }; |
| 629 | assert!(v.is_empty()); | 634 | assert!(v.is_empty()); |
| ... | @@ -637,6 +642,7 @@ impl DisplayRepr { | ... | @@ -637,6 +642,7 @@ impl DisplayRepr { |
| 637 | ) { | 642 | ) { |
| 638 | let node = tree.nodes.get(idx).unwrap(); | 643 | let node = tree.nodes.get(idx).unwrap(); |
| 639 | let name = node.debug_info.name.clone(); | 644 | let name = node.debug_info.name.clone(); |
| 645 | let exposed = node.is_exposed; | ||
| 640 | let children_sorted = { | 646 | let children_sorted = { |
| 641 | let mut children = node.children.iter().cloned().collect::<Vec<_>>(); | 647 | let mut children = node.children.iter().cloned().collect::<Vec<_>>(); |
| 642 | children.sort_by_key(|idx| tree.nodes.get(*idx).unwrap().tag); | 648 | children.sort_by_key(|idx| tree.nodes.get(*idx).unwrap().tag); |
| ... | @@ -661,12 +667,13 @@ impl DisplayRepr { | ... | @@ -661,12 +667,13 @@ impl DisplayRepr { |
| 661 | for child_idx in children_sorted { | 667 | for child_idx in children_sorted { |
| 662 | extraction_aux(tree, child_idx, show_unnamed, &mut children); | 668 | extraction_aux(tree, child_idx, show_unnamed, &mut children); |
| 663 | } | 669 | } |
| 664 | acc.push(DisplayRepr { tag: node.tag, name, rperm, children }); | 670 | acc.push(DisplayRepr { tag: node.tag, name, rperm, children, exposed }); |
| 665 | } | 671 | } |
| 666 | } | 672 | } |
| 667 | } | 673 | } |
| 668 | fn print( | 674 | fn print( |
| 669 | &self, | 675 | main_root: &Option<DisplayRepr>, |
| 676 | wildcard_subtrees: &[DisplayRepr], | ||
| 670 | fmt: &DisplayFmt, | 677 | fmt: &DisplayFmt, |
| 671 | indenter: &mut DisplayIndent, | 678 | indenter: &mut DisplayIndent, |
| 672 | protected_tags: &FxHashMap<BorTag, ProtectorKind>, | 679 | protected_tags: &FxHashMap<BorTag, ProtectorKind>, |
| ... | @@ -703,15 +710,41 @@ impl DisplayRepr { | ... | @@ -703,15 +710,41 @@ impl DisplayRepr { |
| 703 | block.push(s); | 710 | block.push(s); |
| 704 | } | 711 | } |
| 705 | // This is the actual work | 712 | // This is the actual work |
| 706 | print_aux( | 713 | if let Some(root) = main_root { |
| 707 | self, | 714 | print_aux( |
| 708 | &range_padding, | 715 | root, |
| 709 | fmt, | 716 | &range_padding, |
| 710 | indenter, | 717 | fmt, |
| 711 | protected_tags, | 718 | indenter, |
| 712 | true, /* root _is_ the last child */ | 719 | protected_tags, |
| 713 | &mut block, | 720 | true, /* root _is_ the last child */ |
| 714 | ); | 721 | false, /* not a wildcard_root*/ |
| 722 | &mut block, | ||
| 723 | ); | ||
| 724 | } | ||
| 725 | for tree in wildcard_subtrees.iter() { | ||
| 726 | let mut gap_line = String::new(); | ||
| 727 | gap_line.push_str(fmt.perm.open); | ||
| 728 | for (i, &pad) in range_padding.iter().enumerate() { | ||
| 729 | if i > 0 { | ||
| 730 | gap_line.push_str(fmt.perm.sep); | ||
| 731 | } | ||
| 732 | gap_line.push_str(&format!("{}{}", char_repeat(' ', pad), " ")); | ||
| 733 | } | ||
| 734 | gap_line.push_str(fmt.perm.close); | ||
| 735 | block.push(gap_line); | ||
| 736 | |||
| 737 | print_aux( | ||
| 738 | tree, | ||
| 739 | &range_padding, | ||
| 740 | fmt, | ||
| 741 | indenter, | ||
| 742 | protected_tags, | ||
| 743 | true, /* root _is_ the last child */ | ||
| 744 | true, /* wildcard_root*/ | ||
| 745 | &mut block, | ||
| 746 | ); | ||
| 747 | } | ||
| 715 | // Then it's just prettifying it with a border of dashes. | 748 | // Then it's just prettifying it with a border of dashes. |
| 716 | { | 749 | { |
| 717 | let wr = &fmt.wrapper; | 750 | let wr = &fmt.wrapper; |
| ... | @@ -741,6 +774,7 @@ impl DisplayRepr { | ... | @@ -741,6 +774,7 @@ impl DisplayRepr { |
| 741 | indent: &mut DisplayIndent, | 774 | indent: &mut DisplayIndent, |
| 742 | protected_tags: &FxHashMap<BorTag, ProtectorKind>, | 775 | protected_tags: &FxHashMap<BorTag, ProtectorKind>, |
| 743 | is_last_child: bool, | 776 | is_last_child: bool, |
| 777 | is_wildcard_root: bool, | ||
| 744 | acc: &mut Vec<String>, | 778 | acc: &mut Vec<String>, |
| 745 | ) { | 779 | ) { |
| 746 | let mut line = String::new(); | 780 | let mut line = String::new(); |
| ... | @@ -760,7 +794,9 @@ impl DisplayRepr { | ... | @@ -760,7 +794,9 @@ impl DisplayRepr { |
| 760 | indent.write(&mut line); | 794 | indent.write(&mut line); |
| 761 | { | 795 | { |
| 762 | // padding | 796 | // padding |
| 763 | line.push_str(if is_last_child { | 797 | line.push_str(if is_wildcard_root { |
| 798 | fmt.padding.wildcard_root | ||
| 799 | } else if is_last_child { | ||
| 764 | fmt.padding.join_last | 800 | fmt.padding.join_last |
| 765 | } else { | 801 | } else { |
| 766 | fmt.padding.join_middle | 802 | fmt.padding.join_middle |
| ... | @@ -777,12 +813,22 @@ impl DisplayRepr { | ... | @@ -777,12 +813,22 @@ impl DisplayRepr { |
| 777 | line.push_str(&fmt.print_tag(tree.tag, &tree.name)); | 813 | line.push_str(&fmt.print_tag(tree.tag, &tree.name)); |
| 778 | let protector = protected_tags.get(&tree.tag); | 814 | let protector = protected_tags.get(&tree.tag); |
| 779 | line.push_str(fmt.print_protector(protector)); | 815 | line.push_str(fmt.print_protector(protector)); |
| 816 | line.push_str(fmt.print_exposed(tree.exposed)); | ||
| 780 | // Push the line to the accumulator then recurse. | 817 | // Push the line to the accumulator then recurse. |
| 781 | acc.push(line); | 818 | acc.push(line); |
| 782 | let nb_children = tree.children.len(); | 819 | let nb_children = tree.children.len(); |
| 783 | for (i, child) in tree.children.iter().enumerate() { | 820 | for (i, child) in tree.children.iter().enumerate() { |
| 784 | indent.increment(fmt, is_last_child); | 821 | indent.increment(fmt, is_last_child); |
| 785 | print_aux(child, padding, fmt, indent, protected_tags, i + 1 == nb_children, acc); | 822 | print_aux( |
| 823 | child, | ||
| 824 | padding, | ||
| 825 | fmt, | ||
| 826 | indent, | ||
| 827 | protected_tags, | ||
| 828 | /* is_last_child */ i + 1 == nb_children, | ||
| 829 | /* is_wildcard_root */ false, | ||
| 830 | acc, | ||
| 831 | ); | ||
| 786 | indent.decrement(fmt); | 832 | indent.decrement(fmt); |
| 787 | } | 833 | } |
| 788 | } | 834 | } |
| ... | @@ -803,6 +849,7 @@ const DEFAULT_FORMATTER: DisplayFmt = DisplayFmt { | ... | @@ -803,6 +849,7 @@ const DEFAULT_FORMATTER: DisplayFmt = DisplayFmt { |
| 803 | indent_last: " ", | 849 | indent_last: " ", |
| 804 | join_haschild: "┬", | 850 | join_haschild: "┬", |
| 805 | join_default: "─", | 851 | join_default: "─", |
| 852 | wildcard_root: "*", | ||
| 806 | }, | 853 | }, |
| 807 | accessed: DisplayFmtAccess { yes: " ", no: "?", meh: "-" }, | 854 | accessed: DisplayFmtAccess { yes: " ", no: "?", meh: "-" }, |
| 808 | }; | 855 | }; |
| ... | @@ -816,15 +863,27 @@ impl<'tcx> Tree { | ... | @@ -816,15 +863,27 @@ impl<'tcx> Tree { |
| 816 | ) -> InterpResult<'tcx> { | 863 | ) -> InterpResult<'tcx> { |
| 817 | let mut indenter = DisplayIndent::new(); | 864 | let mut indenter = DisplayIndent::new(); |
| 818 | let ranges = self.locations.iter_all().map(|(range, _loc)| range).collect::<Vec<_>>(); | 865 | let ranges = self.locations.iter_all().map(|(range, _loc)| range).collect::<Vec<_>>(); |
| 819 | if let Some(repr) = DisplayRepr::from(self, show_unnamed) { | 866 | let main_tree = DisplayRepr::from(self, self.roots[0], show_unnamed); |
| 820 | repr.print( | 867 | let wildcard_subtrees = self.roots[1..] |
| 821 | &DEFAULT_FORMATTER, | 868 | .iter() |
| 822 | &mut indenter, | 869 | .filter_map(|root| DisplayRepr::from(self, *root, show_unnamed)) |
| 823 | protected_tags, | 870 | .collect::<Vec<_>>(); |
| 824 | ranges, | 871 | |
| 825 | /* print warning message about tags not shown */ !show_unnamed, | 872 | if main_tree.is_none() && wildcard_subtrees.is_empty() { |
| 873 | eprintln!( | ||
| 874 | "This allocation does not contain named tags. Use `miri_print_borrow_state(_, true)` to also print unnamed tags." | ||
| 826 | ); | 875 | ); |
| 827 | } | 876 | } |
| 877 | |||
| 878 | DisplayRepr::print( | ||
| 879 | &main_tree, | ||
| 880 | wildcard_subtrees.as_slice(), | ||
| 881 | &DEFAULT_FORMATTER, | ||
| 882 | &mut indenter, | ||
| 883 | protected_tags, | ||
| 884 | ranges, | ||
| 885 | /* print warning message about tags not shown */ !show_unnamed, | ||
| 886 | ); | ||
| 828 | interp_ok(()) | 887 | interp_ok(()) |
| 829 | } | 888 | } |
| 830 | } | 889 | } |
src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs+6-10| ... | @@ -13,6 +13,7 @@ pub mod diagnostics; | ... | @@ -13,6 +13,7 @@ pub mod diagnostics; |
| 13 | mod foreign_access_skipping; | 13 | mod foreign_access_skipping; |
| 14 | mod perms; | 14 | mod perms; |
| 15 | mod tree; | 15 | mod tree; |
| 16 | mod tree_visitor; | ||
| 16 | mod unimap; | 17 | mod unimap; |
| 17 | mod wildcard; | 18 | mod wildcard; |
| 18 | 19 | ||
| ... | @@ -239,18 +240,14 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -239,18 +240,14 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 239 | return interp_ok(new_prov); | 240 | return interp_ok(new_prov); |
| 240 | } | 241 | } |
| 241 | }; | 242 | }; |
| 243 | let new_prov = Provenance::Concrete { alloc_id, tag: new_tag }; | ||
| 242 | 244 | ||
| 243 | log_creation(this, Some((alloc_id, base_offset, parent_prov)))?; | 245 | log_creation(this, Some((alloc_id, base_offset, parent_prov)))?; |
| 244 | 246 | ||
| 245 | let orig_tag = match parent_prov { | ||
| 246 | ProvenanceExtra::Wildcard => return interp_ok(place.ptr().provenance), // TODO: handle retagging wildcard pointers | ||
| 247 | ProvenanceExtra::Concrete(tag) => tag, | ||
| 248 | }; | ||
| 249 | |||
| 250 | trace!( | 247 | trace!( |
| 251 | "reborrow: reference {:?} derived from {:?} (pointee {}): {:?}, size {}", | 248 | "reborrow: reference {:?} derived from {:?} (pointee {}): {:?}, size {}", |
| 252 | new_tag, | 249 | new_tag, |
| 253 | orig_tag, | 250 | parent_prov, |
| 254 | place.layout.ty, | 251 | place.layout.ty, |
| 255 | interpret::Pointer::new(alloc_id, base_offset), | 252 | interpret::Pointer::new(alloc_id, base_offset), |
| 256 | ptr_size.bytes() | 253 | ptr_size.bytes() |
| ... | @@ -281,7 +278,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -281,7 +278,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 281 | assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here | 278 | assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here |
| 282 | // There's not actually any bytes here where accesses could even be tracked. | 279 | // There's not actually any bytes here where accesses could even be tracked. |
| 283 | // Just produce the new provenance, nothing else to do. | 280 | // Just produce the new provenance, nothing else to do. |
| 284 | return interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })); | 281 | return interp_ok(Some(new_prov)); |
| 285 | } | 282 | } |
| 286 | 283 | ||
| 287 | let protected = new_perm.protector.is_some(); | 284 | let protected = new_perm.protector.is_some(); |
| ... | @@ -367,11 +364,10 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -367,11 +364,10 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 367 | } | 364 | } |
| 368 | } | 365 | } |
| 369 | } | 366 | } |
| 370 | |||
| 371 | // Record the parent-child pair in the tree. | 367 | // Record the parent-child pair in the tree. |
| 372 | tree_borrows.new_child( | 368 | tree_borrows.new_child( |
| 373 | base_offset, | 369 | base_offset, |
| 374 | orig_tag, | 370 | parent_prov, |
| 375 | new_tag, | 371 | new_tag, |
| 376 | inside_perms, | 372 | inside_perms, |
| 377 | new_perm.outside_perm, | 373 | new_perm.outside_perm, |
| ... | @@ -380,7 +376,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -380,7 +376,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 380 | )?; | 376 | )?; |
| 381 | drop(tree_borrows); | 377 | drop(tree_borrows); |
| 382 | 378 | ||
| 383 | interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })) | 379 | interp_ok(Some(new_prov)) |
| 384 | } | 380 | } |
| 385 | 381 | ||
| 386 | fn tb_retag_place( | 382 | fn tb_retag_place( |
src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs+278-436| ... | @@ -18,15 +18,15 @@ use rustc_data_structures::fx::FxHashSet; | ... | @@ -18,15 +18,15 @@ use rustc_data_structures::fx::FxHashSet; |
| 18 | use rustc_span::Span; | 18 | use rustc_span::Span; |
| 19 | use smallvec::SmallVec; | 19 | use smallvec::SmallVec; |
| 20 | 20 | ||
| 21 | use super::diagnostics::AccessCause; | 21 | use super::Permission; |
| 22 | use super::wildcard::WildcardState; | 22 | use super::diagnostics::{ |
| 23 | use crate::borrow_tracker::tree_borrows::Permission; | 23 | self, AccessCause, NodeDebugInfo, TbError, TransitionError, no_valid_exposed_references_error, |
| 24 | use crate::borrow_tracker::tree_borrows::diagnostics::{ | ||
| 25 | self, NodeDebugInfo, TbError, TransitionError, no_valid_exposed_references_error, | ||
| 26 | }; | 24 | }; |
| 27 | use crate::borrow_tracker::tree_borrows::foreign_access_skipping::IdempotentForeignAccess; | 25 | use super::foreign_access_skipping::IdempotentForeignAccess; |
| 28 | use crate::borrow_tracker::tree_borrows::perms::PermTransition; | 26 | use super::perms::PermTransition; |
| 29 | use crate::borrow_tracker::tree_borrows::unimap::{UniIndex, UniKeyMap, UniValMap}; | 27 | use super::tree_visitor::{ChildrenVisitMode, ContinueTraversal, NodeAppArgs, TreeVisitor}; |
| 28 | use super::unimap::{UniIndex, UniKeyMap, UniValMap}; | ||
| 29 | use super::wildcard::WildcardState; | ||
| 30 | use crate::borrow_tracker::{AccessKind, GlobalState, ProtectorKind}; | 30 | use crate::borrow_tracker::{AccessKind, GlobalState, ProtectorKind}; |
| 31 | use crate::*; | 31 | use crate::*; |
| 32 | 32 | ||
| ... | @@ -91,11 +91,11 @@ impl LocationState { | ... | @@ -91,11 +91,11 @@ impl LocationState { |
| 91 | nodes: &mut UniValMap<Node>, | 91 | nodes: &mut UniValMap<Node>, |
| 92 | wildcard_accesses: &mut UniValMap<WildcardState>, | 92 | wildcard_accesses: &mut UniValMap<WildcardState>, |
| 93 | access_kind: AccessKind, | 93 | access_kind: AccessKind, |
| 94 | access_cause: AccessCause, | 94 | access_cause: AccessCause, //diagnostics |
| 95 | access_range: Option<AllocRange>, | 95 | access_range: Option<AllocRange>, //diagnostics |
| 96 | relatedness: AccessRelatedness, | 96 | relatedness: AccessRelatedness, |
| 97 | span: Span, | 97 | span: Span, //diagnostics |
| 98 | location_range: Range<u64>, | 98 | location_range: Range<u64>, //diagnostics |
| 99 | protected: bool, | 99 | protected: bool, |
| 100 | ) -> Result<(), TransitionError> { | 100 | ) -> Result<(), TransitionError> { |
| 101 | // Call this function now (i.e. only if we know `relatedness`), which | 101 | // Call this function now (i.e. only if we know `relatedness`), which |
| ... | @@ -294,8 +294,22 @@ pub struct Tree { | ... | @@ -294,8 +294,22 @@ pub struct Tree { |
| 294 | pub(super) nodes: UniValMap<Node>, | 294 | pub(super) nodes: UniValMap<Node>, |
| 295 | /// Associates with each location its state and wildcard access tracking. | 295 | /// Associates with each location its state and wildcard access tracking. |
| 296 | pub(super) locations: DedupRangeMap<LocationTree>, | 296 | pub(super) locations: DedupRangeMap<LocationTree>, |
| 297 | /// The index of the root node. | 297 | /// Contains both the root of the main tree as well as the roots of the wildcard subtrees. |
| 298 | pub(super) root: UniIndex, | 298 | /// |
| 299 | /// If we reborrow a reference which has wildcard provenance, then we do not know where in | ||
| 300 | /// the tree to attach them. Instead we create a new additional tree for this allocation | ||
| 301 | /// with this new reference as a root. We call this additional tree a wildcard subtree. | ||
| 302 | /// | ||
| 303 | /// The actual structure should be a single tree but with wildcard provenance we approximate | ||
| 304 | /// this with this ordered set of trees. Each wildcard subtree is the direct child of *some* exposed | ||
| 305 | /// tag (that is smaller than the root), but we do not know which. This also means that it can only be the | ||
| 306 | /// child of a tree that comes before it in the vec ensuring we don't have any cycles in our | ||
| 307 | /// approximated tree. | ||
| 308 | /// | ||
| 309 | /// Sorted according to `BorTag` from low to high. This also means the main root is `root[0]`. | ||
| 310 | /// | ||
| 311 | /// Has array size 2 because that still ensures the minimum size for SmallVec. | ||
| 312 | pub(super) roots: SmallVec<[UniIndex; 2]>, | ||
| 299 | } | 313 | } |
| 300 | 314 | ||
| 301 | /// A node in the borrow tree. Each node is uniquely identified by a tag via | 315 | /// A node in the borrow tree. Each node is uniquely identified by a tag via |
| ... | @@ -325,262 +339,6 @@ pub(super) struct Node { | ... | @@ -325,262 +339,6 @@ pub(super) struct Node { |
| 325 | pub debug_info: NodeDebugInfo, | 339 | pub debug_info: NodeDebugInfo, |
| 326 | } | 340 | } |
| 327 | 341 | ||
| 328 | /// Data given to the transition function | ||
| 329 | struct NodeAppArgs<'visit> { | ||
| 330 | /// The index of the current node. | ||
| 331 | idx: UniIndex, | ||
| 332 | /// Relative position of the access. | ||
| 333 | rel_pos: AccessRelatedness, | ||
| 334 | /// The node map of this tree. | ||
| 335 | nodes: &'visit mut UniValMap<Node>, | ||
| 336 | /// The permissions map of this tree. | ||
| 337 | loc: &'visit mut LocationTree, | ||
| 338 | } | ||
| 339 | /// Internal contents of `Tree` with the minimum of mutable access for | ||
| 340 | /// For soundness do not modify the children or parent indexes of nodes | ||
| 341 | /// during traversal. | ||
| 342 | struct TreeVisitor<'tree> { | ||
| 343 | nodes: &'tree mut UniValMap<Node>, | ||
| 344 | loc: &'tree mut LocationTree, | ||
| 345 | } | ||
| 346 | |||
| 347 | /// Whether to continue exploring the children recursively or not. | ||
| 348 | enum ContinueTraversal { | ||
| 349 | Recurse, | ||
| 350 | SkipSelfAndChildren, | ||
| 351 | } | ||
| 352 | |||
| 353 | #[derive(Clone, Copy)] | ||
| 354 | pub enum ChildrenVisitMode { | ||
| 355 | VisitChildrenOfAccessed, | ||
| 356 | SkipChildrenOfAccessed, | ||
| 357 | } | ||
| 358 | |||
| 359 | enum RecursionState { | ||
| 360 | BeforeChildren, | ||
| 361 | AfterChildren, | ||
| 362 | } | ||
| 363 | |||
| 364 | /// Stack of nodes left to explore in a tree traversal. | ||
| 365 | /// See the docs of `traverse_this_parents_children_other` for details on the | ||
| 366 | /// traversal order. | ||
| 367 | struct TreeVisitorStack<NodeContinue, NodeApp> { | ||
| 368 | /// Function describing whether to continue at a tag. | ||
| 369 | /// This is only invoked for foreign accesses. | ||
| 370 | f_continue: NodeContinue, | ||
| 371 | /// Function to apply to each tag. | ||
| 372 | f_propagate: NodeApp, | ||
| 373 | /// Mutable state of the visit: the tags left to handle. | ||
| 374 | /// Every tag pushed should eventually be handled, | ||
| 375 | /// and the precise order is relevant for diagnostics. | ||
| 376 | /// Since the traversal is piecewise bottom-up, we need to | ||
| 377 | /// remember whether we're here initially, or after visiting all children. | ||
| 378 | /// The last element indicates this. | ||
| 379 | /// This is just an artifact of how you hand-roll recursion, | ||
| 380 | /// it does not have a deeper meaning otherwise. | ||
| 381 | stack: Vec<(UniIndex, AccessRelatedness, RecursionState)>, | ||
| 382 | } | ||
| 383 | |||
| 384 | impl<NodeContinue, NodeApp, Err> TreeVisitorStack<NodeContinue, NodeApp> | ||
| 385 | where | ||
| 386 | NodeContinue: Fn(&NodeAppArgs<'_>) -> ContinueTraversal, | ||
| 387 | NodeApp: Fn(NodeAppArgs<'_>) -> Result<(), Err>, | ||
| 388 | { | ||
| 389 | fn should_continue_at( | ||
| 390 | &self, | ||
| 391 | this: &mut TreeVisitor<'_>, | ||
| 392 | idx: UniIndex, | ||
| 393 | rel_pos: AccessRelatedness, | ||
| 394 | ) -> ContinueTraversal { | ||
| 395 | let args = NodeAppArgs { idx, rel_pos, nodes: this.nodes, loc: this.loc }; | ||
| 396 | (self.f_continue)(&args) | ||
| 397 | } | ||
| 398 | |||
| 399 | fn propagate_at( | ||
| 400 | &mut self, | ||
| 401 | this: &mut TreeVisitor<'_>, | ||
| 402 | idx: UniIndex, | ||
| 403 | rel_pos: AccessRelatedness, | ||
| 404 | ) -> Result<(), Err> { | ||
| 405 | (self.f_propagate)(NodeAppArgs { idx, rel_pos, nodes: this.nodes, loc: this.loc }) | ||
| 406 | } | ||
| 407 | |||
| 408 | fn go_upwards_from_accessed( | ||
| 409 | &mut self, | ||
| 410 | this: &mut TreeVisitor<'_>, | ||
| 411 | accessed_node: UniIndex, | ||
| 412 | visit_children: ChildrenVisitMode, | ||
| 413 | ) -> Result<(), Err> { | ||
| 414 | // We want to visit the accessed node's children first. | ||
| 415 | // However, we will below walk up our parents and push their children (our cousins) | ||
| 416 | // onto the stack. To ensure correct iteration order, this method thus finishes | ||
| 417 | // by reversing the stack. This only works if the stack is empty initially. | ||
| 418 | assert!(self.stack.is_empty()); | ||
| 419 | // First, handle accessed node. A bunch of things need to | ||
| 420 | // be handled differently here compared to the further parents | ||
| 421 | // of `accesssed_node`. | ||
| 422 | { | ||
| 423 | self.propagate_at(this, accessed_node, AccessRelatedness::LocalAccess)?; | ||
| 424 | if matches!(visit_children, ChildrenVisitMode::VisitChildrenOfAccessed) { | ||
| 425 | let accessed_node = this.nodes.get(accessed_node).unwrap(); | ||
| 426 | // We `rev()` here because we reverse the entire stack later. | ||
| 427 | for &child in accessed_node.children.iter().rev() { | ||
| 428 | self.stack.push(( | ||
| 429 | child, | ||
| 430 | AccessRelatedness::ForeignAccess, | ||
| 431 | RecursionState::BeforeChildren, | ||
| 432 | )); | ||
| 433 | } | ||
| 434 | } | ||
| 435 | } | ||
| 436 | // Then, handle the accessed node's parents. Here, we need to | ||
| 437 | // make sure we only mark the "cousin" subtrees for later visitation, | ||
| 438 | // not the subtree that contains the accessed node. | ||
| 439 | let mut last_node = accessed_node; | ||
| 440 | while let Some(current) = this.nodes.get(last_node).unwrap().parent { | ||
| 441 | self.propagate_at(this, current, AccessRelatedness::LocalAccess)?; | ||
| 442 | let node = this.nodes.get(current).unwrap(); | ||
| 443 | // We `rev()` here because we reverse the entire stack later. | ||
| 444 | for &child in node.children.iter().rev() { | ||
| 445 | if last_node == child { | ||
| 446 | continue; | ||
| 447 | } | ||
| 448 | self.stack.push(( | ||
| 449 | child, | ||
| 450 | AccessRelatedness::ForeignAccess, | ||
| 451 | RecursionState::BeforeChildren, | ||
| 452 | )); | ||
| 453 | } | ||
| 454 | last_node = current; | ||
| 455 | } | ||
| 456 | // Reverse the stack, as discussed above. | ||
| 457 | self.stack.reverse(); | ||
| 458 | Ok(()) | ||
| 459 | } | ||
| 460 | |||
| 461 | fn finish_foreign_accesses(&mut self, this: &mut TreeVisitor<'_>) -> Result<(), Err> { | ||
| 462 | while let Some((idx, rel_pos, step)) = self.stack.last_mut() { | ||
| 463 | let idx = *idx; | ||
| 464 | let rel_pos = *rel_pos; | ||
| 465 | match *step { | ||
| 466 | // How to do bottom-up traversal, 101: Before you handle a node, you handle all children. | ||
| 467 | // For this, you must first find the children, which is what this code here does. | ||
| 468 | RecursionState::BeforeChildren => { | ||
| 469 | // Next time we come back will be when all the children are handled. | ||
| 470 | *step = RecursionState::AfterChildren; | ||
| 471 | // Now push the children, except if we are told to skip this subtree. | ||
| 472 | let handle_children = self.should_continue_at(this, idx, rel_pos); | ||
| 473 | match handle_children { | ||
| 474 | ContinueTraversal::Recurse => { | ||
| 475 | let node = this.nodes.get(idx).unwrap(); | ||
| 476 | for &child in node.children.iter() { | ||
| 477 | self.stack.push((child, rel_pos, RecursionState::BeforeChildren)); | ||
| 478 | } | ||
| 479 | } | ||
| 480 | ContinueTraversal::SkipSelfAndChildren => { | ||
| 481 | // skip self | ||
| 482 | self.stack.pop(); | ||
| 483 | continue; | ||
| 484 | } | ||
| 485 | } | ||
| 486 | } | ||
| 487 | // All the children are handled, let's actually visit this node | ||
| 488 | RecursionState::AfterChildren => { | ||
| 489 | self.stack.pop(); | ||
| 490 | self.propagate_at(this, idx, rel_pos)?; | ||
| 491 | } | ||
| 492 | } | ||
| 493 | } | ||
| 494 | Ok(()) | ||
| 495 | } | ||
| 496 | |||
| 497 | fn new(f_continue: NodeContinue, f_propagate: NodeApp) -> Self { | ||
| 498 | Self { f_continue, f_propagate, stack: Vec::new() } | ||
| 499 | } | ||
| 500 | } | ||
| 501 | |||
| 502 | impl<'tree> TreeVisitor<'tree> { | ||
| 503 | /// Applies `f_propagate` to every vertex of the tree in a piecewise bottom-up way: First, visit | ||
| 504 | /// all ancestors of `start_idx` (starting with `start_idx` itself), then children of `start_idx`, then the rest, | ||
| 505 | /// going bottom-up in each of these two "pieces" / sections. | ||
| 506 | /// This ensures that errors are triggered in the following order | ||
| 507 | /// - first invalid accesses with insufficient permissions, closest to the accessed node first, | ||
| 508 | /// - then protector violations, bottom-up, starting with the children of the accessed node, and then | ||
| 509 | /// going upwards and outwards. | ||
| 510 | /// | ||
| 511 | /// The following graphic visualizes it, with numbers indicating visitation order and `start_idx` being | ||
| 512 | /// the node that is visited first ("1"): | ||
| 513 | /// | ||
| 514 | /// ```text | ||
| 515 | /// 3 | ||
| 516 | /// /| | ||
| 517 | /// / | | ||
| 518 | /// 9 2 | ||
| 519 | /// | |\ | ||
| 520 | /// | | \ | ||
| 521 | /// 8 1 7 | ||
| 522 | /// / \ | ||
| 523 | /// 4 6 | ||
| 524 | /// | | ||
| 525 | /// 5 | ||
| 526 | /// ``` | ||
| 527 | /// | ||
| 528 | /// `f_propagate` should follow the following format: for a given `Node` it updates its | ||
| 529 | /// `Permission` depending on the position relative to `start_idx` (given by an | ||
| 530 | /// `AccessRelatedness`). | ||
| 531 | /// `f_continue` is called earlier on foreign nodes, and describes whether to even start | ||
| 532 | /// visiting the subtree at that node. If it e.g. returns `SkipSelfAndChildren` on node 6 | ||
| 533 | /// above, then nodes 5 _and_ 6 would not be visited by `f_propagate`. It is not used for | ||
| 534 | /// notes having a child access (nodes 1, 2, 3). | ||
| 535 | /// | ||
| 536 | /// Finally, remember that the iteration order is not relevant for UB, it only affects | ||
| 537 | /// diagnostics. It also affects tree traversal optimizations built on top of this, so | ||
| 538 | /// those need to be reviewed carefully as well whenever this changes. | ||
| 539 | fn traverse_this_parents_children_other<Err>( | ||
| 540 | mut self, | ||
| 541 | start_idx: UniIndex, | ||
| 542 | f_continue: impl Fn(&NodeAppArgs<'_>) -> ContinueTraversal, | ||
| 543 | f_propagate: impl Fn(NodeAppArgs<'_>) -> Result<(), Err>, | ||
| 544 | ) -> Result<(), Err> { | ||
| 545 | let mut stack = TreeVisitorStack::new(f_continue, f_propagate); | ||
| 546 | // Visits the accessed node itself, and all its parents, i.e. all nodes | ||
| 547 | // undergoing a child access. Also pushes the children and the other | ||
| 548 | // cousin nodes (i.e. all nodes undergoing a foreign access) to the stack | ||
| 549 | // to be processed later. | ||
| 550 | stack.go_upwards_from_accessed( | ||
| 551 | &mut self, | ||
| 552 | start_idx, | ||
| 553 | ChildrenVisitMode::VisitChildrenOfAccessed, | ||
| 554 | )?; | ||
| 555 | // Now visit all the foreign nodes we remembered earlier. | ||
| 556 | // For this we go bottom-up, but also allow f_continue to skip entire | ||
| 557 | // subtrees from being visited if it would be a NOP. | ||
| 558 | stack.finish_foreign_accesses(&mut self) | ||
| 559 | } | ||
| 560 | |||
| 561 | /// Like `traverse_this_parents_children_other`, but skips the children of `start_idx`. | ||
| 562 | fn traverse_nonchildren<Err>( | ||
| 563 | mut self, | ||
| 564 | start_idx: UniIndex, | ||
| 565 | f_continue: impl Fn(&NodeAppArgs<'_>) -> ContinueTraversal, | ||
| 566 | f_propagate: impl Fn(NodeAppArgs<'_>) -> Result<(), Err>, | ||
| 567 | ) -> Result<(), Err> { | ||
| 568 | let mut stack = TreeVisitorStack::new(f_continue, f_propagate); | ||
| 569 | // Visits the accessed node itself, and all its parents, i.e. all nodes | ||
| 570 | // undergoing a child access. Also pushes the other cousin nodes to the | ||
| 571 | // stack, but not the children of the accessed node. | ||
| 572 | stack.go_upwards_from_accessed( | ||
| 573 | &mut self, | ||
| 574 | start_idx, | ||
| 575 | ChildrenVisitMode::SkipChildrenOfAccessed, | ||
| 576 | )?; | ||
| 577 | // Now visit all the foreign nodes we remembered earlier. | ||
| 578 | // For this we go bottom-up, but also allow f_continue to skip entire | ||
| 579 | // subtrees from being visited if it would be a NOP. | ||
| 580 | stack.finish_foreign_accesses(&mut self) | ||
| 581 | } | ||
| 582 | } | ||
| 583 | |||
| 584 | impl Tree { | 342 | impl Tree { |
| 585 | /// Create a new tree, with only a root pointer. | 343 | /// Create a new tree, with only a root pointer. |
| 586 | pub fn new(root_tag: BorTag, size: Size, span: Span) -> Self { | 344 | pub fn new(root_tag: BorTag, size: Size, span: Span) -> Self { |
| ... | @@ -625,7 +383,7 @@ impl Tree { | ... | @@ -625,7 +383,7 @@ impl Tree { |
| 625 | let wildcard_accesses = UniValMap::default(); | 383 | let wildcard_accesses = UniValMap::default(); |
| 626 | DedupRangeMap::new(size, LocationTree { perms, wildcard_accesses }) | 384 | DedupRangeMap::new(size, LocationTree { perms, wildcard_accesses }) |
| 627 | }; | 385 | }; |
| 628 | Self { root: root_idx, nodes, locations, tag_mapping } | 386 | Self { roots: SmallVec::from_slice(&[root_idx]), nodes, locations, tag_mapping } |
| 629 | } | 387 | } |
| 630 | } | 388 | } |
| 631 | 389 | ||
| ... | @@ -639,7 +397,7 @@ impl<'tcx> Tree { | ... | @@ -639,7 +397,7 @@ impl<'tcx> Tree { |
| 639 | pub(super) fn new_child( | 397 | pub(super) fn new_child( |
| 640 | &mut self, | 398 | &mut self, |
| 641 | base_offset: Size, | 399 | base_offset: Size, |
| 642 | parent_tag: BorTag, | 400 | parent_prov: ProvenanceExtra, |
| 643 | new_tag: BorTag, | 401 | new_tag: BorTag, |
| 644 | inside_perms: DedupRangeMap<LocationState>, | 402 | inside_perms: DedupRangeMap<LocationState>, |
| 645 | outside_perm: Permission, | 403 | outside_perm: Permission, |
| ... | @@ -647,7 +405,11 @@ impl<'tcx> Tree { | ... | @@ -647,7 +405,11 @@ impl<'tcx> Tree { |
| 647 | span: Span, | 405 | span: Span, |
| 648 | ) -> InterpResult<'tcx> { | 406 | ) -> InterpResult<'tcx> { |
| 649 | let idx = self.tag_mapping.insert(new_tag); | 407 | let idx = self.tag_mapping.insert(new_tag); |
| 650 | let parent_idx = self.tag_mapping.get(&parent_tag).unwrap(); | 408 | let parent_idx = match parent_prov { |
| 409 | ProvenanceExtra::Concrete(parent_tag) => | ||
| 410 | Some(self.tag_mapping.get(&parent_tag).unwrap()), | ||
| 411 | ProvenanceExtra::Wildcard => None, | ||
| 412 | }; | ||
| 651 | assert!(outside_perm.is_initial()); | 413 | assert!(outside_perm.is_initial()); |
| 652 | 414 | ||
| 653 | let default_strongest_idempotent = | 415 | let default_strongest_idempotent = |
| ... | @@ -657,7 +419,7 @@ impl<'tcx> Tree { | ... | @@ -657,7 +419,7 @@ impl<'tcx> Tree { |
| 657 | idx, | 419 | idx, |
| 658 | Node { | 420 | Node { |
| 659 | tag: new_tag, | 421 | tag: new_tag, |
| 660 | parent: Some(parent_idx), | 422 | parent: parent_idx, |
| 661 | children: SmallVec::default(), | 423 | children: SmallVec::default(), |
| 662 | default_initial_perm: outside_perm, | 424 | default_initial_perm: outside_perm, |
| 663 | default_initial_idempotent_foreign_access: default_strongest_idempotent, | 425 | default_initial_idempotent_foreign_access: default_strongest_idempotent, |
| ... | @@ -665,9 +427,17 @@ impl<'tcx> Tree { | ... | @@ -665,9 +427,17 @@ impl<'tcx> Tree { |
| 665 | debug_info: NodeDebugInfo::new(new_tag, outside_perm, span), | 427 | debug_info: NodeDebugInfo::new(new_tag, outside_perm, span), |
| 666 | }, | 428 | }, |
| 667 | ); | 429 | ); |
| 668 | let parent_node = self.nodes.get_mut(parent_idx).unwrap(); | 430 | if let Some(parent_idx) = parent_idx { |
| 669 | // Register new_tag as a child of parent_tag | 431 | let parent_node = self.nodes.get_mut(parent_idx).unwrap(); |
| 670 | parent_node.children.push(idx); | 432 | // Register new_tag as a child of parent_tag |
| 433 | parent_node.children.push(idx); | ||
| 434 | } else { | ||
| 435 | // If the parent had wildcard provenance, then register the idx | ||
| 436 | // as a new wildcard root. | ||
| 437 | // This preserves the orderedness of `roots` because a newly created | ||
| 438 | // tag is greater than all previous tags. | ||
| 439 | self.roots.push(idx); | ||
| 440 | } | ||
| 671 | 441 | ||
| 672 | // We need to know the weakest SIFA for `update_idempotent_foreign_access_after_retag`. | 442 | // We need to know the weakest SIFA for `update_idempotent_foreign_access_after_retag`. |
| 673 | let mut min_sifa = default_strongest_idempotent; | 443 | let mut min_sifa = default_strongest_idempotent; |
| ... | @@ -691,19 +461,27 @@ impl<'tcx> Tree { | ... | @@ -691,19 +461,27 @@ impl<'tcx> Tree { |
| 691 | 461 | ||
| 692 | // We need to ensure the consistency of the wildcard access tracking data structure. | 462 | // We need to ensure the consistency of the wildcard access tracking data structure. |
| 693 | // For this, we insert the correct entry for this tag based on its parent, if it exists. | 463 | // For this, we insert the correct entry for this tag based on its parent, if it exists. |
| 464 | // If we are inserting a new wildcard root (with Wildcard as parent_prov) then we insert | ||
| 465 | // the special wildcard root initial state instead. | ||
| 694 | for (_range, loc) in self.locations.iter_mut_all() { | 466 | for (_range, loc) in self.locations.iter_mut_all() { |
| 695 | if let Some(parent_access) = loc.wildcard_accesses.get(parent_idx) { | 467 | if let Some(parent_idx) = parent_idx { |
| 696 | loc.wildcard_accesses.insert(idx, parent_access.for_new_child()); | 468 | if let Some(parent_access) = loc.wildcard_accesses.get(parent_idx) { |
| 469 | loc.wildcard_accesses.insert(idx, parent_access.for_new_child()); | ||
| 470 | } | ||
| 471 | } else { | ||
| 472 | loc.wildcard_accesses.insert(idx, WildcardState::for_wildcard_root()); | ||
| 697 | } | 473 | } |
| 698 | } | 474 | } |
| 699 | 475 | // If the parent is a wildcard pointer, then it doesn't track SIFA and doesn't need to be updated. | |
| 700 | // Inserting the new perms might have broken the SIFA invariant (see | 476 | if let Some(parent_idx) = parent_idx { |
| 701 | // `foreign_access_skipping.rs`) if the SIFA we inserted is weaker than that of some parent. | 477 | // Inserting the new perms might have broken the SIFA invariant (see |
| 702 | // We now weaken the recorded SIFA for our parents, until the invariant is restored. We | 478 | // `foreign_access_skipping.rs`) if the SIFA we inserted is weaker than that of some parent. |
| 703 | // could weaken them all to `None`, but it is more efficient to compute the SIFA for the new | 479 | // We now weaken the recorded SIFA for our parents, until the invariant is restored. We |
| 704 | // permission statically, and use that. For this we need the *minimum* SIFA (`None` needs | 480 | // could weaken them all to `None`, but it is more efficient to compute the SIFA for the new |
| 705 | // more fixup than `Write`). | 481 | // permission statically, and use that. For this we need the *minimum* SIFA (`None` needs |
| 706 | self.update_idempotent_foreign_access_after_retag(parent_idx, min_sifa); | 482 | // more fixup than `Write`). |
| 483 | self.update_idempotent_foreign_access_after_retag(parent_idx, min_sifa); | ||
| 484 | } | ||
| 707 | 485 | ||
| 708 | interp_ok(()) | 486 | interp_ok(()) |
| 709 | } | 487 | } |
| ... | @@ -772,52 +550,68 @@ impl<'tcx> Tree { | ... | @@ -772,52 +550,68 @@ impl<'tcx> Tree { |
| 772 | span, | 550 | span, |
| 773 | )?; | 551 | )?; |
| 774 | 552 | ||
| 775 | // The order in which we check if any nodes are invalidated only | ||
| 776 | // matters to diagnostics, so we use the root as a default tag. | ||
| 777 | let start_idx = match prov { | 553 | let start_idx = match prov { |
| 778 | ProvenanceExtra::Concrete(tag) => self.tag_mapping.get(&tag).unwrap(), | 554 | ProvenanceExtra::Concrete(tag) => Some(self.tag_mapping.get(&tag).unwrap()), |
| 779 | ProvenanceExtra::Wildcard => self.root, | 555 | ProvenanceExtra::Wildcard => None, |
| 780 | }; | 556 | }; |
| 781 | 557 | ||
| 782 | // Check if this breaks any strong protector. | 558 | // Check if this breaks any strong protector. |
| 783 | // (Weak protectors are already handled by `perform_access`.) | 559 | // (Weak protectors are already handled by `perform_access`.) |
| 784 | for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) { | 560 | for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) { |
| 785 | TreeVisitor { nodes: &mut self.nodes, loc }.traverse_this_parents_children_other( | 561 | // Checks the tree containing `idx` for strong protector violations. |
| 786 | start_idx, | 562 | // It does this in traversal order. |
| 787 | // Visit all children, skipping none. | 563 | let mut check_tree = |idx| { |
| 788 | |_| ContinueTraversal::Recurse, | 564 | TreeVisitor { nodes: &mut self.nodes, data: loc } |
| 789 | |args: NodeAppArgs<'_>| { | 565 | .traverse_this_parents_children_other( |
| 790 | let node = args.nodes.get(args.idx).unwrap(); | 566 | idx, |
| 791 | let perm = args.loc.perms.entry(args.idx); | 567 | // Visit all children, skipping none. |
| 792 | 568 | |_| ContinueTraversal::Recurse, | |
| 793 | let perm = perm.get().copied().unwrap_or_else(|| node.default_location_state()); | 569 | |args: NodeAppArgs<'_, _>| { |
| 794 | if global.borrow().protected_tags.get(&node.tag) | 570 | let node = args.nodes.get(args.idx).unwrap(); |
| 795 | == Some(&ProtectorKind::StrongProtector) | 571 | |
| 796 | // Don't check for protector if it is a Cell (see `unsafe_cell_deallocate` in `interior_mutability.rs`). | 572 | let perm = args |
| 797 | // Related to https://github.com/rust-lang/rust/issues/55005. | 573 | .data |
| 798 | && !perm.permission.is_cell() | 574 | .perms |
| 799 | // Only trigger UB if the accessed bit is set, i.e. if the protector is actually protecting this offset. See #4579. | 575 | .get(args.idx) |
| 800 | && perm.accessed | 576 | .copied() |
| 801 | { | 577 | .unwrap_or_else(|| node.default_location_state()); |
| 802 | Err(TbError { | 578 | if global.borrow().protected_tags.get(&node.tag) |
| 803 | conflicting_info: &node.debug_info, | 579 | == Some(&ProtectorKind::StrongProtector) |
| 804 | access_cause: diagnostics::AccessCause::Dealloc, | 580 | // Don't check for protector if it is a Cell (see `unsafe_cell_deallocate` in `interior_mutability.rs`). |
| 805 | alloc_id, | 581 | // Related to https://github.com/rust-lang/rust/issues/55005. |
| 806 | error_offset: loc_range.start, | 582 | && !perm.permission.is_cell() |
| 807 | error_kind: TransitionError::ProtectedDealloc, | 583 | // Only trigger UB if the accessed bit is set, i.e. if the protector is actually protecting this offset. See #4579. |
| 808 | accessed_info: match prov { | 584 | && perm.accessed |
| 809 | ProvenanceExtra::Concrete(_) => | 585 | { |
| 810 | Some(&args.nodes.get(start_idx).unwrap().debug_info), | 586 | Err(TbError { |
| 811 | // We don't know from where the access came during a wildcard access. | 587 | conflicting_info: &node.debug_info, |
| 812 | ProvenanceExtra::Wildcard => None, | 588 | access_cause: diagnostics::AccessCause::Dealloc, |
| 813 | }, | 589 | alloc_id, |
| 814 | } | 590 | error_offset: loc_range.start, |
| 815 | .build()) | 591 | error_kind: TransitionError::ProtectedDealloc, |
| 816 | } else { | 592 | accessed_info: start_idx |
| 817 | Ok(()) | 593 | .map(|idx| &args.nodes.get(idx).unwrap().debug_info), |
| 818 | } | 594 | } |
| 819 | }, | 595 | .build()) |
| 820 | )?; | 596 | } else { |
| 597 | Ok(()) | ||
| 598 | } | ||
| 599 | }, | ||
| 600 | ) | ||
| 601 | }; | ||
| 602 | // If we have a start index we first check its subtree in traversal order. | ||
| 603 | // This results in us showing the error of the closest node instead of an | ||
| 604 | // arbitrary one. | ||
| 605 | let accessed_root = start_idx.map(&mut check_tree).transpose()?; | ||
| 606 | // Afterwards we check all other trees. | ||
| 607 | // We iterate over the list in reverse order to ensure that we do not visit | ||
| 608 | // a parent before its child. | ||
| 609 | for &root in self.roots.iter().rev() { | ||
| 610 | if Some(root) == accessed_root { | ||
| 611 | continue; | ||
| 612 | } | ||
| 613 | check_tree(root)?; | ||
| 614 | } | ||
| 821 | } | 615 | } |
| 822 | interp_ok(()) | 616 | interp_ok(()) |
| 823 | } | 617 | } |
| ... | @@ -849,20 +643,20 @@ impl<'tcx> Tree { | ... | @@ -849,20 +643,20 @@ impl<'tcx> Tree { |
| 849 | span: Span, // diagnostics | 643 | span: Span, // diagnostics |
| 850 | ) -> InterpResult<'tcx> { | 644 | ) -> InterpResult<'tcx> { |
| 851 | #[cfg(feature = "expensive-consistency-checks")] | 645 | #[cfg(feature = "expensive-consistency-checks")] |
| 852 | if matches!(prov, ProvenanceExtra::Wildcard) { | 646 | if self.roots.len() > 1 || matches!(prov, ProvenanceExtra::Wildcard) { |
| 853 | self.verify_wildcard_consistency(global); | 647 | self.verify_wildcard_consistency(global); |
| 854 | } | 648 | } |
| 649 | |||
| 855 | let source_idx = match prov { | 650 | let source_idx = match prov { |
| 856 | ProvenanceExtra::Concrete(tag) => Some(self.tag_mapping.get(&tag).unwrap()), | 651 | ProvenanceExtra::Concrete(tag) => Some(self.tag_mapping.get(&tag).unwrap()), |
| 857 | ProvenanceExtra::Wildcard => None, | 652 | ProvenanceExtra::Wildcard => None, |
| 858 | }; | 653 | }; |
| 859 | |||
| 860 | if let Some((access_range, access_kind, access_cause)) = access_range_and_kind { | 654 | if let Some((access_range, access_kind, access_cause)) = access_range_and_kind { |
| 861 | // Default branch: this is a "normal" access through a known range. | 655 | // Default branch: this is a "normal" access through a known range. |
| 862 | // We iterate over affected locations and traverse the tree for each of them. | 656 | // We iterate over affected locations and traverse the tree for each of them. |
| 863 | for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) { | 657 | for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) { |
| 864 | loc.perform_access( | 658 | loc.perform_access( |
| 865 | self.root, | 659 | self.roots.iter().copied(), |
| 866 | &mut self.nodes, | 660 | &mut self.nodes, |
| 867 | source_idx, | 661 | source_idx, |
| 868 | loc_range, | 662 | loc_range, |
| ... | @@ -898,7 +692,7 @@ impl<'tcx> Tree { | ... | @@ -898,7 +692,7 @@ impl<'tcx> Tree { |
| 898 | { | 692 | { |
| 899 | let access_cause = diagnostics::AccessCause::FnExit(access_kind); | 693 | let access_cause = diagnostics::AccessCause::FnExit(access_kind); |
| 900 | loc.perform_access( | 694 | loc.perform_access( |
| 901 | self.root, | 695 | self.roots.iter().copied(), |
| 902 | &mut self.nodes, | 696 | &mut self.nodes, |
| 903 | Some(source_idx), | 697 | Some(source_idx), |
| 904 | loc_range, | 698 | loc_range, |
| ... | @@ -920,7 +714,9 @@ impl<'tcx> Tree { | ... | @@ -920,7 +714,9 @@ impl<'tcx> Tree { |
| 920 | /// Integration with the BorTag garbage collector | 714 | /// Integration with the BorTag garbage collector |
| 921 | impl Tree { | 715 | impl Tree { |
| 922 | pub fn remove_unreachable_tags(&mut self, live_tags: &FxHashSet<BorTag>) { | 716 | pub fn remove_unreachable_tags(&mut self, live_tags: &FxHashSet<BorTag>) { |
| 923 | self.remove_useless_children(self.root, live_tags); | 717 | for i in 0..(self.roots.len()) { |
| 718 | self.remove_useless_children(self.roots[i], live_tags); | ||
| 719 | } | ||
| 924 | // Right after the GC runs is a good moment to check if we can | 720 | // Right after the GC runs is a good moment to check if we can |
| 925 | // merge some adjacent ranges that were made equal by the removal of some | 721 | // merge some adjacent ranges that were made equal by the removal of some |
| 926 | // tags (this does not necessarily mean that they have identical internal representations, | 722 | // tags (this does not necessarily mean that they have identical internal representations, |
| ... | @@ -1073,20 +869,20 @@ impl<'tcx> LocationTree { | ... | @@ -1073,20 +869,20 @@ impl<'tcx> LocationTree { |
| 1073 | /// * `visit_children`: Whether to skip updating the children of `access_source`. | 869 | /// * `visit_children`: Whether to skip updating the children of `access_source`. |
| 1074 | fn perform_access( | 870 | fn perform_access( |
| 1075 | &mut self, | 871 | &mut self, |
| 1076 | root: UniIndex, | 872 | roots: impl Iterator<Item = UniIndex>, |
| 1077 | nodes: &mut UniValMap<Node>, | 873 | nodes: &mut UniValMap<Node>, |
| 1078 | access_source: Option<UniIndex>, | 874 | access_source: Option<UniIndex>, |
| 1079 | loc_range: Range<u64>, | 875 | loc_range: Range<u64>, // diagnostics |
| 1080 | access_range: Option<AllocRange>, | 876 | access_range: Option<AllocRange>, // diagnostics |
| 1081 | access_kind: AccessKind, | 877 | access_kind: AccessKind, |
| 1082 | access_cause: diagnostics::AccessCause, | 878 | access_cause: diagnostics::AccessCause, // diagnostics |
| 1083 | global: &GlobalState, | 879 | global: &GlobalState, |
| 1084 | alloc_id: AllocId, // diagnostics | 880 | alloc_id: AllocId, // diagnostics |
| 1085 | span: Span, // diagnostics | 881 | span: Span, // diagnostics |
| 1086 | visit_children: ChildrenVisitMode, | 882 | visit_children: ChildrenVisitMode, |
| 1087 | ) -> InterpResult<'tcx> { | 883 | ) -> InterpResult<'tcx> { |
| 1088 | if let Some(idx) = access_source { | 884 | let accessed_root = if let Some(idx) = access_source { |
| 1089 | self.perform_normal_access( | 885 | Some(self.perform_normal_access( |
| 1090 | idx, | 886 | idx, |
| 1091 | nodes, | 887 | nodes, |
| 1092 | loc_range.clone(), | 888 | loc_range.clone(), |
| ... | @@ -1097,13 +893,38 @@ impl<'tcx> LocationTree { | ... | @@ -1097,13 +893,38 @@ impl<'tcx> LocationTree { |
| 1097 | alloc_id, | 893 | alloc_id, |
| 1098 | span, | 894 | span, |
| 1099 | visit_children, | 895 | visit_children, |
| 1100 | ) | 896 | )?) |
| 1101 | } else { | 897 | } else { |
| 1102 | // `SkipChildrenOfAccessed` only gets set on protector release. | 898 | // `SkipChildrenOfAccessed` only gets set on protector release, which only |
| 1103 | // Since a wildcard reference are never protected this assert shouldn't fail. | 899 | // occurs on a known node. |
| 1104 | assert!(matches!(visit_children, ChildrenVisitMode::VisitChildrenOfAccessed)); | 900 | assert!(matches!(visit_children, ChildrenVisitMode::VisitChildrenOfAccessed)); |
| 901 | None | ||
| 902 | }; | ||
| 903 | |||
| 904 | let accessed_root_tag = accessed_root.map(|idx| nodes.get(idx).unwrap().tag); | ||
| 905 | if matches!(visit_children, ChildrenVisitMode::SkipChildrenOfAccessed) { | ||
| 906 | // FIXME: approximate which roots could be children of the accessed node and only skip them instead of all other trees. | ||
| 907 | return interp_ok(()); | ||
| 908 | } | ||
| 909 | for root in roots { | ||
| 910 | // We don't perform a wildcard access on the tree we already performed a | ||
| 911 | // normal access on. | ||
| 912 | if Some(root) == accessed_root { | ||
| 913 | continue; | ||
| 914 | } | ||
| 915 | // The choice of `max_local_tag` requires some thought. | ||
| 916 | // This can only be a local access for nodes that are a parent of the accessed node | ||
| 917 | // and are therefore smaller, so the accessed node itself is a valid choice for `max_local_tag`. | ||
| 918 | // However, using `accessed_root` is better since that will be smaller. It is still a valid choice | ||
| 919 | // because for nodes *in other trees*, if they are a parent of the accessed node then they | ||
| 920 | // are a parent of `accessed_root`. | ||
| 921 | // | ||
| 922 | // As a consequence of this, since the root of the main tree is the smallest tag in the entire | ||
| 923 | // allocation, if the access occurred in the main tree then other subtrees will only see foreign accesses. | ||
| 1105 | self.perform_wildcard_access( | 924 | self.perform_wildcard_access( |
| 1106 | root, | 925 | root, |
| 926 | access_source, | ||
| 927 | /*max_local_tag*/ accessed_root_tag, | ||
| 1107 | nodes, | 928 | nodes, |
| 1108 | loc_range.clone(), | 929 | loc_range.clone(), |
| 1109 | access_range, | 930 | access_range, |
| ... | @@ -1112,11 +933,14 @@ impl<'tcx> LocationTree { | ... | @@ -1112,11 +933,14 @@ impl<'tcx> LocationTree { |
| 1112 | global, | 933 | global, |
| 1113 | alloc_id, | 934 | alloc_id, |
| 1114 | span, | 935 | span, |
| 1115 | ) | 936 | )?; |
| 1116 | } | 937 | } |
| 938 | interp_ok(()) | ||
| 1117 | } | 939 | } |
| 1118 | 940 | ||
| 1119 | /// Performs a normal access on the tree containing `access_source`. | 941 | /// Performs a normal access on the tree containing `access_source`. |
| 942 | /// | ||
| 943 | /// Returns the root index of this tree. | ||
| 1120 | /// * `access_source`: The index of the tag being accessed. | 944 | /// * `access_source`: The index of the tag being accessed. |
| 1121 | /// * `visit_children`: Whether to skip the children of `access_source` | 945 | /// * `visit_children`: Whether to skip the children of `access_source` |
| 1122 | /// during the access. Used for protector end access. | 946 | /// during the access. Used for protector end access. |
| ... | @@ -1124,15 +948,15 @@ impl<'tcx> LocationTree { | ... | @@ -1124,15 +948,15 @@ impl<'tcx> LocationTree { |
| 1124 | &mut self, | 948 | &mut self, |
| 1125 | access_source: UniIndex, | 949 | access_source: UniIndex, |
| 1126 | nodes: &mut UniValMap<Node>, | 950 | nodes: &mut UniValMap<Node>, |
| 1127 | loc_range: Range<u64>, | 951 | loc_range: Range<u64>, // diagnostics |
| 1128 | access_range: Option<AllocRange>, | 952 | access_range: Option<AllocRange>, // diagnostics |
| 1129 | access_kind: AccessKind, | 953 | access_kind: AccessKind, |
| 1130 | access_cause: diagnostics::AccessCause, | 954 | access_cause: diagnostics::AccessCause, // diagnostics |
| 1131 | global: &GlobalState, | 955 | global: &GlobalState, |
| 1132 | alloc_id: AllocId, // diagnostics | 956 | alloc_id: AllocId, // diagnostics |
| 1133 | span: Span, // diagnostics | 957 | span: Span, // diagnostics |
| 1134 | visit_children: ChildrenVisitMode, | 958 | visit_children: ChildrenVisitMode, |
| 1135 | ) -> InterpResult<'tcx> { | 959 | ) -> InterpResult<'tcx, UniIndex> { |
| 1136 | // Performs the per-node work: | 960 | // Performs the per-node work: |
| 1137 | // - insert the permission if it does not exist | 961 | // - insert the permission if it does not exist |
| 1138 | // - perform the access | 962 | // - perform the access |
| ... | @@ -1141,18 +965,18 @@ impl<'tcx> LocationTree { | ... | @@ -1141,18 +965,18 @@ impl<'tcx> LocationTree { |
| 1141 | // - skip the traversal of the children in some cases | 965 | // - skip the traversal of the children in some cases |
| 1142 | // - do not record noop transitions | 966 | // - do not record noop transitions |
| 1143 | // | 967 | // |
| 1144 | // `perms_range` is only for diagnostics (it is the range of | 968 | // `loc_range` is only for diagnostics (it is the range of |
| 1145 | // the `RangeMap` on which we are currently working). | 969 | // the `RangeMap` on which we are currently working). |
| 1146 | let node_skipper = |args: &NodeAppArgs<'_>| -> ContinueTraversal { | 970 | let node_skipper = |args: &NodeAppArgs<'_, LocationTree>| -> ContinueTraversal { |
| 1147 | let node = args.nodes.get(args.idx).unwrap(); | 971 | let node = args.nodes.get(args.idx).unwrap(); |
| 1148 | let perm = args.loc.perms.get(args.idx); | 972 | let perm = args.data.perms.get(args.idx); |
| 1149 | 973 | ||
| 1150 | let old_state = perm.copied().unwrap_or_else(|| node.default_location_state()); | 974 | let old_state = perm.copied().unwrap_or_else(|| node.default_location_state()); |
| 1151 | old_state.skip_if_known_noop(access_kind, args.rel_pos) | 975 | old_state.skip_if_known_noop(access_kind, args.rel_pos) |
| 1152 | }; | 976 | }; |
| 1153 | let node_app = |args: NodeAppArgs<'_>| -> Result<(), _> { | 977 | let node_app = |args: NodeAppArgs<'_, LocationTree>| { |
| 1154 | let node = args.nodes.get_mut(args.idx).unwrap(); | 978 | let node = args.nodes.get_mut(args.idx).unwrap(); |
| 1155 | let mut perm = args.loc.perms.entry(args.idx); | 979 | let mut perm = args.data.perms.entry(args.idx); |
| 1156 | 980 | ||
| 1157 | let state = perm.or_insert(node.default_location_state()); | 981 | let state = perm.or_insert(node.default_location_state()); |
| 1158 | 982 | ||
| ... | @@ -1161,10 +985,10 @@ impl<'tcx> LocationTree { | ... | @@ -1161,10 +985,10 @@ impl<'tcx> LocationTree { |
| 1161 | .perform_transition( | 985 | .perform_transition( |
| 1162 | args.idx, | 986 | args.idx, |
| 1163 | args.nodes, | 987 | args.nodes, |
| 1164 | &mut args.loc.wildcard_accesses, | 988 | &mut args.data.wildcard_accesses, |
| 1165 | access_kind, | 989 | access_kind, |
| 1166 | access_cause, | 990 | access_cause, |
| 1167 | /* access_range */ access_range, | 991 | access_range, |
| 1168 | args.rel_pos, | 992 | args.rel_pos, |
| 1169 | span, | 993 | span, |
| 1170 | loc_range.clone(), | 994 | loc_range.clone(), |
| ... | @@ -1182,7 +1006,8 @@ impl<'tcx> LocationTree { | ... | @@ -1182,7 +1006,8 @@ impl<'tcx> LocationTree { |
| 1182 | .build() | 1006 | .build() |
| 1183 | }) | 1007 | }) |
| 1184 | }; | 1008 | }; |
| 1185 | let visitor = TreeVisitor { nodes, loc: self }; | 1009 | |
| 1010 | let visitor = TreeVisitor { nodes, data: self }; | ||
| 1186 | match visit_children { | 1011 | match visit_children { |
| 1187 | ChildrenVisitMode::VisitChildrenOfAccessed => | 1012 | ChildrenVisitMode::VisitChildrenOfAccessed => |
| 1188 | visitor.traverse_this_parents_children_other(access_source, node_skipper, node_app), | 1013 | visitor.traverse_this_parents_children_other(access_source, node_skipper, node_app), |
| ... | @@ -1191,31 +1016,61 @@ impl<'tcx> LocationTree { | ... | @@ -1191,31 +1016,61 @@ impl<'tcx> LocationTree { |
| 1191 | } | 1016 | } |
| 1192 | .into() | 1017 | .into() |
| 1193 | } | 1018 | } |
| 1019 | |||
| 1194 | /// Performs a wildcard access on the tree with root `root`. Takes the `access_relatedness` | 1020 | /// Performs a wildcard access on the tree with root `root`. Takes the `access_relatedness` |
| 1195 | /// for each node from the `WildcardState` datastructure. | 1021 | /// for each node from the `WildcardState` datastructure. |
| 1196 | /// * `root`: Root of the tree being accessed. | 1022 | /// * `root`: Root of the tree being accessed. |
| 1023 | /// * `access_source`: the index of the accessed tag, if any. | ||
| 1024 | /// This is only used for printing the correct tag on errors. | ||
| 1025 | /// * `max_local_tag`: The access can only be local for nodes whose tag is | ||
| 1026 | /// at most `max_local_tag`. | ||
| 1197 | fn perform_wildcard_access( | 1027 | fn perform_wildcard_access( |
| 1198 | &mut self, | 1028 | &mut self, |
| 1199 | root: UniIndex, | 1029 | root: UniIndex, |
| 1030 | access_source: Option<UniIndex>, | ||
| 1031 | max_local_tag: Option<BorTag>, | ||
| 1200 | nodes: &mut UniValMap<Node>, | 1032 | nodes: &mut UniValMap<Node>, |
| 1201 | loc_range: Range<u64>, | 1033 | loc_range: Range<u64>, // diagnostics |
| 1202 | access_range: Option<AllocRange>, | 1034 | access_range: Option<AllocRange>, // diagnostics |
| 1203 | access_kind: AccessKind, | 1035 | access_kind: AccessKind, |
| 1204 | access_cause: diagnostics::AccessCause, | 1036 | access_cause: diagnostics::AccessCause, // diagnostics |
| 1205 | global: &GlobalState, | 1037 | global: &GlobalState, |
| 1206 | alloc_id: AllocId, // diagnostics | 1038 | alloc_id: AllocId, // diagnostics |
| 1207 | span: Span, // diagnostics | 1039 | span: Span, // diagnostics |
| 1208 | ) -> InterpResult<'tcx> { | 1040 | ) -> InterpResult<'tcx> { |
| 1209 | let f_continue = | 1041 | let get_relatedness = |idx: UniIndex, node: &Node, loc: &LocationTree| { |
| 1210 | |idx: UniIndex, nodes: &UniValMap<Node>, loc: &LocationTree| -> ContinueTraversal { | 1042 | let wildcard_state = loc.wildcard_accesses.get(idx).cloned().unwrap_or_default(); |
| 1211 | let node = nodes.get(idx).unwrap(); | 1043 | // If the tag is larger than `max_local_tag` then the access can only be foreign. |
| 1212 | let perm = loc.perms.get(idx); | 1044 | let only_foreign = max_local_tag.is_some_and(|max_local_tag| max_local_tag < node.tag); |
| 1213 | let wildcard_state = loc.wildcard_accesses.get(idx).cloned().unwrap_or_default(); | 1045 | wildcard_state.access_relatedness(access_kind, only_foreign) |
| 1046 | }; | ||
| 1047 | |||
| 1048 | // This does a traversal across the tree updating children before their parents. The | ||
| 1049 | // difference to `perform_normal_access` is that we take the access relatedness from | ||
| 1050 | // the wildcard tracking state of the node instead of from the visitor itself. | ||
| 1051 | // | ||
| 1052 | // Unlike for a normal access, the iteration order is important for improving the | ||
| 1053 | // accuracy of wildcard accesses if `max_local_tag` is `Some`: processing the effects of this | ||
| 1054 | // access further down the tree can cause exposed nodes to lose permissions, thus updating | ||
| 1055 | // the wildcard data structure, which will be taken into account when processing the parent | ||
| 1056 | // nodes. Also see the test `cross_tree_update_older_invalid_exposed2.rs` | ||
| 1057 | // (Doing accesses in the opposite order cannot help with precision but the reasons are complicated; | ||
| 1058 | // see <https://github.com/rust-lang/miri/pull/4707#discussion_r2581661123>.) | ||
| 1059 | // | ||
| 1060 | // Note, however, that this is an approximation: there can be situations where a node is | ||
| 1061 | // marked as having an exposed foreign node, but actually that foreign node cannot be | ||
| 1062 | // the source of the access due to `max_local_tag`. The wildcard tracking cannot know | ||
| 1063 | // about `max_local_tag` so we will incorrectly assume that this might be a foreign access. | ||
| 1064 | TreeVisitor { data: self, nodes }.traverse_children_this( | ||
| 1065 | root, | ||
| 1066 | |args| -> ContinueTraversal { | ||
| 1067 | let node = args.nodes.get(args.idx).unwrap(); | ||
| 1068 | let perm = args.data.perms.get(args.idx); | ||
| 1214 | 1069 | ||
| 1215 | let old_state = perm.copied().unwrap_or_else(|| node.default_location_state()); | 1070 | let old_state = perm.copied().unwrap_or_else(|| node.default_location_state()); |
| 1216 | // If we know where, relative to this node, the wildcard access occurs, | 1071 | // If we know where, relative to this node, the wildcard access occurs, |
| 1217 | // then check if we can skip the entire subtree. | 1072 | // then check if we can skip the entire subtree. |
| 1218 | if let Some(relatedness) = wildcard_state.access_relatedness(access_kind) | 1073 | if let Some(relatedness) = get_relatedness(args.idx, node, args.data) |
| 1219 | && let Some(relatedness) = relatedness.to_relatedness() | 1074 | && let Some(relatedness) = relatedness.to_relatedness() |
| 1220 | { | 1075 | { |
| 1221 | // We can use the usual SIFA machinery to skip nodes. | 1076 | // We can use the usual SIFA machinery to skip nodes. |
| ... | @@ -1223,78 +1078,64 @@ impl<'tcx> LocationTree { | ... | @@ -1223,78 +1078,64 @@ impl<'tcx> LocationTree { |
| 1223 | } else { | 1078 | } else { |
| 1224 | ContinueTraversal::Recurse | 1079 | ContinueTraversal::Recurse |
| 1225 | } | 1080 | } |
| 1226 | }; | 1081 | }, |
| 1227 | // This does a traversal starting from the root through the tree updating | 1082 | |args| { |
| 1228 | // the permissions of each node. | 1083 | let node = args.nodes.get_mut(args.idx).unwrap(); |
| 1229 | // The difference to `perform_access` is that we take the access | 1084 | |
| 1230 | // relatedness from the wildcard tracking state of the node instead of | 1085 | let protected = global.borrow().protected_tags.contains_key(&node.tag); |
| 1231 | // from the visitor itself. | 1086 | |
| 1232 | TreeVisitor { loc: self, nodes } | 1087 | let Some(wildcard_relatedness) = get_relatedness(args.idx, node, args.data) else { |
| 1233 | .traverse_this_parents_children_other( | 1088 | // There doesn't exist a valid exposed reference for this access to |
| 1234 | root, | 1089 | // happen through. |
| 1235 | |args| f_continue(args.idx, args.nodes, args.loc), | 1090 | // This can only happen if `root` is the main root: We set |
| 1236 | |args| { | 1091 | // `max_foreign_access==Write` on all wildcard roots, so at least a foreign access |
| 1237 | let node = args.nodes.get_mut(args.idx).unwrap(); | 1092 | // is always possible on all nodes in a wildcard subtree. |
| 1238 | let mut entry = args.loc.perms.entry(args.idx); | 1093 | return Err(no_valid_exposed_references_error( |
| 1239 | let perm = entry.or_insert(node.default_location_state()); | 1094 | alloc_id, |
| 1240 | 1095 | loc_range.start, | |
| 1241 | let protected = global.borrow().protected_tags.contains_key(&node.tag); | 1096 | access_cause, |
| 1097 | )); | ||
| 1098 | }; | ||
| 1242 | 1099 | ||
| 1243 | let Some(wildcard_relatedness) = args | 1100 | let Some(relatedness) = wildcard_relatedness.to_relatedness() else { |
| 1244 | .loc | 1101 | // If the access type is Either, then we do not apply any transition |
| 1245 | .wildcard_accesses | 1102 | // to this node, but we still update each of its children. |
| 1246 | .get(args.idx) | 1103 | // This is an imprecision! In the future, maybe we can still do some sort |
| 1247 | .and_then(|s| s.access_relatedness(access_kind)) | 1104 | // of best-effort update here. |
| 1248 | else { | 1105 | return Ok(()); |
| 1249 | // There doesn't exist a valid exposed reference for this access to | 1106 | }; |
| 1250 | // happen through. | ||
| 1251 | // If this fails for one id, then it fails for all ids so this. | ||
| 1252 | // Since we always check the root first, this means it should always | ||
| 1253 | // fail on the root. | ||
| 1254 | assert_eq!(root, args.idx); | ||
| 1255 | return Err(no_valid_exposed_references_error( | ||
| 1256 | alloc_id, | ||
| 1257 | loc_range.start, | ||
| 1258 | access_cause, | ||
| 1259 | )); | ||
| 1260 | }; | ||
| 1261 | 1107 | ||
| 1262 | let Some(relatedness) = wildcard_relatedness.to_relatedness() else { | 1108 | let mut entry = args.data.perms.entry(args.idx); |
| 1263 | // If the access type is Either, then we do not apply any transition | 1109 | let perm = entry.or_insert(node.default_location_state()); |
| 1264 | // to this node, but we still update each of its children. | 1110 | // We know the exact relatedness, so we can actually do precise checks. |
| 1265 | // This is an imprecision! In the future, maybe we can still do some sort | 1111 | perm.perform_transition( |
| 1266 | // of best-effort update here. | 1112 | args.idx, |
| 1267 | return Ok(()); | 1113 | args.nodes, |
| 1268 | }; | 1114 | &mut args.data.wildcard_accesses, |
| 1269 | // We know the exact relatedness, so we can actually do precise checks. | 1115 | access_kind, |
| 1270 | perm.perform_transition( | 1116 | access_cause, |
| 1271 | args.idx, | 1117 | access_range, |
| 1272 | args.nodes, | 1118 | relatedness, |
| 1273 | &mut args.loc.wildcard_accesses, | 1119 | span, |
| 1274 | access_kind, | 1120 | loc_range.clone(), |
| 1121 | protected, | ||
| 1122 | ) | ||
| 1123 | .map_err(|trans| { | ||
| 1124 | let node = args.nodes.get(args.idx).unwrap(); | ||
| 1125 | TbError { | ||
| 1126 | conflicting_info: &node.debug_info, | ||
| 1275 | access_cause, | 1127 | access_cause, |
| 1276 | access_range, | 1128 | alloc_id, |
| 1277 | relatedness, | 1129 | error_offset: loc_range.start, |
| 1278 | span, | 1130 | error_kind: trans, |
| 1279 | loc_range.clone(), | 1131 | accessed_info: access_source |
| 1280 | protected, | 1132 | .map(|idx| &args.nodes.get(idx).unwrap().debug_info), |
| 1281 | ) | 1133 | } |
| 1282 | .map_err(|trans| { | 1134 | .build() |
| 1283 | let node = args.nodes.get(args.idx).unwrap(); | 1135 | }) |
| 1284 | TbError { | 1136 | }, |
| 1285 | conflicting_info: &node.debug_info, | 1137 | )?; |
| 1286 | access_cause, | 1138 | interp_ok(()) |
| 1287 | alloc_id, | ||
| 1288 | error_offset: loc_range.start, | ||
| 1289 | error_kind: trans, | ||
| 1290 | // We don't know from where the access came during a wildcard access. | ||
| 1291 | accessed_info: None, | ||
| 1292 | } | ||
| 1293 | .build() | ||
| 1294 | }) | ||
| 1295 | }, | ||
| 1296 | ) | ||
| 1297 | .into() | ||
| 1298 | } | 1139 | } |
| 1299 | } | 1140 | } |
| 1300 | 1141 | ||
| ... | @@ -1309,10 +1150,11 @@ impl Node { | ... | @@ -1309,10 +1150,11 @@ impl Node { |
| 1309 | 1150 | ||
| 1310 | impl VisitProvenance for Tree { | 1151 | impl VisitProvenance for Tree { |
| 1311 | fn visit_provenance(&self, visit: &mut VisitWith<'_>) { | 1152 | fn visit_provenance(&self, visit: &mut VisitWith<'_>) { |
| 1312 | // To ensure that the root never gets removed, we visit it | 1153 | // To ensure that the roots never get removed, we visit them. |
| 1313 | // (the `root` node of `Tree` is not an `Option<_>`) | 1154 | // FIXME: it should be possible to GC wildcard tree roots. |
| 1314 | visit(None, Some(self.nodes.get(self.root).unwrap().tag)); | 1155 | for id in self.roots.iter().copied() { |
| 1315 | 1156 | visit(None, Some(self.nodes.get(id).unwrap().tag)); | |
| 1157 | } | ||
| 1316 | // We also need to keep around any exposed tags through which | 1158 | // We also need to keep around any exposed tags through which |
| 1317 | // an access could still happen. | 1159 | // an access could still happen. |
| 1318 | for (_id, node) in self.nodes.iter() { | 1160 | for (_id, node) in self.nodes.iter() { |
src/tools/miri/src/borrow_tracker/tree_borrows/tree_visitor.rs created+289| ... | @@ -0,0 +1,289 @@ | ||
| 1 | use std::marker::PhantomData; | ||
| 2 | |||
| 3 | use super::tree::{AccessRelatedness, Node}; | ||
| 4 | use super::unimap::{UniIndex, UniValMap}; | ||
| 5 | |||
| 6 | /// Data given to the transition function | ||
| 7 | pub struct NodeAppArgs<'visit, T> { | ||
| 8 | /// The index of the current node. | ||
| 9 | pub idx: UniIndex, | ||
| 10 | /// Relative position of the access. | ||
| 11 | pub rel_pos: AccessRelatedness, | ||
| 12 | /// The node map of this tree. | ||
| 13 | pub nodes: &'visit mut UniValMap<Node>, | ||
| 14 | /// Additional data we want to be able to modify in f_propagate and read in f_continue. | ||
| 15 | pub data: &'visit mut T, | ||
| 16 | } | ||
| 17 | /// Internal contents of `Tree` with the minimum of mutable access for | ||
| 18 | /// For soundness do not modify the children or parent indexes of nodes | ||
| 19 | /// during traversal. | ||
| 20 | pub struct TreeVisitor<'tree, T> { | ||
| 21 | pub nodes: &'tree mut UniValMap<Node>, | ||
| 22 | pub data: &'tree mut T, | ||
| 23 | } | ||
| 24 | |||
| 25 | /// Whether to continue exploring the children recursively or not. | ||
| 26 | #[derive(Debug)] | ||
| 27 | pub enum ContinueTraversal { | ||
| 28 | Recurse, | ||
| 29 | SkipSelfAndChildren, | ||
| 30 | } | ||
| 31 | |||
| 32 | #[derive(Clone, Copy, Debug)] | ||
| 33 | pub enum ChildrenVisitMode { | ||
| 34 | VisitChildrenOfAccessed, | ||
| 35 | SkipChildrenOfAccessed, | ||
| 36 | } | ||
| 37 | |||
| 38 | enum RecursionState { | ||
| 39 | BeforeChildren, | ||
| 40 | AfterChildren, | ||
| 41 | } | ||
| 42 | |||
| 43 | /// Stack of nodes left to explore in a tree traversal. | ||
| 44 | /// See the docs of `traverse_this_parents_children_other` for details on the | ||
| 45 | /// traversal order. | ||
| 46 | struct TreeVisitorStack<NodeContinue, NodeApp, T> { | ||
| 47 | /// Function describing whether to continue at a tag. | ||
| 48 | /// This is only invoked for foreign accesses. | ||
| 49 | f_continue: NodeContinue, | ||
| 50 | /// Function to apply to each tag. | ||
| 51 | f_propagate: NodeApp, | ||
| 52 | /// Mutable state of the visit: the tags left to handle. | ||
| 53 | /// Every tag pushed should eventually be handled, | ||
| 54 | /// and the precise order is relevant for diagnostics. | ||
| 55 | /// Since the traversal is piecewise bottom-up, we need to | ||
| 56 | /// remember whether we're here initially, or after visiting all children. | ||
| 57 | /// The last element indicates this. | ||
| 58 | /// This is just an artifact of how you hand-roll recursion, | ||
| 59 | /// it does not have a deeper meaning otherwise. | ||
| 60 | stack: Vec<(UniIndex, AccessRelatedness, RecursionState)>, | ||
| 61 | phantom: PhantomData<T>, | ||
| 62 | } | ||
| 63 | |||
| 64 | impl<NodeContinue, NodeApp, T, Err> TreeVisitorStack<NodeContinue, NodeApp, T> | ||
| 65 | where | ||
| 66 | NodeContinue: Fn(&NodeAppArgs<'_, T>) -> ContinueTraversal, | ||
| 67 | NodeApp: FnMut(NodeAppArgs<'_, T>) -> Result<(), Err>, | ||
| 68 | { | ||
| 69 | fn should_continue_at( | ||
| 70 | &self, | ||
| 71 | this: &mut TreeVisitor<'_, T>, | ||
| 72 | idx: UniIndex, | ||
| 73 | rel_pos: AccessRelatedness, | ||
| 74 | ) -> ContinueTraversal { | ||
| 75 | let args = NodeAppArgs { idx, rel_pos, nodes: this.nodes, data: this.data }; | ||
| 76 | (self.f_continue)(&args) | ||
| 77 | } | ||
| 78 | |||
| 79 | fn propagate_at( | ||
| 80 | &mut self, | ||
| 81 | this: &mut TreeVisitor<'_, T>, | ||
| 82 | idx: UniIndex, | ||
| 83 | rel_pos: AccessRelatedness, | ||
| 84 | ) -> Result<(), Err> { | ||
| 85 | (self.f_propagate)(NodeAppArgs { idx, rel_pos, nodes: this.nodes, data: this.data }) | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Returns the root of this tree. | ||
| 89 | fn go_upwards_from_accessed( | ||
| 90 | &mut self, | ||
| 91 | this: &mut TreeVisitor<'_, T>, | ||
| 92 | accessed_node: UniIndex, | ||
| 93 | visit_children: ChildrenVisitMode, | ||
| 94 | ) -> Result<UniIndex, Err> { | ||
| 95 | // We want to visit the accessed node's children first. | ||
| 96 | // However, we will below walk up our parents and push their children (our cousins) | ||
| 97 | // onto the stack. To ensure correct iteration order, this method thus finishes | ||
| 98 | // by reversing the stack. This only works if the stack is empty initially. | ||
| 99 | assert!(self.stack.is_empty()); | ||
| 100 | // First, handle accessed node. A bunch of things need to | ||
| 101 | // be handled differently here compared to the further parents | ||
| 102 | // of `accesssed_node`. | ||
| 103 | { | ||
| 104 | self.propagate_at(this, accessed_node, AccessRelatedness::LocalAccess)?; | ||
| 105 | if matches!(visit_children, ChildrenVisitMode::VisitChildrenOfAccessed) { | ||
| 106 | let accessed_node = this.nodes.get(accessed_node).unwrap(); | ||
| 107 | // We `rev()` here because we reverse the entire stack later. | ||
| 108 | for &child in accessed_node.children.iter().rev() { | ||
| 109 | self.stack.push(( | ||
| 110 | child, | ||
| 111 | AccessRelatedness::ForeignAccess, | ||
| 112 | RecursionState::BeforeChildren, | ||
| 113 | )); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
| 117 | // Then, handle the accessed node's parents. Here, we need to | ||
| 118 | // make sure we only mark the "cousin" subtrees for later visitation, | ||
| 119 | // not the subtree that contains the accessed node. | ||
| 120 | let mut last_node = accessed_node; | ||
| 121 | while let Some(current) = this.nodes.get(last_node).unwrap().parent { | ||
| 122 | self.propagate_at(this, current, AccessRelatedness::LocalAccess)?; | ||
| 123 | let node = this.nodes.get(current).unwrap(); | ||
| 124 | // We `rev()` here because we reverse the entire stack later. | ||
| 125 | for &child in node.children.iter().rev() { | ||
| 126 | if last_node == child { | ||
| 127 | continue; | ||
| 128 | } | ||
| 129 | self.stack.push(( | ||
| 130 | child, | ||
| 131 | AccessRelatedness::ForeignAccess, | ||
| 132 | RecursionState::BeforeChildren, | ||
| 133 | )); | ||
| 134 | } | ||
| 135 | last_node = current; | ||
| 136 | } | ||
| 137 | // Reverse the stack, as discussed above. | ||
| 138 | self.stack.reverse(); | ||
| 139 | Ok(last_node) | ||
| 140 | } | ||
| 141 | |||
| 142 | fn finish_foreign_accesses(&mut self, this: &mut TreeVisitor<'_, T>) -> Result<(), Err> { | ||
| 143 | while let Some((idx, rel_pos, step)) = self.stack.last_mut() { | ||
| 144 | let idx = *idx; | ||
| 145 | let rel_pos = *rel_pos; | ||
| 146 | match *step { | ||
| 147 | // How to do bottom-up traversal, 101: Before you handle a node, you handle all children. | ||
| 148 | // For this, you must first find the children, which is what this code here does. | ||
| 149 | RecursionState::BeforeChildren => { | ||
| 150 | // Next time we come back will be when all the children are handled. | ||
| 151 | *step = RecursionState::AfterChildren; | ||
| 152 | // Now push the children, except if we are told to skip this subtree. | ||
| 153 | let handle_children = self.should_continue_at(this, idx, rel_pos); | ||
| 154 | match handle_children { | ||
| 155 | ContinueTraversal::Recurse => { | ||
| 156 | let node = this.nodes.get(idx).unwrap(); | ||
| 157 | for &child in node.children.iter() { | ||
| 158 | self.stack.push((child, rel_pos, RecursionState::BeforeChildren)); | ||
| 159 | } | ||
| 160 | } | ||
| 161 | ContinueTraversal::SkipSelfAndChildren => { | ||
| 162 | // skip self | ||
| 163 | self.stack.pop(); | ||
| 164 | continue; | ||
| 165 | } | ||
| 166 | } | ||
| 167 | } | ||
| 168 | // All the children are handled, let's actually visit this node | ||
| 169 | RecursionState::AfterChildren => { | ||
| 170 | self.stack.pop(); | ||
| 171 | self.propagate_at(this, idx, rel_pos)?; | ||
| 172 | } | ||
| 173 | } | ||
| 174 | } | ||
| 175 | Ok(()) | ||
| 176 | } | ||
| 177 | |||
| 178 | fn new(f_continue: NodeContinue, f_propagate: NodeApp) -> Self { | ||
| 179 | Self { f_continue, f_propagate, stack: Vec::new(), phantom: PhantomData } | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | impl<'tree, T> TreeVisitor<'tree, T> { | ||
| 184 | /// Applies `f_propagate` to every vertex of the tree in a piecewise bottom-up way: First, visit | ||
| 185 | /// all ancestors of `start_idx` (starting with `start_idx` itself), then children of `start_idx`, then the rest, | ||
| 186 | /// going bottom-up in each of these two "pieces" / sections. | ||
| 187 | /// This ensures that errors are triggered in the following order | ||
| 188 | /// - first invalid accesses with insufficient permissions, closest to the accessed node first, | ||
| 189 | /// - then protector violations, bottom-up, starting with the children of the accessed node, and then | ||
| 190 | /// going upwards and outwards. | ||
| 191 | /// | ||
| 192 | /// The following graphic visualizes it, with numbers indicating visitation order and `start_idx` being | ||
| 193 | /// the node that is visited first ("1"): | ||
| 194 | /// | ||
| 195 | /// ```text | ||
| 196 | /// 3 | ||
| 197 | /// /| | ||
| 198 | /// / | | ||
| 199 | /// 9 2 | ||
| 200 | /// | |\ | ||
| 201 | /// | | \ | ||
| 202 | /// 8 1 7 | ||
| 203 | /// / \ | ||
| 204 | /// 4 6 | ||
| 205 | /// | | ||
| 206 | /// 5 | ||
| 207 | /// ``` | ||
| 208 | /// | ||
| 209 | /// `f_propagate` should follow the following format: for a given `Node` it updates its | ||
| 210 | /// `Permission` depending on the position relative to `start_idx` (given by an | ||
| 211 | /// `AccessRelatedness`). | ||
| 212 | /// `f_continue` is called earlier on foreign nodes, and describes whether to even start | ||
| 213 | /// visiting the subtree at that node. If it e.g. returns `SkipSelfAndChildren` on node 6 | ||
| 214 | /// above, then nodes 5 _and_ 6 would not be visited by `f_propagate`. It is not used for | ||
| 215 | /// notes having a child access (nodes 1, 2, 3). | ||
| 216 | /// | ||
| 217 | /// Finally, remember that the iteration order is not relevant for UB, it only affects | ||
| 218 | /// diagnostics. It also affects tree traversal optimizations built on top of this, so | ||
| 219 | /// those need to be reviewed carefully as well whenever this changes. | ||
| 220 | /// | ||
| 221 | /// Returns the index of the root of the accessed tree. | ||
| 222 | pub fn traverse_this_parents_children_other<Err>( | ||
| 223 | mut self, | ||
| 224 | start_idx: UniIndex, | ||
| 225 | f_continue: impl Fn(&NodeAppArgs<'_, T>) -> ContinueTraversal, | ||
| 226 | f_propagate: impl FnMut(NodeAppArgs<'_, T>) -> Result<(), Err>, | ||
| 227 | ) -> Result<UniIndex, Err> { | ||
| 228 | let mut stack = TreeVisitorStack::new(f_continue, f_propagate); | ||
| 229 | // Visits the accessed node itself, and all its parents, i.e. all nodes | ||
| 230 | // undergoing a child access. Also pushes the children and the other | ||
| 231 | // cousin nodes (i.e. all nodes undergoing a foreign access) to the stack | ||
| 232 | // to be processed later. | ||
| 233 | let root = stack.go_upwards_from_accessed( | ||
| 234 | &mut self, | ||
| 235 | start_idx, | ||
| 236 | ChildrenVisitMode::VisitChildrenOfAccessed, | ||
| 237 | )?; | ||
| 238 | // Now visit all the foreign nodes we remembered earlier. | ||
| 239 | // For this we go bottom-up, but also allow f_continue to skip entire | ||
| 240 | // subtrees from being visited if it would be a NOP. | ||
| 241 | stack.finish_foreign_accesses(&mut self)?; | ||
| 242 | Ok(root) | ||
| 243 | } | ||
| 244 | |||
| 245 | /// Like `traverse_this_parents_children_other`, but skips the children of `start_idx`. | ||
| 246 | /// | ||
| 247 | /// Returns the index of the root of the accessed tree. | ||
| 248 | pub fn traverse_nonchildren<Err>( | ||
| 249 | mut self, | ||
| 250 | start_idx: UniIndex, | ||
| 251 | f_continue: impl Fn(&NodeAppArgs<'_, T>) -> ContinueTraversal, | ||
| 252 | f_propagate: impl FnMut(NodeAppArgs<'_, T>) -> Result<(), Err>, | ||
| 253 | ) -> Result<UniIndex, Err> { | ||
| 254 | let mut stack = TreeVisitorStack::new(f_continue, f_propagate); | ||
| 255 | // Visits the accessed node itself, and all its parents, i.e. all nodes | ||
| 256 | // undergoing a child access. Also pushes the other cousin nodes to the | ||
| 257 | // stack, but not the children of the accessed node. | ||
| 258 | let root = stack.go_upwards_from_accessed( | ||
| 259 | &mut self, | ||
| 260 | start_idx, | ||
| 261 | ChildrenVisitMode::SkipChildrenOfAccessed, | ||
| 262 | )?; | ||
| 263 | // Now visit all the foreign nodes we remembered earlier. | ||
| 264 | // For this we go bottom-up, but also allow f_continue to skip entire | ||
| 265 | // subtrees from being visited if it would be a NOP. | ||
| 266 | stack.finish_foreign_accesses(&mut self)?; | ||
| 267 | Ok(root) | ||
| 268 | } | ||
| 269 | |||
| 270 | /// Traverses all children of `start_idx` including `start_idx` itself. | ||
| 271 | /// Uses `f_continue` to filter out subtrees and then processes each node | ||
| 272 | /// with `f_propagate` so that the children get processed before their | ||
| 273 | /// parents. | ||
| 274 | pub fn traverse_children_this<Err>( | ||
| 275 | mut self, | ||
| 276 | start_idx: UniIndex, | ||
| 277 | f_continue: impl Fn(&NodeAppArgs<'_, T>) -> ContinueTraversal, | ||
| 278 | f_propagate: impl FnMut(NodeAppArgs<'_, T>) -> Result<(), Err>, | ||
| 279 | ) -> Result<(), Err> { | ||
| 280 | let mut stack = TreeVisitorStack::new(f_continue, f_propagate); | ||
| 281 | |||
| 282 | stack.stack.push(( | ||
| 283 | start_idx, | ||
| 284 | AccessRelatedness::ForeignAccess, | ||
| 285 | RecursionState::BeforeChildren, | ||
| 286 | )); | ||
| 287 | stack.finish_foreign_accesses(&mut self) | ||
| 288 | } | ||
| 289 | } | ||
src/tools/miri/src/borrow_tracker/tree_borrows/wildcard.rs+43-4| ... | @@ -88,10 +88,26 @@ impl WildcardState { | ... | @@ -88,10 +88,26 @@ impl WildcardState { |
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | /// From where relative to the node with this wildcard info a read or write access could happen. | 90 | /// From where relative to the node with this wildcard info a read or write access could happen. |
| 91 | pub fn access_relatedness(&self, kind: AccessKind) -> Option<WildcardAccessRelatedness> { | 91 | /// If `only_foreign` is true then we treat `LocalAccess` as impossible. This means we return |
| 92 | match kind { | 92 | /// `None` if only a `LocalAccess` is possible, and we treat `EitherAccess` as a |
| 93 | /// `ForeignAccess`. | ||
| 94 | pub fn access_relatedness( | ||
| 95 | &self, | ||
| 96 | kind: AccessKind, | ||
| 97 | only_foreign: bool, | ||
| 98 | ) -> Option<WildcardAccessRelatedness> { | ||
| 99 | let rel = match kind { | ||
| 93 | AccessKind::Read => self.read_access_relatedness(), | 100 | AccessKind::Read => self.read_access_relatedness(), |
| 94 | AccessKind::Write => self.write_access_relatedness(), | 101 | AccessKind::Write => self.write_access_relatedness(), |
| 102 | }; | ||
| 103 | if only_foreign { | ||
| 104 | use WildcardAccessRelatedness as E; | ||
| 105 | match rel { | ||
| 106 | Some(E::EitherAccess | E::ForeignAccess) => Some(E::ForeignAccess), | ||
| 107 | Some(E::LocalAccess) | None => None, | ||
| 108 | } | ||
| 109 | } else { | ||
| 110 | rel | ||
| 95 | } | 111 | } |
| 96 | } | 112 | } |
| 97 | 113 | ||
| ... | @@ -131,6 +147,15 @@ impl WildcardState { | ... | @@ -131,6 +147,15 @@ impl WildcardState { |
| 131 | ..Default::default() | 147 | ..Default::default() |
| 132 | } | 148 | } |
| 133 | } | 149 | } |
| 150 | /// Crates the initial `WildcardState` for a wildcard root. | ||
| 151 | /// This has `max_foreign_access==Write` as it actually is the child of *some* exposed node | ||
| 152 | /// through which we can receive foreign accesses. | ||
| 153 | /// | ||
| 154 | /// This is different from the main root which has `max_foreign_access==None`, since there | ||
| 155 | /// cannot be a foreign access to the root of the allocation. | ||
| 156 | pub fn for_wildcard_root() -> Self { | ||
| 157 | Self { max_foreign_access: WildcardAccessLevel::Write, ..Default::default() } | ||
| 158 | } | ||
| 134 | 159 | ||
| 135 | /// Pushes the nodes of `children` onto the stack who's `max_foreign_access` | 160 | /// Pushes the nodes of `children` onto the stack who's `max_foreign_access` |
| 136 | /// needs to be updated. | 161 | /// needs to be updated. |
| ... | @@ -435,6 +460,10 @@ impl Tree { | ... | @@ -435,6 +460,10 @@ impl Tree { |
| 435 | /// Checks that the wildcard tracking data structure is internally consistent and | 460 | /// Checks that the wildcard tracking data structure is internally consistent and |
| 436 | /// has the correct `exposed_as` values. | 461 | /// has the correct `exposed_as` values. |
| 437 | pub fn verify_wildcard_consistency(&self, global: &GlobalState) { | 462 | pub fn verify_wildcard_consistency(&self, global: &GlobalState) { |
| 463 | // We rely on the fact that `roots` is ordered according to tag from low to high. | ||
| 464 | assert!(self.roots.is_sorted_by_key(|idx| self.nodes.get(*idx).unwrap().tag)); | ||
| 465 | let main_root_idx = self.roots[0]; | ||
| 466 | |||
| 438 | let protected_tags = &global.borrow().protected_tags; | 467 | let protected_tags = &global.borrow().protected_tags; |
| 439 | for (_, loc) in self.locations.iter_all() { | 468 | for (_, loc) in self.locations.iter_all() { |
| 440 | let wildcard_accesses = &loc.wildcard_accesses; | 469 | let wildcard_accesses = &loc.wildcard_accesses; |
| ... | @@ -447,7 +476,8 @@ impl Tree { | ... | @@ -447,7 +476,8 @@ impl Tree { |
| 447 | let state = wildcard_accesses.get(id).unwrap(); | 476 | let state = wildcard_accesses.get(id).unwrap(); |
| 448 | 477 | ||
| 449 | let expected_exposed_as = if node.is_exposed { | 478 | let expected_exposed_as = if node.is_exposed { |
| 450 | let perm = perms.get(id).unwrap(); | 479 | let perm = |
| 480 | perms.get(id).copied().unwrap_or_else(|| node.default_location_state()); | ||
| 451 | 481 | ||
| 452 | perm.permission() | 482 | perm.permission() |
| 453 | .strongest_allowed_child_access(protected_tags.contains_key(&node.tag)) | 483 | .strongest_allowed_child_access(protected_tags.contains_key(&node.tag)) |
| ... | @@ -477,7 +507,16 @@ impl Tree { | ... | @@ -477,7 +507,16 @@ impl Tree { |
| 477 | .max(parent_state.max_foreign_access) | 507 | .max(parent_state.max_foreign_access) |
| 478 | .max(parent_state.exposed_as) | 508 | .max(parent_state.exposed_as) |
| 479 | } else { | 509 | } else { |
| 480 | WildcardAccessLevel::None | 510 | if main_root_idx == id { |
| 511 | // There can never be a foreign access to the root of the allocation. | ||
| 512 | // So its foreign access level is always `None`. | ||
| 513 | WildcardAccessLevel::None | ||
| 514 | } else { | ||
| 515 | // For wildcard roots any access on a different subtree can be foreign | ||
| 516 | // to it. So a wildcard root has the maximum possible foreign access | ||
| 517 | // level. | ||
| 518 | WildcardAccessLevel::Write | ||
| 519 | } | ||
| 481 | }; | 520 | }; |
| 482 | 521 | ||
| 483 | // Count how many children can be the source of wildcard reads or writes | 522 | // Count how many children can be the source of wildcard reads or writes |
src/tools/miri/src/concurrency/thread.rs+17-6| ... | @@ -14,7 +14,7 @@ use rustc_hir::def_id::DefId; | ... | @@ -14,7 +14,7 @@ use rustc_hir::def_id::DefId; |
| 14 | use rustc_index::{Idx, IndexVec}; | 14 | use rustc_index::{Idx, IndexVec}; |
| 15 | use rustc_middle::mir::Mutability; | 15 | use rustc_middle::mir::Mutability; |
| 16 | use rustc_middle::ty::layout::TyAndLayout; | 16 | use rustc_middle::ty::layout::TyAndLayout; |
| 17 | use rustc_span::Span; | 17 | use rustc_span::{DUMMY_SP, Span}; |
| 18 | use rustc_target::spec::Os; | 18 | use rustc_target::spec::Os; |
| 19 | 19 | ||
| 20 | use crate::concurrency::GlobalDataRaceHandler; | 20 | use crate::concurrency::GlobalDataRaceHandler; |
| ... | @@ -174,6 +174,10 @@ pub struct Thread<'tcx> { | ... | @@ -174,6 +174,10 @@ pub struct Thread<'tcx> { |
| 174 | /// The virtual call stack. | 174 | /// The virtual call stack. |
| 175 | stack: Vec<Frame<'tcx, Provenance, FrameExtra<'tcx>>>, | 175 | stack: Vec<Frame<'tcx, Provenance, FrameExtra<'tcx>>>, |
| 176 | 176 | ||
| 177 | /// A span that explains where the thread (or more specifically, its current root | ||
| 178 | /// frame) "comes from". | ||
| 179 | pub(crate) origin_span: Span, | ||
| 180 | |||
| 177 | /// The function to call when the stack ran empty, to figure out what to do next. | 181 | /// The function to call when the stack ran empty, to figure out what to do next. |
| 178 | /// Conceptually, this is the interpreter implementation of the things that happen 'after' the | 182 | /// Conceptually, this is the interpreter implementation of the things that happen 'after' the |
| 179 | /// Rust language entry point for this thread returns (usually implemented by the C or OS runtime). | 183 | /// Rust language entry point for this thread returns (usually implemented by the C or OS runtime). |
| ... | @@ -303,6 +307,7 @@ impl<'tcx> Thread<'tcx> { | ... | @@ -303,6 +307,7 @@ impl<'tcx> Thread<'tcx> { |
| 303 | state: ThreadState::Enabled, | 307 | state: ThreadState::Enabled, |
| 304 | thread_name: name.map(|name| Vec::from(name.as_bytes())), | 308 | thread_name: name.map(|name| Vec::from(name.as_bytes())), |
| 305 | stack: Vec::new(), | 309 | stack: Vec::new(), |
| 310 | origin_span: DUMMY_SP, | ||
| 306 | top_user_relevant_frame: None, | 311 | top_user_relevant_frame: None, |
| 307 | join_status: ThreadJoinStatus::Joinable, | 312 | join_status: ThreadJoinStatus::Joinable, |
| 308 | unwind_payloads: Vec::new(), | 313 | unwind_payloads: Vec::new(), |
| ... | @@ -318,6 +323,7 @@ impl VisitProvenance for Thread<'_> { | ... | @@ -318,6 +323,7 @@ impl VisitProvenance for Thread<'_> { |
| 318 | unwind_payloads: panic_payload, | 323 | unwind_payloads: panic_payload, |
| 319 | last_error, | 324 | last_error, |
| 320 | stack, | 325 | stack, |
| 326 | origin_span: _, | ||
| 321 | top_user_relevant_frame: _, | 327 | top_user_relevant_frame: _, |
| 322 | state: _, | 328 | state: _, |
| 323 | thread_name: _, | 329 | thread_name: _, |
| ... | @@ -584,6 +590,10 @@ impl<'tcx> ThreadManager<'tcx> { | ... | @@ -584,6 +590,10 @@ impl<'tcx> ThreadManager<'tcx> { |
| 584 | &self.threads[self.active_thread] | 590 | &self.threads[self.active_thread] |
| 585 | } | 591 | } |
| 586 | 592 | ||
| 593 | pub fn thread_ref(&self, thread_id: ThreadId) -> &Thread<'tcx> { | ||
| 594 | &self.threads[thread_id] | ||
| 595 | } | ||
| 596 | |||
| 587 | /// Mark the thread as detached, which means that no other thread will try | 597 | /// Mark the thread as detached, which means that no other thread will try |
| 588 | /// to join it and the thread is responsible for cleaning up. | 598 | /// to join it and the thread is responsible for cleaning up. |
| 589 | /// | 599 | /// |
| ... | @@ -704,8 +714,9 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { | ... | @@ -704,8 +714,9 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { |
| 704 | #[inline] | 714 | #[inline] |
| 705 | fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> { | 715 | fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> { |
| 706 | let this = self.eval_context_mut(); | 716 | let this = self.eval_context_mut(); |
| 707 | let mut callback = this | 717 | let active_thread = this.active_thread_mut(); |
| 708 | .active_thread_mut() | 718 | active_thread.origin_span = DUMMY_SP; // reset, the old value no longer applied |
| 719 | let mut callback = active_thread | ||
| 709 | .on_stack_empty | 720 | .on_stack_empty |
| 710 | .take() | 721 | .take() |
| 711 | .expect("`on_stack_empty` not set up, or already running"); | 722 | .expect("`on_stack_empty` not set up, or already running"); |
| ... | @@ -891,11 +902,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -891,11 +902,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 891 | let this = self.eval_context_mut(); | 902 | let this = self.eval_context_mut(); |
| 892 | 903 | ||
| 893 | // Create the new thread | 904 | // Create the new thread |
| 905 | let current_span = this.machine.current_user_relevant_span(); | ||
| 894 | let new_thread_id = this.machine.threads.create_thread({ | 906 | let new_thread_id = this.machine.threads.create_thread({ |
| 895 | let mut state = tls::TlsDtorsState::default(); | 907 | let mut state = tls::TlsDtorsState::default(); |
| 896 | Box::new(move |m| state.on_stack_empty(m)) | 908 | Box::new(move |m| state.on_stack_empty(m)) |
| 897 | }); | 909 | }); |
| 898 | let current_span = this.machine.current_user_relevant_span(); | ||
| 899 | match &mut this.machine.data_race { | 910 | match &mut this.machine.data_race { |
| 900 | GlobalDataRaceHandler::None => {} | 911 | GlobalDataRaceHandler::None => {} |
| 901 | GlobalDataRaceHandler::Vclocks(data_race) => | 912 | GlobalDataRaceHandler::Vclocks(data_race) => |
| ... | @@ -934,12 +945,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -934,12 +945,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 934 | // it. | 945 | // it. |
| 935 | let ret_place = this.allocate(ret_layout, MiriMemoryKind::Machine.into())?; | 946 | let ret_place = this.allocate(ret_layout, MiriMemoryKind::Machine.into())?; |
| 936 | 947 | ||
| 937 | this.call_function( | 948 | this.call_thread_root_function( |
| 938 | instance, | 949 | instance, |
| 939 | start_abi, | 950 | start_abi, |
| 940 | &[func_arg], | 951 | &[func_arg], |
| 941 | Some(&ret_place), | 952 | Some(&ret_place), |
| 942 | ReturnContinuation::Stop { cleanup: true }, | 953 | current_span, |
| 943 | )?; | 954 | )?; |
| 944 | 955 | ||
| 945 | // Restore the old active thread frame. | 956 | // Restore the old active thread frame. |
src/tools/miri/src/diagnostics.rs+19-2| ... | @@ -444,7 +444,11 @@ pub fn report_result<'tcx>( | ... | @@ -444,7 +444,11 @@ pub fn report_result<'tcx>( |
| 444 | write!(primary_msg, "{}", format_interp_error(ecx.tcx.dcx(), res)).unwrap(); | 444 | write!(primary_msg, "{}", format_interp_error(ecx.tcx.dcx(), res)).unwrap(); |
| 445 | 445 | ||
| 446 | if labels.is_empty() { | 446 | if labels.is_empty() { |
| 447 | labels.push(format!("{} occurred here", title.unwrap_or("error"))); | 447 | labels.push(format!( |
| 448 | "{} occurred {}", | ||
| 449 | title.unwrap_or("error"), | ||
| 450 | if stacktrace.is_empty() { "due to this code" } else { "here" } | ||
| 451 | )); | ||
| 448 | } | 452 | } |
| 449 | 453 | ||
| 450 | report_msg( | 454 | report_msg( |
| ... | @@ -552,7 +556,14 @@ pub fn report_msg<'tcx>( | ... | @@ -552,7 +556,14 @@ pub fn report_msg<'tcx>( |
| 552 | thread: Option<ThreadId>, | 556 | thread: Option<ThreadId>, |
| 553 | machine: &MiriMachine<'tcx>, | 557 | machine: &MiriMachine<'tcx>, |
| 554 | ) { | 558 | ) { |
| 555 | let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span); | 559 | let span = match stacktrace.first() { |
| 560 | Some(fi) => fi.span, | ||
| 561 | None => | ||
| 562 | match thread { | ||
| 563 | Some(thread_id) => machine.threads.thread_ref(thread_id).origin_span, | ||
| 564 | None => DUMMY_SP, | ||
| 565 | }, | ||
| 566 | }; | ||
| 556 | let sess = machine.tcx.sess; | 567 | let sess = machine.tcx.sess; |
| 557 | let level = match diag_level { | 568 | let level = match diag_level { |
| 558 | DiagLevel::Error => Level::Error, | 569 | DiagLevel::Error => Level::Error, |
| ... | @@ -620,6 +631,12 @@ pub fn report_msg<'tcx>( | ... | @@ -620,6 +631,12 @@ pub fn report_msg<'tcx>( |
| 620 | err.note(format!("{frame_info} at {span}")); | 631 | err.note(format!("{frame_info} at {span}")); |
| 621 | } | 632 | } |
| 622 | } | 633 | } |
| 634 | } else if stacktrace.len() == 0 && !span.is_dummy() { | ||
| 635 | err.note(format!( | ||
| 636 | "this {} occurred while pushing a call frame onto an empty stack", | ||
| 637 | level.to_str() | ||
| 638 | )); | ||
| 639 | err.note("the span indicates which code caused the function to be called, but may not be the literal call site"); | ||
| 623 | } | 640 | } |
| 624 | 641 | ||
| 625 | err.emit(); | 642 | err.emit(); |
src/tools/miri/src/helpers.rs+22-4| ... | @@ -472,6 +472,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -472,6 +472,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 472 | ) | 472 | ) |
| 473 | } | 473 | } |
| 474 | 474 | ||
| 475 | /// Call a function in an "empty" thread. | ||
| 476 | fn call_thread_root_function( | ||
| 477 | &mut self, | ||
| 478 | f: ty::Instance<'tcx>, | ||
| 479 | caller_abi: ExternAbi, | ||
| 480 | args: &[ImmTy<'tcx>], | ||
| 481 | dest: Option<&MPlaceTy<'tcx>>, | ||
| 482 | span: Span, | ||
| 483 | ) -> InterpResult<'tcx> { | ||
| 484 | let this = self.eval_context_mut(); | ||
| 485 | assert!(this.active_thread_stack().is_empty()); | ||
| 486 | assert!(this.active_thread_ref().origin_span.is_dummy()); | ||
| 487 | this.active_thread_mut().origin_span = span; | ||
| 488 | this.call_function(f, caller_abi, args, dest, ReturnContinuation::Stop { cleanup: true }) | ||
| 489 | } | ||
| 490 | |||
| 475 | /// Visits the memory covered by `place`, sensitive to freezing: the 2nd parameter | 491 | /// Visits the memory covered by `place`, sensitive to freezing: the 2nd parameter |
| 476 | /// of `action` will be true if this is frozen, false if this is in an `UnsafeCell`. | 492 | /// of `action` will be true if this is frozen, false if this is in an `UnsafeCell`. |
| 477 | /// The range is relative to `place`. | 493 | /// The range is relative to `place`. |
| ... | @@ -995,11 +1011,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -995,11 +1011,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 995 | interp_ok(()) | 1011 | interp_ok(()) |
| 996 | } | 1012 | } |
| 997 | 1013 | ||
| 998 | /// Lookup an array of immediates from any linker sections matching the provided predicate. | 1014 | /// Lookup an array of immediates from any linker sections matching the provided predicate, |
| 1015 | /// with the spans of where they were found. | ||
| 999 | fn lookup_link_section( | 1016 | fn lookup_link_section( |
| 1000 | &mut self, | 1017 | &mut self, |
| 1001 | include_name: impl Fn(&str) -> bool, | 1018 | include_name: impl Fn(&str) -> bool, |
| 1002 | ) -> InterpResult<'tcx, Vec<ImmTy<'tcx>>> { | 1019 | ) -> InterpResult<'tcx, Vec<(ImmTy<'tcx>, Span)>> { |
| 1003 | let this = self.eval_context_mut(); | 1020 | let this = self.eval_context_mut(); |
| 1004 | let tcx = this.tcx.tcx; | 1021 | let tcx = this.tcx.tcx; |
| 1005 | 1022 | ||
| ... | @@ -1012,6 +1029,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -1012,6 +1029,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 1012 | }; | 1029 | }; |
| 1013 | if include_name(link_section.as_str()) { | 1030 | if include_name(link_section.as_str()) { |
| 1014 | let instance = ty::Instance::mono(tcx, def_id); | 1031 | let instance = ty::Instance::mono(tcx, def_id); |
| 1032 | let span = tcx.def_span(def_id); | ||
| 1015 | let const_val = this.eval_global(instance).unwrap_or_else(|err| { | 1033 | let const_val = this.eval_global(instance).unwrap_or_else(|err| { |
| 1016 | panic!( | 1034 | panic!( |
| 1017 | "failed to evaluate static in required link_section: {def_id:?}\n{err:?}" | 1035 | "failed to evaluate static in required link_section: {def_id:?}\n{err:?}" |
| ... | @@ -1019,12 +1037,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -1019,12 +1037,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 1019 | }); | 1037 | }); |
| 1020 | match const_val.layout.ty.kind() { | 1038 | match const_val.layout.ty.kind() { |
| 1021 | ty::FnPtr(..) => { | 1039 | ty::FnPtr(..) => { |
| 1022 | array.push(this.read_immediate(&const_val)?); | 1040 | array.push((this.read_immediate(&const_val)?, span)); |
| 1023 | } | 1041 | } |
| 1024 | ty::Array(elem_ty, _) if matches!(elem_ty.kind(), ty::FnPtr(..)) => { | 1042 | ty::Array(elem_ty, _) if matches!(elem_ty.kind(), ty::FnPtr(..)) => { |
| 1025 | let mut elems = this.project_array_fields(&const_val)?; | 1043 | let mut elems = this.project_array_fields(&const_val)?; |
| 1026 | while let Some((_idx, elem)) = elems.next(this)? { | 1044 | while let Some((_idx, elem)) = elems.next(this)? { |
| 1027 | array.push(this.read_immediate(&elem)?); | 1045 | array.push((this.read_immediate(&elem)?, span)); |
| 1028 | } | 1046 | } |
| 1029 | } | 1047 | } |
| 1030 | _ => | 1048 | _ => |
src/tools/miri/src/lib.rs+1| ... | @@ -39,6 +39,7 @@ | ... | @@ -39,6 +39,7 @@ |
| 39 | clippy::needless_question_mark, | 39 | clippy::needless_question_mark, |
| 40 | clippy::needless_lifetimes, | 40 | clippy::needless_lifetimes, |
| 41 | clippy::too_long_first_doc_paragraph, | 41 | clippy::too_long_first_doc_paragraph, |
| 42 | clippy::len_zero, | ||
| 42 | // We don't use translatable diagnostics | 43 | // We don't use translatable diagnostics |
| 43 | rustc::diagnostic_outside_of_impl, | 44 | rustc::diagnostic_outside_of_impl, |
| 44 | // We are not implementing queries here so it's fine | 45 | // We are not implementing queries here so it's fine |
src/tools/miri/src/shims/global_ctor.rs+5-4| ... | @@ -3,6 +3,7 @@ | ... | @@ -3,6 +3,7 @@ |
| 3 | use std::task::Poll; | 3 | use std::task::Poll; |
| 4 | 4 | ||
| 5 | use rustc_abi::ExternAbi; | 5 | use rustc_abi::ExternAbi; |
| 6 | use rustc_span::Span; | ||
| 6 | use rustc_target::spec::BinaryFormat; | 7 | use rustc_target::spec::BinaryFormat; |
| 7 | 8 | ||
| 8 | use crate::*; | 9 | use crate::*; |
| ... | @@ -15,7 +16,7 @@ enum GlobalCtorStatePriv<'tcx> { | ... | @@ -15,7 +16,7 @@ enum GlobalCtorStatePriv<'tcx> { |
| 15 | #[default] | 16 | #[default] |
| 16 | Init, | 17 | Init, |
| 17 | /// The list of constructor functions that we still have to call. | 18 | /// The list of constructor functions that we still have to call. |
| 18 | Ctors(Vec<ImmTy<'tcx>>), | 19 | Ctors(Vec<(ImmTy<'tcx>, Span)>), |
| 19 | Done, | 20 | Done, |
| 20 | } | 21 | } |
| 21 | 22 | ||
| ... | @@ -67,19 +68,19 @@ impl<'tcx> GlobalCtorState<'tcx> { | ... | @@ -67,19 +68,19 @@ impl<'tcx> GlobalCtorState<'tcx> { |
| 67 | break 'new_state Ctors(ctors); | 68 | break 'new_state Ctors(ctors); |
| 68 | } | 69 | } |
| 69 | Ctors(ctors) => { | 70 | Ctors(ctors) => { |
| 70 | if let Some(ctor) = ctors.pop() { | 71 | if let Some((ctor, span)) = ctors.pop() { |
| 71 | let this = this.eval_context_mut(); | 72 | let this = this.eval_context_mut(); |
| 72 | 73 | ||
| 73 | let ctor = ctor.to_scalar().to_pointer(this)?; | 74 | let ctor = ctor.to_scalar().to_pointer(this)?; |
| 74 | let thread_callback = this.get_ptr_fn(ctor)?.as_instance()?; | 75 | let thread_callback = this.get_ptr_fn(ctor)?.as_instance()?; |
| 75 | 76 | ||
| 76 | // The signature of this function is `unsafe extern "C" fn()`. | 77 | // The signature of this function is `unsafe extern "C" fn()`. |
| 77 | this.call_function( | 78 | this.call_thread_root_function( |
| 78 | thread_callback, | 79 | thread_callback, |
| 79 | ExternAbi::C { unwind: false }, | 80 | ExternAbi::C { unwind: false }, |
| 80 | &[], | 81 | &[], |
| 81 | None, | 82 | None, |
| 82 | ReturnContinuation::Stop { cleanup: true }, | 83 | span, |
| 83 | )?; | 84 | )?; |
| 84 | 85 | ||
| 85 | return interp_ok(Poll::Pending); // we stay in this state (but `ctors` got shorter) | 86 | return interp_ok(Poll::Pending); // we stay in this state (but `ctors` got shorter) |
src/tools/miri/src/shims/tls.rs+23-22| ... | @@ -6,6 +6,7 @@ use std::task::Poll; | ... | @@ -6,6 +6,7 @@ use std::task::Poll; |
| 6 | 6 | ||
| 7 | use rustc_abi::{ExternAbi, HasDataLayout, Size}; | 7 | use rustc_abi::{ExternAbi, HasDataLayout, Size}; |
| 8 | use rustc_middle::ty; | 8 | use rustc_middle::ty; |
| 9 | use rustc_span::Span; | ||
| 9 | use rustc_target::spec::Os; | 10 | use rustc_target::spec::Os; |
| 10 | 11 | ||
| 11 | use crate::*; | 12 | use crate::*; |
| ... | @@ -17,7 +18,7 @@ pub struct TlsEntry<'tcx> { | ... | @@ -17,7 +18,7 @@ pub struct TlsEntry<'tcx> { |
| 17 | /// The data for this key. None is used to represent NULL. | 18 | /// The data for this key. None is used to represent NULL. |
| 18 | /// (We normalize this early to avoid having to do a NULL-ptr-test each time we access the data.) | 19 | /// (We normalize this early to avoid having to do a NULL-ptr-test each time we access the data.) |
| 19 | data: BTreeMap<ThreadId, Scalar>, | 20 | data: BTreeMap<ThreadId, Scalar>, |
| 20 | dtor: Option<ty::Instance<'tcx>>, | 21 | dtor: Option<(ty::Instance<'tcx>, Span)>, |
| 21 | } | 22 | } |
| 22 | 23 | ||
| 23 | #[derive(Default, Debug)] | 24 | #[derive(Default, Debug)] |
| ... | @@ -38,7 +39,7 @@ pub struct TlsData<'tcx> { | ... | @@ -38,7 +39,7 @@ pub struct TlsData<'tcx> { |
| 38 | 39 | ||
| 39 | /// On macOS, each thread holds a list of destructor functions with their | 40 | /// On macOS, each thread holds a list of destructor functions with their |
| 40 | /// respective data arguments. | 41 | /// respective data arguments. |
| 41 | macos_thread_dtors: BTreeMap<ThreadId, Vec<(ty::Instance<'tcx>, Scalar)>>, | 42 | macos_thread_dtors: BTreeMap<ThreadId, Vec<(ty::Instance<'tcx>, Scalar, Span)>>, |
| 42 | } | 43 | } |
| 43 | 44 | ||
| 44 | impl<'tcx> Default for TlsData<'tcx> { | 45 | impl<'tcx> Default for TlsData<'tcx> { |
| ... | @@ -57,7 +58,7 @@ impl<'tcx> TlsData<'tcx> { | ... | @@ -57,7 +58,7 @@ impl<'tcx> TlsData<'tcx> { |
| 57 | #[expect(clippy::arithmetic_side_effects)] | 58 | #[expect(clippy::arithmetic_side_effects)] |
| 58 | pub fn create_tls_key( | 59 | pub fn create_tls_key( |
| 59 | &mut self, | 60 | &mut self, |
| 60 | dtor: Option<ty::Instance<'tcx>>, | 61 | dtor: Option<(ty::Instance<'tcx>, Span)>, |
| 61 | max_size: Size, | 62 | max_size: Size, |
| 62 | ) -> InterpResult<'tcx, TlsKey> { | 63 | ) -> InterpResult<'tcx, TlsKey> { |
| 63 | let new_key = self.next_key; | 64 | let new_key = self.next_key; |
| ... | @@ -126,8 +127,9 @@ impl<'tcx> TlsData<'tcx> { | ... | @@ -126,8 +127,9 @@ impl<'tcx> TlsData<'tcx> { |
| 126 | thread: ThreadId, | 127 | thread: ThreadId, |
| 127 | dtor: ty::Instance<'tcx>, | 128 | dtor: ty::Instance<'tcx>, |
| 128 | data: Scalar, | 129 | data: Scalar, |
| 130 | span: Span, | ||
| 129 | ) -> InterpResult<'tcx> { | 131 | ) -> InterpResult<'tcx> { |
| 130 | self.macos_thread_dtors.entry(thread).or_default().push((dtor, data)); | 132 | self.macos_thread_dtors.entry(thread).or_default().push((dtor, data, span)); |
| 131 | interp_ok(()) | 133 | interp_ok(()) |
| 132 | } | 134 | } |
| 133 | 135 | ||
| ... | @@ -154,7 +156,7 @@ impl<'tcx> TlsData<'tcx> { | ... | @@ -154,7 +156,7 @@ impl<'tcx> TlsData<'tcx> { |
| 154 | &mut self, | 156 | &mut self, |
| 155 | key: Option<TlsKey>, | 157 | key: Option<TlsKey>, |
| 156 | thread_id: ThreadId, | 158 | thread_id: ThreadId, |
| 157 | ) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey)> { | 159 | ) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey, Span)> { |
| 158 | use std::ops::Bound::*; | 160 | use std::ops::Bound::*; |
| 159 | 161 | ||
| 160 | let thread_local = &mut self.keys; | 162 | let thread_local = &mut self.keys; |
| ... | @@ -172,11 +174,10 @@ impl<'tcx> TlsData<'tcx> { | ... | @@ -172,11 +174,10 @@ impl<'tcx> TlsData<'tcx> { |
| 172 | for (&key, TlsEntry { data, dtor }) in thread_local.range_mut((start, Unbounded)) { | 174 | for (&key, TlsEntry { data, dtor }) in thread_local.range_mut((start, Unbounded)) { |
| 173 | match data.entry(thread_id) { | 175 | match data.entry(thread_id) { |
| 174 | BTreeEntry::Occupied(entry) => { | 176 | BTreeEntry::Occupied(entry) => { |
| 175 | if let Some(dtor) = dtor { | 177 | if let Some((dtor, span)) = dtor { |
| 176 | // Set TLS data to NULL, and call dtor with old value. | 178 | // Set TLS data to NULL, and call dtor with old value. |
| 177 | let data_scalar = entry.remove(); | 179 | let data_scalar = entry.remove(); |
| 178 | let ret = Some((*dtor, data_scalar, key)); | 180 | return Some((*dtor, data_scalar, key, *span)); |
| 179 | return ret; | ||
| 180 | } | 181 | } |
| 181 | } | 182 | } |
| 182 | BTreeEntry::Vacant(_) => {} | 183 | BTreeEntry::Vacant(_) => {} |
| ... | @@ -205,7 +206,7 @@ impl VisitProvenance for TlsData<'_> { | ... | @@ -205,7 +206,7 @@ impl VisitProvenance for TlsData<'_> { |
| 205 | for scalar in keys.values().flat_map(|v| v.data.values()) { | 206 | for scalar in keys.values().flat_map(|v| v.data.values()) { |
| 206 | scalar.visit_provenance(visit); | 207 | scalar.visit_provenance(visit); |
| 207 | } | 208 | } |
| 208 | for (_, scalar) in macos_thread_dtors.values().flatten() { | 209 | for (_, scalar, _) in macos_thread_dtors.values().flatten() { |
| 209 | scalar.visit_provenance(visit); | 210 | scalar.visit_provenance(visit); |
| 210 | } | 211 | } |
| 211 | } | 212 | } |
| ... | @@ -222,7 +223,7 @@ enum TlsDtorsStatePriv<'tcx> { | ... | @@ -222,7 +223,7 @@ enum TlsDtorsStatePriv<'tcx> { |
| 222 | PthreadDtors(RunningDtorState), | 223 | PthreadDtors(RunningDtorState), |
| 223 | /// For Windows Dtors, we store the list of functions that we still have to call. | 224 | /// For Windows Dtors, we store the list of functions that we still have to call. |
| 224 | /// These are functions from the magic `.CRT$XLB` linker section. | 225 | /// These are functions from the magic `.CRT$XLB` linker section. |
| 225 | WindowsDtors(Vec<ImmTy<'tcx>>), | 226 | WindowsDtors(Vec<(ImmTy<'tcx>, Span)>), |
| 226 | Done, | 227 | Done, |
| 227 | } | 228 | } |
| 228 | 229 | ||
| ... | @@ -273,8 +274,8 @@ impl<'tcx> TlsDtorsState<'tcx> { | ... | @@ -273,8 +274,8 @@ impl<'tcx> TlsDtorsState<'tcx> { |
| 273 | } | 274 | } |
| 274 | } | 275 | } |
| 275 | WindowsDtors(dtors) => { | 276 | WindowsDtors(dtors) => { |
| 276 | if let Some(dtor) = dtors.pop() { | 277 | if let Some((dtor, span)) = dtors.pop() { |
| 277 | this.schedule_windows_tls_dtor(dtor)?; | 278 | this.schedule_windows_tls_dtor(dtor, span)?; |
| 278 | return interp_ok(Poll::Pending); // we stay in this state (but `dtors` got shorter) | 279 | return interp_ok(Poll::Pending); // we stay in this state (but `dtors` got shorter) |
| 279 | } else { | 280 | } else { |
| 280 | // No more destructors to run. | 281 | // No more destructors to run. |
| ... | @@ -297,7 +298,7 @@ impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {} | ... | @@ -297,7 +298,7 @@ impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {} |
| 297 | trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | 298 | trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 298 | /// Schedule TLS destructors for Windows. | 299 | /// Schedule TLS destructors for Windows. |
| 299 | /// On windows, TLS destructors are managed by std. | 300 | /// On windows, TLS destructors are managed by std. |
| 300 | fn lookup_windows_tls_dtors(&mut self) -> InterpResult<'tcx, Vec<ImmTy<'tcx>>> { | 301 | fn lookup_windows_tls_dtors(&mut self) -> InterpResult<'tcx, Vec<(ImmTy<'tcx>, Span)>> { |
| 301 | let this = self.eval_context_mut(); | 302 | let this = self.eval_context_mut(); |
| 302 | 303 | ||
| 303 | // Windows has a special magic linker section that is run on certain events. | 304 | // Windows has a special magic linker section that is run on certain events. |
| ... | @@ -305,7 +306,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -305,7 +306,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 305 | interp_ok(this.lookup_link_section(|section| section == ".CRT$XLB")?) | 306 | interp_ok(this.lookup_link_section(|section| section == ".CRT$XLB")?) |
| 306 | } | 307 | } |
| 307 | 308 | ||
| 308 | fn schedule_windows_tls_dtor(&mut self, dtor: ImmTy<'tcx>) -> InterpResult<'tcx> { | 309 | fn schedule_windows_tls_dtor(&mut self, dtor: ImmTy<'tcx>, span: Span) -> InterpResult<'tcx> { |
| 309 | let this = self.eval_context_mut(); | 310 | let this = self.eval_context_mut(); |
| 310 | 311 | ||
| 311 | let dtor = dtor.to_scalar().to_pointer(this)?; | 312 | let dtor = dtor.to_scalar().to_pointer(this)?; |
| ... | @@ -320,12 +321,12 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -320,12 +321,12 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 320 | // The signature of this function is `unsafe extern "system" fn(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID)`. | 321 | // The signature of this function is `unsafe extern "system" fn(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID)`. |
| 321 | // FIXME: `h` should be a handle to the current module and what `pv` should be is unknown | 322 | // FIXME: `h` should be a handle to the current module and what `pv` should be is unknown |
| 322 | // but both are ignored by std. | 323 | // but both are ignored by std. |
| 323 | this.call_function( | 324 | this.call_thread_root_function( |
| 324 | thread_callback, | 325 | thread_callback, |
| 325 | ExternAbi::System { unwind: false }, | 326 | ExternAbi::System { unwind: false }, |
| 326 | &[null_ptr.clone(), ImmTy::from_scalar(reason, this.machine.layouts.u32), null_ptr], | 327 | &[null_ptr.clone(), ImmTy::from_scalar(reason, this.machine.layouts.u32), null_ptr], |
| 327 | None, | 328 | None, |
| 328 | ReturnContinuation::Stop { cleanup: true }, | 329 | span, |
| 329 | )?; | 330 | )?; |
| 330 | interp_ok(()) | 331 | interp_ok(()) |
| 331 | } | 332 | } |
| ... | @@ -338,15 +339,15 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -338,15 +339,15 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 338 | // registers another destructor, it will be run next. | 339 | // registers another destructor, it will be run next. |
| 339 | // See https://github.com/apple-oss-distributions/dyld/blob/d552c40cd1de105f0ec95008e0e0c0972de43456/dyld/DyldRuntimeState.cpp#L2277 | 340 | // See https://github.com/apple-oss-distributions/dyld/blob/d552c40cd1de105f0ec95008e0e0c0972de43456/dyld/DyldRuntimeState.cpp#L2277 |
| 340 | let dtor = this.machine.tls.macos_thread_dtors.get_mut(&thread_id).and_then(Vec::pop); | 341 | let dtor = this.machine.tls.macos_thread_dtors.get_mut(&thread_id).and_then(Vec::pop); |
| 341 | if let Some((instance, data)) = dtor { | 342 | if let Some((instance, data, span)) = dtor { |
| 342 | trace!("Running macos dtor {:?} on {:?} at {:?}", instance, data, thread_id); | 343 | trace!("Running macos dtor {:?} on {:?} at {:?}", instance, data, thread_id); |
| 343 | 344 | ||
| 344 | this.call_function( | 345 | this.call_thread_root_function( |
| 345 | instance, | 346 | instance, |
| 346 | ExternAbi::C { unwind: false }, | 347 | ExternAbi::C { unwind: false }, |
| 347 | &[ImmTy::from_scalar(data, this.machine.layouts.mut_raw_ptr)], | 348 | &[ImmTy::from_scalar(data, this.machine.layouts.mut_raw_ptr)], |
| 348 | None, | 349 | None, |
| 349 | ReturnContinuation::Stop { cleanup: true }, | 350 | span, |
| 350 | )?; | 351 | )?; |
| 351 | 352 | ||
| 352 | return interp_ok(Poll::Pending); | 353 | return interp_ok(Poll::Pending); |
| ... | @@ -370,7 +371,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -370,7 +371,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 370 | // We ran each dtor once, start over from the beginning. | 371 | // We ran each dtor once, start over from the beginning. |
| 371 | None => this.machine.tls.fetch_tls_dtor(None, active_thread), | 372 | None => this.machine.tls.fetch_tls_dtor(None, active_thread), |
| 372 | }; | 373 | }; |
| 373 | if let Some((instance, ptr, key)) = dtor { | 374 | if let Some((instance, ptr, key, span)) = dtor { |
| 374 | state.last_key = Some(key); | 375 | state.last_key = Some(key); |
| 375 | trace!("Running TLS dtor {:?} on {:?} at {:?}", instance, ptr, active_thread); | 376 | trace!("Running TLS dtor {:?} on {:?} at {:?}", instance, ptr, active_thread); |
| 376 | assert!( | 377 | assert!( |
| ... | @@ -378,12 +379,12 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -378,12 +379,12 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 378 | "data can't be NULL when dtor is called!" | 379 | "data can't be NULL when dtor is called!" |
| 379 | ); | 380 | ); |
| 380 | 381 | ||
| 381 | this.call_function( | 382 | this.call_thread_root_function( |
| 382 | instance, | 383 | instance, |
| 383 | ExternAbi::C { unwind: false }, | 384 | ExternAbi::C { unwind: false }, |
| 384 | &[ImmTy::from_scalar(ptr, this.machine.layouts.mut_raw_ptr)], | 385 | &[ImmTy::from_scalar(ptr, this.machine.layouts.mut_raw_ptr)], |
| 385 | None, | 386 | None, |
| 386 | ReturnContinuation::Stop { cleanup: true }, | 387 | span, |
| 387 | )?; | 388 | )?; |
| 388 | 389 | ||
| 389 | return interp_ok(Poll::Pending); | 390 | return interp_ok(Poll::Pending); |
src/tools/miri/src/shims/unix/foreign_items.rs+10-71| ... | @@ -235,33 +235,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -235,33 +235,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 235 | trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset); | 235 | trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset); |
| 236 | this.write(fd, buf, count, Some(offset), dest)?; | 236 | this.write(fd, buf, count, Some(offset), dest)?; |
| 237 | } | 237 | } |
| 238 | "pread64" => { | ||
| 239 | let [fd, buf, count, offset] = this.check_shim_sig( | ||
| 240 | shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off64_t) -> isize), | ||
| 241 | link_name, | ||
| 242 | abi, | ||
| 243 | args, | ||
| 244 | )?; | ||
| 245 | let fd = this.read_scalar(fd)?.to_i32()?; | ||
| 246 | let buf = this.read_pointer(buf)?; | ||
| 247 | let count = this.read_target_usize(count)?; | ||
| 248 | let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?; | ||
| 249 | this.read(fd, buf, count, Some(offset), dest)?; | ||
| 250 | } | ||
| 251 | "pwrite64" => { | ||
| 252 | let [fd, buf, n, offset] = this.check_shim_sig( | ||
| 253 | shim_sig!(extern "C" fn(i32, *const _, usize, libc::off64_t) -> isize), | ||
| 254 | link_name, | ||
| 255 | abi, | ||
| 256 | args, | ||
| 257 | )?; | ||
| 258 | let fd = this.read_scalar(fd)?.to_i32()?; | ||
| 259 | let buf = this.read_pointer(buf)?; | ||
| 260 | let count = this.read_target_usize(n)?; | ||
| 261 | let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?; | ||
| 262 | trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset); | ||
| 263 | this.write(fd, buf, count, Some(offset), dest)?; | ||
| 264 | } | ||
| 265 | "close" => { | 238 | "close" => { |
| 266 | let [fd] = this.check_shim_sig( | 239 | let [fd] = this.check_shim_sig( |
| 267 | shim_sig!(extern "C" fn(i32) -> i32), | 240 | shim_sig!(extern "C" fn(i32) -> i32), |
| ... | @@ -317,7 +290,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -317,7 +290,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 317 | } | 290 | } |
| 318 | 291 | ||
| 319 | // File and file system access | 292 | // File and file system access |
| 320 | "open" | "open64" => { | 293 | "open" => { |
| 321 | // `open` is variadic, the third argument is only present when the second argument | 294 | // `open` is variadic, the third argument is only present when the second argument |
| 322 | // has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set | 295 | // has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set |
| 323 | let ([path_raw, flag], varargs) = | 296 | let ([path_raw, flag], varargs) = |
| ... | @@ -345,6 +318,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -345,6 +318,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 345 | let result = this.symlink(target, linkpath)?; | 318 | let result = this.symlink(target, linkpath)?; |
| 346 | this.write_scalar(result, dest)?; | 319 | this.write_scalar(result, dest)?; |
| 347 | } | 320 | } |
| 321 | "fstat" => { | ||
| 322 | let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; | ||
| 323 | let result = this.fstat(fd, buf)?; | ||
| 324 | this.write_scalar(result, dest)?; | ||
| 325 | } | ||
| 348 | "rename" => { | 326 | "rename" => { |
| 349 | let [oldpath, newpath] = this.check_shim_sig( | 327 | let [oldpath, newpath] = this.check_shim_sig( |
| 350 | shim_sig!(extern "C" fn(*const _, *const _) -> i32), | 328 | shim_sig!(extern "C" fn(*const _, *const _) -> i32), |
| ... | @@ -395,18 +373,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -395,18 +373,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 395 | let result = this.closedir(dirp)?; | 373 | let result = this.closedir(dirp)?; |
| 396 | this.write_scalar(result, dest)?; | 374 | this.write_scalar(result, dest)?; |
| 397 | } | 375 | } |
| 398 | "lseek64" => { | ||
| 399 | let [fd, offset, whence] = this.check_shim_sig( | ||
| 400 | shim_sig!(extern "C" fn(i32, libc::off64_t, i32) -> libc::off64_t), | ||
| 401 | link_name, | ||
| 402 | abi, | ||
| 403 | args, | ||
| 404 | )?; | ||
| 405 | let fd = this.read_scalar(fd)?.to_i32()?; | ||
| 406 | let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?; | ||
| 407 | let whence = this.read_scalar(whence)?.to_i32()?; | ||
| 408 | this.lseek64(fd, offset, whence, dest)?; | ||
| 409 | } | ||
| 410 | "lseek" => { | 376 | "lseek" => { |
| 411 | let [fd, offset, whence] = this.check_shim_sig( | 377 | let [fd, offset, whence] = this.check_shim_sig( |
| 412 | shim_sig!(extern "C" fn(i32, libc::off_t, i32) -> libc::off_t), | 378 | shim_sig!(extern "C" fn(i32, libc::off_t, i32) -> libc::off_t), |
| ... | @@ -419,18 +385,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -419,18 +385,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 419 | let whence = this.read_scalar(whence)?.to_i32()?; | 385 | let whence = this.read_scalar(whence)?.to_i32()?; |
| 420 | this.lseek64(fd, offset, whence, dest)?; | 386 | this.lseek64(fd, offset, whence, dest)?; |
| 421 | } | 387 | } |
| 422 | "ftruncate64" => { | ||
| 423 | let [fd, length] = this.check_shim_sig( | ||
| 424 | shim_sig!(extern "C" fn(i32, libc::off64_t) -> i32), | ||
| 425 | link_name, | ||
| 426 | abi, | ||
| 427 | args, | ||
| 428 | )?; | ||
| 429 | let fd = this.read_scalar(fd)?.to_i32()?; | ||
| 430 | let length = this.read_scalar(length)?.to_int(length.layout.size)?; | ||
| 431 | let result = this.ftruncate64(fd, length)?; | ||
| 432 | this.write_scalar(result, dest)?; | ||
| 433 | } | ||
| 434 | "ftruncate" => { | 388 | "ftruncate" => { |
| 435 | let [fd, length] = this.check_shim_sig( | 389 | let [fd, length] = this.check_shim_sig( |
| 436 | shim_sig!(extern "C" fn(i32, libc::off_t) -> i32), | 390 | shim_sig!(extern "C" fn(i32, libc::off_t) -> i32), |
| ... | @@ -511,24 +465,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -511,24 +465,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 511 | this.write_scalar(result, dest)?; | 465 | this.write_scalar(result, dest)?; |
| 512 | } | 466 | } |
| 513 | 467 | ||
| 514 | "posix_fallocate64" => { | ||
| 515 | // posix_fallocate64 is only supported on Linux and Android | ||
| 516 | this.check_target_os(&[Os::Linux, Os::Android], link_name)?; | ||
| 517 | let [fd, offset, len] = this.check_shim_sig( | ||
| 518 | shim_sig!(extern "C" fn(i32, libc::off64_t, libc::off64_t) -> i32), | ||
| 519 | link_name, | ||
| 520 | abi, | ||
| 521 | args, | ||
| 522 | )?; | ||
| 523 | |||
| 524 | let fd = this.read_scalar(fd)?.to_i32()?; | ||
| 525 | let offset = this.read_scalar(offset)?.to_i64()?; | ||
| 526 | let len = this.read_scalar(len)?.to_i64()?; | ||
| 527 | |||
| 528 | let result = this.posix_fallocate(fd, offset, len)?; | ||
| 529 | this.write_scalar(result, dest)?; | ||
| 530 | } | ||
| 531 | |||
| 532 | "realpath" => { | 468 | "realpath" => { |
| 533 | let [path, resolved_path] = this.check_shim_sig( | 469 | let [path, resolved_path] = this.check_shim_sig( |
| 534 | shim_sig!(extern "C" fn(*const _, *mut _) -> *mut _), | 470 | shim_sig!(extern "C" fn(*const _, *mut _) -> *mut _), |
| ... | @@ -698,7 +634,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -698,7 +634,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 698 | 634 | ||
| 699 | // Extract the function type out of the signature (that seems easier than constructing it ourselves). | 635 | // Extract the function type out of the signature (that seems easier than constructing it ourselves). |
| 700 | let dtor = if !this.ptr_is_null(dtor)? { | 636 | let dtor = if !this.ptr_is_null(dtor)? { |
| 701 | Some(this.get_ptr_fn(dtor)?.as_instance()?) | 637 | Some(( |
| 638 | this.get_ptr_fn(dtor)?.as_instance()?, | ||
| 639 | this.machine.current_user_relevant_span(), | ||
| 640 | )) | ||
| 702 | } else { | 641 | } else { |
| 703 | None | 642 | None |
| 704 | }; | 643 | }; |
src/tools/miri/src/shims/unix/freebsd/foreign_items.rs+2-8| ... | @@ -148,15 +148,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -148,15 +148,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 148 | let result = this.macos_fbsd_solarish_lstat(path, buf)?; | 148 | let result = this.macos_fbsd_solarish_lstat(path, buf)?; |
| 149 | this.write_scalar(result, dest)?; | 149 | this.write_scalar(result, dest)?; |
| 150 | } | 150 | } |
| 151 | "fstat" | "fstat@FBSD_1.0" => { | 151 | "fstat@FBSD_1.0" => { |
| 152 | let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; | 152 | let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; |
| 153 | let result = this.macos_fbsd_solarish_fstat(fd, buf)?; | 153 | let result = this.fstat(fd, buf)?; |
| 154 | this.write_scalar(result, dest)?; | ||
| 155 | } | ||
| 156 | "readdir_r" | "readdir_r@FBSD_1.0" => { | ||
| 157 | let [dirp, entry, result] = | ||
| 158 | this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; | ||
| 159 | let result = this.macos_fbsd_readdir_r(dirp, entry, result)?; | ||
| 160 | this.write_scalar(result, dest)?; | 154 | this.write_scalar(result, dest)?; |
| 161 | } | 155 | } |
| 162 | "readdir" | "readdir@FBSD_1.0" => { | 156 | "readdir" | "readdir@FBSD_1.0" => { |
src/tools/miri/src/shims/unix/fs.rs+22-48| ... | @@ -118,7 +118,7 @@ impl UnixFileDescription for FileHandle { | ... | @@ -118,7 +118,7 @@ impl UnixFileDescription for FileHandle { |
| 118 | 118 | ||
| 119 | impl<'tcx> EvalContextExtPrivate<'tcx> for crate::MiriInterpCx<'tcx> {} | 119 | impl<'tcx> EvalContextExtPrivate<'tcx> for crate::MiriInterpCx<'tcx> {} |
| 120 | trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { | 120 | trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 121 | fn macos_fbsd_solarish_write_stat_buf( | 121 | fn write_stat_buf( |
| 122 | &mut self, | 122 | &mut self, |
| 123 | metadata: FileMetadata, | 123 | metadata: FileMetadata, |
| 124 | buf_op: &OpTy<'tcx>, | 124 | buf_op: &OpTy<'tcx>, |
| ... | @@ -130,7 +130,11 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -130,7 +130,11 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 130 | let (modified_sec, modified_nsec) = metadata.modified.unwrap_or((0, 0)); | 130 | let (modified_sec, modified_nsec) = metadata.modified.unwrap_or((0, 0)); |
| 131 | let mode = metadata.mode.to_uint(this.libc_ty_layout("mode_t").size)?; | 131 | let mode = metadata.mode.to_uint(this.libc_ty_layout("mode_t").size)?; |
| 132 | 132 | ||
| 133 | let buf = this.deref_pointer_as(buf_op, this.libc_ty_layout("stat"))?; | 133 | // We do *not* use `deref_pointer_as` here since determining the right pointee type |
| 134 | // is highly non-trivial: it depends on which exact alias of the function was invoked | ||
| 135 | // (e.g. `fstat` vs `fstat64`), and then on FreeBSD it also depends on the ABI level | ||
| 136 | // which can be different between the libc used by std and the libc used by everyone else. | ||
| 137 | let buf = this.deref_pointer(buf_op)?; | ||
| 134 | this.write_int_fields_named( | 138 | this.write_int_fields_named( |
| 135 | &[ | 139 | &[ |
| 136 | ("st_dev", metadata.dev.into()), | 140 | ("st_dev", metadata.dev.into()), |
| ... | @@ -141,8 +145,11 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -141,8 +145,11 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 141 | ("st_gid", metadata.gid.into()), | 145 | ("st_gid", metadata.gid.into()), |
| 142 | ("st_rdev", 0), | 146 | ("st_rdev", 0), |
| 143 | ("st_atime", access_sec.into()), | 147 | ("st_atime", access_sec.into()), |
| 148 | ("st_atime_nsec", access_nsec.into()), | ||
| 144 | ("st_mtime", modified_sec.into()), | 149 | ("st_mtime", modified_sec.into()), |
| 150 | ("st_mtime_nsec", modified_nsec.into()), | ||
| 145 | ("st_ctime", 0), | 151 | ("st_ctime", 0), |
| 152 | ("st_ctime_nsec", 0), | ||
| 146 | ("st_size", metadata.size.into()), | 153 | ("st_size", metadata.size.into()), |
| 147 | ("st_blocks", 0), | 154 | ("st_blocks", 0), |
| 148 | ("st_blksize", 0), | 155 | ("st_blksize", 0), |
| ... | @@ -153,9 +160,6 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -153,9 +160,6 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 153 | if matches!(&this.tcx.sess.target.os, Os::MacOs | Os::FreeBsd) { | 160 | if matches!(&this.tcx.sess.target.os, Os::MacOs | Os::FreeBsd) { |
| 154 | this.write_int_fields_named( | 161 | this.write_int_fields_named( |
| 155 | &[ | 162 | &[ |
| 156 | ("st_atime_nsec", access_nsec.into()), | ||
| 157 | ("st_mtime_nsec", modified_nsec.into()), | ||
| 158 | ("st_ctime_nsec", 0), | ||
| 159 | ("st_birthtime", created_sec.into()), | 163 | ("st_birthtime", created_sec.into()), |
| 160 | ("st_birthtime_nsec", created_nsec.into()), | 164 | ("st_birthtime_nsec", created_nsec.into()), |
| 161 | ("st_flags", 0), | 165 | ("st_flags", 0), |
| ... | @@ -550,7 +554,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -550,7 +554,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 550 | Err(err) => return this.set_last_error_and_return_i32(err), | 554 | Err(err) => return this.set_last_error_and_return_i32(err), |
| 551 | }; | 555 | }; |
| 552 | 556 | ||
| 553 | interp_ok(Scalar::from_i32(this.macos_fbsd_solarish_write_stat_buf(metadata, buf_op)?)) | 557 | interp_ok(Scalar::from_i32(this.write_stat_buf(metadata, buf_op)?)) |
| 554 | } | 558 | } |
| 555 | 559 | ||
| 556 | // `lstat` is used to get symlink metadata. | 560 | // `lstat` is used to get symlink metadata. |
| ... | @@ -583,22 +587,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -583,22 +587,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 583 | Err(err) => return this.set_last_error_and_return_i32(err), | 587 | Err(err) => return this.set_last_error_and_return_i32(err), |
| 584 | }; | 588 | }; |
| 585 | 589 | ||
| 586 | interp_ok(Scalar::from_i32(this.macos_fbsd_solarish_write_stat_buf(metadata, buf_op)?)) | 590 | interp_ok(Scalar::from_i32(this.write_stat_buf(metadata, buf_op)?)) |
| 587 | } | 591 | } |
| 588 | 592 | ||
| 589 | fn macos_fbsd_solarish_fstat( | 593 | fn fstat(&mut self, fd_op: &OpTy<'tcx>, buf_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { |
| 590 | &mut self, | ||
| 591 | fd_op: &OpTy<'tcx>, | ||
| 592 | buf_op: &OpTy<'tcx>, | ||
| 593 | ) -> InterpResult<'tcx, Scalar> { | ||
| 594 | let this = self.eval_context_mut(); | 594 | let this = self.eval_context_mut(); |
| 595 | 595 | ||
| 596 | if !matches!(&this.tcx.sess.target.os, Os::MacOs | Os::FreeBsd | Os::Solaris | Os::Illumos) | 596 | if !matches!( |
| 597 | { | 597 | &this.tcx.sess.target.os, |
| 598 | panic!( | 598 | Os::MacOs | Os::FreeBsd | Os::Solaris | Os::Illumos | Os::Linux |
| 599 | "`macos_fbsd_solaris_fstat` should not be called on {}", | 599 | ) { |
| 600 | this.tcx.sess.target.os | 600 | panic!("`fstat` should not be called on {}", this.tcx.sess.target.os); |
| 601 | ); | ||
| 602 | } | 601 | } |
| 603 | 602 | ||
| 604 | let fd = this.read_scalar(fd_op)?.to_i32()?; | 603 | let fd = this.read_scalar(fd_op)?.to_i32()?; |
| ... | @@ -614,7 +613,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -614,7 +613,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 614 | Ok(metadata) => metadata, | 613 | Ok(metadata) => metadata, |
| 615 | Err(err) => return this.set_last_error_and_return_i32(err), | 614 | Err(err) => return this.set_last_error_and_return_i32(err), |
| 616 | }; | 615 | }; |
| 617 | interp_ok(Scalar::from_i32(this.macos_fbsd_solarish_write_stat_buf(metadata, buf_op)?)) | 616 | interp_ok(Scalar::from_i32(this.write_stat_buf(metadata, buf_op)?)) |
| 618 | } | 617 | } |
| 619 | 618 | ||
| 620 | fn linux_statx( | 619 | fn linux_statx( |
| ... | @@ -1031,7 +1030,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -1031,7 +1030,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 1031 | interp_ok(Scalar::from_maybe_pointer(entry.unwrap_or_else(Pointer::null), this)) | 1030 | interp_ok(Scalar::from_maybe_pointer(entry.unwrap_or_else(Pointer::null), this)) |
| 1032 | } | 1031 | } |
| 1033 | 1032 | ||
| 1034 | fn macos_fbsd_readdir_r( | 1033 | fn macos_readdir_r( |
| 1035 | &mut self, | 1034 | &mut self, |
| 1036 | dirp_op: &OpTy<'tcx>, | 1035 | dirp_op: &OpTy<'tcx>, |
| 1037 | entry_op: &OpTy<'tcx>, | 1036 | entry_op: &OpTy<'tcx>, |
| ... | @@ -1039,9 +1038,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -1039,9 +1038,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 1039 | ) -> InterpResult<'tcx, Scalar> { | 1038 | ) -> InterpResult<'tcx, Scalar> { |
| 1040 | let this = self.eval_context_mut(); | 1039 | let this = self.eval_context_mut(); |
| 1041 | 1040 | ||
| 1042 | if !matches!(&this.tcx.sess.target.os, Os::MacOs | Os::FreeBsd) { | 1041 | this.assert_target_os(Os::MacOs, "readdir_r"); |
| 1043 | panic!("`macos_fbsd_readdir_r` should not be called on {}", this.tcx.sess.target.os); | ||
| 1044 | } | ||
| 1045 | 1042 | ||
| 1046 | let dirp = this.read_target_usize(dirp_op)?; | 1043 | let dirp = this.read_target_usize(dirp_op)?; |
| 1047 | let result_place = this.deref_pointer_as(result_op, this.machine.layouts.mut_raw_ptr)?; | 1044 | let result_place = this.deref_pointer_as(result_op, this.machine.layouts.mut_raw_ptr)?; |
| ... | @@ -1097,39 +1094,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -1097,39 +1094,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 1097 | 1094 | ||
| 1098 | let file_type = this.file_type_to_d_type(dir_entry.file_type())?; | 1095 | let file_type = this.file_type_to_d_type(dir_entry.file_type())?; |
| 1099 | 1096 | ||
| 1100 | // Common fields. | ||
| 1101 | this.write_int_fields_named( | 1097 | this.write_int_fields_named( |
| 1102 | &[ | 1098 | &[ |
| 1103 | ("d_reclen", 0), | 1099 | ("d_reclen", 0), |
| 1104 | ("d_namlen", file_name_len.into()), | 1100 | ("d_namlen", file_name_len.into()), |
| 1105 | ("d_type", file_type.into()), | 1101 | ("d_type", file_type.into()), |
| 1102 | ("d_ino", ino.into()), | ||
| 1103 | ("d_seekoff", 0), | ||
| 1106 | ], | 1104 | ], |
| 1107 | &entry_place, | 1105 | &entry_place, |
| 1108 | )?; | 1106 | )?; |
| 1109 | // Special fields. | ||
| 1110 | match this.tcx.sess.target.os { | ||
| 1111 | Os::MacOs => { | ||
| 1112 | #[rustfmt::skip] | ||
| 1113 | this.write_int_fields_named( | ||
| 1114 | &[ | ||
| 1115 | ("d_ino", ino.into()), | ||
| 1116 | ("d_seekoff", 0), | ||
| 1117 | ], | ||
| 1118 | &entry_place, | ||
| 1119 | )?; | ||
| 1120 | } | ||
| 1121 | Os::FreeBsd => { | ||
| 1122 | #[rustfmt::skip] | ||
| 1123 | this.write_int_fields_named( | ||
| 1124 | &[ | ||
| 1125 | ("d_fileno", ino.into()), | ||
| 1126 | ("d_off", 0), | ||
| 1127 | ], | ||
| 1128 | &entry_place, | ||
| 1129 | )?; | ||
| 1130 | } | ||
| 1131 | _ => unreachable!(), | ||
| 1132 | } | ||
| 1133 | this.write_scalar(this.read_scalar(entry_op)?, &result_place)?; | 1107 | this.write_scalar(this.read_scalar(entry_op)?, &result_place)?; |
| 1134 | 1108 | ||
| 1135 | Scalar::from_i32(0) | 1109 | Scalar::from_i32(0) |
src/tools/miri/src/shims/unix/linux/foreign_items.rs+74-1| ... | @@ -36,6 +36,80 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -36,6 +36,80 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 36 | 36 | ||
| 37 | match link_name.as_str() { | 37 | match link_name.as_str() { |
| 38 | // File related shims | 38 | // File related shims |
| 39 | "open64" => { | ||
| 40 | // `open64` is variadic, the third argument is only present when the second argument | ||
| 41 | // has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set | ||
| 42 | let ([path_raw, flag], varargs) = | ||
| 43 | this.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?; | ||
| 44 | let result = this.open(path_raw, flag, varargs)?; | ||
| 45 | this.write_scalar(result, dest)?; | ||
| 46 | } | ||
| 47 | "pread64" => { | ||
| 48 | let [fd, buf, count, offset] = this.check_shim_sig( | ||
| 49 | shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off64_t) -> isize), | ||
| 50 | link_name, | ||
| 51 | abi, | ||
| 52 | args, | ||
| 53 | )?; | ||
| 54 | let fd = this.read_scalar(fd)?.to_i32()?; | ||
| 55 | let buf = this.read_pointer(buf)?; | ||
| 56 | let count = this.read_target_usize(count)?; | ||
| 57 | let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?; | ||
| 58 | this.read(fd, buf, count, Some(offset), dest)?; | ||
| 59 | } | ||
| 60 | "pwrite64" => { | ||
| 61 | let [fd, buf, n, offset] = this.check_shim_sig( | ||
| 62 | shim_sig!(extern "C" fn(i32, *const _, usize, libc::off64_t) -> isize), | ||
| 63 | link_name, | ||
| 64 | abi, | ||
| 65 | args, | ||
| 66 | )?; | ||
| 67 | let fd = this.read_scalar(fd)?.to_i32()?; | ||
| 68 | let buf = this.read_pointer(buf)?; | ||
| 69 | let count = this.read_target_usize(n)?; | ||
| 70 | let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?; | ||
| 71 | trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset); | ||
| 72 | this.write(fd, buf, count, Some(offset), dest)?; | ||
| 73 | } | ||
| 74 | "lseek64" => { | ||
| 75 | let [fd, offset, whence] = this.check_shim_sig( | ||
| 76 | shim_sig!(extern "C" fn(i32, libc::off64_t, i32) -> libc::off64_t), | ||
| 77 | link_name, | ||
| 78 | abi, | ||
| 79 | args, | ||
| 80 | )?; | ||
| 81 | let fd = this.read_scalar(fd)?.to_i32()?; | ||
| 82 | let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?; | ||
| 83 | let whence = this.read_scalar(whence)?.to_i32()?; | ||
| 84 | this.lseek64(fd, offset, whence, dest)?; | ||
| 85 | } | ||
| 86 | "ftruncate64" => { | ||
| 87 | let [fd, length] = this.check_shim_sig( | ||
| 88 | shim_sig!(extern "C" fn(i32, libc::off64_t) -> i32), | ||
| 89 | link_name, | ||
| 90 | abi, | ||
| 91 | args, | ||
| 92 | )?; | ||
| 93 | let fd = this.read_scalar(fd)?.to_i32()?; | ||
| 94 | let length = this.read_scalar(length)?.to_int(length.layout.size)?; | ||
| 95 | let result = this.ftruncate64(fd, length)?; | ||
| 96 | this.write_scalar(result, dest)?; | ||
| 97 | } | ||
| 98 | "posix_fallocate64" => { | ||
| 99 | let [fd, offset, len] = this.check_shim_sig( | ||
| 100 | shim_sig!(extern "C" fn(i32, libc::off64_t, libc::off64_t) -> i32), | ||
| 101 | link_name, | ||
| 102 | abi, | ||
| 103 | args, | ||
| 104 | )?; | ||
| 105 | |||
| 106 | let fd = this.read_scalar(fd)?.to_i32()?; | ||
| 107 | let offset = this.read_scalar(offset)?.to_i64()?; | ||
| 108 | let len = this.read_scalar(len)?.to_i64()?; | ||
| 109 | |||
| 110 | let result = this.posix_fallocate(fd, offset, len)?; | ||
| 111 | this.write_scalar(result, dest)?; | ||
| 112 | } | ||
| 39 | "readdir64" => { | 113 | "readdir64" => { |
| 40 | let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; | 114 | let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; |
| 41 | let result = this.readdir64("dirent64", dirp)?; | 115 | let result = this.readdir64("dirent64", dirp)?; |
| ... | @@ -53,7 +127,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -53,7 +127,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 53 | let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?; | 127 | let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?; |
| 54 | this.write_scalar(result, dest)?; | 128 | this.write_scalar(result, dest)?; |
| 55 | } | 129 | } |
| 56 | |||
| 57 | // epoll, eventfd | 130 | // epoll, eventfd |
| 58 | "epoll_create1" => { | 131 | "epoll_create1" => { |
| 59 | let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; | 132 | let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; |
src/tools/miri/src/shims/unix/macos/foreign_items.rs+11-6| ... | @@ -46,19 +46,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -46,19 +46,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 46 | let result = this.close(result)?; | 46 | let result = this.close(result)?; |
| 47 | this.write_scalar(result, dest)?; | 47 | this.write_scalar(result, dest)?; |
| 48 | } | 48 | } |
| 49 | "stat" | "stat64" | "stat$INODE64" => { | 49 | "stat" | "stat$INODE64" => { |
| 50 | let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; | 50 | let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; |
| 51 | let result = this.macos_fbsd_solarish_stat(path, buf)?; | 51 | let result = this.macos_fbsd_solarish_stat(path, buf)?; |
| 52 | this.write_scalar(result, dest)?; | 52 | this.write_scalar(result, dest)?; |
| 53 | } | 53 | } |
| 54 | "lstat" | "lstat64" | "lstat$INODE64" => { | 54 | "lstat" | "lstat$INODE64" => { |
| 55 | let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; | 55 | let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; |
| 56 | let result = this.macos_fbsd_solarish_lstat(path, buf)?; | 56 | let result = this.macos_fbsd_solarish_lstat(path, buf)?; |
| 57 | this.write_scalar(result, dest)?; | 57 | this.write_scalar(result, dest)?; |
| 58 | } | 58 | } |
| 59 | "fstat" | "fstat64" | "fstat$INODE64" => { | 59 | "fstat$INODE64" => { |
| 60 | let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; | 60 | let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; |
| 61 | let result = this.macos_fbsd_solarish_fstat(fd, buf)?; | 61 | let result = this.fstat(fd, buf)?; |
| 62 | this.write_scalar(result, dest)?; | 62 | this.write_scalar(result, dest)?; |
| 63 | } | 63 | } |
| 64 | "opendir$INODE64" => { | 64 | "opendir$INODE64" => { |
| ... | @@ -69,7 +69,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -69,7 +69,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 69 | "readdir_r" | "readdir_r$INODE64" => { | 69 | "readdir_r" | "readdir_r$INODE64" => { |
| 70 | let [dirp, entry, result] = | 70 | let [dirp, entry, result] = |
| 71 | this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; | 71 | this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; |
| 72 | let result = this.macos_fbsd_readdir_r(dirp, entry, result)?; | 72 | let result = this.macos_readdir_r(dirp, entry, result)?; |
| 73 | this.write_scalar(result, dest)?; | 73 | this.write_scalar(result, dest)?; |
| 74 | } | 74 | } |
| 75 | "realpath$DARWIN_EXTSN" => { | 75 | "realpath$DARWIN_EXTSN" => { |
| ... | @@ -158,7 +158,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -158,7 +158,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 158 | let dtor = this.get_ptr_fn(dtor)?.as_instance()?; | 158 | let dtor = this.get_ptr_fn(dtor)?.as_instance()?; |
| 159 | let data = this.read_scalar(data)?; | 159 | let data = this.read_scalar(data)?; |
| 160 | let active_thread = this.active_thread(); | 160 | let active_thread = this.active_thread(); |
| 161 | this.machine.tls.add_macos_thread_dtor(active_thread, dtor, data)?; | 161 | this.machine.tls.add_macos_thread_dtor( |
| 162 | active_thread, | ||
| 163 | dtor, | ||
| 164 | data, | ||
| 165 | this.machine.current_user_relevant_span(), | ||
| 166 | )?; | ||
| 162 | } | 167 | } |
| 163 | 168 | ||
| 164 | // Querying system information | 169 | // Querying system information |
src/tools/miri/src/shims/unix/solarish/foreign_items.rs+2-7| ... | @@ -90,21 +90,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | ... | @@ -90,21 +90,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | // File related shims | 92 | // File related shims |
| 93 | "stat" | "stat64" => { | 93 | "stat" => { |
| 94 | let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; | 94 | let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; |
| 95 | let result = this.macos_fbsd_solarish_stat(path, buf)?; | 95 | let result = this.macos_fbsd_solarish_stat(path, buf)?; |
| 96 | this.write_scalar(result, dest)?; | 96 | this.write_scalar(result, dest)?; |
| 97 | } | 97 | } |
| 98 | "lstat" | "lstat64" => { | 98 | "lstat" => { |
| 99 | let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; | 99 | let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; |
| 100 | let result = this.macos_fbsd_solarish_lstat(path, buf)?; | 100 | let result = this.macos_fbsd_solarish_lstat(path, buf)?; |
| 101 | this.write_scalar(result, dest)?; | 101 | this.write_scalar(result, dest)?; |
| 102 | } | 102 | } |
| 103 | "fstat" | "fstat64" => { | ||
| 104 | let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; | ||
| 105 | let result = this.macos_fbsd_solarish_fstat(fd, buf)?; | ||
| 106 | this.write_scalar(result, dest)?; | ||
| 107 | } | ||
| 108 | "readdir" => { | 103 | "readdir" => { |
| 109 | let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; | 104 | let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; |
| 110 | let result = this.readdir64("dirent", dirp)?; | 105 | let result = this.readdir64("dirent", dirp)?; |
src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs+1-1| ... | @@ -1,5 +1,4 @@ | ... | @@ -1,5 +1,4 @@ |
| 1 | //@ignore-target: windows # No pthreads on Windows | 1 | //@ignore-target: windows # No pthreads on Windows |
| 2 | //~^ERROR: calling a function with more arguments than it expected | ||
| 3 | 2 | ||
| 4 | //! The thread function must have exactly one argument. | 3 | //! The thread function must have exactly one argument. |
| 5 | 4 | ||
| ... | @@ -17,6 +16,7 @@ fn main() { | ... | @@ -17,6 +16,7 @@ fn main() { |
| 17 | mem::transmute(thread_start); | 16 | mem::transmute(thread_start); |
| 18 | assert_eq!( | 17 | assert_eq!( |
| 19 | libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()), | 18 | libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()), |
| 19 | //~^ERROR: calling a function with more arguments than it expected | ||
| 20 | 0 | 20 | 0 |
| 21 | ); | 21 | ); |
| 22 | assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0); | 22 | assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0); |
src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.stderr+6-2| ... | @@ -1,9 +1,13 @@ | ... | @@ -1,9 +1,13 @@ |
| 1 | error: Undefined Behavior: calling a function with more arguments than it expected | 1 | error: Undefined Behavior: calling a function with more arguments than it expected |
| 2 | --> tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()), | ||
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code | ||
| 2 | | | 6 | | |
| 3 | = note: Undefined Behavior occurred here | ||
| 4 | = note: (no span available) | ||
| 5 | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior | 7 | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior |
| 6 | = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information | 8 | = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information |
| 9 | = note: this error occurred while pushing a call frame onto an empty stack | ||
| 10 | = note: the span indicates which code caused the function to be called, but may not be the literal call site | ||
| 7 | 11 | ||
| 8 | error: aborting due to 1 previous error | 12 | error: aborting due to 1 previous error |
| 9 | 13 |
src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs+1-1| ... | @@ -1,5 +1,4 @@ | ... | @@ -1,5 +1,4 @@ |
| 1 | //@ignore-target: windows # No pthreads on Windows | 1 | //@ignore-target: windows # No pthreads on Windows |
| 2 | //~^ERROR: calling a function with fewer arguments than it requires | ||
| 3 | 2 | ||
| 4 | //! The thread function must have exactly one argument. | 3 | //! The thread function must have exactly one argument. |
| 5 | 4 | ||
| ... | @@ -17,6 +16,7 @@ fn main() { | ... | @@ -17,6 +16,7 @@ fn main() { |
| 17 | mem::transmute(thread_start); | 16 | mem::transmute(thread_start); |
| 18 | assert_eq!( | 17 | assert_eq!( |
| 19 | libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()), | 18 | libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()), |
| 19 | //~^ERROR: calling a function with fewer arguments than it requires | ||
| 20 | 0 | 20 | 0 |
| 21 | ); | 21 | ); |
| 22 | assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0); | 22 | assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0); |
src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.stderr+6-2| ... | @@ -1,9 +1,13 @@ | ... | @@ -1,9 +1,13 @@ |
| 1 | error: Undefined Behavior: calling a function with fewer arguments than it requires | 1 | error: Undefined Behavior: calling a function with fewer arguments than it requires |
| 2 | --> tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()), | ||
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code | ||
| 2 | | | 6 | | |
| 3 | = note: Undefined Behavior occurred here | ||
| 4 | = note: (no span available) | ||
| 5 | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior | 7 | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior |
| 6 | = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information | 8 | = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information |
| 9 | = note: this error occurred while pushing a call frame onto an empty stack | ||
| 10 | = note: the span indicates which code caused the function to be called, but may not be the literal call site | ||
| 7 | 11 | ||
| 8 | error: aborting due to 1 previous error | 12 | error: aborting due to 1 previous error |
| 9 | 13 |
src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_read_while_queued.rs+1-1| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | //@ignore-target: windows # No pthreads on Windows | 1 | //@ignore-target: windows # No pthreads on Windows |
| 2 | //@compile-flags: -Zmiri-fixed-schedule | 2 | //@compile-flags: -Zmiri-deterministic-concurrency |
| 3 | 3 | ||
| 4 | use std::cell::UnsafeCell; | 4 | use std::cell::UnsafeCell; |
| 5 | use std::sync::atomic::*; | 5 | use std::sync::atomic::*; |
src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_write_while_queued.rs+1-1| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | //@ignore-target: windows # No pthreads on Windows | 1 | //@ignore-target: windows # No pthreads on Windows |
| 2 | //@compile-flags: -Zmiri-fixed-schedule | 2 | //@compile-flags: -Zmiri-deterministic-concurrency |
| 3 | 3 | ||
| 4 | use std::cell::UnsafeCell; | 4 | use std::cell::UnsafeCell; |
| 5 | use std::sync::atomic::*; | 5 | use std::sync::atomic::*; |
src/tools/miri/tests/fail-dep/concurrency/tls_pthread_dtor_wrong_abi.rs created+21| ... | @@ -0,0 +1,21 @@ | ||
| 1 | //@ignore-target: windows # No pthreads on Windows | ||
| 2 | |||
| 3 | use std::{mem, ptr}; | ||
| 4 | |||
| 5 | pub type Key = libc::pthread_key_t; | ||
| 6 | |||
| 7 | pub unsafe fn create(dtor: unsafe fn(*mut u8)) -> Key { | ||
| 8 | let mut key = 0; | ||
| 9 | assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0); | ||
| 10 | //~^ERROR: calling a function with calling convention "Rust" | ||
| 11 | key | ||
| 12 | } | ||
| 13 | |||
| 14 | unsafe fn dtor(_ptr: *mut u8) {} | ||
| 15 | |||
| 16 | fn main() { | ||
| 17 | unsafe { | ||
| 18 | let key = create(dtor); | ||
| 19 | libc::pthread_setspecific(key, ptr::without_provenance(1)); | ||
| 20 | } | ||
| 21 | } | ||
src/tools/miri/tests/fail-dep/concurrency/tls_pthread_dtor_wrong_abi.stderr created+13| ... | @@ -0,0 +1,13 @@ | ||
| 1 | error: Undefined Behavior: calling a function with calling convention "Rust" using calling convention "C" | ||
| 2 | --> tests/fail-dep/concurrency/tls_pthread_dtor_wrong_abi.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0); | ||
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code | ||
| 6 | | | ||
| 7 | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior | ||
| 8 | = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information | ||
| 9 | = note: this error occurred while pushing a call frame onto an empty stack | ||
| 10 | = note: the span indicates which code caused the function to be called, but may not be the literal call site | ||
| 11 | |||
| 12 | error: aborting due to 1 previous error | ||
| 13 | |||
src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed1.rs created+21| ... | @@ -0,0 +1,21 @@ | ||
| 1 | //@revisions: stack tree | ||
| 2 | //@compile-flags: -Zmiri-permissive-provenance | ||
| 3 | //@[tree]compile-flags: -Zmiri-tree-borrows | ||
| 4 | |||
| 5 | fn main() { | ||
| 6 | unsafe { | ||
| 7 | let root = &mut 42; | ||
| 8 | let addr = root as *mut i32 as usize; | ||
| 9 | let exposed_ptr = addr as *mut i32; | ||
| 10 | // From the exposed ptr, we get a new unique ptr. | ||
| 11 | let root2 = &mut *exposed_ptr; | ||
| 12 | let _fool = root2 as *mut _; // this would have fooled the old untagged pointer logic | ||
| 13 | // Stack: Unknown(<N), Unique(N), SRW(N+1) | ||
| 14 | // And we test that it has uniqueness by doing a conflicting write. | ||
| 15 | *exposed_ptr = 0; | ||
| 16 | // Stack: Unknown(<N) | ||
| 17 | let _val = *root2; | ||
| 18 | //~[stack]^ ERROR: /read access .* tag does not exist in the borrow stack/ | ||
| 19 | //~[tree]| ERROR: /read access through .* is forbidden/ | ||
| 20 | } | ||
| 21 | } | ||
src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed1.stack.stderr created+23| ... | @@ -0,0 +1,23 @@ | ||
| 1 | error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location | ||
| 2 | --> tests/fail/both_borrows/illegal_read_despite_exposed1.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | let _val = *root2; | ||
| 5 | | ^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4] | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information | ||
| 9 | help: <TAG> was created by a Unique retag at offsets [0x0..0x4] | ||
| 10 | --> tests/fail/both_borrows/illegal_read_despite_exposed1.rs:LL:CC | ||
| 11 | | | ||
| 12 | LL | let root2 = &mut *exposed_ptr; | ||
| 13 | | ^^^^^^^^^^^^^^^^^ | ||
| 14 | help: <TAG> was later invalidated at offsets [0x0..0x4] by a write access | ||
| 15 | --> tests/fail/both_borrows/illegal_read_despite_exposed1.rs:LL:CC | ||
| 16 | | | ||
| 17 | LL | *exposed_ptr = 0; | ||
| 18 | | ^^^^^^^^^^^^^^^^ | ||
| 19 | |||
| 20 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 21 | |||
| 22 | error: aborting due to 1 previous error | ||
| 23 | |||
src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed1.tree.stderr created+25| ... | @@ -0,0 +1,25 @@ | ||
| 1 | error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden | ||
| 2 | --> tests/fail/both_borrows/illegal_read_despite_exposed1.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | let _val = *root2; | ||
| 5 | | ^^^^^^ Undefined Behavior occurred here | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information | ||
| 9 | = help: the accessed tag <TAG> has state Disabled which forbids this child read access | ||
| 10 | help: the accessed tag <TAG> was created here, in the initial state Reserved | ||
| 11 | --> tests/fail/both_borrows/illegal_read_despite_exposed1.rs:LL:CC | ||
| 12 | | | ||
| 13 | LL | let root2 = &mut *exposed_ptr; | ||
| 14 | | ^^^^^^^^^^^^^^^^^ | ||
| 15 | help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] | ||
| 16 | --> tests/fail/both_borrows/illegal_read_despite_exposed1.rs:LL:CC | ||
| 17 | | | ||
| 18 | LL | *exposed_ptr = 0; | ||
| 19 | | ^^^^^^^^^^^^^^^^ | ||
| 20 | = help: this transition corresponds to a loss of read and write permissions | ||
| 21 | |||
| 22 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 23 | |||
| 24 | error: aborting due to 1 previous error | ||
| 25 | |||
src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed2.rs created+28| ... | @@ -0,0 +1,28 @@ | ||
| 1 | //@revisions: stack tree | ||
| 2 | //@compile-flags: -Zmiri-permissive-provenance | ||
| 3 | //@[tree]compile-flags: -Zmiri-tree-borrows | ||
| 4 | |||
| 5 | fn main() { | ||
| 6 | unsafe { | ||
| 7 | let root = &mut 42; | ||
| 8 | let addr = root as *mut i32 as usize; | ||
| 9 | let exposed_ptr = addr as *mut i32; | ||
| 10 | // From the exposed ptr, we get a new unique ptr. | ||
| 11 | let root2 = &mut *exposed_ptr; | ||
| 12 | // Activate the reference (unnecessary on Stacked Borrows). | ||
| 13 | *root2 = 42; | ||
| 14 | // let _fool = root2 as *mut _; // this would fool us, since SRW(N+1) remains on the stack | ||
| 15 | // Stack: Unknown(<N), Unique(N) | ||
| 16 | // Stack if _fool existed: Unknown(<N), Unique(N), SRW(N+1) | ||
| 17 | // And we test that it has uniqueness by doing a conflicting read. | ||
| 18 | let _val = *exposed_ptr; | ||
| 19 | // Stack: Unknown(<N), Disabled(N) | ||
| 20 | // collapsed to Unknown(<N) | ||
| 21 | // Stack if _fool existed: Unknown(<N), Disabled(N), SRW(N+1); collapsed to Unknown(<N+2) which would not cause an ERROR | ||
| 22 | |||
| 23 | // Stack borrows would also fail if we replaced this with a read, but tree borrows would let it pass. | ||
| 24 | *root2 = 3; | ||
| 25 | //~[stack]^ ERROR: /write access .* tag does not exist in the borrow stack/ | ||
| 26 | //~[tree]| ERROR: /write access through .* is forbidden/ | ||
| 27 | } | ||
| 28 | } | ||
src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed2.stack.stderr created+23| ... | @@ -0,0 +1,23 @@ | ||
| 1 | error: Undefined Behavior: attempting a write access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location | ||
| 2 | --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | *root2 = 3; | ||
| 5 | | ^^^^^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4] | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information | ||
| 9 | help: <TAG> was created by a Unique retag at offsets [0x0..0x4] | ||
| 10 | --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC | ||
| 11 | | | ||
| 12 | LL | let root2 = &mut *exposed_ptr; | ||
| 13 | | ^^^^^^^^^^^^^^^^^ | ||
| 14 | help: <TAG> was later invalidated at offsets [0x0..0x4] by a read access | ||
| 15 | --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC | ||
| 16 | | | ||
| 17 | LL | let _val = *exposed_ptr; | ||
| 18 | | ^^^^^^^^^^^^ | ||
| 19 | |||
| 20 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 21 | |||
| 22 | error: aborting due to 1 previous error | ||
| 23 | |||
src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed2.tree.stderr created+31| ... | @@ -0,0 +1,31 @@ | ||
| 1 | error: Undefined Behavior: write access through <TAG> at ALLOC[0x0] is forbidden | ||
| 2 | --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | *root2 = 3; | ||
| 5 | | ^^^^^^^^^^ Undefined Behavior occurred here | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information | ||
| 9 | = help: the accessed tag <TAG> has state Frozen which forbids this child write access | ||
| 10 | help: the accessed tag <TAG> was created here, in the initial state Reserved | ||
| 11 | --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC | ||
| 12 | | | ||
| 13 | LL | let root2 = &mut *exposed_ptr; | ||
| 14 | | ^^^^^^^^^^^^^^^^^ | ||
| 15 | help: the accessed tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4] | ||
| 16 | --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC | ||
| 17 | | | ||
| 18 | LL | *root2 = 42; | ||
| 19 | | ^^^^^^^^^^^ | ||
| 20 | = help: this transition corresponds to the first write to a 2-phase borrowed mutable reference | ||
| 21 | help: the accessed tag <TAG> later transitioned to Frozen due to a foreign read access at offsets [0x0..0x4] | ||
| 22 | --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC | ||
| 23 | | | ||
| 24 | LL | let _val = *exposed_ptr; | ||
| 25 | | ^^^^^^^^^^^^ | ||
| 26 | = help: this transition corresponds to a loss of write permissions | ||
| 27 | |||
| 28 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 29 | |||
| 30 | error: aborting due to 1 previous error | ||
| 31 | |||
src/tools/miri/tests/fail/both_borrows/illegal_write_despite_exposed1.rs created+22| ... | @@ -0,0 +1,22 @@ | ||
| 1 | //@revisions: stack tree | ||
| 2 | //@compile-flags: -Zmiri-permissive-provenance | ||
| 3 | //@[tree]compile-flags: -Zmiri-tree-borrows | ||
| 4 | |||
| 5 | fn main() { | ||
| 6 | unsafe { | ||
| 7 | let root = &mut 42; | ||
| 8 | let addr = root as *mut i32 as usize; | ||
| 9 | let exposed_ptr = addr as *mut i32; | ||
| 10 | // From the exposed ptr, we get a new SRO ptr. | ||
| 11 | let root2 = &*exposed_ptr; | ||
| 12 | // Stack: Unknown(<N), SRO(N), SRO(N+1) | ||
| 13 | // And we test that it is read-only by doing a conflicting write. | ||
| 14 | // (The write is still fine, using the `root as *mut i32` provenance which got exposed.) | ||
| 15 | *exposed_ptr = 0; | ||
| 16 | // Stack: Unknown(<N) | ||
| 17 | |||
| 18 | let _val = *root2; | ||
| 19 | //~[stack]^ ERROR: /read access .* tag does not exist in the borrow stack/ | ||
| 20 | //~[tree]| ERROR: /read access through .* is forbidden/ | ||
| 21 | } | ||
| 22 | } | ||
src/tools/miri/tests/fail/both_borrows/illegal_write_despite_exposed1.stack.stderr created+23| ... | @@ -0,0 +1,23 @@ | ||
| 1 | error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location | ||
| 2 | --> tests/fail/both_borrows/illegal_write_despite_exposed1.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | let _val = *root2; | ||
| 5 | | ^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4] | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information | ||
| 9 | help: <TAG> was created by a SharedReadOnly retag at offsets [0x0..0x4] | ||
| 10 | --> tests/fail/both_borrows/illegal_write_despite_exposed1.rs:LL:CC | ||
| 11 | | | ||
| 12 | LL | let root2 = &*exposed_ptr; | ||
| 13 | | ^^^^^^^^^^^^^ | ||
| 14 | help: <TAG> was later invalidated at offsets [0x0..0x4] by a write access | ||
| 15 | --> tests/fail/both_borrows/illegal_write_despite_exposed1.rs:LL:CC | ||
| 16 | | | ||
| 17 | LL | *exposed_ptr = 0; | ||
| 18 | | ^^^^^^^^^^^^^^^^ | ||
| 19 | |||
| 20 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 21 | |||
| 22 | error: aborting due to 1 previous error | ||
| 23 | |||
src/tools/miri/tests/fail/both_borrows/illegal_write_despite_exposed1.tree.stderr created+25| ... | @@ -0,0 +1,25 @@ | ||
| 1 | error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden | ||
| 2 | --> tests/fail/both_borrows/illegal_write_despite_exposed1.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | let _val = *root2; | ||
| 5 | | ^^^^^^ Undefined Behavior occurred here | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information | ||
| 9 | = help: the accessed tag <TAG> has state Disabled which forbids this child read access | ||
| 10 | help: the accessed tag <TAG> was created here, in the initial state Frozen | ||
| 11 | --> tests/fail/both_borrows/illegal_write_despite_exposed1.rs:LL:CC | ||
| 12 | | | ||
| 13 | LL | let root2 = &*exposed_ptr; | ||
| 14 | | ^^^^^^^^^^^^^ | ||
| 15 | help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] | ||
| 16 | --> tests/fail/both_borrows/illegal_write_despite_exposed1.rs:LL:CC | ||
| 17 | | | ||
| 18 | LL | *exposed_ptr = 0; | ||
| 19 | | ^^^^^^^^^^^^^^^^ | ||
| 20 | = help: this transition corresponds to a loss of read permissions | ||
| 21 | |||
| 22 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 23 | |||
| 24 | error: aborting due to 1 previous error | ||
| 25 | |||
src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.rs+1-1| ... | @@ -1,5 +1,4 @@ | ... | @@ -1,5 +1,4 @@ |
| 1 | unsafe extern "C" fn ctor() -> i32 { | 1 | unsafe extern "C" fn ctor() -> i32 { |
| 2 | //~^ERROR: calling a function with return type i32 passing return place of type () | ||
| 3 | 0 | 2 | 0 |
| 4 | } | 3 | } |
| 5 | 4 | ||
| ... | @@ -31,6 +30,7 @@ macro_rules! ctor { | ... | @@ -31,6 +30,7 @@ macro_rules! ctor { |
| 31 | )] | 30 | )] |
| 32 | #[used] | 31 | #[used] |
| 33 | static $ident: unsafe extern "C" fn() -> i32 = $ctor; | 32 | static $ident: unsafe extern "C" fn() -> i32 = $ctor; |
| 33 | //~^ERROR: calling a function with return type i32 passing return place of type () | ||
| 34 | }; | 34 | }; |
| 35 | } | 35 | } |
| 36 | 36 |
src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.stderr+10-2| ... | @@ -1,11 +1,19 @@ | ... | @@ -1,11 +1,19 @@ |
| 1 | error: Undefined Behavior: calling a function with return type i32 passing return place of type () | 1 | error: Undefined Behavior: calling a function with return type i32 passing return place of type () |
| 2 | --> tests/fail/shims/ctor_wrong_ret_type.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | static $ident: unsafe extern "C" fn() -> i32 = $ctor; | ||
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code | ||
| 6 | ... | ||
| 7 | LL | ctor! { CTOR = ctor } | ||
| 8 | | --------------------- in this macro invocation | ||
| 2 | | | 9 | | |
| 3 | = note: Undefined Behavior occurred here | ||
| 4 | = note: (no span available) | ||
| 5 | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior | 10 | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior |
| 6 | = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information | 11 | = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information |
| 7 | = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets | 12 | = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets |
| 8 | = help: if you think this code should be accepted anyway, please report an issue with Miri | 13 | = help: if you think this code should be accepted anyway, please report an issue with Miri |
| 14 | = note: this error occurred while pushing a call frame onto an empty stack | ||
| 15 | = note: the span indicates which code caused the function to be called, but may not be the literal call site | ||
| 16 | = note: this error originates in the macro `ctor` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
| 9 | 17 | ||
| 10 | error: aborting due to 1 previous error | 18 | error: aborting due to 1 previous error |
| 11 | 19 |
src/tools/miri/tests/fail/shims/macos_tlv_atexit_wrong_abi.rs created+18| ... | @@ -0,0 +1,18 @@ | ||
| 1 | //@only-target: darwin | ||
| 2 | |||
| 3 | use std::{mem, ptr}; | ||
| 4 | |||
| 5 | extern "C" { | ||
| 6 | fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8); | ||
| 7 | } | ||
| 8 | |||
| 9 | fn register(dtor: unsafe fn(*mut u8)) { | ||
| 10 | unsafe { | ||
| 11 | _tlv_atexit(mem::transmute(dtor), ptr::null_mut()); | ||
| 12 | //~^ERROR: calling a function with calling convention "Rust" | ||
| 13 | } | ||
| 14 | } | ||
| 15 | |||
| 16 | fn main() { | ||
| 17 | register(|_| ()); | ||
| 18 | } | ||
src/tools/miri/tests/fail/shims/macos_tlv_atexit_wrong_abi.stderr created+13| ... | @@ -0,0 +1,13 @@ | ||
| 1 | error: Undefined Behavior: calling a function with calling convention "Rust" using calling convention "C" | ||
| 2 | --> tests/fail/shims/macos_tlv_atexit_wrong_abi.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | _tlv_atexit(mem::transmute(dtor), ptr::null_mut()); | ||
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code | ||
| 6 | | | ||
| 7 | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior | ||
| 8 | = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information | ||
| 9 | = note: this error occurred while pushing a call frame onto an empty stack | ||
| 10 | = note: the span indicates which code caused the function to be called, but may not be the literal call site | ||
| 11 | |||
| 12 | error: aborting due to 1 previous error | ||
| 13 | |||
src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed1.rs deleted-17| ... | @@ -1,17 +0,0 @@ | ||
| 1 | //@compile-flags: -Zmiri-permissive-provenance | ||
| 2 | |||
| 3 | fn main() { | ||
| 4 | unsafe { | ||
| 5 | let root = &mut 42; | ||
| 6 | let addr = root as *mut i32 as usize; | ||
| 7 | let exposed_ptr = addr as *mut i32; | ||
| 8 | // From the exposed ptr, we get a new unique ptr. | ||
| 9 | let root2 = &mut *exposed_ptr; | ||
| 10 | let _fool = root2 as *mut _; // this would have fooled the old untagged pointer logic | ||
| 11 | // Stack: Unknown(<N), Unique(N), SRW(N+1) | ||
| 12 | // And we test that it has uniqueness by doing a conflicting write. | ||
| 13 | *exposed_ptr = 0; | ||
| 14 | // Stack: Unknown(<N) | ||
| 15 | let _val = *root2; //~ ERROR: /read access .* tag does not exist in the borrow stack/ | ||
| 16 | } | ||
| 17 | } | ||
src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed1.stderr deleted-23| ... | @@ -1,23 +0,0 @@ | ||
| 1 | error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location | ||
| 2 | --> tests/fail/stacked_borrows/illegal_read_despite_exposed1.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | let _val = *root2; | ||
| 5 | | ^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4] | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information | ||
| 9 | help: <TAG> was created by a Unique retag at offsets [0x0..0x4] | ||
| 10 | --> tests/fail/stacked_borrows/illegal_read_despite_exposed1.rs:LL:CC | ||
| 11 | | | ||
| 12 | LL | let root2 = &mut *exposed_ptr; | ||
| 13 | | ^^^^^^^^^^^^^^^^^ | ||
| 14 | help: <TAG> was later invalidated at offsets [0x0..0x4] by a write access | ||
| 15 | --> tests/fail/stacked_borrows/illegal_read_despite_exposed1.rs:LL:CC | ||
| 16 | | | ||
| 17 | LL | *exposed_ptr = 0; | ||
| 18 | | ^^^^^^^^^^^^^^^^ | ||
| 19 | |||
| 20 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 21 | |||
| 22 | error: aborting due to 1 previous error | ||
| 23 | |||
src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed2.rs deleted-20| ... | @@ -1,20 +0,0 @@ | ||
| 1 | //@compile-flags: -Zmiri-permissive-provenance | ||
| 2 | |||
| 3 | fn main() { | ||
| 4 | unsafe { | ||
| 5 | let root = &mut 42; | ||
| 6 | let addr = root as *mut i32 as usize; | ||
| 7 | let exposed_ptr = addr as *mut i32; | ||
| 8 | // From the exposed ptr, we get a new unique ptr. | ||
| 9 | let root2 = &mut *exposed_ptr; | ||
| 10 | // let _fool = root2 as *mut _; // this would fool us, since SRW(N+1) remains on the stack | ||
| 11 | // Stack: Unknown(<N), Unique(N) | ||
| 12 | // Stack if _fool existed: Unknown(<N), Unique(N), SRW(N+1) | ||
| 13 | // And we test that it has uniqueness by doing a conflicting read. | ||
| 14 | let _val = *exposed_ptr; | ||
| 15 | // Stack: Unknown(<N), Disabled(N) | ||
| 16 | // collapsed to Unknown(<N) | ||
| 17 | // Stack if _fool existed: Unknown(<N), Disabled(N), SRW(N+1); collapsed to Unknown(<N+2) which would not cause an ERROR | ||
| 18 | let _val = *root2; //~ ERROR: /read access .* tag does not exist in the borrow stack/ | ||
| 19 | } | ||
| 20 | } | ||
src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed2.stderr deleted-23| ... | @@ -1,23 +0,0 @@ | ||
| 1 | error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location | ||
| 2 | --> tests/fail/stacked_borrows/illegal_read_despite_exposed2.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | let _val = *root2; | ||
| 5 | | ^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4] | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information | ||
| 9 | help: <TAG> was created by a Unique retag at offsets [0x0..0x4] | ||
| 10 | --> tests/fail/stacked_borrows/illegal_read_despite_exposed2.rs:LL:CC | ||
| 11 | | | ||
| 12 | LL | let root2 = &mut *exposed_ptr; | ||
| 13 | | ^^^^^^^^^^^^^^^^^ | ||
| 14 | help: <TAG> was later invalidated at offsets [0x0..0x4] by a read access | ||
| 15 | --> tests/fail/stacked_borrows/illegal_read_despite_exposed2.rs:LL:CC | ||
| 16 | | | ||
| 17 | LL | let _val = *exposed_ptr; | ||
| 18 | | ^^^^^^^^^^^^ | ||
| 19 | |||
| 20 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 21 | |||
| 22 | error: aborting due to 1 previous error | ||
| 23 | |||
src/tools/miri/tests/fail/stacked_borrows/illegal_write_despite_exposed1.rs deleted-17| ... | @@ -1,17 +0,0 @@ | ||
| 1 | //@compile-flags: -Zmiri-permissive-provenance | ||
| 2 | |||
| 3 | fn main() { | ||
| 4 | unsafe { | ||
| 5 | let root = &mut 42; | ||
| 6 | let addr = root as *mut i32 as usize; | ||
| 7 | let exposed_ptr = addr as *mut i32; | ||
| 8 | // From the exposed ptr, we get a new SRO ptr. | ||
| 9 | let root2 = &*exposed_ptr; | ||
| 10 | // Stack: Unknown(<N), SRO(N), SRO(N+1) | ||
| 11 | // And we test that it is read-only by doing a conflicting write. | ||
| 12 | // (The write is still fine, using the `root as *mut i32` provenance which got exposed.) | ||
| 13 | *exposed_ptr = 0; | ||
| 14 | // Stack: Unknown(<N) | ||
| 15 | let _val = *root2; //~ ERROR: /read access .* tag does not exist in the borrow stack/ | ||
| 16 | } | ||
| 17 | } | ||
src/tools/miri/tests/fail/stacked_borrows/illegal_write_despite_exposed1.stderr deleted-23| ... | @@ -1,23 +0,0 @@ | ||
| 1 | error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location | ||
| 2 | --> tests/fail/stacked_borrows/illegal_write_despite_exposed1.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | let _val = *root2; | ||
| 5 | | ^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4] | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information | ||
| 9 | help: <TAG> was created by a SharedReadOnly retag at offsets [0x0..0x4] | ||
| 10 | --> tests/fail/stacked_borrows/illegal_write_despite_exposed1.rs:LL:CC | ||
| 11 | | | ||
| 12 | LL | let root2 = &*exposed_ptr; | ||
| 13 | | ^^^^^^^^^^^^^ | ||
| 14 | help: <TAG> was later invalidated at offsets [0x0..0x4] by a write access | ||
| 15 | --> tests/fail/stacked_borrows/illegal_write_despite_exposed1.rs:LL:CC | ||
| 16 | | | ||
| 17 | LL | *exposed_ptr = 0; | ||
| 18 | | ^^^^^^^^^^^^^^^^ | ||
| 19 | |||
| 20 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 21 | |||
| 22 | error: aborting due to 1 previous error | ||
| 23 | |||
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_from_main.rs created+36| ... | @@ -0,0 +1,36 @@ | ||
| 1 | //@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance | ||
| 2 | |||
| 3 | /// Checks how accesses from one subtree affect other subtrees. | ||
| 4 | /// This test checks the case where the access is to the main tree. | ||
| 5 | pub fn main() { | ||
| 6 | let mut x: u32 = 42; | ||
| 7 | |||
| 8 | let ptr_base = &mut x as *mut u32; | ||
| 9 | let ref1 = unsafe { &mut *ptr_base }; | ||
| 10 | let ref2 = unsafe { &mut *ptr_base }; | ||
| 11 | |||
| 12 | let int1 = ref1 as *mut u32 as usize; | ||
| 13 | let wild = int1 as *mut u32; | ||
| 14 | |||
| 15 | let reb3 = unsafe { &mut *wild }; | ||
| 16 | |||
| 17 | // ┌────────────┐ | ||
| 18 | // │ │ | ||
| 19 | // │ ptr_base ├───────────┐ * | ||
| 20 | // │ │ │ │ | ||
| 21 | // └──────┬─────┘ │ │ | ||
| 22 | // │ │ │ | ||
| 23 | // │ │ │ | ||
| 24 | // ▼ ▼ ▼ | ||
| 25 | // ┌────────────┐ ┌───────────┐ ┌───────────┐ | ||
| 26 | // │ │ │ │ │ │ | ||
| 27 | // │ ref1(Res)* │ │ ref2(Res) │ │ reb3(Res) │ | ||
| 28 | // │ │ │ │ │ │ | ||
| 29 | // └────────────┘ └───────────┘ └───────────┘ | ||
| 30 | |||
| 31 | // ref2 is part of the main tree and therefore foreign to all subtrees. | ||
| 32 | // Therefore, this disables reb3. | ||
| 33 | *ref2 = 13; | ||
| 34 | |||
| 35 | let _fail = *reb3; //~ ERROR: /read access through .* is forbidden/ | ||
| 36 | } | ||
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_from_main.stderr created+25| ... | @@ -0,0 +1,25 @@ | ||
| 1 | error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden | ||
| 2 | --> tests/fail/tree_borrows/wildcard/cross_tree_from_main.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | let _fail = *reb3; | ||
| 5 | | ^^^^^ Undefined Behavior occurred here | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information | ||
| 9 | = help: the accessed tag <TAG> has state Disabled which forbids this child read access | ||
| 10 | help: the accessed tag <TAG> was created here, in the initial state Reserved | ||
| 11 | --> tests/fail/tree_borrows/wildcard/cross_tree_from_main.rs:LL:CC | ||
| 12 | | | ||
| 13 | LL | let reb3 = unsafe { &mut *wild }; | ||
| 14 | | ^^^^^^^^^^ | ||
| 15 | help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] | ||
| 16 | --> tests/fail/tree_borrows/wildcard/cross_tree_from_main.rs:LL:CC | ||
| 17 | | | ||
| 18 | LL | *ref2 = 13; | ||
| 19 | | ^^^^^^^^^^ | ||
| 20 | = help: this transition corresponds to a loss of read and write permissions | ||
| 21 | |||
| 22 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 23 | |||
| 24 | error: aborting due to 1 previous error | ||
| 25 | |||
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main.rs created+37| ... | @@ -0,0 +1,37 @@ | ||
| 1 | //@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance | ||
| 2 | |||
| 3 | /// Checks how accesses from one subtree affect other subtrees. | ||
| 4 | /// This tests how main is effected by an access through a subtree. | ||
| 5 | pub fn main() { | ||
| 6 | let mut x: u32 = 42; | ||
| 7 | |||
| 8 | let ptr_base = &mut x as *mut u32; | ||
| 9 | let ref1 = unsafe { &mut *ptr_base }; | ||
| 10 | let ref2 = unsafe { &mut *ptr_base }; | ||
| 11 | |||
| 12 | let int1 = ref1 as *mut u32 as usize; | ||
| 13 | let wild = int1 as *mut u32; | ||
| 14 | |||
| 15 | let reb = unsafe { &mut *wild }; | ||
| 16 | |||
| 17 | // ┌────────────┐ | ||
| 18 | // │ │ | ||
| 19 | // │ ptr_base ├───────────┐ * | ||
| 20 | // │ │ │ │ | ||
| 21 | // └──────┬─────┘ │ │ | ||
| 22 | // │ │ │ | ||
| 23 | // │ │ │ | ||
| 24 | // ▼ ▼ ▼ | ||
| 25 | // ┌────────────┐ ┌───────────┐ ┌───────────┐ | ||
| 26 | // │ │ │ │ │ │ | ||
| 27 | // │ ref1(Res)* │ │ ref2(Res) │ │ reb(Res) │ | ||
| 28 | // │ │ │ │ │ │ | ||
| 29 | // └────────────┘ └───────────┘ └───────────┘ | ||
| 30 | |||
| 31 | // Writes through the reborrowed reference causing a wildcard | ||
| 32 | // write on the main tree. This disables ref2 as it doesn't | ||
| 33 | // have any exposed children. | ||
| 34 | *reb = 13; | ||
| 35 | |||
| 36 | let _fail = *ref2; //~ ERROR: /read access through .* is forbidden/ | ||
| 37 | } | ||
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main.stderr created+25| ... | @@ -0,0 +1,25 @@ | ||
| 1 | error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden | ||
| 2 | --> tests/fail/tree_borrows/wildcard/cross_tree_update_main.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | let _fail = *ref2; | ||
| 5 | | ^^^^^ Undefined Behavior occurred here | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information | ||
| 9 | = help: the accessed tag <TAG> has state Disabled which forbids this child read access | ||
| 10 | help: the accessed tag <TAG> was created here, in the initial state Reserved | ||
| 11 | --> tests/fail/tree_borrows/wildcard/cross_tree_update_main.rs:LL:CC | ||
| 12 | | | ||
| 13 | LL | let ref2 = unsafe { &mut *ptr_base }; | ||
| 14 | | ^^^^^^^^^^^^^^ | ||
| 15 | help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] | ||
| 16 | --> tests/fail/tree_borrows/wildcard/cross_tree_update_main.rs:LL:CC | ||
| 17 | | | ||
| 18 | LL | *reb = 13; | ||
| 19 | | ^^^^^^^^^ | ||
| 20 | = help: this transition corresponds to a loss of read and write permissions | ||
| 21 | |||
| 22 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 23 | |||
| 24 | error: aborting due to 1 previous error | ||
| 25 | |||
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed.rs created+44| ... | @@ -0,0 +1,44 @@ | ||
| 1 | //@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance | ||
| 2 | |||
| 3 | /// Checks how accesses from one subtree affect other subtrees. | ||
| 4 | /// This test checks that an access from a subtree performs a | ||
| 5 | /// wildcard access on all earlier trees, and that local | ||
| 6 | /// accesses are treated as access errors for tags that are | ||
| 7 | /// larger than the root of the accessed subtree. | ||
| 8 | pub fn main() { | ||
| 9 | let mut x: u32 = 42; | ||
| 10 | |||
| 11 | let ptr_base = &mut x as *mut u32; | ||
| 12 | let ref1 = unsafe { &mut *ptr_base }; | ||
| 13 | |||
| 14 | // Activates ref1. | ||
| 15 | *ref1 = 4; | ||
| 16 | |||
| 17 | let int1 = ref1 as *mut u32 as usize; | ||
| 18 | let wild = int1 as *mut u32; | ||
| 19 | |||
| 20 | let ref2 = unsafe { &mut *wild }; | ||
| 21 | |||
| 22 | // Freezes ref1. | ||
| 23 | let ref3 = unsafe { &mut *ptr_base }; | ||
| 24 | let _int3 = ref3 as *mut u32 as usize; | ||
| 25 | |||
| 26 | // ┌──────────────┐ | ||
| 27 | // │ │ | ||
| 28 | // │ptr_base(Act) ├───────────┐ * | ||
| 29 | // │ │ │ │ | ||
| 30 | // └──────┬───────┘ │ │ | ||
| 31 | // │ │ │ | ||
| 32 | // │ │ │ | ||
| 33 | // ▼ ▼ ▼ | ||
| 34 | // ┌─────────────┐ ┌────────────┐ ┌───────────┐ | ||
| 35 | // │ │ │ │ │ │ | ||
| 36 | // │ ref1(Frz)* │ │ ref3(Res)* │ │ ref2(Res) │ | ||
| 37 | // │ │ │ │ │ │ | ||
| 38 | // └─────────────┘ └────────────┘ └───────────┘ | ||
| 39 | |||
| 40 | // Performs a wildcard access on the main root. However, as there are | ||
| 41 | // no exposed tags with write permissions and a tag smaller than ref2 | ||
| 42 | // this access fails. | ||
| 43 | *ref2 = 13; //~ ERROR: /write access through .* is forbidden/ | ||
| 44 | } | ||
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed.stderr created+14| ... | @@ -0,0 +1,14 @@ | ||
| 1 | error: Undefined Behavior: write access through <wildcard> at ALLOC[0x0] is forbidden | ||
| 2 | --> tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | *ref2 = 13; | ||
| 5 | | ^^^^^^^^^^ Undefined Behavior occurred here | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information | ||
| 9 | = help: there are no exposed tags which may perform this access here | ||
| 10 | |||
| 11 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 12 | |||
| 13 | error: aborting due to 1 previous error | ||
| 14 | |||
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer.rs created+47| ... | @@ -0,0 +1,47 @@ | ||
| 1 | //@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance | ||
| 2 | |||
| 3 | /// Checks how accesses from one subtree affect other subtrees. | ||
| 4 | /// This test checks that an access from an earlier created subtree | ||
| 5 | /// is foreign to a later created one. | ||
| 6 | pub fn main() { | ||
| 7 | let mut x: u32 = 42; | ||
| 8 | |||
| 9 | let ref_base = &mut x; | ||
| 10 | |||
| 11 | let int0 = ref_base as *mut u32 as usize; | ||
| 12 | let wild = int0 as *mut u32; | ||
| 13 | |||
| 14 | let reb1 = unsafe { &mut *wild }; | ||
| 15 | |||
| 16 | let reb2 = unsafe { &mut *wild }; | ||
| 17 | |||
| 18 | let ref3 = &mut *reb1; | ||
| 19 | let _int3 = ref3 as *mut u32 as usize; | ||
| 20 | // ┌──────────────┐ | ||
| 21 | // │ │ | ||
| 22 | // │ptr_base(Res)*│ * * | ||
| 23 | // │ │ │ │ | ||
| 24 | // └──────────────┘ │ │ | ||
| 25 | // │ │ | ||
| 26 | // │ │ | ||
| 27 | // ▼ ▼ | ||
| 28 | // ┌────────────┐ ┌────────────┐ | ||
| 29 | // │ │ │ │ | ||
| 30 | // │ reb1(Res) ├ │ reb2(Res) ├ | ||
| 31 | // │ │ │ │ | ||
| 32 | // └──────┬─────┘ └────────────┘ | ||
| 33 | // │ | ||
| 34 | // │ | ||
| 35 | // ▼ | ||
| 36 | // ┌────────────┐ | ||
| 37 | // │ │ | ||
| 38 | // │ ref3(Res)* │ | ||
| 39 | // │ │ | ||
| 40 | // └────────────┘ | ||
| 41 | |||
| 42 | // This access disables reb2 because ref3 cannot be a child of it | ||
| 43 | // as reb2 both has a higher tag and doesn't have any exposed children. | ||
| 44 | *ref3 = 13; | ||
| 45 | |||
| 46 | let _fail = *reb2; //~ ERROR: /read access through .* is forbidden/ | ||
| 47 | } | ||
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer.stderr created+25| ... | @@ -0,0 +1,25 @@ | ||
| 1 | error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden | ||
| 2 | --> tests/fail/tree_borrows/wildcard/cross_tree_update_newer.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | let _fail = *reb2; | ||
| 5 | | ^^^^^ Undefined Behavior occurred here | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information | ||
| 9 | = help: the accessed tag <TAG> has state Disabled which forbids this child read access | ||
| 10 | help: the accessed tag <TAG> was created here, in the initial state Reserved | ||
| 11 | --> tests/fail/tree_borrows/wildcard/cross_tree_update_newer.rs:LL:CC | ||
| 12 | | | ||
| 13 | LL | let reb2 = unsafe { &mut *wild }; | ||
| 14 | | ^^^^^^^^^^ | ||
| 15 | help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] | ||
| 16 | --> tests/fail/tree_borrows/wildcard/cross_tree_update_newer.rs:LL:CC | ||
| 17 | | | ||
| 18 | LL | *ref3 = 13; | ||
| 19 | | ^^^^^^^^^^ | ||
| 20 | = help: this transition corresponds to a loss of read and write permissions | ||
| 21 | |||
| 22 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 23 | |||
| 24 | error: aborting due to 1 previous error | ||
| 25 | |||
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer_exposed.rs created+48| ... | @@ -0,0 +1,48 @@ | ||
| 1 | //@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance | ||
| 2 | |||
| 3 | /// Checks how accesses from one subtree affect other subtrees. | ||
| 4 | /// This test checks that an access from an earlier created subtree | ||
| 5 | /// is foreign to a later created one. | ||
| 6 | pub fn main() { | ||
| 7 | let mut x: u32 = 42; | ||
| 8 | |||
| 9 | let ref_base = &mut x; | ||
| 10 | |||
| 11 | let int0 = ref_base as *mut u32 as usize; | ||
| 12 | let wild = int0 as *mut u32; | ||
| 13 | |||
| 14 | let reb1 = unsafe { &mut *wild }; | ||
| 15 | |||
| 16 | let reb2 = unsafe { &mut *wild }; | ||
| 17 | let _int2 = reb2 as *mut u32 as usize; | ||
| 18 | |||
| 19 | let ref3 = &mut *reb1; | ||
| 20 | let _int3 = ref3 as *mut u32 as usize; | ||
| 21 | // ┌──────────────┐ | ||
| 22 | // │ │ | ||
| 23 | // │ptr_base(Res)*│ * * | ||
| 24 | // │ │ │ │ | ||
| 25 | // └──────────────┘ │ │ | ||
| 26 | // │ │ | ||
| 27 | // │ │ | ||
| 28 | // ▼ ▼ | ||
| 29 | // ┌────────────┐ ┌────────────┐ | ||
| 30 | // │ │ │ │ | ||
| 31 | // │ reb1(Res) │ │ reb2(Res)* │ | ||
| 32 | // │ │ │ │ | ||
| 33 | // └──────┬─────┘ └────────────┘ | ||
| 34 | // │ | ||
| 35 | // │ | ||
| 36 | // ▼ | ||
| 37 | // ┌────────────┐ | ||
| 38 | // │ │ | ||
| 39 | // │ ref3(Res)* │ | ||
| 40 | // │ │ | ||
| 41 | // └────────────┘ | ||
| 42 | |||
| 43 | // This access disables reb2 because ref3 cannot be a child of it | ||
| 44 | // as ref3's root has a lower tag than reb2. | ||
| 45 | *ref3 = 13; | ||
| 46 | |||
| 47 | let _fail = *reb2; //~ ERROR: /read access through .* is forbidden/ | ||
| 48 | } | ||
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer_exposed.stderr created+25| ... | @@ -0,0 +1,25 @@ | ||
| 1 | error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden | ||
| 2 | --> tests/fail/tree_borrows/wildcard/cross_tree_update_newer_exposed.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | let _fail = *reb2; | ||
| 5 | | ^^^^^ Undefined Behavior occurred here | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information | ||
| 9 | = help: the accessed tag <TAG> has state Disabled which forbids this child read access | ||
| 10 | help: the accessed tag <TAG> was created here, in the initial state Reserved | ||
| 11 | --> tests/fail/tree_borrows/wildcard/cross_tree_update_newer_exposed.rs:LL:CC | ||
| 12 | | | ||
| 13 | LL | let reb2 = unsafe { &mut *wild }; | ||
| 14 | | ^^^^^^^^^^ | ||
| 15 | help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] | ||
| 16 | --> tests/fail/tree_borrows/wildcard/cross_tree_update_newer_exposed.rs:LL:CC | ||
| 17 | | | ||
| 18 | LL | *ref3 = 13; | ||
| 19 | | ^^^^^^^^^^ | ||
| 20 | = help: this transition corresponds to a loss of read and write permissions | ||
| 21 | |||
| 22 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 23 | |||
| 24 | error: aborting due to 1 previous error | ||
| 25 | |||
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older.rs created+47| ... | @@ -0,0 +1,47 @@ | ||
| 1 | //@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance | ||
| 2 | |||
| 3 | /// Checks how accesses from one subtree affect other subtrees. | ||
| 4 | /// This test checks that an access from a newer created subtree | ||
| 5 | /// performs a wildcard access on all earlier trees. | ||
| 6 | pub fn main() { | ||
| 7 | let mut x: u32 = 42; | ||
| 8 | |||
| 9 | let ref_base = &mut x; | ||
| 10 | |||
| 11 | let int0 = ref_base as *mut u32 as usize; | ||
| 12 | let wild = int0 as *mut u32; | ||
| 13 | |||
| 14 | let reb1 = unsafe { &mut *wild }; | ||
| 15 | |||
| 16 | let reb2 = unsafe { &mut *wild }; | ||
| 17 | |||
| 18 | let ref3 = &mut *reb2; | ||
| 19 | let _int3 = ref3 as *mut u32 as usize; | ||
| 20 | // ┌──────────────┐ | ||
| 21 | // │ │ | ||
| 22 | // │ptr_base(Res)*│ * * | ||
| 23 | // │ │ │ │ | ||
| 24 | // └──────────────┘ │ │ | ||
| 25 | // │ │ | ||
| 26 | // │ │ | ||
| 27 | // ▼ ▼ | ||
| 28 | // ┌────────────┐ ┌────────────┐ | ||
| 29 | // │ │ │ │ | ||
| 30 | // │ reb1(Res) ├ │ reb2(Res) ├ | ||
| 31 | // │ │ │ │ | ||
| 32 | // └────────────┘ └──────┬─────┘ | ||
| 33 | // │ | ||
| 34 | // │ | ||
| 35 | // ▼ | ||
| 36 | // ┌────────────┐ | ||
| 37 | // │ │ | ||
| 38 | // │ ref3(Res)* │ | ||
| 39 | // │ │ | ||
| 40 | // └────────────┘ | ||
| 41 | |||
| 42 | // this access disables reb2 because ref3 cannot be a child of it | ||
| 43 | // as reb1 does not have any exposed children. | ||
| 44 | *ref3 = 13; | ||
| 45 | |||
| 46 | let _fail = *reb1; //~ ERROR: /read access through .* is forbidden/ | ||
| 47 | } | ||
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older.stderr created+25| ... | @@ -0,0 +1,25 @@ | ||
| 1 | error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden | ||
| 2 | --> tests/fail/tree_borrows/wildcard/cross_tree_update_older.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | let _fail = *reb1; | ||
| 5 | | ^^^^^ Undefined Behavior occurred here | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information | ||
| 9 | = help: the accessed tag <TAG> has state Disabled which forbids this child read access | ||
| 10 | help: the accessed tag <TAG> was created here, in the initial state Reserved | ||
| 11 | --> tests/fail/tree_borrows/wildcard/cross_tree_update_older.rs:LL:CC | ||
| 12 | | | ||
| 13 | LL | let reb1 = unsafe { &mut *wild }; | ||
| 14 | | ^^^^^^^^^^ | ||
| 15 | help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] | ||
| 16 | --> tests/fail/tree_borrows/wildcard/cross_tree_update_older.rs:LL:CC | ||
| 17 | | | ||
| 18 | LL | *ref3 = 13; | ||
| 19 | | ^^^^^^^^^^ | ||
| 20 | = help: this transition corresponds to a loss of read and write permissions | ||
| 21 | |||
| 22 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 23 | |||
| 24 | error: aborting due to 1 previous error | ||
| 25 | |||
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed.rs created+53| ... | @@ -0,0 +1,53 @@ | ||
| 1 | //@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance | ||
| 2 | |||
| 3 | /// Checks how accesses from one subtree affect other subtrees. | ||
| 4 | /// This test checks that an access from a newer created subtree | ||
| 5 | /// performs a wildcard access on all earlier trees, and that | ||
| 6 | /// either accesses are treated as foreign for tags that are | ||
| 7 | /// larger than the root of the accessed subtree. | ||
| 8 | pub fn main() { | ||
| 9 | let mut x: u32 = 42; | ||
| 10 | |||
| 11 | let ref_base = &mut x; | ||
| 12 | |||
| 13 | let int0 = ref_base as *mut u32 as usize; | ||
| 14 | let wild = int0 as *mut u32; | ||
| 15 | |||
| 16 | let reb1 = unsafe { &mut *wild }; | ||
| 17 | |||
| 18 | let reb2 = unsafe { &mut *wild }; | ||
| 19 | |||
| 20 | let ref3 = &mut *reb1; | ||
| 21 | let _int3 = ref3 as *mut u32 as usize; | ||
| 22 | |||
| 23 | let ref4 = &mut *reb2; | ||
| 24 | let _int4 = ref4 as *mut u32 as usize; | ||
| 25 | // ┌──────────────┐ | ||
| 26 | // │ │ | ||
| 27 | // │ptr_base(Res)*│ * * | ||
| 28 | // │ │ │ │ | ||
| 29 | // └──────────────┘ │ │ | ||
| 30 | // │ │ | ||
| 31 | // │ │ | ||
| 32 | // ▼ ▼ | ||
| 33 | // ┌────────────┐ ┌────────────┐ | ||
| 34 | // │ │ │ │ | ||
| 35 | // │ reb1(Res) ├ │ reb2(Res) ├ | ||
| 36 | // │ │ │ │ | ||
| 37 | // └──────┬─────┘ └──────┬─────┘ | ||
| 38 | // │ │ | ||
| 39 | // │ │ | ||
| 40 | // ▼ ▼ | ||
| 41 | // ┌────────────┐ ┌────────────┐ | ||
| 42 | // │ │ │ │ | ||
| 43 | // │ ref3(Res)* │ │ ref4(Res)* │ | ||
| 44 | // │ │ │ │ | ||
| 45 | // └────────────┘ └────────────┘ | ||
| 46 | |||
| 47 | // This access disables ref3 and reb1 because ref4 cannot be a child of it | ||
| 48 | // as reb2 has a smaller tag than ref3. | ||
| 49 | *ref4 = 13; | ||
| 50 | |||
| 51 | // Fails because ref3 is disabled. | ||
| 52 | let _fail = *ref3; //~ ERROR: /read access through .* is forbidden/ | ||
| 53 | } | ||
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed.stderr created+25| ... | @@ -0,0 +1,25 @@ | ||
| 1 | error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden | ||
| 2 | --> tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | let _fail = *ref3; | ||
| 5 | | ^^^^^ Undefined Behavior occurred here | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information | ||
| 9 | = help: the accessed tag <TAG> has state Disabled which forbids this child read access | ||
| 10 | help: the accessed tag <TAG> was created here, in the initial state Reserved | ||
| 11 | --> tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed.rs:LL:CC | ||
| 12 | | | ||
| 13 | LL | let ref3 = &mut *reb1; | ||
| 14 | | ^^^^^^^^^^ | ||
| 15 | help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] | ||
| 16 | --> tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed.rs:LL:CC | ||
| 17 | | | ||
| 18 | LL | *ref4 = 13; | ||
| 19 | | ^^^^^^^^^^ | ||
| 20 | = help: this transition corresponds to a loss of read and write permissions | ||
| 21 | |||
| 22 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 23 | |||
| 24 | error: aborting due to 1 previous error | ||
| 25 | |||
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed2.rs created+59| ... | @@ -0,0 +1,59 @@ | ||
| 1 | //@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance | ||
| 2 | |||
| 3 | /// Checks how accesses from one subtree affect other subtrees. | ||
| 4 | /// This test checks that an access from a newer created subtree | ||
| 5 | /// performs a wildcard access on all earlier trees, and that | ||
| 6 | /// either accesses are treated as foreign for tags that are | ||
| 7 | /// larger than the root of the accessed subtree. | ||
| 8 | /// This tests the special case where these updates get propagated | ||
| 9 | /// up the tree. | ||
| 10 | pub fn main() { | ||
| 11 | let mut x: u32 = 42; | ||
| 12 | |||
| 13 | let ref_base = &mut x; | ||
| 14 | |||
| 15 | let int0 = ref_base as *mut u32 as usize; | ||
| 16 | let wild = int0 as *mut u32; | ||
| 17 | |||
| 18 | let reb1 = unsafe { &mut *wild }; | ||
| 19 | |||
| 20 | let reb2 = unsafe { &mut *wild }; | ||
| 21 | |||
| 22 | let ref3 = &mut *reb1; | ||
| 23 | let _int3 = ref3 as *mut u32 as usize; | ||
| 24 | |||
| 25 | let ref4 = &mut *reb2; | ||
| 26 | let _int4 = ref4 as *mut u32 as usize; | ||
| 27 | // ┌──────────────┐ | ||
| 28 | // │ │ | ||
| 29 | // │ptr_base(Res)*│ * * | ||
| 30 | // │ │ │ │ | ||
| 31 | // └──────────────┘ │ │ | ||
| 32 | // │ │ | ||
| 33 | // │ │ | ||
| 34 | // ▼ ▼ | ||
| 35 | // ┌────────────┐ ┌────────────┐ | ||
| 36 | // │ │ │ │ | ||
| 37 | // │ reb1(Res) ├ │ reb2(Res) ├ | ||
| 38 | // │ │ │ │ | ||
| 39 | // └──────┬─────┘ └──────┬─────┘ | ||
| 40 | // │ │ | ||
| 41 | // │ │ | ||
| 42 | // ▼ ▼ | ||
| 43 | // ┌────────────┐ ┌────────────┐ | ||
| 44 | // │ │ │ │ | ||
| 45 | // │ ref3(Res)* │ │ ref4(Res)* │ | ||
| 46 | // │ │ │ │ | ||
| 47 | // └────────────┘ └────────────┘ | ||
| 48 | |||
| 49 | // This access disables ref3 and reb1 because ref4 cannot be a child of it | ||
| 50 | // as reb2 has a smaller tag than ref3. | ||
| 51 | // | ||
| 52 | // Because of the update order during a wildcard access (child before parent) | ||
| 53 | // ref3 gets disabled before we update reb1. So reb1 has no exposed children | ||
| 54 | // with write access at the time it gets updated so it also gets disabled. | ||
| 55 | *ref4 = 13; | ||
| 56 | |||
| 57 | // Fails because reb1 is disabled. | ||
| 58 | let _fail = *reb1; //~ ERROR: /read access through .* is forbidden/ | ||
| 59 | } | ||
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed2.stderr created+25| ... | @@ -0,0 +1,25 @@ | ||
| 1 | error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden | ||
| 2 | --> tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed2.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | let _fail = *reb1; | ||
| 5 | | ^^^^^ Undefined Behavior occurred here | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information | ||
| 9 | = help: the accessed tag <TAG> has state Disabled which forbids this child read access | ||
| 10 | help: the accessed tag <TAG> was created here, in the initial state Reserved | ||
| 11 | --> tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed2.rs:LL:CC | ||
| 12 | | | ||
| 13 | LL | let reb1 = unsafe { &mut *wild }; | ||
| 14 | | ^^^^^^^^^^ | ||
| 15 | help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] | ||
| 16 | --> tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed2.rs:LL:CC | ||
| 17 | | | ||
| 18 | LL | *ref4 = 13; | ||
| 19 | | ^^^^^^^^^^ | ||
| 20 | = help: this transition corresponds to a loss of read and write permissions | ||
| 21 | |||
| 22 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 23 | |||
| 24 | error: aborting due to 1 previous error | ||
| 25 | |||
src/tools/miri/tests/fail/tree_borrows/wildcard/protected_wildcard.rs created+39| ... | @@ -0,0 +1,39 @@ | ||
| 1 | //@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance | ||
| 2 | |||
| 3 | /// Checks if we pass a reference derived from a wildcard pointer | ||
| 4 | /// that it gets correctly protected. | ||
| 5 | pub fn main() { | ||
| 6 | let mut x: u32 = 32; | ||
| 7 | let ref1 = &mut x; | ||
| 8 | |||
| 9 | let ref2 = &mut *ref1; | ||
| 10 | let int2 = ref2 as *mut u32 as usize; | ||
| 11 | |||
| 12 | let wild = int2 as *mut u32; | ||
| 13 | let wild_ref = unsafe { &mut *wild }; | ||
| 14 | |||
| 15 | let mut protect = |_arg: &mut u32| { | ||
| 16 | // _arg is a protected pointer with wildcard parent. | ||
| 17 | |||
| 18 | // ┌────────────┐ | ||
| 19 | // │ │ | ||
| 20 | // │ ref1(Res) │ * | ||
| 21 | // │ │ │ | ||
| 22 | // └──────┬─────┘ │ | ||
| 23 | // │ │ | ||
| 24 | // │ │ | ||
| 25 | // ▼ ▼ | ||
| 26 | // ┌────────────┐ ┌────────────┐ | ||
| 27 | // │ │ │ │ | ||
| 28 | // │ ref2(Res)* │ │ _arg(Res) │ | ||
| 29 | // │ │ │ │ | ||
| 30 | // └────────────┘ └────────────┘ | ||
| 31 | |||
| 32 | // Writes to ref1, causing a foreign write to ref2 and _arg. | ||
| 33 | // Since _arg is protected this is UB. | ||
| 34 | *ref1 = 13; //~ ERROR: /write access through .* is forbidden/ | ||
| 35 | }; | ||
| 36 | |||
| 37 | // We pass a pointer with wildcard provenance to the function. | ||
| 38 | protect(wild_ref); | ||
| 39 | } | ||
src/tools/miri/tests/fail/tree_borrows/wildcard/protected_wildcard.stderr created+37| ... | @@ -0,0 +1,37 @@ | ||
| 1 | error: Undefined Behavior: write access through <TAG> at ALLOC[0x0] is forbidden | ||
| 2 | --> tests/fail/tree_borrows/wildcard/protected_wildcard.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | *ref1 = 13; | ||
| 5 | | ^^^^^^^^^^ Undefined Behavior occurred here | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information | ||
| 9 | = help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child) | ||
| 10 | = help: this foreign write access would cause the protected tag <TAG> (currently Reserved) to become Disabled | ||
| 11 | = help: protected tags must never be Disabled | ||
| 12 | help: the accessed tag <TAG> was created here | ||
| 13 | --> tests/fail/tree_borrows/wildcard/protected_wildcard.rs:LL:CC | ||
| 14 | | | ||
| 15 | LL | let mut protect = |_arg: &mut u32| { | ||
| 16 | | _______________________^ | ||
| 17 | ... | | ||
| 18 | LL | | *ref1 = 13; | ||
| 19 | LL | | }; | ||
| 20 | | |_____^ | ||
| 21 | help: the protected tag <TAG> was created here, in the initial state Reserved | ||
| 22 | --> tests/fail/tree_borrows/wildcard/protected_wildcard.rs:LL:CC | ||
| 23 | | | ||
| 24 | LL | let mut protect = |_arg: &mut u32| { | ||
| 25 | | ^^^^ | ||
| 26 | = note: BACKTRACE (of the first span): | ||
| 27 | = note: inside closure at tests/fail/tree_borrows/wildcard/protected_wildcard.rs:LL:CC | ||
| 28 | note: inside `main` | ||
| 29 | --> tests/fail/tree_borrows/wildcard/protected_wildcard.rs:LL:CC | ||
| 30 | | | ||
| 31 | LL | protect(wild_ref); | ||
| 32 | | ^^^^^^^^^^^^^^^^^ | ||
| 33 | |||
| 34 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 35 | |||
| 36 | error: aborting due to 1 previous error | ||
| 37 | |||
src/tools/miri/tests/fail/tree_borrows/wildcard/strongly_protected_wildcard.stderr+7-2| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | error: Undefined Behavior: deallocation through <wildcard> at ALLOC[0x0] is forbidden | 1 | error: Undefined Behavior: deallocation through <TAG> at ALLOC[0x0] is forbidden |
| 2 | --> RUSTLIB/alloc/src/boxed.rs:LL:CC | 2 | --> RUSTLIB/alloc/src/boxed.rs:LL:CC |
| 3 | | | 3 | | |
| 4 | LL | self.1.deallocate(From::from(ptr.cast()), layout); | 4 | LL | self.1.deallocate(From::from(ptr.cast()), layout); |
| ... | @@ -6,8 +6,13 @@ LL | self.1.deallocate(From::from(ptr.cast()), layout); | ... | @@ -6,8 +6,13 @@ LL | self.1.deallocate(From::from(ptr.cast()), layout); |
| 6 | | | 6 | | |
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental | 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental |
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information | 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information |
| 9 | = help: the allocation of the accessed tag <wildcard> also contains the strongly protected tag <TAG> | 9 | = help: the allocation of the accessed tag <TAG> also contains the strongly protected tag <TAG> |
| 10 | = help: the strongly protected tag <TAG> disallows deallocations | 10 | = help: the strongly protected tag <TAG> disallows deallocations |
| 11 | help: the accessed tag <TAG> was created here | ||
| 12 | --> tests/fail/tree_borrows/wildcard/strongly_protected_wildcard.rs:LL:CC | ||
| 13 | | | ||
| 14 | LL | drop(unsafe { Box::from_raw(raw as *mut i32) }); | ||
| 15 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| 11 | help: the strongly protected tag <TAG> was created here, in the initial state Reserved | 16 | help: the strongly protected tag <TAG> was created here, in the initial state Reserved |
| 12 | --> tests/fail/tree_borrows/wildcard/strongly_protected_wildcard.rs:LL:CC | 17 | --> tests/fail/tree_borrows/wildcard/strongly_protected_wildcard.rs:LL:CC |
| 13 | | | 18 | | |
src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.rs created+44| ... | @@ -0,0 +1,44 @@ | ||
| 1 | //@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance | ||
| 2 | |||
| 3 | // Checks if we correctly infer the relatedness of nodes that are | ||
| 4 | // part of the same wildcard root. | ||
| 5 | pub fn main() { | ||
| 6 | let mut x: u32 = 42; | ||
| 7 | |||
| 8 | let ref_base = &mut x; | ||
| 9 | |||
| 10 | let int1 = ref_base as *mut u32 as usize; | ||
| 11 | let wild = int1 as *mut u32; | ||
| 12 | |||
| 13 | let reb = unsafe { &mut *wild }; | ||
| 14 | let ptr_reb = reb as *mut u32; | ||
| 15 | let ref1 = unsafe { &mut *ptr_reb }; | ||
| 16 | let ref2 = unsafe { &mut *ptr_reb }; | ||
| 17 | |||
| 18 | // ┌──────────────┐ | ||
| 19 | // │ │ | ||
| 20 | // │ptr_base(Res)*│ * | ||
| 21 | // │ │ │ | ||
| 22 | // └──────────────┘ │ | ||
| 23 | // │ | ||
| 24 | // │ | ||
| 25 | // ▼ | ||
| 26 | // ┌────────────┐ | ||
| 27 | // │ │ | ||
| 28 | // │ reb(Res) ├───────────┐ | ||
| 29 | // │ │ │ | ||
| 30 | // └──────┬─────┘ │ | ||
| 31 | // │ │ | ||
| 32 | // │ │ | ||
| 33 | // ▼ ▼ | ||
| 34 | // ┌────────────┐ ┌───────────┐ | ||
| 35 | // │ │ │ │ | ||
| 36 | // │ ref1(Res) │ │ ref2(Res) │ | ||
| 37 | // │ │ │ │ | ||
| 38 | // └────────────┘ └───────────┘ | ||
| 39 | |||
| 40 | // ref1 is foreign to ref2, so this should disable ref2. | ||
| 41 | *ref1 = 13; | ||
| 42 | |||
| 43 | let _fail = *ref2; //~ ERROR: /read access through .* is forbidden/ | ||
| 44 | } | ||
src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.stderr created+25| ... | @@ -0,0 +1,25 @@ | ||
| 1 | error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden | ||
| 2 | --> tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | let _fail = *ref2; | ||
| 5 | | ^^^^^ Undefined Behavior occurred here | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information | ||
| 9 | = help: the accessed tag <TAG> has state Disabled which forbids this child read access | ||
| 10 | help: the accessed tag <TAG> was created here, in the initial state Reserved | ||
| 11 | --> tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.rs:LL:CC | ||
| 12 | | | ||
| 13 | LL | let ref2 = unsafe { &mut *ptr_reb }; | ||
| 14 | | ^^^^^^^^^^^^^ | ||
| 15 | help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] | ||
| 16 | --> tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.rs:LL:CC | ||
| 17 | | | ||
| 18 | LL | *ref1 = 13; | ||
| 19 | | ^^^^^^^^^^ | ||
| 20 | = help: this transition corresponds to a loss of read and write permissions | ||
| 21 | |||
| 22 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 23 | |||
| 24 | error: aborting due to 1 previous error | ||
| 25 | |||
src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.rs created+49| ... | @@ -0,0 +1,49 @@ | ||
| 1 | //@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance | ||
| 2 | |||
| 3 | // Checks if we correctly infer the relatedness of nodes that are | ||
| 4 | // part of the same wildcard root during a wildcard access. | ||
| 5 | pub fn main() { | ||
| 6 | let mut x: u32 = 42; | ||
| 7 | |||
| 8 | let ref_base = &mut x; | ||
| 9 | |||
| 10 | let int = ref_base as *mut u32 as usize; | ||
| 11 | let wild = int as *mut u32; | ||
| 12 | |||
| 13 | let reb = unsafe { &mut *wild }; | ||
| 14 | let ptr_reb = reb as *mut u32; | ||
| 15 | let ref1 = unsafe { &mut *ptr_reb }; | ||
| 16 | let _int1 = ref1 as *mut u32 as usize; | ||
| 17 | let ref2 = unsafe { &mut *ptr_reb }; | ||
| 18 | |||
| 19 | // ┌──────────────┐ | ||
| 20 | // │ │ | ||
| 21 | // │ptr_base(Res)*│ * | ||
| 22 | // │ │ │ | ||
| 23 | // └──────────────┘ │ | ||
| 24 | // │ | ||
| 25 | // │ | ||
| 26 | // ▼ | ||
| 27 | // ┌────────────┐ | ||
| 28 | // │ │ | ||
| 29 | // │ reb(Res) ├───────────┐ | ||
| 30 | // │ │ │ | ||
| 31 | // └──────┬─────┘ │ | ||
| 32 | // │ │ | ||
| 33 | // │ │ | ||
| 34 | // ▼ ▼ | ||
| 35 | // ┌────────────┐ ┌───────────┐ | ||
| 36 | // │ │ │ │ | ||
| 37 | // │ ref1(Res)* │ │ ref2(Res) │ | ||
| 38 | // │ │ │ │ | ||
| 39 | // └────────────┘ └───────────┘ | ||
| 40 | |||
| 41 | // Writes either through ref1 or ptr_base. | ||
| 42 | // This disables ref2 as the access is foreign to it in either case. | ||
| 43 | unsafe { *wild = 13 }; | ||
| 44 | |||
| 45 | // This is fine because the earlier write could have come from ref1. | ||
| 46 | let _succ = *ref1; | ||
| 47 | // ref2 is disabled so this fails. | ||
| 48 | let _fail = *ref2; //~ ERROR: /read access through .* is forbidden/ | ||
| 49 | } | ||
src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.stderr created+25| ... | @@ -0,0 +1,25 @@ | ||
| 1 | error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden | ||
| 2 | --> tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | let _fail = *ref2; | ||
| 5 | | ^^^^^ Undefined Behavior occurred here | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental | ||
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information | ||
| 9 | = help: the accessed tag <TAG> has state Disabled which forbids this child read access | ||
| 10 | help: the accessed tag <TAG> was created here, in the initial state Reserved | ||
| 11 | --> tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.rs:LL:CC | ||
| 12 | | | ||
| 13 | LL | let ref2 = unsafe { &mut *ptr_reb }; | ||
| 14 | | ^^^^^^^^^^^^^ | ||
| 15 | help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] | ||
| 16 | --> tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.rs:LL:CC | ||
| 17 | | | ||
| 18 | LL | unsafe { *wild = 13 }; | ||
| 19 | | ^^^^^^^^^^ | ||
| 20 | = help: this transition corresponds to a loss of read and write permissions | ||
| 21 | |||
| 22 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 23 | |||
| 24 | error: aborting due to 1 previous error | ||
| 25 | |||
src/tools/miri/tests/fail/tree_borrows/write_to_shr.stderr deleted-26| ... | @@ -1,26 +0,0 @@ | ||
| 1 | error: Undefined Behavior: write access through <TAG> is forbidden | ||
| 2 | --> $DIR/write_to_shr.rs:LL:CC | ||
| 3 | | | ||
| 4 | LL | *xmut = 31; | ||
| 5 | | ^^^^^^^^^^ write access through <TAG> is forbidden | ||
| 6 | | | ||
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental | ||
| 8 | = help: the accessed tag <TAG> is a child of the conflicting tag <TAG> | ||
| 9 | = help: the conflicting tag <TAG> has state Frozen which forbids child write accesses | ||
| 10 | help: the accessed tag <TAG> was created here | ||
| 11 | --> $DIR/write_to_shr.rs:LL:CC | ||
| 12 | | | ||
| 13 | LL | let xmut = unsafe { &mut *(xref as *const u64 as *mut u64) }; | ||
| 14 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| 15 | help: the conflicting tag <TAG> was created here, in the initial state Frozen | ||
| 16 | --> $DIR/write_to_shr.rs:LL:CC | ||
| 17 | | | ||
| 18 | LL | let xref = unsafe { &*(x as *mut u64) }; | ||
| 19 | | ^^^^^^^^^^^^^^^^^ | ||
| 20 | = note: BACKTRACE (of the first span): | ||
| 21 | = note: inside `main` at $DIR/write_to_shr.rs:LL:CC | ||
| 22 | |||
| 23 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | ||
| 24 | |||
| 25 | error: aborting due to 1 previous error | ||
| 26 | |||
src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.stderr+2-2| ... | @@ -18,7 +18,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir | ... | @@ -18,7 +18,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir |
| 18 | = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC | 18 | = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC |
| 19 | = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC | 19 | = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC |
| 20 | = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC | 20 | = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC |
| 21 | = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC | 21 | = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC |
| 22 | note: inside `miri_start` | 22 | note: inside `miri_start` |
| 23 | --> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC | 23 | --> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC |
| 24 | | | 24 | | |
| ... | @@ -48,7 +48,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir | ... | @@ -48,7 +48,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir |
| 48 | = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC | 48 | = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC |
| 49 | = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC | 49 | = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC |
| 50 | = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC | 50 | = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC |
| 51 | = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC | 51 | = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC |
| 52 | note: inside `miri_start` | 52 | note: inside `miri_start` |
| 53 | --> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC | 53 | --> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC |
| 54 | | | 54 | | |
src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr+3-3| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | Running GenMC Verification... | 1 | Running GenMC Verification... |
| 2 | warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. | 2 | warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. |
| 3 | --> RUSTLIB/std/src/thread/mod.rs:LL:CC | 3 | --> RUSTLIB/std/src/thread/id.rs:LL:CC |
| 4 | | | 4 | | |
| 5 | LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { | 5 | LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { |
| 6 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code | 6 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code |
| ... | @@ -46,7 +46,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir | ... | @@ -46,7 +46,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir |
| 46 | | | 46 | | |
| 47 | = note: BACKTRACE: | 47 | = note: BACKTRACE: |
| 48 | = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC | 48 | = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC |
| 49 | = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC | 49 | = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC |
| 50 | note: inside `main` | 50 | note: inside `main` |
| 51 | --> tests/genmc/pass/std/arc.rs:LL:CC | 51 | --> tests/genmc/pass/std/arc.rs:LL:CC |
| 52 | | | 52 | | |
| ... | @@ -67,7 +67,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir | ... | @@ -67,7 +67,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir |
| 67 | | | 67 | | |
| 68 | = note: BACKTRACE: | 68 | = note: BACKTRACE: |
| 69 | = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC | 69 | = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC |
| 70 | = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC | 70 | = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC |
| 71 | note: inside `main` | 71 | note: inside `main` |
| 72 | --> tests/genmc/pass/std/arc.rs:LL:CC | 72 | --> tests/genmc/pass/std/arc.rs:LL:CC |
| 73 | | | 73 | | |
src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr+3-3| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | Running GenMC Verification... | 1 | Running GenMC Verification... |
| 2 | warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. | 2 | warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. |
| 3 | --> RUSTLIB/std/src/thread/mod.rs:LL:CC | 3 | --> RUSTLIB/std/src/thread/id.rs:LL:CC |
| 4 | | | 4 | | |
| 5 | LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { | 5 | LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { |
| 6 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code | 6 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code |
| ... | @@ -46,7 +46,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir | ... | @@ -46,7 +46,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir |
| 46 | | | 46 | | |
| 47 | = note: BACKTRACE: | 47 | = note: BACKTRACE: |
| 48 | = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC | 48 | = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC |
| 49 | = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC | 49 | = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC |
| 50 | note: inside `main` | 50 | note: inside `main` |
| 51 | --> tests/genmc/pass/std/arc.rs:LL:CC | 51 | --> tests/genmc/pass/std/arc.rs:LL:CC |
| 52 | | | 52 | | |
| ... | @@ -67,7 +67,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir | ... | @@ -67,7 +67,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir |
| 67 | | | 67 | | |
| 68 | = note: BACKTRACE: | 68 | = note: BACKTRACE: |
| 69 | = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC | 69 | = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC |
| 70 | = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC | 70 | = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC |
| 71 | note: inside `main` | 71 | note: inside `main` |
| 72 | --> tests/genmc/pass/std/arc.rs:LL:CC | 72 | --> tests/genmc/pass/std/arc.rs:LL:CC |
| 73 | | | 73 | | |
src/tools/miri/tests/genmc/pass/std/empty_main.stderr+1-1| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | Running GenMC Verification... | 1 | Running GenMC Verification... |
| 2 | warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. | 2 | warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. |
| 3 | --> RUSTLIB/std/src/thread/mod.rs:LL:CC | 3 | --> RUSTLIB/std/src/thread/id.rs:LL:CC |
| 4 | | | 4 | | |
| 5 | LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { | 5 | LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { |
| 6 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code | 6 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code |
src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr+3-3| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | Running GenMC Verification... | 1 | Running GenMC Verification... |
| 2 | warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. | 2 | warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. |
| 3 | --> RUSTLIB/std/src/thread/mod.rs:LL:CC | 3 | --> RUSTLIB/std/src/thread/id.rs:LL:CC |
| 4 | | | 4 | | |
| 5 | LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { | 5 | LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { |
| 6 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code | 6 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code |
| ... | @@ -20,7 +20,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir | ... | @@ -20,7 +20,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir |
| 20 | | | 20 | | |
| 21 | = note: BACKTRACE: | 21 | = note: BACKTRACE: |
| 22 | = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC | 22 | = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC |
| 23 | = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC | 23 | = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC |
| 24 | note: inside closure | 24 | note: inside closure |
| 25 | --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC | 25 | --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC |
| 26 | | | 26 | | |
| ... | @@ -52,7 +52,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir | ... | @@ -52,7 +52,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir |
| 52 | | | 52 | | |
| 53 | = note: BACKTRACE: | 53 | = note: BACKTRACE: |
| 54 | = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC | 54 | = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC |
| 55 | = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC | 55 | = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC |
| 56 | note: inside closure | 56 | note: inside closure |
| 57 | --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC | 57 | --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC |
| 58 | | | 58 | | |
src/tools/miri/tests/genmc/pass/std/thread_locals.stderr+3-3| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | Running GenMC Verification... | 1 | Running GenMC Verification... |
| 2 | warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. | 2 | warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. |
| 3 | --> RUSTLIB/std/src/thread/mod.rs:LL:CC | 3 | --> RUSTLIB/std/src/thread/id.rs:LL:CC |
| 4 | | | 4 | | |
| 5 | LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { | 5 | LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { |
| 6 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code | 6 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code |
| ... | @@ -20,7 +20,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir | ... | @@ -20,7 +20,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir |
| 20 | | | 20 | | |
| 21 | = note: BACKTRACE: | 21 | = note: BACKTRACE: |
| 22 | = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC | 22 | = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC |
| 23 | = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC | 23 | = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC |
| 24 | note: inside `main` | 24 | note: inside `main` |
| 25 | --> tests/genmc/pass/std/thread_locals.rs:LL:CC | 25 | --> tests/genmc/pass/std/thread_locals.rs:LL:CC |
| 26 | | | 26 | | |
| ... | @@ -42,7 +42,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir | ... | @@ -42,7 +42,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir |
| 42 | | | 42 | | |
| 43 | = note: BACKTRACE: | 43 | = note: BACKTRACE: |
| 44 | = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC | 44 | = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC |
| 45 | = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC | 45 | = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC |
| 46 | note: inside `main` | 46 | note: inside `main` |
| 47 | --> tests/genmc/pass/std/thread_locals.rs:LL:CC | 47 | --> tests/genmc/pass/std/thread_locals.rs:LL:CC |
| 48 | | | 48 | | |
src/tools/miri/tests/pass-dep/libc/libc-fs.rs+37-1| ... | @@ -38,10 +38,11 @@ fn main() { | ... | @@ -38,10 +38,11 @@ fn main() { |
| 38 | test_posix_fadvise(); | 38 | test_posix_fadvise(); |
| 39 | #[cfg(not(target_os = "macos"))] | 39 | #[cfg(not(target_os = "macos"))] |
| 40 | test_posix_fallocate::<libc::off_t>(libc::posix_fallocate); | 40 | test_posix_fallocate::<libc::off_t>(libc::posix_fallocate); |
| 41 | #[cfg(any(target_os = "linux", target_os = "android"))] | 41 | #[cfg(target_os = "linux")] |
| 42 | test_posix_fallocate::<libc::off64_t>(libc::posix_fallocate64); | 42 | test_posix_fallocate::<libc::off64_t>(libc::posix_fallocate64); |
| 43 | #[cfg(target_os = "linux")] | 43 | #[cfg(target_os = "linux")] |
| 44 | test_sync_file_range(); | 44 | test_sync_file_range(); |
| 45 | test_fstat(); | ||
| 45 | test_isatty(); | 46 | test_isatty(); |
| 46 | test_read_and_uninit(); | 47 | test_read_and_uninit(); |
| 47 | test_nofollow_not_symlink(); | 48 | test_nofollow_not_symlink(); |
| ... | @@ -452,6 +453,41 @@ fn test_sync_file_range() { | ... | @@ -452,6 +453,41 @@ fn test_sync_file_range() { |
| 452 | assert_eq!(result_2, 0); | 453 | assert_eq!(result_2, 0); |
| 453 | } | 454 | } |
| 454 | 455 | ||
| 456 | fn test_fstat() { | ||
| 457 | use std::mem::MaybeUninit; | ||
| 458 | use std::os::unix::io::AsRawFd; | ||
| 459 | |||
| 460 | let path = utils::prepare_with_content("miri_test_libc_fstat.txt", b"hello"); | ||
| 461 | let file = File::open(&path).unwrap(); | ||
| 462 | let fd = file.as_raw_fd(); | ||
| 463 | |||
| 464 | let mut stat = MaybeUninit::<libc::stat>::uninit(); | ||
| 465 | let res = unsafe { libc::fstat(fd, stat.as_mut_ptr()) }; | ||
| 466 | assert_eq!(res, 0); | ||
| 467 | let stat = unsafe { stat.assume_init_ref() }; | ||
| 468 | |||
| 469 | assert_eq!(stat.st_size, 5); | ||
| 470 | assert_eq!(stat.st_mode & libc::S_IFMT, libc::S_IFREG); | ||
| 471 | |||
| 472 | // Check that all fields are initialized. | ||
| 473 | let _st_nlink = stat.st_nlink; | ||
| 474 | let _st_blksize = stat.st_blksize; | ||
| 475 | let _st_blocks = stat.st_blocks; | ||
| 476 | let _st_ino = stat.st_ino; | ||
| 477 | let _st_dev = stat.st_dev; | ||
| 478 | let _st_uid = stat.st_uid; | ||
| 479 | let _st_gid = stat.st_gid; | ||
| 480 | let _st_rdev = stat.st_rdev; | ||
| 481 | let _st_atime = stat.st_atime; | ||
| 482 | let _st_mtime = stat.st_mtime; | ||
| 483 | let _st_ctime = stat.st_ctime; | ||
| 484 | let _st_atime_nsec = stat.st_atime_nsec; | ||
| 485 | let _st_mtime_nsec = stat.st_mtime_nsec; | ||
| 486 | let _st_ctime_nsec = stat.st_ctime_nsec; | ||
| 487 | |||
| 488 | remove_file(&path).unwrap(); | ||
| 489 | } | ||
| 490 | |||
| 455 | fn test_isatty() { | 491 | fn test_isatty() { |
| 456 | // Testing whether our isatty shim returns the right value would require controlling whether | 492 | // Testing whether our isatty shim returns the right value would require controlling whether |
| 457 | // these streams are actually TTYs, which is hard. | 493 | // these streams are actually TTYs, which is hard. |
src/tools/miri/tests/pass/tree_borrows/wildcard/formatting.rs created+36| ... | @@ -0,0 +1,36 @@ | ||
| 1 | // We disable the GC for this test because it would change what is printed. | ||
| 2 | //@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance -Zmiri-provenance-gc=0 | ||
| 3 | |||
| 4 | #[path = "../../../utils/mod.rs"] | ||
| 5 | #[macro_use] | ||
| 6 | mod utils; | ||
| 7 | |||
| 8 | fn main() { | ||
| 9 | unsafe { | ||
| 10 | let x = &0u8; | ||
| 11 | name!(x); | ||
| 12 | let xa = &*x; | ||
| 13 | name!(xa); | ||
| 14 | let xb = &*x; | ||
| 15 | name!(xb); | ||
| 16 | let wild = xb as *const u8 as usize as *const u8; | ||
| 17 | |||
| 18 | let y = &*wild; | ||
| 19 | name!(y); | ||
| 20 | let ya = &*y; | ||
| 21 | name!(ya); | ||
| 22 | let yb = &*y; | ||
| 23 | name!(yb); | ||
| 24 | let _int = ya as *const u8 as usize; | ||
| 25 | |||
| 26 | let z = &*wild; | ||
| 27 | name!(z); | ||
| 28 | |||
| 29 | let u = &*wild; | ||
| 30 | name!(u); | ||
| 31 | let ua = &*u; | ||
| 32 | name!(ua); | ||
| 33 | let alloc_id = alloc_id!(x); | ||
| 34 | print_state!(alloc_id); | ||
| 35 | } | ||
| 36 | } | ||
src/tools/miri/tests/pass/tree_borrows/wildcard/formatting.stderr created+17| ... | @@ -0,0 +1,17 @@ | ||
| 1 | ────────────────────────────────────────────────── | ||
| 2 | Warning: this tree is indicative only. Some tags may have been hidden. | ||
| 3 | 0.. 1 | ||
| 4 | | Act | └─┬──<TAG=root of the allocation> | ||
| 5 | | Frz | └─┬──<TAG=x> | ||
| 6 | | Frz | ├────<TAG=xa> | ||
| 7 | | Frz | └────<TAG=xb> (exposed) | ||
| 8 | | | | ||
| 9 | | Frz | *─┬──<TAG=y> | ||
| 10 | | Frz | ├────<TAG=ya> (exposed) | ||
| 11 | | Frz | └────<TAG=yb> | ||
| 12 | | | | ||
| 13 | | Frz | *────<TAG=z> | ||
| 14 | | | | ||
| 15 | | Frz | *─┬──<TAG=u> | ||
| 16 | | Frz | └────<TAG=ua> | ||
| 17 | ────────────────────────────────────────────────── | ||
src/tools/miri/tests/pass/tree_borrows/wildcard/reborrow.rs created+224| ... | @@ -0,0 +1,224 @@ | ||
| 1 | //@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance | ||
| 2 | |||
| 3 | pub fn main() { | ||
| 4 | multiple_exposed_siblings1(); | ||
| 5 | multiple_exposed_siblings2(); | ||
| 6 | reborrow3(); | ||
| 7 | returned_mut_is_usable(); | ||
| 8 | only_foreign_is_temporary(); | ||
| 9 | } | ||
| 10 | |||
| 11 | /// Checks that accessing through a reborrowed wildcard doesn't | ||
| 12 | /// disable any exposed reference. | ||
| 13 | fn multiple_exposed_siblings1() { | ||
| 14 | let mut x: u32 = 42; | ||
| 15 | |||
| 16 | let ptr_base = &mut x as *mut u32; | ||
| 17 | |||
| 18 | let ref1 = unsafe { &mut *ptr_base }; | ||
| 19 | let int1 = ref1 as *mut u32 as usize; | ||
| 20 | |||
| 21 | let ref2 = unsafe { &mut *ptr_base }; | ||
| 22 | let _int2 = ref2 as *mut u32 as usize; | ||
| 23 | |||
| 24 | let wild = int1 as *mut u32; | ||
| 25 | |||
| 26 | let reb = unsafe { &mut *wild }; | ||
| 27 | |||
| 28 | // ┌────────────┐ | ||
| 29 | // │ │ | ||
| 30 | // │ ptr_base ├────────────┐ * | ||
| 31 | // │ │ │ │ | ||
| 32 | // └──────┬─────┘ │ │ | ||
| 33 | // │ │ │ | ||
| 34 | // │ │ │ | ||
| 35 | // ▼ ▼ ▼ | ||
| 36 | // ┌────────────┐ ┌────────────┐ ┌────────────┐ | ||
| 37 | // │ │ │ │ │ │ | ||
| 38 | // │ ref1(Res)* │ │ ref2(Res)* │ │ reb(Res) │ | ||
| 39 | // │ │ │ │ │ │ | ||
| 40 | // └────────────┘ └────────────┘ └────────────┘ | ||
| 41 | |||
| 42 | // Could either have as a parent ref1 or ref2. | ||
| 43 | // So we can't disable either of them. | ||
| 44 | *reb = 13; | ||
| 45 | |||
| 46 | // We can still access either ref1 or ref2. | ||
| 47 | // Although it is actually UB to access both of them. | ||
| 48 | assert_eq!(*ref2, 13); | ||
| 49 | assert_eq!(*ref1, 13); | ||
| 50 | } | ||
| 51 | |||
| 52 | /// Checks that wildcard accesses do not invalidate any exposed | ||
| 53 | /// nodes through which the access could have happened. | ||
| 54 | /// It checks this for the case where some reborrowed wildcard | ||
| 55 | /// pointers are exposed as well. | ||
| 56 | fn multiple_exposed_siblings2() { | ||
| 57 | let mut x: u32 = 42; | ||
| 58 | |||
| 59 | let ptr_base = &mut x as *mut u32; | ||
| 60 | let int = ptr_base as usize; | ||
| 61 | |||
| 62 | let wild = int as *mut u32; | ||
| 63 | |||
| 64 | let reb_ptr = unsafe { &mut *wild } as *mut u32; | ||
| 65 | |||
| 66 | let ref1 = unsafe { &mut *reb_ptr }; | ||
| 67 | let _int1 = ref1 as *mut u32 as usize; | ||
| 68 | |||
| 69 | let ref2 = unsafe { &mut *reb_ptr }; | ||
| 70 | let _int2 = ref2 as *mut u32 as usize; | ||
| 71 | |||
| 72 | // ┌────────────┐ | ||
| 73 | // │ │ | ||
| 74 | // │ ptr_base* │ * | ||
| 75 | // │ │ │ | ||
| 76 | // └────────────┘ │ | ||
| 77 | // │ | ||
| 78 | // │ | ||
| 79 | // ▼ | ||
| 80 | // ┌────────────┐ | ||
| 81 | // │ │ | ||
| 82 | // │ reb ├────────────┐ | ||
| 83 | // │ │ │ | ||
| 84 | // └──────┬─────┘ │ | ||
| 85 | // │ │ | ||
| 86 | // │ │ | ||
| 87 | // ▼ ▼ | ||
| 88 | // ┌────────────┐ ┌────────────┐ | ||
| 89 | // │ │ │ │ | ||
| 90 | // │ ref1(Res)* │ │ ref2(Res)* │ | ||
| 91 | // │ │ │ │ | ||
| 92 | // └────────────┘ └────────────┘ | ||
| 93 | |||
| 94 | // Writes either through ref1, ref2 or ptr_base, which are all exposed. | ||
| 95 | // Since we don't know which we do not apply any transitions to any of | ||
| 96 | // the references. | ||
| 97 | unsafe { wild.write(13) }; | ||
| 98 | |||
| 99 | // We should be able to access either ref1 or ref2. | ||
| 100 | // Although it is actually UB to access ref1 and ref2 together. | ||
| 101 | assert_eq!(*ref2, 13); | ||
| 102 | assert_eq!(*ref1, 13); | ||
| 103 | } | ||
| 104 | |||
| 105 | /// Checks that accessing a reborrowed wildcard reference doesn't | ||
| 106 | /// invalidate other reborrowed wildcard references, if they | ||
| 107 | /// are also exposed. | ||
| 108 | fn reborrow3() { | ||
| 109 | let mut x: u32 = 42; | ||
| 110 | |||
| 111 | let ptr_base = &mut x as *mut u32; | ||
| 112 | let int = ptr_base as usize; | ||
| 113 | |||
| 114 | let wild = int as *mut u32; | ||
| 115 | |||
| 116 | let reb1 = unsafe { &mut *wild }; | ||
| 117 | let ref2 = &mut *reb1; | ||
| 118 | let _int = ref2 as *mut u32 as usize; | ||
| 119 | |||
| 120 | let reb3 = unsafe { &mut *wild }; | ||
| 121 | |||
| 122 | // ┌────────────┐ | ||
| 123 | // │ │ | ||
| 124 | // │ ptr_base* │ * * | ||
| 125 | // │ │ │ │ | ||
| 126 | // └────────────┘ │ │ | ||
| 127 | // │ │ | ||
| 128 | // │ │ | ||
| 129 | // ▼ ▼ | ||
| 130 | // ┌────────────┐ ┌────────────┐ | ||
| 131 | // │ │ │ │ | ||
| 132 | // │ reb1(Res) | │ reb3(Res) | | ||
| 133 | // │ │ │ │ | ||
| 134 | // └──────┬─────┘ └────────────┘ | ||
| 135 | // │ | ||
| 136 | // │ | ||
| 137 | // ▼ | ||
| 138 | // ┌────────────┐ | ||
| 139 | // │ │ | ||
| 140 | // │ ref2(Res)* │ | ||
| 141 | // │ │ | ||
| 142 | // └────────────┘ | ||
| 143 | |||
| 144 | // This is the only valid ordering these accesses can happen in. | ||
| 145 | |||
| 146 | // reb3 could be a child of ref2 so we don't disable ref2, reb1. | ||
| 147 | *reb3 = 1; | ||
| 148 | // Disables reb3 as it cannot be an ancestor of ref2. | ||
| 149 | *ref2 = 2; | ||
| 150 | // Disables ref2 (and reb3 if it wasn't already). | ||
| 151 | *reb1 = 3; | ||
| 152 | } | ||
| 153 | |||
| 154 | /// Analogous to same test in `../tree-borrows.rs` but with returning a | ||
| 155 | /// reborrowed wildcard reference. | ||
| 156 | fn returned_mut_is_usable() { | ||
| 157 | let mut x: u32 = 32; | ||
| 158 | let ref1 = &mut x; | ||
| 159 | |||
| 160 | let y = protect(ref1); | ||
| 161 | |||
| 162 | fn protect(arg: &mut u32) -> &mut u32 { | ||
| 163 | // Reborrow `arg` through a wildcard. | ||
| 164 | let int = arg as *mut u32 as usize; | ||
| 165 | let wild = int as *mut u32; | ||
| 166 | let ref2 = unsafe { &mut *wild }; | ||
| 167 | |||
| 168 | // Activate the reference so that it is vulnerable to foreign reads. | ||
| 169 | *ref2 = 42; | ||
| 170 | |||
| 171 | ref2 | ||
| 172 | // An implicit read through `arg` is inserted here. | ||
| 173 | } | ||
| 174 | |||
| 175 | *y = 4; | ||
| 176 | } | ||
| 177 | |||
| 178 | /// When accessing an allocation through a tag that was created from wildcard reference | ||
| 179 | /// we treat nodes with a larger tag as if the access could only have been foreign to them. | ||
| 180 | /// This change in access relatedness should not be visible in later accesses. | ||
| 181 | fn only_foreign_is_temporary() { | ||
| 182 | let mut x = 0u32; | ||
| 183 | let wild = &mut x as *mut u32 as usize as *mut u32; | ||
| 184 | |||
| 185 | let reb1 = unsafe { &mut *wild }; | ||
| 186 | let reb2 = unsafe { &mut *wild }; | ||
| 187 | let ref3 = &mut *reb1; | ||
| 188 | let _int = ref3 as *mut u32 as usize; | ||
| 189 | |||
| 190 | let reb4 = unsafe { &mut *wild }; | ||
| 191 | |||
| 192 | // | ||
| 193 | // | ||
| 194 | // * * * | ||
| 195 | // │ │ │ | ||
| 196 | // │ │ │ | ||
| 197 | // │ │ │ | ||
| 198 | // │ │ │ | ||
| 199 | // ▼ ▼ ▼ | ||
| 200 | // ┌────────────┐ ┌────────────┐ ┌────────────┐ | ||
| 201 | // │ │ │ │ │ │ | ||
| 202 | // │ reb1(Res) │ │ reb2(Res) │ │ reb4(Res) │ | ||
| 203 | // │ │ │ │ │ │ | ||
| 204 | // └──────┬─────┘ └────────────┘ └────────────┘ | ||
| 205 | // │ | ||
| 206 | // │ | ||
| 207 | // ▼ | ||
| 208 | // ┌────────────┐ | ||
| 209 | // │ │ | ||
| 210 | // │ ref3(Res)* │ | ||
| 211 | // │ │ | ||
| 212 | // └────────────┘ | ||
| 213 | |||
| 214 | // Performs a foreign read on ref3 and doesn't update reb1. | ||
| 215 | // This temporarily treats ref3 as if only foreign accesses are possible to | ||
| 216 | // it. This is because the accessed tag reb2 has a larger tag than ref3. | ||
| 217 | let _x = *reb2; | ||
| 218 | // Should not update ref3, reb1 as we don't know if the access is local or foreign. | ||
| 219 | // This should stop treating ref3 as only foreign because the accessed tag reb4 | ||
| 220 | // has a larger tag than ref3. | ||
| 221 | *reb4 = 32; | ||
| 222 | // The previous write could have been local to ref3, so this access should still work. | ||
| 223 | *ref3 = 4; | ||
| 224 | } | ||
src/tools/miri/tests/pass/tree_borrows/wildcard/undetected_ub.rs+55-39| ... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
| 4 | pub fn main() { | 4 | pub fn main() { |
| 5 | uncertain_provenance(); | 5 | uncertain_provenance(); |
| 6 | protected_exposed(); | 6 | protected_exposed(); |
| 7 | protected_wildcard(); | 7 | cross_tree_update_older_invalid_exposed(); |
| 8 | } | 8 | } |
| 9 | 9 | ||
| 10 | /// Currently, if we do not know for a tag if an access is local or foreign, | 10 | /// Currently, if we do not know for a tag if an access is local or foreign, |
| ... | @@ -101,45 +101,61 @@ pub fn protected_exposed() { | ... | @@ -101,45 +101,61 @@ pub fn protected_exposed() { |
| 101 | } | 101 | } |
| 102 | protect(ref1); | 102 | protect(ref1); |
| 103 | 103 | ||
| 104 | // ref2 is disabled, so this read causes UB, but we currently don't protect this. | 104 | // ref2 should be disabled, so this read causes UB, but we currently don't detect this. |
| 105 | let _fail = *ref2; | 105 | let _fail = *ref2; |
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | /// Currently, we do not assign protectors to wildcard references. | 108 | /// Checks how accesses from one subtree affect other subtrees. |
| 109 | /// This test has UB because it does a foreign write to a protected reference. | 109 | /// This test shows an example where we don't update a node whose exposed |
| 110 | /// However, that reference is a wildcard, so this doesn't get detected. | 110 | /// children are greater than `max_local_tag`. |
| 111 | #[allow(unused_variables)] | 111 | pub fn cross_tree_update_older_invalid_exposed() { |
| 112 | pub fn protected_wildcard() { | 112 | let mut x: [u32; 2] = [42, 43]; |
| 113 | let mut x: u32 = 32; | 113 | |
| 114 | let ref1 = &mut x; | 114 | let ref_base = &mut x; |
| 115 | let ref2 = &mut *ref1; | 115 | |
| 116 | 116 | let int0 = ref_base as *mut u32 as usize; | |
| 117 | let int = ref2 as *mut u32 as usize; | 117 | let wild = int0 as *mut u32; |
| 118 | let wild = int as *mut u32; | 118 | |
| 119 | let wild_ref = unsafe { &mut *wild }; | 119 | let reb1 = unsafe { &mut *wild }; |
| 120 | 120 | *reb1 = 44; | |
| 121 | let mut protect = |arg: &mut u32| { | 121 | |
| 122 | // arg is a protected pointer with wildcard provenance. | 122 | // We need this reference to be at a different location, |
| 123 | 123 | // so that creating it doesn't freeze reb1. | |
| 124 | // ┌────────────┐ | 124 | let reb2 = unsafe { &mut *wild.wrapping_add(1) }; |
| 125 | // │ │ | 125 | let reb2_ptr = (reb2 as *mut u32).wrapping_sub(1); |
| 126 | // │ ref1(Res) │ | 126 | |
| 127 | // │ │ | 127 | let ref3 = &mut *reb1; |
| 128 | // └──────┬─────┘ | 128 | let _int3 = ref3 as *mut u32 as usize; |
| 129 | // │ | 129 | |
| 130 | // │ | 130 | // ┌──────────────┐ |
| 131 | // ▼ | 131 | // │ │ |
| 132 | // ┌────────────┐ | 132 | // │ptr_base(Res)*│ * * |
| 133 | // │ │ | 133 | // │ │ │ │ |
| 134 | // │ ref2(Res)* │ | 134 | // └──────────────┘ │ │ |
| 135 | // │ │ | 135 | // │ │ |
| 136 | // └────────────┘ | 136 | // │ │ |
| 137 | 137 | // ▼ ▼ | |
| 138 | // Writes to ref1, disabling ref2, i.e. disabling all exposed references. | 138 | // ┌────────────┐ ┌────────────┐ |
| 139 | // Since a wildcard reference is protected, this is UB. But we currently don't detect this. | 139 | // │ │ │ │ |
| 140 | *ref1 = 13; | 140 | // │ reb1(Act) │ │ reb2(Res) │ |
| 141 | }; | 141 | // │ │ │ │ |
| 142 | 142 | // └──────┬─────┘ └────────────┘ | |
| 143 | // We pass a pointer with wildcard provenance to the function. | 143 | // │ |
| 144 | protect(wild_ref); | 144 | // │ |
| 145 | // ▼ | ||
| 146 | // ┌────────────┐ | ||
| 147 | // │ │ | ||
| 148 | // │ ref3(Res)* │ | ||
| 149 | // │ │ | ||
| 150 | // └────────────┘ | ||
| 151 | |||
| 152 | // This access doesn't freeze reb1 even though no access could have come from its | ||
| 153 | // child ref3 (since ref3>reb2). This is because ref3 doesnt get disabled during this | ||
| 154 | // access. | ||
| 155 | // | ||
| 156 | // ref3 doesn't get frozen because it's still reserved. | ||
| 157 | let _y = unsafe { *reb2_ptr }; | ||
| 158 | |||
| 159 | // reb1 should be frozen so a write should be UB. But we currently don't detect this. | ||
| 160 | *reb1 = 4; | ||
| 145 | } | 161 | } |
src/tools/miri/tests/pass/tree_borrows/wildcard/wildcard.rs-1| ... | @@ -151,7 +151,6 @@ fn protector_conflicted_release() { | ... | @@ -151,7 +151,6 @@ fn protector_conflicted_release() { |
| 151 | 151 | ||
| 152 | /// Analogous to same test in `../tree-borrows.rs` but with a protected wildcard reference. | 152 | /// Analogous to same test in `../tree-borrows.rs` but with a protected wildcard reference. |
| 153 | fn returned_mut_is_usable() { | 153 | fn returned_mut_is_usable() { |
| 154 | // NOTE: Currently we ignore protectors on wildcard references. | ||
| 155 | fn reborrow(x: &mut u8) -> &mut u8 { | 154 | fn reborrow(x: &mut u8) -> &mut u8 { |
| 156 | let y = &mut *x; | 155 | let y = &mut *x; |
| 157 | // Activate the reference so that it is vulnerable to foreign reads. | 156 | // Activate the reference so that it is vulnerable to foreign reads. |
src/tools/miri/tests/utils/macros.rs+2-2| ... | @@ -9,7 +9,7 @@ | ... | @@ -9,7 +9,7 @@ |
| 9 | /// The id obtained can be passed directly to `print_state!`. | 9 | /// The id obtained can be passed directly to `print_state!`. |
| 10 | macro_rules! alloc_id { | 10 | macro_rules! alloc_id { |
| 11 | ($ptr:expr) => { | 11 | ($ptr:expr) => { |
| 12 | $crate::utils::miri_get_alloc_id($ptr as *const u8 as *const ()) | 12 | $crate::utils::miri_get_alloc_id($ptr as *const _ as *const ()) |
| 13 | }; | 13 | }; |
| 14 | } | 14 | } |
| 15 | 15 | ||
| ... | @@ -52,6 +52,6 @@ macro_rules! name { | ... | @@ -52,6 +52,6 @@ macro_rules! name { |
| 52 | }; | 52 | }; |
| 53 | ($ptr:expr => $nth_parent:expr, $name:expr) => { | 53 | ($ptr:expr => $nth_parent:expr, $name:expr) => { |
| 54 | let name = $name.as_bytes(); | 54 | let name = $name.as_bytes(); |
| 55 | $crate::utils::miri_pointer_name($ptr as *const u8 as *const (), $nth_parent, name); | 55 | $crate::utils::miri_pointer_name($ptr as *const _ as *const (), $nth_parent, name); |
| 56 | }; | 56 | }; |
| 57 | } | 57 | } |
tests/run-make/rustdoc-merge-directory-alias/dep1.rs created+10| ... | @@ -0,0 +1,10 @@ | ||
| 1 | pub struct Dep1; | ||
| 2 | pub struct Dep2; | ||
| 3 | pub struct Dep3; | ||
| 4 | pub struct Dep4; | ||
| 5 | |||
| 6 | //@ hasraw crates.js 'dep1' | ||
| 7 | //@ hasraw search.index/name/*.js 'Dep1' | ||
| 8 | //@ has dep1/index.html | ||
| 9 | #[doc(alias = "dep1_missing")] | ||
| 10 | pub struct Dep5; | ||
tests/run-make/rustdoc-merge-directory-alias/dep2.rs created+4| ... | @@ -0,0 +1,4 @@ | ||
| 1 | //@ hasraw crates.js 'dep2' | ||
| 2 | //@ hasraw search.index/name/*.js 'Second' | ||
| 3 | //@ has dep2/index.html | ||
| 4 | pub struct Second; | ||
tests/run-make/rustdoc-merge-directory-alias/dep_missing.rs created+4| ... | @@ -0,0 +1,4 @@ | ||
| 1 | //@ !hasraw crates.js 'dep_missing' | ||
| 2 | //@ !hasraw search.index/name/*.js 'DepMissing' | ||
| 3 | //@ has dep_missing/index.html | ||
| 4 | pub struct DepMissing; | ||
tests/run-make/rustdoc-merge-directory-alias/rmake.rs created+88| ... | @@ -0,0 +1,88 @@ | ||
| 1 | // Running --merge=finalize without an input crate root should not trigger ICE. | ||
| 2 | // Issue: https://github.com/rust-lang/rust/issues/146646 | ||
| 3 | |||
| 4 | //@ needs-target-std | ||
| 5 | |||
| 6 | use run_make_support::{htmldocck, path, rustdoc}; | ||
| 7 | |||
| 8 | fn main() { | ||
| 9 | let out_dir = path("out"); | ||
| 10 | let merged_dir = path("merged"); | ||
| 11 | let parts_out_dir = path("parts"); | ||
| 12 | |||
| 13 | rustdoc() | ||
| 14 | .input("dep1.rs") | ||
| 15 | .out_dir(&out_dir) | ||
| 16 | .arg("-Zunstable-options") | ||
| 17 | .arg(format!("--parts-out-dir={}", parts_out_dir.display())) | ||
| 18 | .arg("--merge=none") | ||
| 19 | .run(); | ||
| 20 | assert!(parts_out_dir.join("dep1.json").exists()); | ||
| 21 | |||
| 22 | let output = rustdoc() | ||
| 23 | .arg("-Zunstable-options") | ||
| 24 | .out_dir(&out_dir) | ||
| 25 | .arg(format!("--include-parts-dir={}", parts_out_dir.display())) | ||
| 26 | .arg("--merge=finalize") | ||
| 27 | .run(); | ||
| 28 | output.assert_stderr_not_contains("error: the compiler unexpectedly panicked. this is a bug."); | ||
| 29 | |||
| 30 | rustdoc() | ||
| 31 | .input("dep2.rs") | ||
| 32 | .out_dir(&out_dir) | ||
| 33 | .arg("-Zunstable-options") | ||
| 34 | .arg(format!("--parts-out-dir={}", parts_out_dir.display())) | ||
| 35 | .arg("--merge=none") | ||
| 36 | .run(); | ||
| 37 | assert!(parts_out_dir.join("dep2.json").exists()); | ||
| 38 | |||
| 39 | let output2 = rustdoc() | ||
| 40 | .arg("-Zunstable-options") | ||
| 41 | .out_dir(&out_dir) | ||
| 42 | .arg(format!("--include-parts-dir={}", parts_out_dir.display())) | ||
| 43 | .arg("--merge=finalize") | ||
| 44 | .run(); | ||
| 45 | output2.assert_stderr_not_contains("error: the compiler unexpectedly panicked. this is a bug."); | ||
| 46 | |||
| 47 | rustdoc() | ||
| 48 | .input("dep1.rs") | ||
| 49 | .out_dir(&out_dir) | ||
| 50 | .arg("-Zunstable-options") | ||
| 51 | .arg(format!("--parts-out-dir={}", parts_out_dir.display())) | ||
| 52 | .arg("--merge=none") | ||
| 53 | .run(); | ||
| 54 | assert!(parts_out_dir.join("dep1.json").exists()); | ||
| 55 | |||
| 56 | let output3 = rustdoc() | ||
| 57 | .arg("-Zunstable-options") | ||
| 58 | .out_dir(&out_dir) | ||
| 59 | .arg(format!("--include-parts-dir={}", parts_out_dir.display())) | ||
| 60 | .arg("--merge=finalize") | ||
| 61 | .run(); | ||
| 62 | output3.assert_stderr_not_contains("error: the compiler unexpectedly panicked. this is a bug."); | ||
| 63 | |||
| 64 | // dep_missing is different, because --parts-out-dir is not supplied | ||
| 65 | rustdoc().input("dep_missing.rs").out_dir(&out_dir).run(); | ||
| 66 | assert!(parts_out_dir.join("dep2.json").exists()); | ||
| 67 | |||
| 68 | rustdoc() | ||
| 69 | .input("dep1.rs") | ||
| 70 | .out_dir(&out_dir) | ||
| 71 | .arg("-Zunstable-options") | ||
| 72 | .arg(format!("--parts-out-dir={}", parts_out_dir.display())) | ||
| 73 | .arg("--merge=none") | ||
| 74 | .run(); | ||
| 75 | assert!(parts_out_dir.join("dep1.json").exists()); | ||
| 76 | |||
| 77 | let output4 = rustdoc() | ||
| 78 | .arg("-Zunstable-options") | ||
| 79 | .out_dir(&out_dir) | ||
| 80 | .arg(format!("--include-parts-dir={}", parts_out_dir.display())) | ||
| 81 | .arg("--merge=finalize") | ||
| 82 | .run(); | ||
| 83 | output4.assert_stderr_not_contains("error: the compiler unexpectedly panicked. this is a bug."); | ||
| 84 | |||
| 85 | htmldocck().arg(&out_dir).arg("dep1.rs").run(); | ||
| 86 | htmldocck().arg(&out_dir).arg("dep2.rs").run(); | ||
| 87 | htmldocck().arg(&out_dir).arg("dep_missing.rs").run(); | ||
| 88 | } | ||
tests/ui/feature-gates/unknown-feature.rs+14-1| ... | @@ -1,3 +1,16 @@ | ... | @@ -1,3 +1,16 @@ |
| 1 | #![feature(unknown_rust_feature)] //~ ERROR unknown feature | 1 | #![feature( |
| 2 | unknown_rust_feature, | ||
| 3 | //~^ ERROR unknown feature | ||
| 4 | |||
| 5 | // Typo for lang feature | ||
| 6 | associated_types_default, | ||
| 7 | //~^ ERROR unknown feature | ||
| 8 | //~| HELP there is a feature with a similar name | ||
| 9 | |||
| 10 | // Typo for lib feature | ||
| 11 | core_intrnisics, | ||
| 12 | //~^ ERROR unknown feature | ||
| 13 | //~| HELP there is a feature with a similar name | ||
| 14 | )] | ||
| 2 | 15 | ||
| 3 | fn main() {} | 16 | fn main() {} |
tests/ui/feature-gates/unknown-feature.stderr+28-4| ... | @@ -1,9 +1,33 @@ | ... | @@ -1,9 +1,33 @@ |
| 1 | error[E0635]: unknown feature `unknown_rust_feature` | 1 | error[E0635]: unknown feature `unknown_rust_feature` |
| 2 | --> $DIR/unknown-feature.rs:1:12 | 2 | --> $DIR/unknown-feature.rs:2:5 |
| 3 | | | 3 | | |
| 4 | LL | #![feature(unknown_rust_feature)] | 4 | LL | unknown_rust_feature, |
| 5 | | ^^^^^^^^^^^^^^^^^^^^ | 5 | | ^^^^^^^^^^^^^^^^^^^^ |
| 6 | 6 | ||
| 7 | error: aborting due to 1 previous error | 7 | error[E0635]: unknown feature `associated_types_default` |
| 8 | --> $DIR/unknown-feature.rs:6:5 | ||
| 9 | | | ||
| 10 | LL | associated_types_default, | ||
| 11 | | ^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| 12 | | | ||
| 13 | help: there is a feature with a similar name: `associated_type_defaults` | ||
| 14 | | | ||
| 15 | LL - associated_types_default, | ||
| 16 | LL + associated_type_defaults, | ||
| 17 | | | ||
| 18 | |||
| 19 | error[E0635]: unknown feature `core_intrnisics` | ||
| 20 | --> $DIR/unknown-feature.rs:11:5 | ||
| 21 | | | ||
| 22 | LL | core_intrnisics, | ||
| 23 | | ^^^^^^^^^^^^^^^ | ||
| 24 | | | ||
| 25 | help: there is a feature with a similar name: `core_intrinsics` | ||
| 26 | | | ||
| 27 | LL - core_intrnisics, | ||
| 28 | LL + core_intrinsics, | ||
| 29 | | | ||
| 30 | |||
| 31 | error: aborting due to 3 previous errors | ||
| 8 | 32 | ||
| 9 | For more information about this error, try `rustc --explain E0635`. | 33 | For more information about this error, try `rustc --explain E0635`. |
tests/ui/suggestions/deref-path-method.stderr+1-1| ... | @@ -9,7 +9,7 @@ note: if you're trying to build a new `Vec<_, _>` consider using one of the foll | ... | @@ -9,7 +9,7 @@ note: if you're trying to build a new `Vec<_, _>` consider using one of the foll |
| 9 | Vec::<T>::with_capacity | 9 | Vec::<T>::with_capacity |
| 10 | Vec::<T>::try_with_capacity | 10 | Vec::<T>::try_with_capacity |
| 11 | Vec::<T>::from_raw_parts | 11 | Vec::<T>::from_raw_parts |
| 12 | and 6 others | 12 | and 7 others |
| 13 | --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL | 13 | --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL |
| 14 | help: the function `contains` is implemented on `[_]` | 14 | help: the function `contains` is implemented on `[_]` |
| 15 | | | 15 | | |
tests/ui/ufcs/bad-builder.stderr+1-1| ... | @@ -9,7 +9,7 @@ note: if you're trying to build a new `Vec<Q>` consider using one of the followi | ... | @@ -9,7 +9,7 @@ note: if you're trying to build a new `Vec<Q>` consider using one of the followi |
| 9 | Vec::<T>::with_capacity | 9 | Vec::<T>::with_capacity |
| 10 | Vec::<T>::try_with_capacity | 10 | Vec::<T>::try_with_capacity |
| 11 | Vec::<T>::from_raw_parts | 11 | Vec::<T>::from_raw_parts |
| 12 | and 6 others | 12 | and 7 others |
| 13 | --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL | 13 | --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL |
| 14 | help: there is an associated function `new` with a similar name | 14 | help: there is an associated function `new` with a similar name |
| 15 | | | 15 | | |