From 2ad46acd7fee4e193aff3fd243ca91ca092a42aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 22 Sep 2020 01:11:45 +0200 Subject: [PATCH 01/17] EarlyOtherwiseBranch::run_pass(): don't convert Place to Place (clippy::useless_conversion) --- compiler/rustc_mir/src/transform/early_otherwise_branch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_mir/src/transform/early_otherwise_branch.rs b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs index 67e679a8b08d0..7a9089d0f3675 100644 --- a/compiler/rustc_mir/src/transform/early_otherwise_branch.rs +++ b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs @@ -91,7 +91,7 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { let not_equal_rvalue = Rvalue::BinaryOp( not_equal, Operand::Copy(Place::from(second_discriminant_temp)), - Operand::Copy(Place::from(first_descriminant_place)), + Operand::Copy(first_descriminant_place), ); patch.add_statement( end_of_block_location, From d7a5c574b1010360fd840f5dfc810f2aa782eac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 22 Sep 2020 01:32:28 +0200 Subject: [PATCH 02/17] use std::mem::take(x) instead of std::mem::replace(x, Default::default()) (clippy::mem_replace_with_default) --- .../src/borrow_check/type_check/constraint_conversion.rs | 3 +-- compiler/rustc_typeck/src/check/writeback.rs | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_mir/src/borrow_check/type_check/constraint_conversion.rs b/compiler/rustc_mir/src/borrow_check/type_check/constraint_conversion.rs index 711271a63fbff..8513e5e531bd7 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/constraint_conversion.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/constraint_conversion.rs @@ -62,8 +62,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { // `self.constraints`, but we also want to be mutating // `self.member_constraints`. For now, just swap out the value // we want and replace at the end. - let mut tmp = - std::mem::replace(&mut self.constraints.member_constraints, Default::default()); + let mut tmp = std::mem::take(&mut self.constraints.member_constraints); for member_constraint in member_constraints { tmp.push_constraint(member_constraint, |r| self.to_region_vid(r)); } diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index b55f62ee436e1..df7cdae46dc30 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -70,10 +70,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports); wbcx.typeck_results.used_trait_imports = used_trait_imports; - wbcx.typeck_results.closure_captures = mem::replace( - &mut self.typeck_results.borrow_mut().closure_captures, - Default::default(), - ); + wbcx.typeck_results.closure_captures = + mem::take(&mut self.typeck_results.borrow_mut().closure_captures); if self.is_tainted_by_errors() { // FIXME(eddyb) keep track of `ErrorReported` from where the error was emitted. From 6d0390c7d77852618dc6ce894cdf7e2bef01c2ca Mon Sep 17 00:00:00 2001 From: kadmin Date: Wed, 2 Sep 2020 19:19:43 +0000 Subject: [PATCH 03/17] Add regression test --- src/test/ui/issues/issue-75777.rs | 17 +++++++++++++++ src/test/ui/issues/issue-75777.stderr | 30 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/test/ui/issues/issue-75777.rs create mode 100644 src/test/ui/issues/issue-75777.stderr diff --git a/src/test/ui/issues/issue-75777.rs b/src/test/ui/issues/issue-75777.rs new file mode 100644 index 0000000000000..291a3db093672 --- /dev/null +++ b/src/test/ui/issues/issue-75777.rs @@ -0,0 +1,17 @@ +// Regression test for #75777. +// Checks that a boxed future can be properly constructed. + +#![feature(future_readiness_fns)] + +use std::future::{self, Future}; +use std::pin::Pin; + +type BoxFuture<'a, T> = Pin + 'a + Send>>; + +fn inject<'a, Env: 'a, A: 'a + Send>(v: A) -> Box BoxFuture<'a, A>> { + let fut: BoxFuture<'a, A> = Box::pin(future::ready(v)); + Box::new(move |_| fut) + //~^ ERROR: cannot infer an appropriate lifetime +} + +fn main() {} diff --git a/src/test/ui/issues/issue-75777.stderr b/src/test/ui/issues/issue-75777.stderr new file mode 100644 index 0000000000000..1115f6c6d52ca --- /dev/null +++ b/src/test/ui/issues/issue-75777.stderr @@ -0,0 +1,30 @@ +error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements + --> $DIR/issue-75777.rs:13:14 + | +LL | Box::new(move |_| fut) + | ^^^^^^^^^^^^ + | +note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 11:11... + --> $DIR/issue-75777.rs:11:11 + | +LL | fn inject<'a, Env: 'a, A: 'a + Send>(v: A) -> Box BoxFuture<'a, A>> { + | ^^ +note: ...so that the types are compatible + --> $DIR/issue-75777.rs:13:14 + | +LL | Box::new(move |_| fut) + | ^^^^^^^^^^^^ + = note: expected `Pin + Send>>` + found `Pin + Send + 'a)>>` + = note: but, the lifetime must be valid for the static lifetime... +note: ...so that the expression is assignable + --> $DIR/issue-75777.rs:13:5 + | +LL | Box::new(move |_| fut) + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: expected `Box<(dyn FnOnce(&'a Env) -> Pin + Send + 'a)>> + 'static)>` + found `Box Pin + Send + 'a)>>>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0495`. From 3e485d7cf59dafbc37467c3ef1acc51f26b381d9 Mon Sep 17 00:00:00 2001 From: Stein Somers Date: Sat, 26 Sep 2020 16:23:50 +0200 Subject: [PATCH 04/17] 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 05/17] 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 db5b70f193dcc08476ec1bef8dc1863d508eea1e Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Mon, 28 Sep 2020 20:21:44 +0200 Subject: [PATCH 06/17] 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 07/17] 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 08/17] 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 09/17] 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 10/17] 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 11/17] 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 12/17] 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 13/17] 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 f4d5275cbb87637daba704251c043d2b303e36d6 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 29 Sep 2020 15:36:08 -0700 Subject: [PATCH 14/17] 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 93e3db30e9c3b8fb217743e9d06f37b12dcb8745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Wed, 30 Sep 2020 00:00:00 +0000 Subject: [PATCH 15/17] liveness: Use Option::None to represent absent live nodes No functional changes intended. --- compiler/rustc_passes/src/liveness.rs | 76 +++++++++++++-------------- 1 file changed, 36 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index eb2f40a82d1f1..0eb834cbb9816 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -62,13 +62,13 @@ //! - `reader`: the `LiveNode` ID of some node which will read the value //! that `V` holds on entry to `N`. Formally: a node `M` such //! that there exists a path `P` from `N` to `M` where `P` does not -//! write `V`. If the `reader` is `INVALID_NODE`, then the current +//! write `V`. If the `reader` is `None`, then the current //! value will never be read (the variable is dead, essentially). //! //! - `writer`: the `LiveNode` ID of some node which will write the //! variable `V` and which is reachable from `N`. Formally: a node `M` //! such that there exists a path `P` from `N` to `M` and `M` writes -//! `V`. If the `writer` is `INVALID_NODE`, then there is no writer +//! `V`. If the `writer` is `None`, then there is no writer //! of `V` that follows `N`. //! //! - `used`: a boolean value indicating whether `V` is *used*. We @@ -114,7 +114,6 @@ rustc_index::newtype_index! { rustc_index::newtype_index! { pub struct LiveNode { DEBUG_FORMAT = "ln({})", - const INVALID_NODE = LiveNode::MAX_AS_U32, } } @@ -168,12 +167,6 @@ pub fn provide(providers: &mut Providers) { // variable must not be assigned if there is some successor // assignment. And so forth. -impl LiveNode { - fn is_valid(self) -> bool { - self != INVALID_NODE - } -} - struct CaptureInfo { ln: LiveNode, var_hid: HirId, @@ -478,8 +471,8 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { #[derive(Clone, Copy)] struct RWU { - reader: LiveNode, - writer: LiveNode, + reader: Option, + writer: Option, used: bool, } @@ -501,10 +494,10 @@ struct RWUTable { unpacked_rwus: Vec, } -// A constant representing `RWU { reader: INVALID_NODE; writer: INVALID_NODE; used: false }`. +// A constant representing `RWU { reader: None; writer: None; used: false }`. const INV_INV_FALSE: u32 = u32::MAX; -// A constant representing `RWU { reader: INVALID_NODE; writer: INVALID_NODE; used: true }`. +// A constant representing `RWU { reader: None; writer: None; used: true }`. const INV_INV_TRUE: u32 = u32::MAX - 1; impl RWUTable { @@ -515,24 +508,24 @@ impl RWUTable { fn get(&self, idx: usize) -> RWU { let packed_rwu = self.packed_rwus[idx]; match packed_rwu { - INV_INV_FALSE => RWU { reader: INVALID_NODE, writer: INVALID_NODE, used: false }, - INV_INV_TRUE => RWU { reader: INVALID_NODE, writer: INVALID_NODE, used: true }, + INV_INV_FALSE => RWU { reader: None, writer: None, used: false }, + INV_INV_TRUE => RWU { reader: None, writer: None, used: true }, _ => self.unpacked_rwus[packed_rwu as usize], } } - fn get_reader(&self, idx: usize) -> LiveNode { + fn get_reader(&self, idx: usize) -> Option { let packed_rwu = self.packed_rwus[idx]; match packed_rwu { - INV_INV_FALSE | INV_INV_TRUE => INVALID_NODE, + INV_INV_FALSE | INV_INV_TRUE => None, _ => self.unpacked_rwus[packed_rwu as usize].reader, } } - fn get_writer(&self, idx: usize) -> LiveNode { + fn get_writer(&self, idx: usize) -> Option { let packed_rwu = self.packed_rwus[idx]; match packed_rwu { - INV_INV_FALSE | INV_INV_TRUE => INVALID_NODE, + INV_INV_FALSE | INV_INV_TRUE => None, _ => self.unpacked_rwus[packed_rwu as usize].writer, } } @@ -552,7 +545,7 @@ impl RWUTable { } fn assign_unpacked(&mut self, idx: usize, rwu: RWU) { - if rwu.reader == INVALID_NODE && rwu.writer == INVALID_NODE { + if rwu.reader == None && rwu.writer == None { // When we overwrite an indexing entry in `self.packed_rwus` with // `INV_INV_{TRUE,FALSE}` we don't remove the corresponding entry // from `self.unpacked_rwus`; it's not worth the effort, and we @@ -581,7 +574,7 @@ struct Liveness<'a, 'tcx> { typeck_results: &'a ty::TypeckResults<'tcx>, param_env: ty::ParamEnv<'tcx>, upvars: Option<&'tcx FxIndexMap>, - successors: IndexVec, + successors: IndexVec>, rwu_table: RWUTable, /// A live node representing a point of execution before closure entry & @@ -617,7 +610,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { typeck_results, param_env, upvars, - successors: IndexVec::from_elem_n(INVALID_NODE, num_live_nodes), + successors: IndexVec::from_elem_n(None, num_live_nodes), rwu_table: RWUTable::new(num_live_nodes * num_vars), closure_ln, exit_ln, @@ -662,30 +655,33 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } fn live_on_entry(&self, ln: LiveNode, var: Variable) -> Option { - assert!(ln.is_valid()); - let reader = self.rwu_table.get_reader(self.idx(ln, var)); - if reader.is_valid() { Some(self.ir.lnks[reader]) } else { None } + if let Some(reader) = self.rwu_table.get_reader(self.idx(ln, var)) { + Some(self.ir.lnks[reader]) + } else { + None + } } // Is this variable live on entry to any of its successor nodes? fn live_on_exit(&self, ln: LiveNode, var: Variable) -> Option { - let successor = self.successors[ln]; + let successor = self.successors[ln].unwrap(); self.live_on_entry(successor, var) } fn used_on_entry(&self, ln: LiveNode, var: Variable) -> bool { - assert!(ln.is_valid()); self.rwu_table.get_used(self.idx(ln, var)) } fn assigned_on_entry(&self, ln: LiveNode, var: Variable) -> Option { - assert!(ln.is_valid()); - let writer = self.rwu_table.get_writer(self.idx(ln, var)); - if writer.is_valid() { Some(self.ir.lnks[writer]) } else { None } + if let Some(writer) = self.rwu_table.get_writer(self.idx(ln, var)) { + Some(self.ir.lnks[writer]) + } else { + None + } } fn assigned_on_exit(&self, ln: LiveNode, var: Variable) -> Option { - let successor = self.successors[ln]; + let successor = self.successors[ln].unwrap(); self.assigned_on_entry(successor, var) } @@ -720,9 +716,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { { let wr = &mut wr as &mut dyn Write; write!(wr, "[{:?} of kind {:?} reads", ln, self.ir.lnks[ln]); - self.write_vars(wr, ln, |idx| self.rwu_table.get_reader(idx).is_valid()); + self.write_vars(wr, ln, |idx| self.rwu_table.get_reader(idx).is_some()); write!(wr, " writes"); - self.write_vars(wr, ln, |idx| self.rwu_table.get_writer(idx).is_valid()); + self.write_vars(wr, ln, |idx| self.rwu_table.get_writer(idx).is_some()); write!(wr, " uses"); self.write_vars(wr, ln, |idx| self.rwu_table.get_used(idx)); @@ -746,7 +742,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } fn init_empty(&mut self, ln: LiveNode, succ_ln: LiveNode) { - self.successors[ln] = succ_ln; + self.successors[ln] = Some(succ_ln); // It is not necessary to initialize the RWUs here because they are all // set to INV_INV_FALSE when they are created, and the sets only grow @@ -755,7 +751,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { fn init_from_succ(&mut self, ln: LiveNode, succ_ln: LiveNode) { // more efficient version of init_empty() / merge_from_succ() - self.successors[ln] = succ_ln; + self.successors[ln] = Some(succ_ln); self.indices2(ln, succ_ln, |this, idx, succ_idx| { this.rwu_table.copy_packed(idx, succ_idx); @@ -779,12 +775,12 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { let mut changed = false; let mut rwu = this.rwu_table.get(idx); let succ_rwu = this.rwu_table.get(succ_idx); - if succ_rwu.reader.is_valid() && !rwu.reader.is_valid() { + if succ_rwu.reader.is_some() && rwu.reader.is_none() { rwu.reader = succ_rwu.reader; changed = true } - if succ_rwu.writer.is_valid() && !rwu.writer.is_valid() { + if succ_rwu.writer.is_some() && rwu.writer.is_none() { rwu.writer = succ_rwu.writer; changed = true } @@ -828,14 +824,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { let mut rwu = self.rwu_table.get(idx); if (acc & ACC_WRITE) != 0 { - rwu.reader = INVALID_NODE; - rwu.writer = ln; + rwu.reader = None; + rwu.writer = Some(ln); } // Important: if we both read/write, must do read second // or else the write will override. if (acc & ACC_READ) != 0 { - rwu.reader = ln; + rwu.reader = Some(ln); } if (acc & ACC_USE) != 0 { From 607d30dcfa8c40b91d0073a18c5897eee00a4cda Mon Sep 17 00:00:00 2001 From: samlich <1349989+samlich@users.noreply.github.com> Date: Tue, 29 Sep 2020 19:46:01 +0000 Subject: [PATCH 16/17] Add test for issue #74761 --- src/test/ui/type-alias-impl-trait/issue-74761.rs | 16 ++++++++++++++++ .../ui/type-alias-impl-trait/issue-74761.stderr | 15 +++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 src/test/ui/type-alias-impl-trait/issue-74761.rs create mode 100644 src/test/ui/type-alias-impl-trait/issue-74761.stderr diff --git a/src/test/ui/type-alias-impl-trait/issue-74761.rs b/src/test/ui/type-alias-impl-trait/issue-74761.rs new file mode 100644 index 0000000000000..4345b5d886ee2 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-74761.rs @@ -0,0 +1,16 @@ +#![feature(member_constraints)] +#![feature(type_alias_impl_trait)] + +pub trait A { + type B; + fn f(&self) -> Self::B; +} +impl<'a, 'b> A for () { + //~^ ERROR the lifetime parameter `'a` is not constrained + //~| ERROR the lifetime parameter `'b` is not constrained + type B = impl core::fmt::Debug; + + fn f(&self) -> Self::B {} +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/issue-74761.stderr b/src/test/ui/type-alias-impl-trait/issue-74761.stderr new file mode 100644 index 0000000000000..3f38fa4de0165 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-74761.stderr @@ -0,0 +1,15 @@ +error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates + --> $DIR/issue-74761.rs:8:6 + | +LL | impl<'a, 'b> A for () { + | ^^ unconstrained lifetime parameter + +error[E0207]: the lifetime parameter `'b` is not constrained by the impl trait, self type, or predicates + --> $DIR/issue-74761.rs:8:10 + | +LL | impl<'a, 'b> A for () { + | ^^ unconstrained lifetime parameter + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0207`. From 609786dbd8b07599a2bc6618c777f859d7a01451 Mon Sep 17 00:00:00 2001 From: varkor Date: Tue, 29 Sep 2020 17:44:32 +0100 Subject: [PATCH 17/17] Validate `rustc_args_required_const` --- .../rustc_mir/src/transform/promote_consts.rs | 2 +- compiler/rustc_passes/src/check_attr.rs | 110 ++++++++++++++++-- ...lid-rustc_args_required_const-arguments.rs | 26 +++++ ...rustc_args_required_const-arguments.stderr | 48 ++++++++ 4 files changed, 176 insertions(+), 10 deletions(-) create mode 100644 src/test/ui/invalid-rustc_args_required_const-arguments.rs create mode 100644 src/test/ui/invalid-rustc_args_required_const-arguments.stderr diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs index cd361e430fa61..89f7531b3a7ff 100644 --- a/compiler/rustc_mir/src/transform/promote_consts.rs +++ b/compiler/rustc_mir/src/transform/promote_consts.rs @@ -137,7 +137,7 @@ fn args_required_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { LitKind::Int(a, _) => { ret.push(a as usize); } - _ => return None, + _ => bug!("invalid arg index"), } } Some(ret) diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index efe947daa2866..1ec251326a8f6 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -8,12 +8,12 @@ use rustc_middle::hir::map::Map; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; -use rustc_ast::{Attribute, NestedMetaItem}; -use rustc_errors::struct_span_err; +use rustc_ast::{Attribute, LitKind, NestedMetaItem}; +use rustc_errors::{pluralize, struct_span_err}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{self, HirId, Item, ItemKind, TraitItem}; +use rustc_hir::{self, FnSig, ForeignItem, ForeignItemKind, HirId, Item, ItemKind, TraitItem}; use rustc_hir::{MethodKind, Target}; use rustc_session::lint::builtin::{CONFLICTING_REPR_HINTS, UNUSED_ATTRIBUTES}; use rustc_session::parse::feature_err; @@ -43,6 +43,12 @@ pub(crate) fn target_from_impl_item<'tcx>( } } +#[derive(Clone, Copy)] +enum ItemLike<'tcx> { + Item(&'tcx Item<'tcx>), + ForeignItem(&'tcx ForeignItem<'tcx>), +} + struct CheckAttrVisitor<'tcx> { tcx: TyCtxt<'tcx>, } @@ -55,7 +61,7 @@ impl CheckAttrVisitor<'tcx> { attrs: &'hir [Attribute], span: &Span, target: Target, - item: Option<&Item<'_>>, + item: Option>, ) { let mut is_valid = true; for attr in attrs { @@ -75,6 +81,8 @@ impl CheckAttrVisitor<'tcx> { self.check_no_link(&attr, span, target) } else if self.tcx.sess.check_name(attr, sym::export_name) { self.check_export_name(&attr, span, target) + } else if self.tcx.sess.check_name(attr, sym::rustc_args_required_const) { + self.check_rustc_args_required_const(&attr, span, target, item) } else { // lint-only checks if self.tcx.sess.check_name(attr, sym::cold) { @@ -400,6 +408,71 @@ impl CheckAttrVisitor<'tcx> { } } + /// Checks if `#[rustc_args_required_const]` is applied to a function and has a valid argument. + fn check_rustc_args_required_const( + &self, + attr: &Attribute, + span: &Span, + target: Target, + item: Option>, + ) -> bool { + if let Target::Fn | Target::Method(..) | Target::ForeignFn = target { + let mut invalid_args = vec![]; + for meta in attr.meta_item_list().expect("no meta item list") { + if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) { + if let Some(ItemLike::Item(Item { + kind: ItemKind::Fn(FnSig { decl, .. }, ..), + .. + })) + | Some(ItemLike::ForeignItem(ForeignItem { + kind: ForeignItemKind::Fn(decl, ..), + .. + })) = item + { + let arg_count = decl.inputs.len() as u128; + if *val >= arg_count { + let span = meta.span(); + self.tcx + .sess + .struct_span_err(span, "index exceeds number of arguments") + .span_label( + span, + format!( + "there {} only {} argument{}", + if arg_count != 1 { "are" } else { "is" }, + arg_count, + pluralize!(arg_count) + ), + ) + .emit(); + return false; + } + } else { + bug!("should be a function item"); + } + } else { + invalid_args.push(meta.span()); + } + } + if !invalid_args.is_empty() { + self.tcx + .sess + .struct_span_err(invalid_args, "arguments should be non-negative integers") + .emit(); + false + } else { + true + } + } else { + self.tcx + .sess + .struct_span_err(attr.span, "attribute should be applied to a function") + .span_label(*span, "not a function") + .emit(); + false + } + } + /// Checks if `#[link_section]` is applied to a function or static. fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) { match target { @@ -448,7 +521,7 @@ impl CheckAttrVisitor<'tcx> { attrs: &'hir [Attribute], span: &Span, target: Target, - item: Option<&Item<'_>>, + item: Option>, hir_id: HirId, ) { // Extract the names of all repr hints, e.g., [foo, bar, align] for: @@ -564,7 +637,14 @@ impl CheckAttrVisitor<'tcx> { // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8) if (int_reprs > 1) || (is_simd && is_c) - || (int_reprs == 1 && is_c && item.map_or(false, |item| is_c_like_enum(item))) + || (int_reprs == 1 + && is_c + && item.map_or(false, |item| { + if let ItemLike::Item(item) = item { + return is_c_like_enum(item); + } + return false; + })) { self.tcx.struct_span_lint_hir( CONFLICTING_REPR_HINTS, @@ -649,7 +729,13 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> { fn visit_item(&mut self, item: &'tcx Item<'tcx>) { let target = Target::from_item(item); - self.check_attributes(item.hir_id, item.attrs, &item.span, target, Some(item)); + self.check_attributes( + item.hir_id, + item.attrs, + &item.span, + target, + Some(ItemLike::Item(item)), + ); intravisit::walk_item(self, item) } @@ -659,9 +745,15 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> { intravisit::walk_trait_item(self, trait_item) } - fn visit_foreign_item(&mut self, f_item: &'tcx hir::ForeignItem<'tcx>) { + fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) { let target = Target::from_foreign_item(f_item); - self.check_attributes(f_item.hir_id, &f_item.attrs, &f_item.span, target, None); + self.check_attributes( + f_item.hir_id, + &f_item.attrs, + &f_item.span, + target, + Some(ItemLike::ForeignItem(f_item)), + ); intravisit::walk_foreign_item(self, f_item) } diff --git a/src/test/ui/invalid-rustc_args_required_const-arguments.rs b/src/test/ui/invalid-rustc_args_required_const-arguments.rs new file mode 100644 index 0000000000000..76c01c2130138 --- /dev/null +++ b/src/test/ui/invalid-rustc_args_required_const-arguments.rs @@ -0,0 +1,26 @@ +#![feature(rustc_attrs)] + +#[rustc_args_required_const(0)] //~ ERROR index exceeds number of arguments +fn foo1() {} + +#[rustc_args_required_const(1)] //~ ERROR index exceeds number of arguments +fn foo2(_: u8) {} + +#[rustc_args_required_const(a)] //~ ERROR arguments should be non-negative integers +fn foo4() {} + +#[rustc_args_required_const(1, a, 2, b)] //~ ERROR arguments should be non-negative integers +fn foo5(_: u8, _: u8, _: u8) {} + +#[rustc_args_required_const(0)] //~ ERROR attribute should be applied to a function +struct S; + +#[rustc_args_required_const(0usize)] //~ ERROR suffixed literals are not allowed in attributes +fn foo6(_: u8) {} + +extern { + #[rustc_args_required_const(1)] //~ ERROR index exceeds number of arguments + fn foo7(_: u8); +} + +fn main() {} diff --git a/src/test/ui/invalid-rustc_args_required_const-arguments.stderr b/src/test/ui/invalid-rustc_args_required_const-arguments.stderr new file mode 100644 index 0000000000000..39d0462616873 --- /dev/null +++ b/src/test/ui/invalid-rustc_args_required_const-arguments.stderr @@ -0,0 +1,48 @@ +error: suffixed literals are not allowed in attributes + --> $DIR/invalid-rustc_args_required_const-arguments.rs:18:29 + | +LL | #[rustc_args_required_const(0usize)] + | ^^^^^^ + | + = help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) + +error: index exceeds number of arguments + --> $DIR/invalid-rustc_args_required_const-arguments.rs:3:29 + | +LL | #[rustc_args_required_const(0)] + | ^ there are only 0 arguments + +error: index exceeds number of arguments + --> $DIR/invalid-rustc_args_required_const-arguments.rs:6:29 + | +LL | #[rustc_args_required_const(1)] + | ^ there is only 1 argument + +error: arguments should be non-negative integers + --> $DIR/invalid-rustc_args_required_const-arguments.rs:9:29 + | +LL | #[rustc_args_required_const(a)] + | ^ + +error: arguments should be non-negative integers + --> $DIR/invalid-rustc_args_required_const-arguments.rs:12:32 + | +LL | #[rustc_args_required_const(1, a, 2, b)] + | ^ ^ + +error: attribute should be applied to a function + --> $DIR/invalid-rustc_args_required_const-arguments.rs:15:1 + | +LL | #[rustc_args_required_const(0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | struct S; + | --------- not a function + +error: index exceeds number of arguments + --> $DIR/invalid-rustc_args_required_const-arguments.rs:22:33 + | +LL | #[rustc_args_required_const(1)] + | ^ there is only 1 argument + +error: aborting due to 7 previous errors +