From 3e485d7cf59dafbc37467c3ef1acc51f26b381d9 Mon Sep 17 00:00:00 2001 From: Stein Somers Date: Sat, 26 Sep 2020 16:23:50 +0200 Subject: [PATCH 01/14] BTreeMap: keep an eye out on the size of the main components --- library/alloc/src/collections/btree/node/tests.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/library/alloc/src/collections/btree/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs index e2416974ddca3..54c3709821acd 100644 --- a/library/alloc/src/collections/btree/node/tests.rs +++ b/library/alloc/src/collections/btree/node/tests.rs @@ -23,3 +23,12 @@ fn test_splitpoint() { assert!(left_len + right_len == CAPACITY); } } + +#[test] +#[cfg(target_arch = "x86_64")] +fn test_sizes() { + assert_eq!(core::mem::size_of::>(), 16); + assert_eq!(core::mem::size_of::>(), 16 + CAPACITY * 8 * 2); + assert_eq!(core::mem::size_of::>(), 112); + assert_eq!(core::mem::size_of::>(), 112 + CAPACITY * 8 * 2); +} From 37f795697c7d0530deb0c5c94a8f99aab587e2e4 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Sun, 27 Sep 2020 20:31:06 -0700 Subject: [PATCH 02/14] libary: Forward compiler-builtins "mem" feature This fixes https://github.com/rust-lang/wg-cargo-std-aware/issues/53 Now users will be able to do: ``` cargo build -Zbuild-std=core -Zbuild-std-features=compiler-builtins-mem ``` and correctly get the Rust implemenations for `memcpy` and friends. Signed-off-by: Joe Richey --- library/std/Cargo.toml | 1 + library/test/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 01babeffd98f0..b27b056086a64 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -59,6 +59,7 @@ gimli-symbolize = [] panic-unwind = ["panic_unwind"] profiler = ["profiler_builtins"] compiler-builtins-c = ["alloc/compiler-builtins-c"] +compiler-builtins-mem = ["alloc/compiler-builtins-mem"] llvm-libunwind = ["unwind/llvm-libunwind"] # Make panics and failed asserts immediately abort without formatting any message diff --git a/library/test/Cargo.toml b/library/test/Cargo.toml index 7b76dc83aa253..e44c781113583 100644 --- a/library/test/Cargo.toml +++ b/library/test/Cargo.toml @@ -25,6 +25,7 @@ proc_macro = { path = "../proc_macro" } default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind"] backtrace = ["std/backtrace"] compiler-builtins-c = ["std/compiler-builtins-c"] +compiler-builtins-mem = ["std/compiler-builtins-mem"] llvm-libunwind = ["std/llvm-libunwind"] panic-unwind = ["std/panic_unwind"] panic_immediate_abort = ["std/panic_immediate_abort"] From d537067c97531af7f8343f75fafb4d6b9e79eaaf Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 28 Sep 2020 10:28:15 -0400 Subject: [PATCH 03/14] Don't warn if the config file is somewhere other than `config.toml` Previously, `config.config` was always hardcoded as `"config.toml"`. I thought that it was being overridden with the actual value later, but it turns out `flags.config` was being completely discarded. This keeps `config.config` in sync with `flags.config`. --- src/bootstrap/config.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index b14746dabb93a..93bde400f0674 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -515,7 +515,6 @@ impl Config { config.rust_codegen_backends = vec![INTERNER.intern_str("llvm")]; config.deny_warnings = true; config.missing_tools = false; - config.config = PathBuf::from("config.toml"); // set by bootstrap.py config.build = TargetSelection::from_user(&env!("BUILD_TRIPLE")); @@ -558,10 +557,10 @@ impl Config { #[cfg(test)] let get_toml = |_| TomlConfig::default(); #[cfg(not(test))] - let get_toml = |file: PathBuf| { + let get_toml = |file: &Path| { use std::process; - let contents = t!(fs::read_to_string(&file), "`include` config not found"); + let contents = t!(fs::read_to_string(file), "`include` config not found"); match toml::from_str(&contents) { Ok(table) => table, Err(err) => { @@ -571,18 +570,21 @@ impl Config { } }; - let mut toml = flags.config.map(get_toml).unwrap_or_else(TomlConfig::default); + let mut toml = flags.config.as_deref().map(get_toml).unwrap_or_else(TomlConfig::default); if let Some(include) = &toml.profile { let mut include_path = config.src.clone(); include_path.push("src"); include_path.push("bootstrap"); include_path.push("defaults"); include_path.push(format!("config.toml.{}", include)); - let included_toml = get_toml(include_path); + let included_toml = get_toml(&include_path); toml.merge(included_toml); } config.changelog_seen = toml.changelog_seen; + if let Some(cfg) = flags.config { + config.config = cfg; + } let build = toml.build.unwrap_or_default(); From db5b70f193dcc08476ec1bef8dc1863d508eea1e Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Mon, 28 Sep 2020 20:21:44 +0200 Subject: [PATCH 04/14] move candidate_from_obligation_no_cache --- .../src/traits/select/candidate_assembly.rs | 162 +++++++++++++++++- .../src/traits/select/mod.rs | 155 ----------------- 2 files changed, 161 insertions(+), 156 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index a4943231dfdff..9cb5c232646f8 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -7,14 +7,19 @@ //! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly use rustc_hir as hir; use rustc_infer::traits::{Obligation, SelectionError, TraitObligation}; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, TypeFoldable}; use rustc_target::spec::abi::Abi; +use crate::traits::coherence::Conflict; use crate::traits::{util, SelectionResult}; +use crate::traits::{Overflow, Unimplemented}; use super::BuiltinImplConditions; +use super::IntercrateAmbiguityCause; +use super::OverflowError; use super::SelectionCandidate::{self, *}; -use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack}; +use super::{EvaluatedCandidate, SelectionCandidateSet, SelectionContext, TraitObligationStack}; impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { pub(super) fn candidate_from_obligation<'o>( @@ -62,6 +67,161 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidate } + fn candidate_from_obligation_no_cache<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + if let Some(conflict) = self.is_knowable(stack) { + debug!("coherence stage: not knowable"); + if self.intercrate_ambiguity_causes.is_some() { + debug!("evaluate_stack: intercrate_ambiguity_causes is some"); + // Heuristics: show the diagnostics when there are no candidates in crate. + if let Ok(candidate_set) = self.assemble_candidates(stack) { + let mut no_candidates_apply = true; + + for c in candidate_set.vec.iter() { + if self.evaluate_candidate(stack, &c)?.may_apply() { + no_candidates_apply = false; + break; + } + } + + if !candidate_set.ambiguous && no_candidates_apply { + let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; + let self_ty = trait_ref.self_ty(); + let (trait_desc, self_desc) = with_no_trimmed_paths(|| { + let trait_desc = trait_ref.print_only_trait_path().to_string(); + let self_desc = if self_ty.has_concrete_skeleton() { + Some(self_ty.to_string()) + } else { + None + }; + (trait_desc, self_desc) + }); + let cause = if let Conflict::Upstream = conflict { + IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc } + } else { + IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } + }; + debug!("evaluate_stack: pushing cause = {:?}", cause); + self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); + } + } + } + return Ok(None); + } + + let candidate_set = self.assemble_candidates(stack)?; + + if candidate_set.ambiguous { + debug!("candidate set contains ambig"); + return Ok(None); + } + + let mut candidates = candidate_set.vec; + + debug!("assembled {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); + + // At this point, we know that each of the entries in the + // candidate set is *individually* applicable. Now we have to + // figure out if they contain mutual incompatibilities. This + // frequently arises if we have an unconstrained input type -- + // for example, we are looking for `$0: Eq` where `$0` is some + // unconstrained type variable. In that case, we'll get a + // candidate which assumes $0 == int, one that assumes `$0 == + // usize`, etc. This spells an ambiguity. + + // If there is more than one candidate, first winnow them down + // by considering extra conditions (nested obligations and so + // forth). We don't winnow if there is exactly one + // candidate. This is a relatively minor distinction but it + // can lead to better inference and error-reporting. An + // example would be if there was an impl: + // + // impl Vec { fn push_clone(...) { ... } } + // + // and we were to see some code `foo.push_clone()` where `boo` + // is a `Vec` and `Bar` does not implement `Clone`. If + // we were to winnow, we'd wind up with zero candidates. + // Instead, we select the right impl now but report "`Bar` does + // not implement `Clone`". + if candidates.len() == 1 { + return self.filter_negative_and_reservation_impls(candidates.pop().unwrap()); + } + + // Winnow, but record the exact outcome of evaluation, which + // is needed for specialization. Propagate overflow if it occurs. + let mut candidates = candidates + .into_iter() + .map(|c| match self.evaluate_candidate(stack, &c) { + Ok(eval) if eval.may_apply() => { + Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) + } + Ok(_) => Ok(None), + Err(OverflowError) => Err(Overflow), + }) + .flat_map(Result::transpose) + .collect::, _>>()?; + + debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); + + let needs_infer = stack.obligation.predicate.needs_infer(); + + // If there are STILL multiple candidates, we can further + // reduce the list by dropping duplicates -- including + // resolving specializations. + if candidates.len() > 1 { + let mut i = 0; + while i < candidates.len() { + let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { + self.candidate_should_be_dropped_in_favor_of( + &candidates[i], + &candidates[j], + needs_infer, + ) + }); + if is_dup { + debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); + candidates.swap_remove(i); + } else { + debug!("Retaining candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); + i += 1; + + // If there are *STILL* multiple candidates, give up + // and report ambiguity. + if i > 1 { + debug!("multiple matches, ambig"); + return Ok(None); + } + } + } + } + + // If there are *NO* candidates, then there are no impls -- + // that we know of, anyway. Note that in the case where there + // are unbound type variables within the obligation, it might + // be the case that you could still satisfy the obligation + // from another crate by instantiating the type variables with + // a type from another crate that does have an impl. This case + // is checked for in `evaluate_stack` (and hence users + // who might care about this case, like coherence, should use + // that function). + if candidates.is_empty() { + // If there's an error type, 'downgrade' our result from + // `Err(Unimplemented)` to `Ok(None)`. This helps us avoid + // emitting additional spurious errors, since we're guaranteed + // to have emitted at least one. + if stack.obligation.references_error() { + debug!("no results for error type, treating as ambiguous"); + return Ok(None); + } + return Err(Unimplemented); + } + + // Just one candidate left. + self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate) + } + pub(super) fn assemble_candidates<'o>( &mut self, stack: &TraitObligationStack<'o, 'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 57f1fedacbe5a..114dc79c44f50 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1029,161 +1029,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(Some(candidate)) } - fn candidate_from_obligation_no_cache<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { - if let Some(conflict) = self.is_knowable(stack) { - debug!("coherence stage: not knowable"); - if self.intercrate_ambiguity_causes.is_some() { - debug!("evaluate_stack: intercrate_ambiguity_causes is some"); - // Heuristics: show the diagnostics when there are no candidates in crate. - if let Ok(candidate_set) = self.assemble_candidates(stack) { - let mut no_candidates_apply = true; - - for c in candidate_set.vec.iter() { - if self.evaluate_candidate(stack, &c)?.may_apply() { - no_candidates_apply = false; - break; - } - } - - if !candidate_set.ambiguous && no_candidates_apply { - let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; - let self_ty = trait_ref.self_ty(); - let (trait_desc, self_desc) = with_no_trimmed_paths(|| { - let trait_desc = trait_ref.print_only_trait_path().to_string(); - let self_desc = if self_ty.has_concrete_skeleton() { - Some(self_ty.to_string()) - } else { - None - }; - (trait_desc, self_desc) - }); - let cause = if let Conflict::Upstream = conflict { - IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc } - } else { - IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } - }; - debug!("evaluate_stack: pushing cause = {:?}", cause); - self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); - } - } - } - return Ok(None); - } - - let candidate_set = self.assemble_candidates(stack)?; - - if candidate_set.ambiguous { - debug!("candidate set contains ambig"); - return Ok(None); - } - - let mut candidates = candidate_set.vec; - - debug!("assembled {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); - - // At this point, we know that each of the entries in the - // candidate set is *individually* applicable. Now we have to - // figure out if they contain mutual incompatibilities. This - // frequently arises if we have an unconstrained input type -- - // for example, we are looking for `$0: Eq` where `$0` is some - // unconstrained type variable. In that case, we'll get a - // candidate which assumes $0 == int, one that assumes `$0 == - // usize`, etc. This spells an ambiguity. - - // If there is more than one candidate, first winnow them down - // by considering extra conditions (nested obligations and so - // forth). We don't winnow if there is exactly one - // candidate. This is a relatively minor distinction but it - // can lead to better inference and error-reporting. An - // example would be if there was an impl: - // - // impl Vec { fn push_clone(...) { ... } } - // - // and we were to see some code `foo.push_clone()` where `boo` - // is a `Vec` and `Bar` does not implement `Clone`. If - // we were to winnow, we'd wind up with zero candidates. - // Instead, we select the right impl now but report "`Bar` does - // not implement `Clone`". - if candidates.len() == 1 { - return self.filter_negative_and_reservation_impls(candidates.pop().unwrap()); - } - - // Winnow, but record the exact outcome of evaluation, which - // is needed for specialization. Propagate overflow if it occurs. - let mut candidates = candidates - .into_iter() - .map(|c| match self.evaluate_candidate(stack, &c) { - Ok(eval) if eval.may_apply() => { - Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) - } - Ok(_) => Ok(None), - Err(OverflowError) => Err(Overflow), - }) - .flat_map(Result::transpose) - .collect::, _>>()?; - - debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); - - let needs_infer = stack.obligation.predicate.needs_infer(); - - // If there are STILL multiple candidates, we can further - // reduce the list by dropping duplicates -- including - // resolving specializations. - if candidates.len() > 1 { - let mut i = 0; - while i < candidates.len() { - let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { - self.candidate_should_be_dropped_in_favor_of( - &candidates[i], - &candidates[j], - needs_infer, - ) - }); - if is_dup { - debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); - candidates.swap_remove(i); - } else { - debug!("Retaining candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); - i += 1; - - // If there are *STILL* multiple candidates, give up - // and report ambiguity. - if i > 1 { - debug!("multiple matches, ambig"); - return Ok(None); - } - } - } - } - - // If there are *NO* candidates, then there are no impls -- - // that we know of, anyway. Note that in the case where there - // are unbound type variables within the obligation, it might - // be the case that you could still satisfy the obligation - // from another crate by instantiating the type variables with - // a type from another crate that does have an impl. This case - // is checked for in `evaluate_stack` (and hence users - // who might care about this case, like coherence, should use - // that function). - if candidates.is_empty() { - // If there's an error type, 'downgrade' our result from - // `Err(Unimplemented)` to `Ok(None)`. This helps us avoid - // emitting additional spurious errors, since we're guaranteed - // to have emitted at least one. - if stack.obligation.references_error() { - debug!("no results for error type, treating as ambiguous"); - return Ok(None); - } - return Err(Unimplemented); - } - - // Just one candidate left. - self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate) - } - fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option { debug!("is_knowable(intercrate={:?})", self.intercrate); From 63bb51d3ac3ec015d914e2b0792b4d5aa6e8e4a5 Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Mon, 28 Sep 2020 22:48:07 -0400 Subject: [PATCH 05/14] Add unstable book docs for `-Zunsound-mir-opts` --- .../unstable-book/src/compiler-flags/unsound-mir-opts.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/doc/unstable-book/src/compiler-flags/unsound-mir-opts.md diff --git a/src/doc/unstable-book/src/compiler-flags/unsound-mir-opts.md b/src/doc/unstable-book/src/compiler-flags/unsound-mir-opts.md new file mode 100644 index 0000000000000..8e46e227c25b4 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/unsound-mir-opts.md @@ -0,0 +1,8 @@ +# `unsound-mir-opts` + +-------------------- + +The `-Zunsound-mir-opts` compiler flag enables [MIR optimization passes] which can cause unsound behavior. +This flag should only be used by MIR optimization tests in the rustc test suite. + +[MIR optimization passes]: https://rustc-dev-guide.rust-lang.org/mir/optimizations.html From c6107c53d72250d8c1d41f161ce54648bef4d9d2 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Mon, 28 Sep 2020 23:51:57 -0400 Subject: [PATCH 06/14] Don't fire `const_item_mutation` lint on writes through a pointer Fixes #77321 --- .../transform/check_const_item_mutation.rs | 14 ++++-- src/test/ui/lint/lint-const-item-mutation.rs | 13 +++++- .../ui/lint/lint-const-item-mutation.stderr | 46 +++++++++---------- 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs index 70c1aed0957f8..0281c478a6ca0 100644 --- a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs +++ b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs @@ -60,11 +60,15 @@ impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> { // so emitting a lint would be redundant. if !lhs.projection.is_empty() { if let Some(def_id) = self.is_const_item(lhs.local) { - self.lint_const_item_usage(def_id, loc, |lint| { - let mut lint = lint.build("attempting to modify a `const` item"); - lint.note("each usage of a `const` item creates a new temporary - the original `const` item will not be modified"); - lint - }) + // Don't lint on writes through a pointer + // (e.g. `unsafe { *FOO = 0; *BAR.field = 1; }`) + if !matches!(lhs.projection.last(), Some(PlaceElem::Deref)) { + self.lint_const_item_usage(def_id, loc, |lint| { + let mut lint = lint.build("attempting to modify a `const` item"); + lint.note("each usage of a `const` item creates a new temporary - the original `const` item will not be modified"); + lint + }) + } } } // We are looking for MIR of the form: diff --git a/src/test/ui/lint/lint-const-item-mutation.rs b/src/test/ui/lint/lint-const-item-mutation.rs index 92d29a7dae475..43371560e02c1 100644 --- a/src/test/ui/lint/lint-const-item-mutation.rs +++ b/src/test/ui/lint/lint-const-item-mutation.rs @@ -3,13 +3,15 @@ struct MyStruct { field: bool, inner_array: [char; 1], + raw_ptr: *mut u8 } impl MyStruct { fn use_mut(&mut self) {} } const ARRAY: [u8; 1] = [25]; -const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; +const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; +const RAW_PTR: *mut u8 = 1 as *mut u8; fn main() { ARRAY[0] = 5; //~ WARN attempting to modify @@ -18,4 +20,13 @@ fn main() { MY_STRUCT.use_mut(); //~ WARN taking &mut MY_STRUCT; //~ WARN taking (&mut MY_STRUCT).use_mut(); //~ WARN taking + + // Test that we don't warn when writing through + // a raw pointer + // This is U.B., but this test is check-pass, + // so this never actually executes + unsafe { + *RAW_PTR = 0; + *MY_STRUCT.raw_ptr = 0; + } } diff --git a/src/test/ui/lint/lint-const-item-mutation.stderr b/src/test/ui/lint/lint-const-item-mutation.stderr index 2d8f2c49744ba..c5a221128ffab 100644 --- a/src/test/ui/lint/lint-const-item-mutation.stderr +++ b/src/test/ui/lint/lint-const-item-mutation.stderr @@ -1,5 +1,5 @@ warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:15:5 + --> $DIR/lint-const-item-mutation.rs:17:5 | LL | ARRAY[0] = 5; | ^^^^^^^^^^^^ @@ -7,39 +7,39 @@ LL | ARRAY[0] = 5; = note: `#[warn(const_item_mutation)]` on by default = note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:11:1 + --> $DIR/lint-const-item-mutation.rs:12:1 | LL | const ARRAY: [u8; 1] = [25]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:16:5 + --> $DIR/lint-const-item-mutation.rs:18:5 | LL | MY_STRUCT.field = false; | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:12:1 + --> $DIR/lint-const-item-mutation.rs:13:1 | -LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:17:5 + --> $DIR/lint-const-item-mutation.rs:19:5 | LL | MY_STRUCT.inner_array[0] = 'b'; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:12:1 + --> $DIR/lint-const-item-mutation.rs:13:1 | -LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:18:5 + --> $DIR/lint-const-item-mutation.rs:20:5 | LL | MY_STRUCT.use_mut(); | ^^^^^^^^^^^^^^^^^^^ @@ -47,18 +47,18 @@ LL | MY_STRUCT.use_mut(); = note: each usage of a `const` item creates a new temporary = note: the mutable reference will refer to this temporary, not the original `const` item note: mutable reference created due to call to this method - --> $DIR/lint-const-item-mutation.rs:8:5 + --> $DIR/lint-const-item-mutation.rs:9:5 | LL | fn use_mut(&mut self) {} | ^^^^^^^^^^^^^^^^^^^^^ note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:12:1 + --> $DIR/lint-const-item-mutation.rs:13:1 | -LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:19:5 + --> $DIR/lint-const-item-mutation.rs:21:5 | LL | &mut MY_STRUCT; | ^^^^^^^^^^^^^^ @@ -66,13 +66,13 @@ LL | &mut MY_STRUCT; = note: each usage of a `const` item creates a new temporary = note: the mutable reference will refer to this temporary, not the original `const` item note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:12:1 + --> $DIR/lint-const-item-mutation.rs:13:1 | -LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:20:5 + --> $DIR/lint-const-item-mutation.rs:22:5 | LL | (&mut MY_STRUCT).use_mut(); | ^^^^^^^^^^^^^^^^ @@ -80,10 +80,10 @@ LL | (&mut MY_STRUCT).use_mut(); = note: each usage of a `const` item creates a new temporary = note: the mutable reference will refer to this temporary, not the original `const` item note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:12:1 + --> $DIR/lint-const-item-mutation.rs:13:1 | -LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: 6 warnings emitted From a2526b416fb4ca7874fa41a3eb595f456703d486 Mon Sep 17 00:00:00 2001 From: hyd-dev Date: Tue, 29 Sep 2020 15:16:46 +0800 Subject: [PATCH 07/14] Use `rtassert!` instead of `assert!` from the child process after fork() in std::sys::unix::process::Command::spawn() `assert!` panics on failure, which is not signal-safe. --- library/std/src/sys/unix/process/process_unix.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 08efe154e4c3b..c3625d306ab73 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -67,7 +67,7 @@ impl Command { // pipe I/O up to PIPE_BUF bytes should be atomic, and then // we want to be sure we *don't* run at_exit destructors as // we're being torn down regardless - assert!(output.write(&bytes).is_ok()); + rtassert!(output.write(&bytes).is_ok()); libc::_exit(1) } n => n, From 9340ee4380dcc9b81e0afb1ef5518730b064a78a Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Mon, 28 Sep 2020 00:06:58 +0300 Subject: [PATCH 08/14] Ensure that all LLVM components requested by tests are available on CI --- src/ci/run.sh | 2 ++ src/tools/compiletest/src/header.rs | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ci/run.sh b/src/ci/run.sh index 5231aa2e76619..181a7fcb73266 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -104,6 +104,8 @@ if [ "$RUST_RELEASE_CHANNEL" = "nightly" ] || [ "$DIST_REQUIRE_ALL_TOOLS" = "" ] RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-missing-tools" fi +export COMPILETEST_NEEDS_ALL_LLVM_COMPONENTS=1 + # Print the date from the local machine and the date from an external source to # check for clock drifts. An HTTP URL is used instead of HTTPS since on Azure # Pipelines it happened that the certificates were marked as expired. diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 17649dfab3750..59f64e7df0f41 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -208,10 +208,13 @@ impl EarlyProps { config.parse_name_value_directive(line, "needs-llvm-components") { let components: HashSet<_> = config.llvm_components.split_whitespace().collect(); - if !needed_components + if let Some(missing_component) = needed_components .split_whitespace() - .all(|needed_component| components.contains(needed_component)) + .find(|needed_component| !components.contains(needed_component)) { + if env::var_os("COMPILETEST_NEEDS_ALL_LLVM_COMPONENTS").is_some() { + panic!("missing LLVM component: {}", missing_component); + } return true; } } From 2c385040550fb1eb98cf14ace99129f05b1d7b3f Mon Sep 17 00:00:00 2001 From: Hameer Abbasi Date: Tue, 29 Sep 2020 13:18:29 +0200 Subject: [PATCH 09/14] Add test for async/await combined with const-generics. --- src/test/ui/const-generics/issue-74906.rs | 25 +++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/test/ui/const-generics/issue-74906.rs diff --git a/src/test/ui/const-generics/issue-74906.rs b/src/test/ui/const-generics/issue-74906.rs new file mode 100644 index 0000000000000..9162d1142b64a --- /dev/null +++ b/src/test/ui/const-generics/issue-74906.rs @@ -0,0 +1,25 @@ +// edition:2018 +// check-pass +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] + +const SIZE: usize = 16; + +struct Bar {} + +struct Foo {} + +impl Foo { + async fn biz(_: &[[u8; SIZE]]) -> Vec<()> { + vec![] + } + + pub async fn baz(&self) -> Bar { + Self::biz(&vec![]).await; + Bar {} + } +} + +fn main() { } From b141e49d8702fa170953aed388eb1510622db79b Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Tue, 29 Sep 2020 22:33:57 +0800 Subject: [PATCH 10/14] Fix typo in alloc vec comment --- library/alloc/src/vec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec.rs index fec4c1e0e501f..53667c188f8ce 100644 --- a/library/alloc/src/vec.rs +++ b/library/alloc/src/vec.rs @@ -2281,7 +2281,7 @@ where // use try-fold since // - it vectorizes better for some iterator adapters - // - unlike most internal iteration methods methods it only takes a &mut self + // - unlike most internal iteration methods, it only takes a &mut self // - it lets us thread the write pointer through its innards and get it back in the end let sink = InPlaceDrop { inner: dst_buf, dst: dst_buf }; let sink = iterator From f9b625f8e004cebdc79f891dea3fb128ec663151 Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Tue, 29 Sep 2020 23:00:02 +0800 Subject: [PATCH 11/14] Alloc vec use imported path mem::ManuallyDrop::new -> ManuallyDrop::new --- library/alloc/src/vec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec.rs index fec4c1e0e501f..6f714044210f9 100644 --- a/library/alloc/src/vec.rs +++ b/library/alloc/src/vec.rs @@ -2288,7 +2288,7 @@ where .try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(dst_end)) .unwrap(); // iteration succeeded, don't drop head - let dst = mem::ManuallyDrop::new(sink).dst; + let dst = ManuallyDrop::new(sink).dst; let src = unsafe { iterator.as_inner().as_into_iter() }; // check if SourceIter contract was upheld From eb0a88f7c041a27f1393420885b1a7a72d1885b4 Mon Sep 17 00:00:00 2001 From: Tyson Nottingham Date: Mon, 28 Sep 2020 17:34:27 -0700 Subject: [PATCH 12/14] SipHasher128: fix platform-independence confusion StableHasher is supposed to ensure platform independence by converting integers to little-endian and extending isize and usize to 64 bits as necessary, but in fact, much of that work is already handled by SipHasher128. In particular, SipHasher128 implements short_write in an endian-independent way, yet both StableHasher and SipHasher128 additionally attempt to achieve endian-independence by byte swapping on BE hardware before invoking short writes. This double swap has no effect, so let's remove it. Because short_write is endian-independent, SipHasher128 is already handling part of the platform-independence, and it would be somewhat difficult to make it *not* handle that part with the current implementation. As splitting platform-independence responsibilities between StableHasher and SipHasher128 would be confusing, let's make SipHasher128 handle all of it. Finally, update some incorrect comments and increase test coverage. Unit tests pass on both LE and BE systems. --- compiler/rustc_data_structures/src/sip128.rs | 70 ++++++++++++------ .../rustc_data_structures/src/sip128/tests.rs | 59 ++++++++++++--- .../src/stable_hasher.rs | 32 ++++---- .../src/stable_hasher/tests.rs | 73 +++++++++++++++++++ 4 files changed, 183 insertions(+), 51 deletions(-) create mode 100644 compiler/rustc_data_structures/src/stable_hasher/tests.rs diff --git a/compiler/rustc_data_structures/src/sip128.rs b/compiler/rustc_data_structures/src/sip128.rs index beb28dd072058..38f734d379722 100644 --- a/compiler/rustc_data_structures/src/sip128.rs +++ b/compiler/rustc_data_structures/src/sip128.rs @@ -8,6 +8,13 @@ use std::ptr; #[cfg(test)] mod tests; +/// When hashing something that ends up affecting properties like symbol names, +/// we want these symbol names to be calculated independently of other factors +/// like what architecture you're compiling *from*. +/// +/// To that end, we always convert integers to little-endian format or handle +/// them in an endian-independent way, and extend the architecture-dependent +/// `isize` and `usize` types to 64 bits if needed before hashing. #[derive(Debug, Clone)] pub struct SipHasher128 { k0: u64, @@ -125,15 +132,17 @@ impl SipHasher128 { // A specialized write function for values with size <= 8. // - // The hashing of multi-byte integers depends on endianness. E.g.: - // - little-endian: `write_u32(0xDDCCBBAA)` == `write([0xAA, 0xBB, 0xCC, 0xDD])` - // - big-endian: `write_u32(0xDDCCBBAA)` == `write([0xDD, 0xCC, 0xBB, 0xAA])` + // The input must be zero-extended to 64-bits by the caller. The extension + // isn't hashed, but the implementation requires it for correctness. + // + // This function, given the same integer type and value, has the same effect + // on both little- and big-endian hardware. It operates on values without + // depending on their sequence in memory, so is independent of endianness. // - // This function does the right thing for little-endian hardware. On - // big-endian hardware `x` must be byte-swapped first to give the right - // behaviour. After any byte-swapping, the input must be zero-extended to - // 64-bits. The caller is responsible for the byte-swapping and - // zero-extension. + // The equivalent write() call *does* need the value's bytes converted to + // little-endian (without zero-extension) for equivalent behavior on little- + // and big-endian hardware, as write() *does* operate on byte sequences. + // I.e. write_u32(0xDDCCBBAA) == write(&0xDDCCBBAA_u32.to_le_bytes()). #[inline] fn short_write(&mut self, _x: T, x: u64) { let size = mem::size_of::(); @@ -167,12 +176,9 @@ impl SipHasher128 { // left-shift it five bytes, giving 0xHHGG_FF00_0000_0000. We then // bitwise-OR that value into `self.tail`, resulting in // 0xHHGG_FFEE_DDCC_BBAA. `self.tail` is now full, and we can use it - // to update `self.state`. (As mentioned above, this assumes a - // little-endian machine; on a big-endian machine we would have - // byte-swapped 0xIIHH_GGFF in the caller, giving 0xFFGG_HHII, and we - // would then end up bitwise-ORing 0xGGHH_II00_0000_0000 into - // `self.tail`). - // + // to update `self.state`. The analysis is the same whether we are on + // a little-endian or big-endian machine, as the bitwise operations + // are endian-independent. self.tail |= x << (8 * self.ntail); if size < needed { self.ntail += size; @@ -186,8 +192,7 @@ impl SipHasher128 { // Continuing scenario 2: we have one byte left over from the input. We // set `self.ntail` to 1 and `self.tail` to `0x0000_0000_IIHH_GGFF >> - // 8*3`, which is 0x0000_0000_0000_00II. (Or on a big-endian machine - // the prior byte-swapping would leave us with 0x0000_0000_0000_00FF.) + // 8*3`, which is 0x0000_0000_0000_00II. // // The `if` is needed to avoid shifting by 64 bits, which Rust // complains about. @@ -222,22 +227,30 @@ impl Hasher for SipHasher128 { #[inline] fn write_u16(&mut self, i: u16) { - self.short_write(i, i.to_le() as u64); + self.short_write(i, i as u64); } #[inline] fn write_u32(&mut self, i: u32) { - self.short_write(i, i.to_le() as u64); + self.short_write(i, i as u64); } #[inline] fn write_u64(&mut self, i: u64) { - self.short_write(i, i.to_le() as u64); + self.short_write(i, i as u64); + } + + #[inline] + fn write_u128(&mut self, i: u128) { + self.write(&i.to_le_bytes()); } #[inline] fn write_usize(&mut self, i: usize) { - self.short_write(i, i.to_le() as u64); + // Always treat usize as u64 so we get the same results on 32 and 64 bit + // platforms. This is important for symbol hashes when cross compiling, + // for example. + self.write_u64(i as u64); } #[inline] @@ -247,22 +260,31 @@ impl Hasher for SipHasher128 { #[inline] fn write_i16(&mut self, i: i16) { - self.short_write(i, (i as u16).to_le() as u64); + self.short_write(i, i as u16 as u64); } #[inline] fn write_i32(&mut self, i: i32) { - self.short_write(i, (i as u32).to_le() as u64); + self.short_write(i, i as u32 as u64); } #[inline] fn write_i64(&mut self, i: i64) { - self.short_write(i, (i as u64).to_le() as u64); + self.short_write(i, i as u64); + } + + #[inline] + fn write_i128(&mut self, i: i128) { + self.write(&i.to_le_bytes()); } #[inline] fn write_isize(&mut self, i: isize) { - self.short_write(i, (i as usize).to_le() as u64); + // Always treat isize as i64 so we get the same results on 32 and 64 bit + // platforms. This is important for symbol hashes when cross compiling, + // for example. Sign extending here is preferable as it means that the + // same negative number hashes the same on both 32 and 64 bit platforms. + self.write_i64(i as i64); } #[inline] diff --git a/compiler/rustc_data_structures/src/sip128/tests.rs b/compiler/rustc_data_structures/src/sip128/tests.rs index 80b7fc7475610..5138ff45bc625 100644 --- a/compiler/rustc_data_structures/src/sip128/tests.rs +++ b/compiler/rustc_data_structures/src/sip128/tests.rs @@ -1,7 +1,6 @@ use super::*; use std::hash::{Hash, Hasher}; -use std::{mem, slice}; // Hash just the bytes of the slice, without length prefix struct Bytes<'a>(&'a [u8]); @@ -399,20 +398,58 @@ fn test_hash_no_concat_alias() { } #[test] -fn test_write_short_works() { - let test_usize = 0xd0c0b0a0usize; +fn test_short_write_works() { + let test_u8 = 0xFF_u8; + let test_u16 = 0x1122_u16; + let test_u32 = 0x22334455_u32; + let test_u64 = 0x33445566_778899AA_u64; + let test_u128 = 0x11223344_55667788_99AABBCC_DDEEFF77_u128; + let test_usize = 0xD0C0B0A0_usize; + + let test_i8 = -1_i8; + let test_i16 = -2_i16; + let test_i32 = -3_i32; + let test_i64 = -4_i64; + let test_i128 = -5_i128; + let test_isize = -6_isize; + let mut h1 = SipHasher128::new_with_keys(0, 0); - h1.write_usize(test_usize); h1.write(b"bytes"); h1.write(b"string"); - h1.write_u8(0xFFu8); - h1.write_u8(0x01u8); + h1.write_u8(test_u8); + h1.write_u16(test_u16); + h1.write_u32(test_u32); + h1.write_u64(test_u64); + h1.write_u128(test_u128); + h1.write_usize(test_usize); + h1.write_i8(test_i8); + h1.write_i16(test_i16); + h1.write_i32(test_i32); + h1.write_i64(test_i64); + h1.write_i128(test_i128); + h1.write_isize(test_isize); + let mut h2 = SipHasher128::new_with_keys(0, 0); - h2.write(unsafe { - slice::from_raw_parts(&test_usize as *const _ as *const u8, mem::size_of::()) - }); h2.write(b"bytes"); h2.write(b"string"); - h2.write(&[0xFFu8, 0x01u8]); - assert_eq!(h1.finish128(), h2.finish128()); + h2.write(&test_u8.to_le_bytes()); + h2.write(&test_u16.to_le_bytes()); + h2.write(&test_u32.to_le_bytes()); + h2.write(&test_u64.to_le_bytes()); + h2.write(&test_u128.to_le_bytes()); + h2.write(&(test_usize as u64).to_le_bytes()); + h2.write(&test_i8.to_le_bytes()); + h2.write(&test_i16.to_le_bytes()); + h2.write(&test_i32.to_le_bytes()); + h2.write(&test_i64.to_le_bytes()); + h2.write(&test_i128.to_le_bytes()); + h2.write(&(test_isize as i64).to_le_bytes()); + + let h1_hash = h1.finish128(); + let h2_hash = h2.finish128(); + + let expected = (5926600258011434223, 10938367019217336666); + + assert_eq!(h1_hash, expected); + assert_eq!(h2_hash, expected); } diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index c1c79b174f415..4099597f7b82e 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -5,6 +5,9 @@ use smallvec::SmallVec; use std::hash::{BuildHasher, Hash, Hasher}; use std::mem; +#[cfg(test)] +mod tests; + /// When hashing something that ends up affecting properties like symbol names, /// we want these symbol names to be calculated independently of other factors /// like what architecture you're compiling *from*. @@ -57,6 +60,9 @@ impl StableHasher { } } +// SipHasher128 currently handles ensuring platform-independent results with +// respect to endianness and `isize` and `usize` differences (to the extent +// possible). The write functions below don't need handle this at this time. impl Hasher for StableHasher { fn finish(&self) -> u64 { panic!("use StableHasher::finalize instead"); @@ -74,30 +80,27 @@ impl Hasher for StableHasher { #[inline] fn write_u16(&mut self, i: u16) { - self.state.write_u16(i.to_le()); + self.state.write_u16(i); } #[inline] fn write_u32(&mut self, i: u32) { - self.state.write_u32(i.to_le()); + self.state.write_u32(i); } #[inline] fn write_u64(&mut self, i: u64) { - self.state.write_u64(i.to_le()); + self.state.write_u64(i); } #[inline] fn write_u128(&mut self, i: u128) { - self.state.write_u128(i.to_le()); + self.state.write_u128(i); } #[inline] fn write_usize(&mut self, i: usize) { - // Always treat usize as u64 so we get the same results on 32 and 64 bit - // platforms. This is important for symbol hashes when cross compiling, - // for example. - self.state.write_u64((i as u64).to_le()); + self.state.write_usize(i); } #[inline] @@ -107,30 +110,27 @@ impl Hasher for StableHasher { #[inline] fn write_i16(&mut self, i: i16) { - self.state.write_i16(i.to_le()); + self.state.write_i16(i); } #[inline] fn write_i32(&mut self, i: i32) { - self.state.write_i32(i.to_le()); + self.state.write_i32(i); } #[inline] fn write_i64(&mut self, i: i64) { - self.state.write_i64(i.to_le()); + self.state.write_i64(i); } #[inline] fn write_i128(&mut self, i: i128) { - self.state.write_i128(i.to_le()); + self.state.write_i128(i); } #[inline] fn write_isize(&mut self, i: isize) { - // Always treat isize as i64 so we get the same results on 32 and 64 bit - // platforms. This is important for symbol hashes when cross compiling, - // for example. - self.state.write_i64((i as i64).to_le()); + self.state.write_isize(i); } } diff --git a/compiler/rustc_data_structures/src/stable_hasher/tests.rs b/compiler/rustc_data_structures/src/stable_hasher/tests.rs new file mode 100644 index 0000000000000..cd6ff96a555f4 --- /dev/null +++ b/compiler/rustc_data_structures/src/stable_hasher/tests.rs @@ -0,0 +1,73 @@ +use super::*; + +// The tests below compare the computed hashes to particular expected values +// in order to test that we produce the same results on different platforms, +// regardless of endianness and `usize` and `isize` size differences (this +// of course assumes we run these tests on platforms that differ in those +// ways). The expected values depend on the hashing algorithm used, so they +// need to be updated whenever StableHasher changes its hashing algorithm. + +#[test] +fn test_hash_integers() { + // Test that integers are handled consistently across platforms. + let test_u8 = 0xAB_u8; + let test_u16 = 0xFFEE_u16; + let test_u32 = 0x445577AA_u32; + let test_u64 = 0x01234567_13243546_u64; + let test_u128 = 0x22114433_66557788_99AACCBB_EEDDFF77_u128; + let test_usize = 0xD0C0B0A0_usize; + + let test_i8 = -100_i8; + let test_i16 = -200_i16; + let test_i32 = -300_i32; + let test_i64 = -400_i64; + let test_i128 = -500_i128; + let test_isize = -600_isize; + + let mut h = StableHasher::new(); + test_u8.hash(&mut h); + test_u16.hash(&mut h); + test_u32.hash(&mut h); + test_u64.hash(&mut h); + test_u128.hash(&mut h); + test_usize.hash(&mut h); + test_i8.hash(&mut h); + test_i16.hash(&mut h); + test_i32.hash(&mut h); + test_i64.hash(&mut h); + test_i128.hash(&mut h); + test_isize.hash(&mut h); + + // This depends on the hashing algorithm. See note at top of file. + let expected = (2736651863462566372, 8121090595289675650); + + assert_eq!(h.finalize(), expected); +} + +#[test] +fn test_hash_usize() { + // Test that usize specifically is handled consistently across platforms. + let test_usize = 0xABCDEF01_usize; + + let mut h = StableHasher::new(); + test_usize.hash(&mut h); + + // This depends on the hashing algorithm. See note at top of file. + let expected = (5798740672699530587, 11186240177685111648); + + assert_eq!(h.finalize(), expected); +} + +#[test] +fn test_hash_isize() { + // Test that isize specifically is handled consistently across platforms. + let test_isize = -7_isize; + + let mut h = StableHasher::new(); + test_isize.hash(&mut h); + + // This depends on the hashing algorithm. See note at top of file. + let expected = (14721296605626097289, 11385941877786388409); + + assert_eq!(h.finalize(), expected); +} From f4d5275cbb87637daba704251c043d2b303e36d6 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 29 Sep 2020 15:36:08 -0700 Subject: [PATCH 13/14] Update books --- src/doc/embedded-book | 2 +- src/doc/rust-by-example | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/embedded-book b/src/doc/embedded-book index 0cd2ca116274b..dd310616308e0 160000 --- a/src/doc/embedded-book +++ b/src/doc/embedded-book @@ -1 +1 @@ -Subproject commit 0cd2ca116274b915924c3a7e07c1e046b6f19b77 +Subproject commit dd310616308e01f6cf227f46347b744aa56b77d9 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 19f0a0372af49..7d3ff1c12db08 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 19f0a0372af497b34369cf182d9d16156cab2969 +Subproject commit 7d3ff1c12db08a847a57a054be4a7951ce532d2d From 15c357377864d43f5ee8a5fad61a0388d30b2b86 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 29 Sep 2020 15:55:35 -0700 Subject: [PATCH 14/14] Update cargo --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index 05c611ae3c425..75615f8e69f74 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 05c611ae3c4255b7a2bcf4fcfa65b20286a07839 +Subproject commit 75615f8e69f748d7ef0df7bc0b064a9b1f5c78b2