diff --git a/Cargo.lock b/Cargo.lock index 53ba61da2b002..208ce841759b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3403,7 +3403,6 @@ dependencies = [ name = "rustc_arena" version = "0.0.0" dependencies = [ - "rustc_data_structures", "smallvec", ] diff --git a/compiler/rustc_arena/Cargo.toml b/compiler/rustc_arena/Cargo.toml index 33ccd0445030f..ee3a7b51b699a 100644 --- a/compiler/rustc_arena/Cargo.toml +++ b/compiler/rustc_arena/Cargo.toml @@ -4,5 +4,4 @@ version = "0.0.0" edition = "2021" [dependencies] -rustc_data_structures = { path = "../rustc_data_structures" } smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 8b4a0840df583..cacc36b616a1e 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -471,9 +471,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // two imports. for new_node_id in [id1, id2] { let new_id = self.resolver.local_def_id(new_node_id); - let res = if let Some(res) = resolutions.next() { - res - } else { + let Some(res) = resolutions.next() else { // Associate an HirId to both ids even if there is no resolution. let _old = self .node_id_to_hir_id diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index a594339296f32..b1e601516ab97 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -32,6 +32,7 @@ #![feature(crate_visibility_modifier)] #![feature(box_patterns)] +#![feature(let_else)] #![feature(never_type)] #![recursion_limit = "256"] #![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index bab50df3dd543..8c5beb1025803 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -217,85 +217,81 @@ where let mut issue_num = None; let mut is_soft = false; for meta in metas { - if let Some(mi) = meta.meta_item() { - match mi.name_or_empty() { - sym::feature => { - if !get(mi, &mut feature) { - continue 'outer; - } + let Some(mi) = meta.meta_item() else { + handle_errors( + &sess.parse_sess, + meta.span(), + AttrError::UnsupportedLiteral("unsupported literal", false), + ); + continue 'outer; + }; + match mi.name_or_empty() { + sym::feature => { + if !get(mi, &mut feature) { + continue 'outer; } - sym::reason => { - if !get(mi, &mut reason) { - continue 'outer; - } + } + sym::reason => { + if !get(mi, &mut reason) { + continue 'outer; + } + } + sym::issue => { + if !get(mi, &mut issue) { + continue 'outer; } - sym::issue => { - if !get(mi, &mut issue) { - continue 'outer; - } - // These unwraps are safe because `get` ensures the meta item - // is a name/value pair string literal. - issue_num = match issue.unwrap().as_str() { - "none" => None, - issue => { - let emit_diag = |msg: &str| { - struct_span_err!( - diagnostic, - mi.span, - E0545, - "`issue` must be a non-zero numeric string \ - or \"none\"", - ) - .span_label( - mi.name_value_literal_span().unwrap(), - msg, - ) - .emit(); - }; - match issue.parse() { - Ok(0) => { - emit_diag( - "`issue` must not be \"0\", \ - use \"none\" instead", - ); - continue 'outer; - } - Ok(num) => NonZeroU32::new(num), - Err(err) => { - emit_diag(&err.to_string()); - continue 'outer; - } + // These unwraps are safe because `get` ensures the meta item + // is a name/value pair string literal. + issue_num = match issue.unwrap().as_str() { + "none" => None, + issue => { + let emit_diag = |msg: &str| { + struct_span_err!( + diagnostic, + mi.span, + E0545, + "`issue` must be a non-zero numeric string \ + or \"none\"", + ) + .span_label(mi.name_value_literal_span().unwrap(), msg) + .emit(); + }; + match issue.parse() { + Ok(0) => { + emit_diag( + "`issue` must not be \"0\", \ + use \"none\" instead", + ); + continue 'outer; + } + Ok(num) => NonZeroU32::new(num), + Err(err) => { + emit_diag(&err.to_string()); + continue 'outer; } } - }; - } - sym::soft => { - if !mi.is_word() { - let msg = "`soft` should not have any arguments"; - sess.parse_sess.span_diagnostic.span_err(mi.span, msg); } - is_soft = true; - } - _ => { - handle_errors( - &sess.parse_sess, - meta.span(), - AttrError::UnknownMetaItem( - pprust::path_to_string(&mi.path), - &["feature", "reason", "issue", "soft"], - ), - ); - continue 'outer; + }; + } + sym::soft => { + if !mi.is_word() { + let msg = "`soft` should not have any arguments"; + sess.parse_sess.span_diagnostic.span_err(mi.span, msg); } + is_soft = true; + } + _ => { + handle_errors( + &sess.parse_sess, + meta.span(), + AttrError::UnknownMetaItem( + pprust::path_to_string(&mi.path), + &["feature", "reason", "issue", "soft"], + ), + ); + continue 'outer; } - } else { - handle_errors( - &sess.parse_sess, - meta.span(), - AttrError::UnsupportedLiteral("unsupported literal", false), - ); - continue 'outer; } } diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr/src/lib.rs index 3fb11f77872be..c95c1c40a34c2 100644 --- a/compiler/rustc_attr/src/lib.rs +++ b/compiler/rustc_attr/src/lib.rs @@ -4,6 +4,8 @@ //! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax` //! to this crate. +#![feature(let_else)] + #[macro_use] extern crate rustc_macros; diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 8ed50075ecbae..049c3aab97933 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -488,12 +488,32 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // don't create labels for compiler-generated spans Some(_) => None, None => { - let (span, suggestion) = suggest_ampmut( - self.infcx.tcx, - local_decl, - opt_assignment_rhs_span, - *opt_ty_info, - ); + let (span, suggestion) = if name != kw::SelfLower { + suggest_ampmut( + self.infcx.tcx, + local_decl, + opt_assignment_rhs_span, + *opt_ty_info, + ) + } else { + match local_decl.local_info.as_deref() { + Some(LocalInfo::User(ClearCrossCrate::Set( + mir::BindingForm::Var(mir::VarBindingForm { + opt_ty_info: None, + .. + }), + ))) => { + suggest_ampmut_self(self.infcx.tcx, local_decl) + } + // explicit self (eg `self: &'a Self`) + _ => suggest_ampmut( + self.infcx.tcx, + local_decl, + opt_assignment_rhs_span, + *opt_ty_info, + ), + } + }; Some((true, span, suggestion)) } } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 01cc72121c7d4..3409f14c98b73 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -311,43 +311,39 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { ty::BoundRegionKind::BrEnv => { let def_ty = self.regioncx.universal_regions().defining_ty; - if let DefiningTy::Closure(_, substs) = def_ty { - let args_span = if let hir::ExprKind::Closure(_, _, _, span, _) = - tcx.hir().expect_expr(self.mir_hir_id()).kind - { - span - } else { - bug!("Closure is not defined by a closure expr"); - }; - let region_name = self.synthesize_region_name(); - - let closure_kind_ty = substs.as_closure().kind_ty(); - let note = match closure_kind_ty.to_opt_closure_kind() { - Some(ty::ClosureKind::Fn) => { - "closure implements `Fn`, so references to captured variables \ - can't escape the closure" - } - Some(ty::ClosureKind::FnMut) => { - "closure implements `FnMut`, so references to captured variables \ - can't escape the closure" - } - Some(ty::ClosureKind::FnOnce) => { - bug!("BrEnv in a `FnOnce` closure"); - } - None => bug!("Closure kind not inferred in borrow check"), - }; - - Some(RegionName { - name: region_name, - source: RegionNameSource::SynthesizedFreeEnvRegion( - args_span, - note.to_string(), - ), - }) - } else { + let DefiningTy::Closure(_, substs) = def_ty else { // Can't have BrEnv in functions, constants or generators. bug!("BrEnv outside of closure."); - } + }; + let hir::ExprKind::Closure(_, _, _, args_span, _) = + tcx.hir().expect_expr(self.mir_hir_id()).kind else { + bug!("Closure is not defined by a closure expr"); + }; + let region_name = self.synthesize_region_name(); + + let closure_kind_ty = substs.as_closure().kind_ty(); + let note = match closure_kind_ty.to_opt_closure_kind() { + Some(ty::ClosureKind::Fn) => { + "closure implements `Fn`, so references to captured variables \ + can't escape the closure" + } + Some(ty::ClosureKind::FnMut) => { + "closure implements `FnMut`, so references to captured variables \ + can't escape the closure" + } + Some(ty::ClosureKind::FnOnce) => { + bug!("BrEnv in a `FnOnce` closure"); + } + None => bug!("Closure kind not inferred in borrow check"), + }; + + Some(RegionName { + name: region_name, + source: RegionNameSource::SynthesizedFreeEnvRegion( + args_span, + note.to_string(), + ), + }) } ty::BoundRegionKind::BrAnon(_) => None, @@ -765,48 +761,45 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { fn get_future_inner_return_ty(&self, hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { let hir = self.infcx.tcx.hir(); - if let hir::TyKind::OpaqueDef(id, _) = hir_ty.kind { - let opaque_ty = hir.item(id); - if let hir::ItemKind::OpaqueTy(hir::OpaqueTy { - bounds: - [ - hir::GenericBound::LangItemTrait( - hir::LangItem::Future, - _, - _, - hir::GenericArgs { - bindings: - [ - hir::TypeBinding { - ident: Ident { name: sym::Output, .. }, - kind: - hir::TypeBindingKind::Equality { - term: hir::Term::Ty(ty), - }, - .. - }, - ], - .. - }, - ), - ], - .. - }) = opaque_ty.kind - { - ty - } else { - span_bug!( - hir_ty.span, - "bounds from lowered return type of async fn did not match expected format: {:?}", - opaque_ty - ); - } - } else { + let hir::TyKind::OpaqueDef(id, _) = hir_ty.kind else { span_bug!( hir_ty.span, "lowered return type of async fn is not OpaqueDef: {:?}", hir_ty ); + }; + let opaque_ty = hir.item(id); + if let hir::ItemKind::OpaqueTy(hir::OpaqueTy { + bounds: + [ + hir::GenericBound::LangItemTrait( + hir::LangItem::Future, + _, + _, + hir::GenericArgs { + bindings: + [ + hir::TypeBinding { + ident: Ident { name: sym::Output, .. }, + kind: + hir::TypeBindingKind::Equality { term: hir::Term::Ty(ty) }, + .. + }, + ], + .. + }, + ), + ], + .. + }) = opaque_ty.kind + { + ty + } else { + span_bug!( + hir_ty.span, + "bounds from lowered return type of async fn did not match expected format: {:?}", + opaque_ty + ); } } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index c288cc96990c2..5597a8b091554 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1427,9 +1427,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { bug!("temporary should be initialized exactly once") }; - let loc = match init.location { - InitLocation::Statement(stmt) => stmt, - _ => bug!("temporary initialized in arguments"), + let InitLocation::Statement(loc) = init.location else { + bug!("temporary initialized in arguments") }; let body = self.body; diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 22ef0b2dda506..cf15fc4ddc3a5 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -55,6 +55,7 @@ impl<'tcx> MirPass<'tcx> for Validator { reachable_blocks: traversal::reachable_as_bitset(body), storage_liveness, place_cache: Vec::new(), + value_cache: Vec::new(), } .visit_body(body); } @@ -109,6 +110,7 @@ struct TypeChecker<'a, 'tcx> { reachable_blocks: BitSet, storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive>, place_cache: Vec>, + value_cache: Vec, } impl<'a, 'tcx> TypeChecker<'a, 'tcx> { @@ -398,6 +400,22 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.check_edge(location, target, EdgeKind::Normal); } self.check_edge(location, targets.otherwise(), EdgeKind::Normal); + + self.value_cache.clear(); + self.value_cache.extend(targets.iter().map(|(value, _)| value)); + let all_len = self.value_cache.len(); + self.value_cache.sort_unstable(); + self.value_cache.dedup(); + let has_duplicates = all_len != self.value_cache.len(); + if has_duplicates { + self.fail( + location, + format!( + "duplicated values in `SwitchInt` terminator: {:?}", + terminator.kind, + ), + ); + } } TerminatorKind::Drop { target, unwind, .. } => { self.check_edge(location, *target, EdgeKind::Normal); diff --git a/compiler/rustc_data_structures/src/fingerprint.rs b/compiler/rustc_data_structures/src/fingerprint.rs index 525cd650dd25d..e931379dd3a70 100644 --- a/compiler/rustc_data_structures/src/fingerprint.rs +++ b/compiler/rustc_data_structures/src/fingerprint.rs @@ -3,6 +3,9 @@ use rustc_serialize::{Decodable, Encodable}; use std::convert::TryInto; use std::hash::{Hash, Hasher}; +#[cfg(test)] +mod tests; + #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy)] #[repr(C)] pub struct Fingerprint(u64, u64); @@ -54,7 +57,7 @@ impl Fingerprint { let c = a.wrapping_add(b); - Fingerprint((c >> 64) as u64, c as u64) + Fingerprint(c as u64, (c >> 64) as u64) } pub fn to_hex(&self) -> String { diff --git a/compiler/rustc_data_structures/src/fingerprint/tests.rs b/compiler/rustc_data_structures/src/fingerprint/tests.rs new file mode 100644 index 0000000000000..9b0783e33ab47 --- /dev/null +++ b/compiler/rustc_data_structures/src/fingerprint/tests.rs @@ -0,0 +1,14 @@ +use super::*; + +// Check that `combine_commutative` is order independent. +#[test] +fn combine_commutative_is_order_independent() { + let a = Fingerprint::new(0xf6622fb349898b06, 0x70be9377b2f9c610); + let b = Fingerprint::new(0xa9562bf5a2a5303c, 0x67d9b6c82034f13d); + let c = Fingerprint::new(0x0d013a27811dbbc3, 0x9a3f7b3d9142ec43); + let permutations = [(a, b, c), (a, c, b), (b, a, c), (b, c, a), (c, a, b), (c, b, a)]; + let f = a.combine_commutative(b).combine_commutative(c); + for p in &permutations { + assert_eq!(f, p.0.combine_commutative(p.1).combine_commutative(p.2)); + } +} diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 3294f2cf64172..6329bcee4fa3f 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -602,33 +602,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { for binding in &candidate_ref.bindings { let local = self.var_local_id(binding.var_id, OutsideGuard); - if let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( + let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. }, - )))) = self.local_decls[local].local_info - { - // `try_upvars_resolved` may fail if it is unable to resolve the given - // `PlaceBuilder` inside a closure. In this case, we don't want to include - // a scrutinee place. `scrutinee_place_builder` will fail for destructured - // assignments. This is because a closure only captures the precise places - // that it will read and as a result a closure may not capture the entire - // tuple/struct and rather have individual places that will be read in the - // final MIR. - // Example: - // ``` - // let foo = (0, 1); - // let c = || { - // let (v1, v2) = foo; - // }; - // ``` - if let Ok(match_pair_resolved) = - initializer.clone().try_upvars_resolved(self.tcx, self.typeck_results) - { - let place = - match_pair_resolved.into_place(self.tcx, self.typeck_results); - *match_place = Some(place); - } - } else { + )))) = self.local_decls[local].local_info else { bug!("Let binding to non-user variable.") + }; + // `try_upvars_resolved` may fail if it is unable to resolve the given + // `PlaceBuilder` inside a closure. In this case, we don't want to include + // a scrutinee place. `scrutinee_place_builder` will fail for destructured + // assignments. This is because a closure only captures the precise places + // that it will read and as a result a closure may not capture the entire + // tuple/struct and rather have individual places that will be read in the + // final MIR. + // Example: + // ``` + // let foo = (0, 1); + // let c = || { + // let (v1, v2) = foo; + // }; + // ``` + if let Ok(match_pair_resolved) = + initializer.clone().try_upvars_resolved(self.tcx, self.typeck_results) + { + let place = match_pair_resolved.into_place(self.tcx, self.typeck_results); + *match_place = Some(place); } } // All of the subcandidates should bind the same locals, so we diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index f4bf28bfa5ce2..49cd21c2137b7 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -227,16 +227,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let target_blocks = make_target_blocks(self); let terminator = if *switch_ty.kind() == ty::Bool { assert!(!options.is_empty() && options.len() <= 2); - if let [first_bb, second_bb] = *target_blocks { - let (true_bb, false_bb) = match options[0] { - 1 => (first_bb, second_bb), - 0 => (second_bb, first_bb), - v => span_bug!(test.span, "expected boolean value but got {:?}", v), - }; - TerminatorKind::if_(self.tcx, Operand::Copy(place), true_bb, false_bb) - } else { + let [first_bb, second_bb] = *target_blocks else { bug!("`TestKind::SwitchInt` on `bool` should have two targets") - } + }; + let (true_bb, false_bb) = match options[0] { + 1 => (first_bb, second_bb), + 0 => (second_bb, first_bb), + v => span_bug!(test.span, "expected boolean value but got {:?}", v), + }; + TerminatorKind::if_(self.tcx, Operand::Copy(place), true_bb, false_bb) } else { // The switch may be inexhaustive so we have a catch all block debug_assert_eq!(options.len() + 1, target_blocks.len()); @@ -285,24 +284,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let hi = self.literal_operand(test.span, hi); let val = Operand::Copy(place); - if let [success, fail] = *target_blocks { - self.compare( - block, - lower_bound_success, - fail, - source_info, - BinOp::Le, - lo, - val.clone(), - ); - let op = match *end { - RangeEnd::Included => BinOp::Le, - RangeEnd::Excluded => BinOp::Lt, - }; - self.compare(lower_bound_success, success, fail, source_info, op, val, hi); - } else { + let [success, fail] = *target_blocks else { bug!("`TestKind::Range` should have two target blocks"); - } + }; + self.compare( + block, + lower_bound_success, + fail, + source_info, + BinOp::Le, + lo, + val.clone(), + ); + let op = match *end { + RangeEnd::Included => BinOp::Le, + RangeEnd::Excluded => BinOp::Lt, + }; + self.compare(lower_bound_success, success, fail, source_info, op, val, hi); } TestKind::Len { len, op } => { @@ -317,21 +315,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // expected = let expected = self.push_usize(block, source_info, len); - if let [true_bb, false_bb] = *target_blocks { - // result = actual == expected OR result = actual < expected - // branch based on result - self.compare( - block, - true_bb, - false_bb, - source_info, - op, - Operand::Move(actual), - Operand::Move(expected), - ); - } else { + let [true_bb, false_bb] = *target_blocks else { bug!("`TestKind::Len` should have two target blocks"); - } + }; + // result = actual == expected OR result = actual < expected + // branch based on result + self.compare( + block, + true_bb, + false_bb, + source_info, + op, + Operand::Move(actual), + Operand::Move(expected), + ); } } } @@ -459,16 +456,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); self.diverge_from(block); - if let [success_block, fail_block] = *make_target_blocks(self) { - // check the result - self.cfg.terminate( - eq_block, - source_info, - TerminatorKind::if_(self.tcx, Operand::Move(eq_result), success_block, fail_block), - ); - } else { + let [success_block, fail_block] = *make_target_blocks(self) else { bug!("`TestKind::Eq` should have two target blocks") - } + }; + // check the result + self.cfg.terminate( + eq_block, + source_info, + TerminatorKind::if_(self.tcx, Operand::Move(eq_result), success_block, fail_block), + ); } /// Given that we are performing `test` against `test_place`, this job diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 419ea9cced0d7..62ed104aef37c 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -4,7 +4,7 @@ use rustc_ast::token; use rustc_ast::{ self as ast, Attribute, GenericBounds, GenericParam, GenericParamKind, WhereClause, }; -use rustc_errors::PResult; +use rustc_errors::{Applicability, PResult}; use rustc_span::symbol::kw; impl<'a> Parser<'a> { @@ -256,7 +256,21 @@ impl<'a> Parser<'a> { break; } - if !self.eat(&token::Comma) { + let prev_token = self.prev_token.span; + let ate_comma = self.eat(&token::Comma); + + if self.eat_keyword_noexpect(kw::Where) { + let msg = &format!("cannot define duplicate `where` clauses on an item"); + let mut err = self.struct_span_err(self.token.span, msg); + err.span_label(lo, "previous `where` clause starts here"); + err.span_suggestion_verbose( + prev_token.shrink_to_hi().to(self.prev_token.span), + "consider joining the two `where` clauses into one", + ",".to_owned(), + Applicability::MaybeIncorrect, + ); + err.emit(); + } else if !ate_comma { break; } } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 06849b3125683..93f5d79c0db13 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1221,7 +1221,7 @@ impl<'a> Parser<'a> { let struct_def = if this.check(&token::OpenDelim(token::Brace)) { // Parse a struct variant. - let (fields, recovered) = this.parse_record_struct_body("struct")?; + let (fields, recovered) = this.parse_record_struct_body("struct", false)?; VariantData::Struct(fields, recovered) } else if this.check(&token::OpenDelim(token::Paren)) { VariantData::Tuple(this.parse_tuple_struct_body()?, DUMMY_NODE_ID) @@ -1275,7 +1275,8 @@ impl<'a> Parser<'a> { VariantData::Unit(DUMMY_NODE_ID) } else { // If we see: `struct Foo where T: Copy { ... }` - let (fields, recovered) = self.parse_record_struct_body("struct")?; + let (fields, recovered) = + self.parse_record_struct_body("struct", generics.where_clause.has_where_token)?; VariantData::Struct(fields, recovered) } // No `where` so: `struct Foo;` @@ -1283,7 +1284,8 @@ impl<'a> Parser<'a> { VariantData::Unit(DUMMY_NODE_ID) // Record-style struct definition } else if self.token == token::OpenDelim(token::Brace) { - let (fields, recovered) = self.parse_record_struct_body("struct")?; + let (fields, recovered) = + self.parse_record_struct_body("struct", generics.where_clause.has_where_token)?; VariantData::Struct(fields, recovered) // Tuple-style struct definition with optional where-clause. } else if self.token == token::OpenDelim(token::Paren) { @@ -1313,10 +1315,12 @@ impl<'a> Parser<'a> { let vdata = if self.token.is_keyword(kw::Where) { generics.where_clause = self.parse_where_clause()?; - let (fields, recovered) = self.parse_record_struct_body("union")?; + let (fields, recovered) = + self.parse_record_struct_body("union", generics.where_clause.has_where_token)?; VariantData::Struct(fields, recovered) } else if self.token == token::OpenDelim(token::Brace) { - let (fields, recovered) = self.parse_record_struct_body("union")?; + let (fields, recovered) = + self.parse_record_struct_body("union", generics.where_clause.has_where_token)?; VariantData::Struct(fields, recovered) } else { let token_str = super::token_descr(&self.token); @@ -1332,6 +1336,7 @@ impl<'a> Parser<'a> { fn parse_record_struct_body( &mut self, adt_ty: &str, + parsed_where: bool, ) -> PResult<'a, (Vec, /* recovered */ bool)> { let mut fields = Vec::new(); let mut recovered = false; @@ -1353,9 +1358,19 @@ impl<'a> Parser<'a> { self.eat(&token::CloseDelim(token::Brace)); } else { let token_str = super::token_descr(&self.token); - let msg = &format!("expected `where`, or `{{` after struct name, found {}", token_str); + let msg = &format!( + "expected {}`{{` after struct name, found {}", + if parsed_where { "" } else { "`where`, or " }, + token_str + ); let mut err = self.struct_span_err(self.token.span, msg); - err.span_label(self.token.span, "expected `where`, or `{` after struct name"); + err.span_label( + self.token.span, + format!( + "expected {}`{{` after struct name", + if parsed_where { "" } else { "`where`, or " } + ), + ); return Err(err); } diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index 8b4ab77dffb5f..a3f7e84b1d524 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -47,11 +47,10 @@ use rls_data::{ use tracing::{debug, error}; +#[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5213 macro_rules! down_cast_data { ($id:ident, $kind:ident, $sp:expr) => { - let $id = if let super::Data::$kind(data) = $id { - data - } else { + let super::Data::$kind($id) = $id else { span_bug!($sp, "unexpected data kind: {:?}", $id); }; }; diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs index c14b459570f5e..2eebddb47df5c 100644 --- a/compiler/rustc_save_analysis/src/lib.rs +++ b/compiler/rustc_save_analysis/src/lib.rs @@ -1,6 +1,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(if_let_guard)] #![feature(nll)] +#![feature(let_else)] #![recursion_limit = "256"] #![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 0e1dbc53806ff..82cda5a2f2e73 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -865,14 +865,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), .. }) => { - // We have a situation like `while Some(0) = value.get(0) {`, where `while let` - // was more likely intended. - err.span_suggestion_verbose( - expr.span.shrink_to_lo(), - "you might have meant to use pattern destructuring", - "let ".to_string(), - Applicability::MachineApplicable, - ); + // Check if our lhs is a child of the condition of a while loop + let expr_is_ancestor = std::iter::successors(Some(lhs.hir_id), |id| { + self.tcx.hir().find_parent_node(*id) + }) + .take_while(|id| *id != parent) + .any(|id| id == expr.hir_id); + // if it is, then we have a situation like `while Some(0) = value.get(0) {`, + // where `while let` was more likely intended. + if expr_is_ancestor { + err.span_suggestion_verbose( + expr.span.shrink_to_lo(), + "you might have meant to use pattern destructuring", + "let ".to_string(), + Applicability::MachineApplicable, + ); + } break; } hir::Node::Item(_) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 7d209accec9b5..95404b33822e3 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1427,15 +1427,25 @@ fn normalize<'tcx>(cx: &mut DocContext<'tcx>, ty: Ty<'_>) -> Option> { return None; } + use crate::rustc_trait_selection::infer::TyCtxtInferExt; + use crate::rustc_trait_selection::traits::query::normalize::AtExt; + use rustc_middle::traits::ObligationCause; + // Try to normalize `::T` to a type let lifted = ty.lift_to_tcx(cx.tcx).unwrap(); - match cx.tcx.try_normalize_erasing_regions(cx.param_env, lifted) { + let normalized = cx.tcx.infer_ctxt().enter(|infcx| { + infcx + .at(&ObligationCause::dummy(), cx.param_env) + .normalize(lifted) + .map(|resolved| infcx.resolve_vars_if_possible(resolved.value)) + }); + match normalized { Ok(normalized_value) => { - trace!("normalized {:?} to {:?}", ty, normalized_value); + debug!("normalized {:?} to {:?}", ty, normalized_value); Some(normalized_value) } Err(err) => { - info!("failed to normalize {:?}: {:?}", ty, err); + debug!("failed to normalize {:?}: {:?}", ty, err); None } } diff --git a/src/test/rustdoc/lifetime-name.rs b/src/test/rustdoc/lifetime-name.rs new file mode 100644 index 0000000000000..5d30a745a61a6 --- /dev/null +++ b/src/test/rustdoc/lifetime-name.rs @@ -0,0 +1,5 @@ +#![crate_name = "foo"] + +// @has 'foo/type.Resolutions.html' +// @has - '//*[@class="rust typedef"]' "pub type Resolutions<'tcx> = &'tcx u8;" +pub type Resolutions<'tcx> = &'tcx u8; diff --git a/src/test/ui/borrowck/issue-93093.rs b/src/test/ui/borrowck/issue-93093.rs new file mode 100644 index 0000000000000..f4db5ecafac40 --- /dev/null +++ b/src/test/ui/borrowck/issue-93093.rs @@ -0,0 +1,14 @@ +// edition:2018 +struct S { + foo: usize, +} +impl S { + async fn bar(&self) { //~ HELP consider changing this to be a mutable reference + //~| SUGGESTION &mut self + self.foo += 1; //~ ERROR cannot assign to `self.foo`, which is behind a `&` reference [E0594] + } +} + +fn main() { + S { foo: 1 }.bar(); +} diff --git a/src/test/ui/borrowck/issue-93093.stderr b/src/test/ui/borrowck/issue-93093.stderr new file mode 100644 index 0000000000000..031128af47655 --- /dev/null +++ b/src/test/ui/borrowck/issue-93093.stderr @@ -0,0 +1,12 @@ +error[E0594]: cannot assign to `self.foo`, which is behind a `&` reference + --> $DIR/issue-93093.rs:8:9 + | +LL | async fn bar(&self) { + | ----- help: consider changing this to be a mutable reference: `&mut self` +LL | +LL | self.foo += 1; + | ^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be written + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0594`. diff --git a/src/test/ui/parser/bad-struct-following-where.rs b/src/test/ui/parser/bad-struct-following-where.rs new file mode 100644 index 0000000000000..823880b1b42c6 --- /dev/null +++ b/src/test/ui/parser/bad-struct-following-where.rs @@ -0,0 +1,2 @@ +struct A where T: Sized ! +//~^ ERROR expected `{` after struct name, found diff --git a/src/test/ui/parser/bad-struct-following-where.stderr b/src/test/ui/parser/bad-struct-following-where.stderr new file mode 100644 index 0000000000000..bb79776dc8459 --- /dev/null +++ b/src/test/ui/parser/bad-struct-following-where.stderr @@ -0,0 +1,8 @@ +error: expected `{` after struct name, found `!` + --> $DIR/bad-struct-following-where.rs:1:25 + | +LL | struct A where T: Sized ! + | ^ expected `{` after struct name + +error: aborting due to previous error + diff --git a/src/test/ui/parser/duplicate-where-clauses.rs b/src/test/ui/parser/duplicate-where-clauses.rs new file mode 100644 index 0000000000000..9eb2ffb06f02d --- /dev/null +++ b/src/test/ui/parser/duplicate-where-clauses.rs @@ -0,0 +1,19 @@ +struct A where (): Sized where (): Sized {} +//~^ ERROR cannot define duplicate `where` clauses on an item + +fn b() where (): Sized where (): Sized {} +//~^ ERROR cannot define duplicate `where` clauses on an item + +enum C where (): Sized where (): Sized {} +//~^ ERROR cannot define duplicate `where` clauses on an item + +struct D where (): Sized, where (): Sized {} +//~^ ERROR cannot define duplicate `where` clauses on an item + +fn e() where (): Sized, where (): Sized {} +//~^ ERROR cannot define duplicate `where` clauses on an item + +enum F where (): Sized, where (): Sized {} +//~^ ERROR cannot define duplicate `where` clauses on an item + +fn main() {} diff --git a/src/test/ui/parser/duplicate-where-clauses.stderr b/src/test/ui/parser/duplicate-where-clauses.stderr new file mode 100644 index 0000000000000..8250d4f1e0568 --- /dev/null +++ b/src/test/ui/parser/duplicate-where-clauses.stderr @@ -0,0 +1,80 @@ +error: cannot define duplicate `where` clauses on an item + --> $DIR/duplicate-where-clauses.rs:1:32 + | +LL | struct A where (): Sized where (): Sized {} + | - ^ + | | + | previous `where` clause starts here + | +help: consider joining the two `where` clauses into one + | +LL | struct A where (): Sized, (): Sized {} + | ~ + +error: cannot define duplicate `where` clauses on an item + --> $DIR/duplicate-where-clauses.rs:4:30 + | +LL | fn b() where (): Sized where (): Sized {} + | - ^ + | | + | previous `where` clause starts here + | +help: consider joining the two `where` clauses into one + | +LL | fn b() where (): Sized, (): Sized {} + | ~ + +error: cannot define duplicate `where` clauses on an item + --> $DIR/duplicate-where-clauses.rs:7:30 + | +LL | enum C where (): Sized where (): Sized {} + | - ^ + | | + | previous `where` clause starts here + | +help: consider joining the two `where` clauses into one + | +LL | enum C where (): Sized, (): Sized {} + | ~ + +error: cannot define duplicate `where` clauses on an item + --> $DIR/duplicate-where-clauses.rs:10:33 + | +LL | struct D where (): Sized, where (): Sized {} + | - ^ + | | + | previous `where` clause starts here + | +help: consider joining the two `where` clauses into one + | +LL | struct D where (): Sized, (): Sized {} + | ~ + +error: cannot define duplicate `where` clauses on an item + --> $DIR/duplicate-where-clauses.rs:13:31 + | +LL | fn e() where (): Sized, where (): Sized {} + | - ^ + | | + | previous `where` clause starts here + | +help: consider joining the two `where` clauses into one + | +LL | fn e() where (): Sized, (): Sized {} + | ~ + +error: cannot define duplicate `where` clauses on an item + --> $DIR/duplicate-where-clauses.rs:16:31 + | +LL | enum F where (): Sized, where (): Sized {} + | - ^ + | | + | previous `where` clause starts here + | +help: consider joining the two `where` clauses into one + | +LL | enum F where (): Sized, (): Sized {} + | ~ + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/typeck/issue-93486.rs b/src/test/ui/typeck/issue-93486.rs new file mode 100644 index 0000000000000..f8f98d5c1c7dc --- /dev/null +++ b/src/test/ui/typeck/issue-93486.rs @@ -0,0 +1,6 @@ +fn main() { + while let 1 = 1 { + vec![].last_mut().unwrap() = 3_u8; + //~^ ERROR invalid left-hand side of assignment + } +} diff --git a/src/test/ui/typeck/issue-93486.stderr b/src/test/ui/typeck/issue-93486.stderr new file mode 100644 index 0000000000000..70b5b63f1cba7 --- /dev/null +++ b/src/test/ui/typeck/issue-93486.stderr @@ -0,0 +1,11 @@ +error[E0070]: invalid left-hand side of assignment + --> $DIR/issue-93486.rs:3:36 + | +LL | vec![].last_mut().unwrap() = 3_u8; + | -------------------------- ^ + | | + | cannot assign to this expression + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0070`.