diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index 9444fffc331c7..7cbfe143b4d83 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -1,8 +1,8 @@ use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext}; -use rustc_ast::{AttrVec, Block, BlockCheckMode, Expr, Local, LocalKind, Stmt, StmtKind}; +use rustc_ast::{Block, BlockCheckMode, Local, LocalKind, Stmt, StmtKind}; use rustc_hir as hir; use rustc_session::parse::feature_err; -use rustc_span::{sym, DesugaringKind}; +use rustc_span::sym; use smallvec::SmallVec; @@ -36,21 +36,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { match s.kind { StmtKind::Local(ref local) => { let hir_id = self.lower_node_id(s.id); - match &local.kind { - LocalKind::InitElse(init, els) => { - let e = self.lower_let_else(hir_id, local, init, els, tail); - expr = Some(e); - // remaining statements are in let-else expression - break; - } - _ => { - let local = self.lower_local(local); - self.alias_attrs(hir_id, local.hir_id); - let kind = hir::StmtKind::Local(local); - let span = self.lower_span(s.span); - stmts.push(hir::Stmt { hir_id, kind, span }); - } - } + let local = self.lower_local(local); + self.alias_attrs(hir_id, local.hir_id); + let kind = hir::StmtKind::Local(local); + let span = self.lower_span(s.span); + stmts.push(hir::Stmt { hir_id, kind, span }); } StmtKind::Item(ref it) => { stmts.extend(self.lower_item_ref(it).into_iter().enumerate().map( @@ -101,10 +91,24 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let init = l.kind.init().map(|init| self.lower_expr(init)); let hir_id = self.lower_node_id(l.id); let pat = self.lower_pat(&l.pat); + let els = if let LocalKind::InitElse(_, els) = &l.kind { + if !self.tcx.features().let_else { + feature_err( + &self.tcx.sess.parse_sess, + sym::let_else, + l.span, + "`let...else` statements are unstable", + ) + .emit(); + } + Some(self.lower_block(els, false)) + } else { + None + }; let span = self.lower_span(l.span); let source = hir::LocalSource::Normal; self.lower_attrs(hir_id, &l.attrs); - self.arena.alloc(hir::Local { hir_id, ty, pat, init, span, source }) + self.arena.alloc(hir::Local { hir_id, ty, pat, init, els, span, source }) } fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode { @@ -115,59 +119,4 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } } - - fn lower_let_else( - &mut self, - stmt_hir_id: hir::HirId, - local: &Local, - init: &Expr, - els: &Block, - tail: &[Stmt], - ) -> &'hir hir::Expr<'hir> { - let ty = local - .ty - .as_ref() - .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Variable))); - let span = self.lower_span(local.span); - let span = self.mark_span_with_reason(DesugaringKind::LetElse, span, None); - let init = self.lower_expr(init); - let local_hir_id = self.lower_node_id(local.id); - self.lower_attrs(local_hir_id, &local.attrs); - let let_expr = { - let lex = self.arena.alloc(hir::Let { - hir_id: local_hir_id, - pat: self.lower_pat(&local.pat), - ty, - init, - span, - }); - self.arena.alloc(self.expr(span, hir::ExprKind::Let(lex), AttrVec::new())) - }; - let then_expr = { - let (stmts, expr) = self.lower_stmts(tail); - let block = self.block_all(span, stmts, expr); - self.arena.alloc(self.expr_block(block, AttrVec::new())) - }; - let else_expr = { - let block = self.lower_block(els, false); - self.arena.alloc(self.expr_block(block, AttrVec::new())) - }; - self.alias_attrs(let_expr.hir_id, local_hir_id); - self.alias_attrs(else_expr.hir_id, local_hir_id); - let if_expr = self.arena.alloc(hir::Expr { - hir_id: stmt_hir_id, - span, - kind: hir::ExprKind::If(let_expr, then_expr, Some(else_expr)), - }); - if !self.tcx.features().let_else { - feature_err( - &self.tcx.sess.parse_sess, - sym::let_else, - local.span, - "`let...else` statements are unstable", - ) - .emit(); - } - if_expr - } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index fdf60e60914a4..4da3096f7c3aa 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2146,7 +2146,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { debug_assert!(!a.is_empty()); self.attrs.insert(hir_id.local_id, a); } - let local = hir::Local { hir_id, init, pat, source, span: self.lower_span(span), ty: None }; + let local = hir::Local { + hir_id, + init, + pat, + els: None, + source, + span: self.lower_span(span), + ty: None, + }; self.stmt(span, hir::StmtKind::Local(self.arena.alloc(local))) } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 04f585df34cc2..ed874ae829b46 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1316,6 +1316,8 @@ pub struct Local<'hir> { pub ty: Option<&'hir Ty<'hir>>, /// Initializer expression to set the value, if any. pub init: Option<&'hir Expr<'hir>>, + /// Else block for a `let...else` binding. + pub els: Option<&'hir Block<'hir>>, pub hir_id: HirId, pub span: Span, /// Can be `ForLoopDesugar` if the `let` statement is part of a `for` loop diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 531d9f1404021..b5d9769c578a5 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -472,6 +472,9 @@ pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local<'v>) { walk_list!(visitor, visit_expr, &local.init); visitor.visit_id(local.hir_id); visitor.visit_pat(&local.pat); + if let Some(els) = local.els { + visitor.visit_block(els); + } walk_list!(visitor, visit_ty, &local.ty); } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 50acb0270b0be..e3c97ec357e87 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -883,7 +883,12 @@ impl<'a> State<'a> { self.ann.post(self, AnnNode::SubItem(ii.hir_id())) } - pub fn print_local(&mut self, init: Option<&hir::Expr<'_>>, decl: impl Fn(&mut Self)) { + pub fn print_local( + &mut self, + init: Option<&hir::Expr<'_>>, + els: Option<&hir::Block<'_>>, + decl: impl Fn(&mut Self), + ) { self.space_if_not_bol(); self.ibox(INDENT_UNIT); self.word_nbsp("let"); @@ -897,6 +902,13 @@ impl<'a> State<'a> { self.word_space("="); self.print_expr(init); } + + if let Some(els) = els { + self.nbsp(); + self.word_space("else"); + self.print_block(els); + } + self.end() } @@ -904,7 +916,7 @@ impl<'a> State<'a> { self.maybe_print_comment(st.span.lo()); match st.kind { hir::StmtKind::Local(loc) => { - self.print_local(loc.init, |this| this.print_local_decl(loc)); + self.print_local(loc.init, loc.els, |this| this.print_local_decl(loc)); } hir::StmtKind::Item(item) => self.ann.nested(self, Nested::Item(item)), hir::StmtKind::Expr(expr) => { @@ -1404,7 +1416,7 @@ impl<'a> State<'a> { // Print `let _t = $init;`: let temp = Ident::from_str("_t"); - self.print_local(Some(init), |this| this.print_ident(temp)); + self.print_local(Some(init), None, |this| this.print_ident(temp)); self.word(";"); // Print `_t`: diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 8c5b4e2dc96f9..7ac3157e7a1de 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -411,8 +411,14 @@ LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, size_t AsmStringLen, extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints, size_t ConstraintsLen) { +#if LLVM_VERSION_LT(15, 0) return InlineAsm::Verify(unwrap(Ty), StringRef(Constraints, ConstraintsLen)); +#else + // llvm::Error converts to true if it is an error. + return !llvm::errorToBool(InlineAsm::verify( + unwrap(Ty), StringRef(Constraints, ConstraintsLen))); +#endif } extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm, diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 3fe6394ad7e9c..3e5f6bb8f0bcf 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -182,6 +182,9 @@ pub enum StmtKind<'tcx> { /// `let pat: ty = ` initializer: Option, + /// `let pat: ty = else { } + else_block: Option, + /// The lint level for this `let` statement. lint_level: LintLevel, }, diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index c8d09875c2896..97249fdd17563 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -167,11 +167,15 @@ pub fn walk_stmt<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, stmt: &Stm init_scope: _, ref pattern, lint_level: _, + else_block, } => { if let Some(init) = initializer { visitor.visit_expr(&visitor.thir()[*init]); } visitor.visit_pat(pattern); + if let Some(block) = else_block { + visitor.visit_block(block) + } } } } diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs index a83328c0cabc6..cb8be51a08562 100644 --- a/compiler/rustc_mir_build/src/build/block.rs +++ b/compiler/rustc_mir_build/src/build/block.rs @@ -99,6 +99,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ref pattern, initializer, lint_level, + else_block, } => { let ignores_expr_result = matches!(*pattern.kind, PatKind::Wild); this.block_context.push(BlockFrame::Statement { ignores_expr_result }); @@ -124,18 +125,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { |this| { let scope = (*init_scope, source_info); this.in_scope(scope, *lint_level, |this| { - this.declare_bindings( - visibility_scope, - remainder_span, - pattern, - ArmHasGuard(false), - Some((None, initializer_span)), - ); - this.expr_into_pattern(block, pattern.clone(), init) + if let Some(else_block) = else_block { + this.ast_let_else( + block, + init, + initializer_span, + else_block, + visibility_scope, + remainder_span, + pattern, + ) + } else { + this.declare_bindings( + visibility_scope, + remainder_span, + pattern, + ArmHasGuard(false), + Some((None, initializer_span)), + ); + this.expr_into_pattern(block, pattern.clone(), init) // irrefutable pattern + } }) - } + }, ) - ); + ) } else { let scope = (*init_scope, source_info); unpack!(this.in_scope(scope, *lint_level, |this| { diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 1628f1a4b850b..7067a48b783ec 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1615,7 +1615,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // those N possible outcomes, create a (initially empty) // vector of candidates. Those are the candidates that still // apply if the test has that particular outcome. - debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair); + debug!("test_candidates: test={:?} match_pair={:?}", test, match_pair); let mut target_candidates: Vec>> = vec![]; target_candidates.resize_with(test.targets(), Default::default); @@ -1635,8 +1635,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } // at least the first candidate ought to be tested assert!(total_candidate_count > candidates.len()); - debug!("tested_candidates: {}", total_candidate_count - candidates.len()); - debug!("untested_candidates: {}", candidates.len()); + debug!("test_candidates: tested_candidates: {}", total_candidate_count - candidates.len()); + debug!("test_candidates: untested_candidates: {}", candidates.len()); // HACK(matthewjasper) This is a closure so that we can let the test // create its blocks before the rest of the match. This currently @@ -2274,4 +2274,75 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug!("declare_binding: vars={:?}", locals); self.var_indices.insert(var_id, locals); } + + pub(crate) fn ast_let_else( + &mut self, + mut block: BasicBlock, + init: &Expr<'tcx>, + initializer_span: Span, + else_block: &Block, + visibility_scope: Option, + remainder_span: Span, + pattern: &Pat<'tcx>, + ) -> BlockAnd<()> { + let scrutinee = unpack!(block = self.lower_scrutinee(block, init, initializer_span)); + let pat = Pat { ty: init.ty, span: else_block.span, kind: Box::new(PatKind::Wild) }; + let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false); + self.declare_bindings( + visibility_scope, + remainder_span, + pattern, + ArmHasGuard(false), + Some((None, initializer_span)), + ); + let mut candidate = Candidate::new(scrutinee.clone(), pattern, false); + let fake_borrow_temps = self.lower_match_tree( + block, + initializer_span, + pattern.span, + false, + &mut [&mut candidate, &mut wildcard], + ); + // This block is for the matching case + let matching = self.bind_pattern( + self.source_info(pattern.span), + candidate, + None, + &fake_borrow_temps, + initializer_span, + None, + None, + None, + ); + // This block is for the failure case + let failure = self.bind_pattern( + self.source_info(else_block.span), + wildcard, + None, + &fake_borrow_temps, + initializer_span, + None, + None, + None, + ); + // This place is not really used because this destination place + // should never be used to take values at the end of the failure + // block. + let dummy_place = Place { local: RETURN_PLACE, projection: ty::List::empty() }; + let failure_block; + unpack!( + failure_block = self.ast_block( + dummy_place, + failure, + else_block, + self.source_info(else_block.span), + ) + ); + self.cfg.terminate( + failure_block, + self.source_info(else_block.span), + TerminatorKind::Unreachable, + ); + matching.unit() + } } diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs index 59750d5d0b88e..dccaa61ed89d4 100644 --- a/compiler/rustc_mir_build/src/thir/cx/block.rs +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -74,6 +74,8 @@ impl<'tcx> Cx<'tcx> { )), }; + let else_block = local.els.map(|els| self.mirror_block(els)); + let mut pattern = self.pattern_from_hir(local.pat); debug!(?pattern); @@ -110,6 +112,7 @@ impl<'tcx> Cx<'tcx> { }, pattern, initializer: local.init.map(|init| self.mirror_expr(init)), + else_block, lint_level: LintLevel::Explicit(local.hir_id), }, opt_destruction_scope: opt_dxn_ext, diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 75fd156ebfdf3..5bd1fad0bcb9f 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -21,7 +21,7 @@ use rustc_session::lint::builtin::{ }; use rustc_session::Session; use rustc_span::source_map::Spanned; -use rustc_span::{BytePos, DesugaringKind, ExpnKind, Span}; +use rustc_span::{BytePos, Span}; pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: DefId) { let body_id = match def_id.as_local() { @@ -77,6 +77,10 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> { fn visit_local(&mut self, loc: &'tcx hir::Local<'tcx>) { intravisit::walk_local(self, loc); + let els = loc.els; + if let Some(init) = loc.init && els.is_some() { + self.check_let(&loc.pat, init, loc.span); + } let (msg, sp) = match loc.source { hir::LocalSource::Normal => ("local binding", Some(loc.span)), @@ -84,7 +88,9 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> { hir::LocalSource::AwaitDesugar => ("`await` future binding", None), hir::LocalSource::AssignDesugar(_) => ("destructuring assignment binding", None), }; - self.check_irrefutable(&loc.pat, msg, sp); + if els.is_none() { + self.check_irrefutable(&loc.pat, msg, sp); + } } fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { @@ -1125,17 +1131,16 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option) -> L }) if Some(*hir_id) == pat_id => { return LetSource::IfLetGuard; } - hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Let(..), span, .. }) => { - let expn_data = span.ctxt().outer_expn_data(); - if let ExpnKind::Desugaring(DesugaringKind::LetElse) = expn_data.kind { - return LetSource::LetElse(expn_data.call_site); - } - } _ => {} } let parent_parent = hir.get_parent_node(parent); let parent_parent_node = hir.get(parent_parent); + if let hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_), span, .. }) = + parent_parent_node + { + return LetSource::LetElse(*span); + } let parent_parent_parent = hir.get_parent_node(parent_parent); let parent_parent_parent_parent = hir.get_parent_node(parent_parent_parent); diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs index efa45883eab4b..c48aa9a90efbe 100644 --- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs @@ -102,7 +102,7 @@ fn is_needs_drop_and_init<'tcx>( let field_needs_drop_and_init = |(f, f_ty, mpi)| { let child = move_path_children_matching(move_data, mpi, |x| x.is_field_to(f)); let Some(mpi) = child else { - return f_ty.needs_drop(tcx, param_env); + return Ty::needs_drop(f_ty, tcx, param_env); }; is_needs_drop_and_init(tcx, param_env, maybe_inits, move_data, f_ty, mpi) diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 0070c0699a4aa..eed3e1579a231 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -278,7 +278,7 @@ impl<'tcx> IrMaps<'tcx> { pats.extend(inner_pat.iter()); } Struct(_, fields, _) => { - let (short, not_short): (Vec<&_>, Vec<&_>) = + let (short, not_short): (Vec<_>, _) = fields.iter().partition(|f| f.is_shorthand); shorthand_field_ids.extend(short.iter().map(|f| f.pat.hir_id)); pats.extend(not_short.iter().map(|f| f.pat)); @@ -298,7 +298,7 @@ impl<'tcx> IrMaps<'tcx> { } } - return shorthand_field_ids; + shorthand_field_ids } fn add_from_pat(&mut self, pat: &hir::Pat<'tcx>) { @@ -368,6 +368,9 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { self.add_from_pat(&local.pat); + if local.els.is_some() { + self.add_live_node_for_node(local.hir_id, ExprNode(local.span, local.hir_id)); + } intravisit::walk_local(self, local); } @@ -800,8 +803,40 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // initialization, which is mildly more complex than checking // once at the func header but otherwise equivalent. - let succ = self.propagate_through_opt_expr(local.init, succ); - self.define_bindings_in_pat(&local.pat, succ) + if let Some(els) = local.els { + // Eventually, `let pat: ty = init else { els };` is mostly equivalent to + // `let (bindings, ...) = match init { pat => (bindings, ...), _ => els };` + // except that extended lifetime applies at the `init` location. + // + // (e) + // | + // v + // (expr) + // / \ + // | | + // v v + // bindings els + // | + // v + // ( succ ) + // + if let Some(init) = local.init { + let else_ln = self.propagate_through_block(els, succ); + let ln = self.live_node(local.hir_id, local.span); + self.init_from_succ(ln, succ); + self.merge_from_succ(ln, else_ln); + let succ = self.propagate_through_expr(init, ln); + self.define_bindings_in_pat(&local.pat, succ) + } else { + span_bug!( + stmt.span, + "variable is uninitialized but an unexpected else branch is found" + ) + } + } else { + let succ = self.propagate_through_opt_expr(local.init, succ); + self.define_bindings_in_pat(&local.pat, succ) + } } hir::StmtKind::Item(..) => succ, hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => { @@ -1121,7 +1156,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // (rvalue) || (rvalue) // | || | // v || v - // (write of place) || (place components) + // (write of place) || (place components) // | || | // v || v // (succ) || (succ) diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index 6eb2f2d929d57..a4175f4c5f377 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -82,14 +82,7 @@ impl<'tcx> DumpVisitor<'tcx> { pub fn new(save_ctxt: SaveContext<'tcx>) -> DumpVisitor<'tcx> { let span_utils = SpanUtils::new(&save_ctxt.tcx.sess); let dumper = Dumper::new(save_ctxt.config.clone()); - DumpVisitor { - tcx: save_ctxt.tcx, - save_ctxt, - dumper, - span: span_utils, - // mac_defs: FxHashSet::default(), - // macro_calls: FxHashSet::default(), - } + DumpVisitor { tcx: save_ctxt.tcx, save_ctxt, dumper, span: span_utils } } pub fn analysis(&self) -> &rls_data::Analysis { @@ -1425,9 +1418,10 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { self.process_macro_use(l.span); self.process_var_decl(&l.pat); - // Just walk the initializer and type (don't want to walk the pattern again). + // Just walk the initializer, the else branch and type (don't want to walk the pattern again). walk_list!(self, visit_ty, &l.ty); walk_list!(self, visit_expr, &l.init); + walk_list!(self, visit_block, l.els); } fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) { diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 3df4dfb74b319..29879c48b04ae 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1141,7 +1141,6 @@ pub enum DesugaringKind { Async, Await, ForLoop, - LetElse, WhileLoop, } @@ -1157,7 +1156,6 @@ impl DesugaringKind { DesugaringKind::YeetExpr => "`do yeet` expression", DesugaringKind::OpaqueTy => "`impl Trait`", DesugaringKind::ForLoop => "`for` loop", - DesugaringKind::LetElse => "`let...else`", DesugaringKind::WhileLoop => "`while` loop", } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 33585de600130..b08fc48218602 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1311,7 +1311,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { visitor.visit_body(&body); let typeck_results = self.in_progress_typeck_results.map(|t| t.borrow()).unwrap(); - let Some(liberated_sig) = typeck_results.liberated_fn_sigs().get(fn_hir_id) else { return false; }; + let Some(liberated_sig) = typeck_results.liberated_fn_sigs().get(fn_hir_id).copied() else { return false; }; let ret_types = visitor .returns diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 1d3608048f284..30b76b922c77a 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -997,26 +997,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { coerce.coerce(self, &self.misc(sp), then_expr, then_ty); if let Some(else_expr) = opt_else_expr { - let else_ty = if sp.desugaring_kind() == Some(DesugaringKind::LetElse) { - // todo introduce `check_expr_with_expectation(.., Expectation::LetElse)` - // for errors that point to the offending expression rather than the entire block. - // We could use `check_expr_eq_type(.., tcx.types.never)`, but then there is no - // way to detect that the expected type originated from let-else and provide - // a customized error. - let else_ty = self.check_expr(else_expr); - let cause = self.cause(else_expr.span, ObligationCauseCode::LetElse); - - if let Some(mut err) = - self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty) - { - err.emit(); - self.tcx.ty_error() - } else { - else_ty - } - } else { - self.check_expr_with_expectation(else_expr, expected) - }; + let else_ty = self.check_expr_with_expectation(else_expr, expected); let else_diverges = self.diverges.get(); let opt_suggest_box_span = self.opt_suggest_box_span(else_ty, orig_expected); diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index a7c7089234aa9..60ee2233ed9c0 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -1215,6 +1215,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr); let pat_ty = self.node_ty(decl.pat.hir_id); self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, pat_ty); + + if let Some(blk) = decl.els { + let previous_diverges = self.diverges.get(); + let else_ty = self.check_block_with_expected(blk, NoExpectation); + let cause = self.cause(blk.span, ObligationCauseCode::LetElse); + if let Some(mut err) = + self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty) + { + err.emit(); + } + self.diverges.set(previous_diverges); + } } /// Type check a `let` statement. @@ -1236,8 +1248,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let old_has_errors = self.has_errors.replace(false); match stmt.kind { - hir::StmtKind::Local(ref l) => { - self.check_decl_local(&l); + hir::StmtKind::Local(l) => { + self.check_decl_local(l); } // Ignore for now. hir::StmtKind::Item(_) => {} diff --git a/compiler/rustc_typeck/src/check/gather_locals.rs b/compiler/rustc_typeck/src/check/gather_locals.rs index 576dc6f127cbd..8f34a970f6ff7 100644 --- a/compiler/rustc_typeck/src/check/gather_locals.rs +++ b/compiler/rustc_typeck/src/check/gather_locals.rs @@ -16,19 +16,20 @@ pub(super) struct Declaration<'a> { pub ty: Option<&'a hir::Ty<'a>>, pub span: Span, pub init: Option<&'a hir::Expr<'a>>, + pub els: Option<&'a hir::Block<'a>>, } impl<'a> From<&'a hir::Local<'a>> for Declaration<'a> { fn from(local: &'a hir::Local<'a>) -> Self { - let hir::Local { hir_id, pat, ty, span, init, .. } = *local; - Declaration { hir_id, pat, ty, span, init } + let hir::Local { hir_id, pat, ty, span, init, els, source: _ } = *local; + Declaration { hir_id, pat, ty, span, init, els } } } impl<'a> From<&'a hir::Let<'a>> for Declaration<'a> { fn from(let_expr: &'a hir::Let<'a>) -> Self { let hir::Let { hir_id, pat, ty, span, init } = *let_expr; - Declaration { hir_id, pat, ty, span, init: Some(init) } + Declaration { hir_id, pat, ty, span, init: Some(init), els: None } } } @@ -101,7 +102,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { // Add explicitly-declared locals. fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { self.declare(local.into()); - intravisit::walk_local(self, local); + intravisit::walk_local(self, local) } fn visit_let_expr(&mut self, let_expr: &'tcx hir::Let<'tcx>) { diff --git a/compiler/rustc_typeck/src/check/region.rs b/compiler/rustc_typeck/src/check/region.rs index 9f1368a3e0777..a1a92c62ad22f 100644 --- a/compiler/rustc_typeck/src/check/region.rs +++ b/compiler/rustc_typeck/src/check/region.rs @@ -460,6 +460,7 @@ fn resolve_local<'tcx>( visitor: &mut RegionResolutionVisitor<'tcx>, pat: Option<&'tcx hir::Pat<'tcx>>, init: Option<&'tcx hir::Expr<'tcx>>, + els: Option<&'tcx hir::Block<'tcx>>, ) { debug!("resolve_local(pat={:?}, init={:?})", pat, init); @@ -537,13 +538,18 @@ fn resolve_local<'tcx>( } } - // Make sure we visit the initializer first, so expr_and_pat_count remains correct + // Make sure we visit the initializer first, so expr_and_pat_count remains correct. + // The correct order, as shared between generator_interior, drop_ranges and intravisitor, + // is to walk initializer, followed by pattern bindings, finally followed by the `else` block. if let Some(expr) = init { visitor.visit_expr(expr); } if let Some(pat) = pat { visitor.visit_pat(pat); } + if let Some(els) = els { + visitor.visit_block(els); + } /// Returns `true` if `pat` match the `P&` non-terminal. /// @@ -764,7 +770,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { // (i.e., `'static`), which means that after `g` returns, it drops, // and all the associated destruction scope rules apply. self.cx.var_parent = None; - resolve_local(self, None, Some(&body.value)); + resolve_local(self, None, Some(&body.value), None); } if body.generator_kind.is_some() { @@ -791,7 +797,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { resolve_expr(self, ex); } fn visit_local(&mut self, l: &'tcx Local<'tcx>) { - resolve_local(self, Some(&l.pat), l.init); + resolve_local(self, Some(&l.pat), l.init, l.els) } } diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 920e3d578c812..9d7420acd2662 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -2,7 +2,10 @@ //! normal visitor, which just walks the entire body in one shot, the //! `ExprUseVisitor` determines how expressions are being used. +use std::slice::from_ref; + use hir::def::DefKind; +use hir::Expr; // Export these here so that Clippy can use them. pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection}; @@ -252,96 +255,16 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } hir::ExprKind::Let(hir::Let { pat, init, .. }) => { - self.walk_local(init, pat, |t| t.borrow_expr(init, ty::ImmBorrow)); + self.walk_local(init, pat, None, |t| t.borrow_expr(init, ty::ImmBorrow)) } hir::ExprKind::Match(ref discr, arms, _) => { let discr_place = return_if_err!(self.mc.cat_expr(discr)); - - // Matching should not always be considered a use of the place, hence - // discr does not necessarily need to be borrowed. - // We only want to borrow discr if the pattern contain something other - // than wildcards. - let ExprUseVisitor { ref mc, body_owner: _, delegate: _ } = *self; - let mut needs_to_be_read = false; - for arm in arms.iter() { - return_if_err!(mc.cat_pattern(discr_place.clone(), arm.pat, |place, pat| { - match &pat.kind { - PatKind::Binding(.., opt_sub_pat) => { - // If the opt_sub_pat is None, than the binding does not count as - // a wildcard for the purpose of borrowing discr. - if opt_sub_pat.is_none() { - needs_to_be_read = true; - } - } - PatKind::Path(qpath) => { - // A `Path` pattern is just a name like `Foo`. This is either a - // named constant or else it refers to an ADT variant - - let res = self.mc.typeck_results.qpath_res(qpath, pat.hir_id); - match res { - Res::Def(DefKind::Const, _) - | Res::Def(DefKind::AssocConst, _) => { - // Named constants have to be equated with the value - // being matched, so that's a read of the value being matched. - // - // FIXME: We don't actually reads for ZSTs. - needs_to_be_read = true; - } - _ => { - // Otherwise, this is a struct/enum variant, and so it's - // only a read if we need to read the discriminant. - needs_to_be_read |= is_multivariant_adt(place.place.ty()); - } - } - } - PatKind::TupleStruct(..) | PatKind::Struct(..) | PatKind::Tuple(..) => { - // For `Foo(..)`, `Foo { ... }` and `(...)` patterns, check if we are matching - // against a multivariant enum or struct. In that case, we have to read - // the discriminant. Otherwise this kind of pattern doesn't actually - // read anything (we'll get invoked for the `...`, which may indeed - // perform some reads). - - let place_ty = place.place.ty(); - needs_to_be_read |= is_multivariant_adt(place_ty); - } - PatKind::Lit(_) | PatKind::Range(..) => { - // If the PatKind is a Lit or a Range then we want - // to borrow discr. - needs_to_be_read = true; - } - PatKind::Or(_) - | PatKind::Box(_) - | PatKind::Slice(..) - | PatKind::Ref(..) - | PatKind::Wild => { - // If the PatKind is Or, Box, Slice or Ref, the decision is made later - // as these patterns contains subpatterns - // If the PatKind is Wild, the decision is made based on the other patterns being - // examined - } - } - })); - } - - if needs_to_be_read { - self.borrow_expr(discr, ty::ImmBorrow); - } else { - let closure_def_id = match discr_place.place.base { - PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()), - _ => None, - }; - - self.delegate.fake_read( - &discr_place, - FakeReadCause::ForMatchedPlace(closure_def_id), - discr_place.hir_id, - ); - - // We always want to walk the discriminant. We want to make sure, for instance, - // that the discriminant has been initialized. - self.walk_expr(discr); - } + self.maybe_read_scrutinee( + discr, + discr_place.clone(), + arms.iter().map(|arm| arm.pat), + ); // treatment of the discriminant is handled while walking the arms. for arm in arms { @@ -453,8 +376,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { fn walk_stmt(&mut self, stmt: &hir::Stmt<'_>) { match stmt.kind { - hir::StmtKind::Local(hir::Local { pat, init: Some(expr), .. }) => { - self.walk_local(expr, pat, |_| {}); + hir::StmtKind::Local(hir::Local { pat, init: Some(expr), els, .. }) => { + self.walk_local(expr, pat, *els, |_| {}) } hir::StmtKind::Local(_) => {} @@ -470,13 +393,114 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } } - fn walk_local(&mut self, expr: &hir::Expr<'_>, pat: &hir::Pat<'_>, mut f: F) - where + fn maybe_read_scrutinee<'t>( + &mut self, + discr: &Expr<'_>, + discr_place: PlaceWithHirId<'tcx>, + pats: impl Iterator>, + ) { + // Matching should not always be considered a use of the place, hence + // discr does not necessarily need to be borrowed. + // We only want to borrow discr if the pattern contain something other + // than wildcards. + let ExprUseVisitor { ref mc, body_owner: _, delegate: _ } = *self; + let mut needs_to_be_read = false; + for pat in pats { + return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| { + match &pat.kind { + PatKind::Binding(.., opt_sub_pat) => { + // If the opt_sub_pat is None, than the binding does not count as + // a wildcard for the purpose of borrowing discr. + if opt_sub_pat.is_none() { + needs_to_be_read = true; + } + } + PatKind::Path(qpath) => { + // A `Path` pattern is just a name like `Foo`. This is either a + // named constant or else it refers to an ADT variant + + let res = self.mc.typeck_results.qpath_res(qpath, pat.hir_id); + match res { + Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => { + // Named constants have to be equated with the value + // being matched, so that's a read of the value being matched. + // + // FIXME: We don't actually reads for ZSTs. + needs_to_be_read = true; + } + _ => { + // Otherwise, this is a struct/enum variant, and so it's + // only a read if we need to read the discriminant. + needs_to_be_read |= is_multivariant_adt(place.place.ty()); + } + } + } + PatKind::TupleStruct(..) | PatKind::Struct(..) | PatKind::Tuple(..) => { + // For `Foo(..)`, `Foo { ... }` and `(...)` patterns, check if we are matching + // against a multivariant enum or struct. In that case, we have to read + // the discriminant. Otherwise this kind of pattern doesn't actually + // read anything (we'll get invoked for the `...`, which may indeed + // perform some reads). + + let place_ty = place.place.ty(); + needs_to_be_read |= is_multivariant_adt(place_ty); + } + PatKind::Lit(_) | PatKind::Range(..) => { + // If the PatKind is a Lit or a Range then we want + // to borrow discr. + needs_to_be_read = true; + } + PatKind::Or(_) + | PatKind::Box(_) + | PatKind::Slice(..) + | PatKind::Ref(..) + | PatKind::Wild => { + // If the PatKind is Or, Box, Slice or Ref, the decision is made later + // as these patterns contains subpatterns + // If the PatKind is Wild, the decision is made based on the other patterns being + // examined + } + } + })); + } + + if needs_to_be_read { + self.borrow_expr(discr, ty::ImmBorrow); + } else { + let closure_def_id = match discr_place.place.base { + PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()), + _ => None, + }; + + self.delegate.fake_read( + &discr_place, + FakeReadCause::ForMatchedPlace(closure_def_id), + discr_place.hir_id, + ); + + // We always want to walk the discriminant. We want to make sure, for instance, + // that the discriminant has been initialized. + self.walk_expr(discr); + } + } + + fn walk_local( + &mut self, + expr: &hir::Expr<'_>, + pat: &hir::Pat<'_>, + els: Option<&hir::Block<'_>>, + mut f: F, + ) where F: FnMut(&mut Self), { self.walk_expr(expr); let expr_place = return_if_err!(self.mc.cat_expr(expr)); f(self); + if let Some(els) = els { + // borrowing because we need to test the descriminant + self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter()); + self.walk_block(els) + } self.walk_irrefutable_pat(&expr_place, &pat); } @@ -667,7 +691,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { let ExprUseVisitor { ref mc, body_owner: _, ref mut delegate } = *self; return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| { if let PatKind::Binding(_, canonical_id, ..) = pat.kind { - debug!("walk_pat: binding place={:?} pat={:?}", place, pat,); + debug!("walk_pat: binding place={:?} pat={:?}", place, pat); if let Some(bm) = mc.typeck_results.extract_binding_mode(tcx.sess, pat.hir_id, pat.span) { diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 316554808c201..db2ad953f6aa0 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -624,7 +624,7 @@ impl FromWithTcx for Struct { let clean::VariantStruct { struct_type, fields } = struct_; Struct { struct_type: from_ctor_kind(struct_type), - generics: Default::default(), + generics: Generics { params: vec![], where_predicates: vec![] }, fields_stripped, fields: ids(fields, tcx), impls: Vec::new(), diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index eb2c8e5bae1c4..1168a89a8b2bf 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -14,7 +14,7 @@ pub const FORMAT_VERSION: u32 = 15; /// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information /// about the language items in the local crate, as well as info about external items to allow /// tools to find or link to them. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Crate { /// The id of the root [`Module`] item of the local crate. pub root: Id, @@ -34,7 +34,7 @@ pub struct Crate { pub format_version: u32, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ExternalCrate { pub name: String, pub html_root_url: Option, @@ -44,7 +44,7 @@ pub struct ExternalCrate { /// information. This struct should contain enough to generate a link/reference to the item in /// question, or can be used by a tool that takes the json output of multiple crates to find /// the actual item definition with all the relevant info. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ItemSummary { /// Can be used to look up the name and html_root_url of the crate this item came from in the /// `external_crates` map. @@ -56,7 +56,7 @@ pub struct ItemSummary { pub kind: ItemKind, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Item { /// The unique identifier of this item. Can be used to find this item in various mappings. pub id: Id, @@ -83,7 +83,7 @@ pub struct Item { pub inner: ItemEnum, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Span { /// The path to the source file for this span relative to the path `rustdoc` was invoked with. pub filename: PathBuf, @@ -93,13 +93,13 @@ pub struct Span { pub end: (usize, usize), } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Deprecation { pub since: Option, pub note: Option, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum Visibility { Public, @@ -115,7 +115,7 @@ pub enum Visibility { }, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum GenericArgs { /// <'a, 32, B: Copy, C = u32> @@ -124,7 +124,7 @@ pub enum GenericArgs { Parenthesized { inputs: Vec, output: Option }, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum GenericArg { Lifetime(String), @@ -133,7 +133,7 @@ pub enum GenericArg { Infer, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Constant { #[serde(rename = "type")] pub type_: Type, @@ -142,14 +142,14 @@ pub struct Constant { pub is_literal: bool, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct TypeBinding { pub name: String, pub args: GenericArgs, pub binding: TypeBindingKind, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum TypeBindingKind { Equality(Term), @@ -159,7 +159,7 @@ pub enum TypeBindingKind { #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Id(pub String); -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum ItemKind { Module, @@ -189,7 +189,7 @@ pub enum ItemKind { Keyword, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(tag = "kind", content = "inner", rename_all = "snake_case")] pub enum ItemEnum { Module(Module), @@ -241,13 +241,13 @@ pub enum ItemEnum { }, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Module { pub is_crate: bool, pub items: Vec, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Union { pub generics: Generics, pub fields_stripped: bool, @@ -255,7 +255,7 @@ pub struct Union { pub impls: Vec, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Struct { pub struct_type: StructType, pub generics: Generics, @@ -264,7 +264,7 @@ pub struct Struct { pub impls: Vec, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Enum { pub generics: Generics, pub variants_stripped: bool, @@ -272,7 +272,7 @@ pub struct Enum { pub impls: Vec, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(tag = "variant_kind", content = "variant_inner")] pub enum Variant { @@ -281,7 +281,7 @@ pub enum Variant { Struct(Vec), } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum StructType { Plain, @@ -289,7 +289,7 @@ pub enum StructType { Unit, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Header { #[serde(rename = "const")] pub const_: bool, @@ -300,7 +300,7 @@ pub struct Header { pub abi: Abi, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Abi { // We only have a concrete listing here for stable ABI's because their are so many // See rustc_ast_passes::feature_gate::PostExpansionVisitor::check_abi for the list @@ -316,14 +316,14 @@ pub enum Abi { Other(String), } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Function { pub decl: FnDecl, pub generics: Generics, pub header: Header, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Method { pub decl: FnDecl, pub generics: Generics, @@ -331,19 +331,19 @@ pub struct Method { pub has_body: bool, } -#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Generics { pub params: Vec, pub where_predicates: Vec, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct GenericParamDef { pub name: String, pub kind: GenericParamDefKind, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum GenericParamDefKind { Lifetime { @@ -384,7 +384,7 @@ pub enum GenericParamDefKind { }, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum WherePredicate { BoundPredicate { @@ -410,7 +410,7 @@ pub enum WherePredicate { }, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum GenericBound { TraitBound { @@ -429,7 +429,7 @@ pub enum GenericBound { Outlives(String), } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum TraitBoundModifier { None, @@ -437,14 +437,14 @@ pub enum TraitBoundModifier { MaybeConst, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum Term { Type(Type), Constant(Constant), } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(tag = "kind", content = "inner")] pub enum Type { @@ -498,7 +498,7 @@ pub enum Type { }, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct FunctionPointer { pub decl: FnDecl, /// Used for Higher-Rank Trait Bounds (HRTBs) @@ -512,14 +512,14 @@ pub struct FunctionPointer { pub header: Header, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct FnDecl { pub inputs: Vec<(String, Type)>, pub output: Option, pub c_variadic: bool, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Trait { pub is_auto: bool, pub is_unsafe: bool, @@ -529,13 +529,13 @@ pub struct Trait { pub implementations: Vec, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct TraitAlias { pub generics: Generics, pub params: Vec, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Impl { pub is_unsafe: bool, pub generics: Generics, @@ -550,7 +550,7 @@ pub struct Impl { pub blanket_impl: Option, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub struct Import { /// The full path being imported. @@ -564,37 +564,37 @@ pub struct Import { pub glob: bool, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ProcMacro { pub kind: MacroKind, pub helpers: Vec, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum MacroKind { /// A bang macro `foo!()`. Bang, /// An attribute macro `#[foo]`. Attr, - /// A derive macro `#[derive(Foo)]` + /// A derive macro `#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]` Derive, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Typedef { #[serde(rename = "type")] pub type_: Type, pub generics: Generics, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct OpaqueTy { pub bounds: Vec, pub generics: Generics, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Static { #[serde(rename = "type")] pub type_: Type, diff --git a/src/test/ui/async-await/async-await-let-else.rs b/src/test/ui/async-await/async-await-let-else.rs new file mode 100644 index 0000000000000..7ea07ae9add1b --- /dev/null +++ b/src/test/ui/async-await/async-await-let-else.rs @@ -0,0 +1,53 @@ +// edition:2021 +#![feature(let_else)] +use std::rc::Rc; + +async fn foo(x: Option) { + let Some(_) = x else { + let r = Rc::new(()); + bar().await + }; +} + +async fn bar() -> ! { + panic!() +} + +fn is_send(_: T) {} + +async fn foo2(x: Option) { + let Some(_) = x else { + bar2(Rc::new(())).await + }; +} + +async fn bar2(_: T) -> ! { + panic!() +} + +async fn foo3(x: Option) { + let Some(_) = x else { + (Rc::new(()), bar().await); + return; + }; +} + +async fn foo4(x: Option) { + let Some(_) = x else { + let r = Rc::new(()); + bar().await; + println!("{:?}", r); + return; + }; +} + +fn main() { + is_send(foo(Some(true))); + //~^ ERROR future cannot be sent between threads safely + is_send(foo2(Some(true))); + //~^ ERROR future cannot be sent between threads safely + is_send(foo3(Some(true))); + //~^ ERROR future cannot be sent between threads safely + is_send(foo4(Some(true))); + //~^ ERROR future cannot be sent between threads safely +} diff --git a/src/test/ui/async-await/async-await-let-else.stderr b/src/test/ui/async-await/async-await-let-else.stderr new file mode 100644 index 0000000000000..4d23e27c426b2 --- /dev/null +++ b/src/test/ui/async-await/async-await-let-else.stderr @@ -0,0 +1,94 @@ +error: future cannot be sent between threads safely + --> $DIR/async-await-let-else.rs:45:13 + | +LL | is_send(foo(Some(true))); + | ^^^^^^^^^^^^^^^ future returned by `foo` is not `Send` + | + = help: within `impl Future`, the trait `Send` is not implemented for `Rc<()>` +note: future is not `Send` as this value is used across an await + --> $DIR/async-await-let-else.rs:8:14 + | +LL | let r = Rc::new(()); + | - has type `Rc<()>` which is not `Send` +LL | bar().await + | ^^^^^^ await occurs here, with `r` maybe used later +LL | }; + | - `r` is later dropped here +note: required by a bound in `is_send` + --> $DIR/async-await-let-else.rs:16:15 + | +LL | fn is_send(_: T) {} + | ^^^^ required by this bound in `is_send` + +error: future cannot be sent between threads safely + --> $DIR/async-await-let-else.rs:47:13 + | +LL | is_send(foo2(Some(true))); + | ^^^^^^^^^^^^^^^^ future returned by `foo2` is not `Send` + | + = help: within `impl Future`, the trait `Send` is not implemented for `Rc<()>` +note: future is not `Send` as this value is used across an await + --> $DIR/async-await-let-else.rs:20:26 + | +LL | bar2(Rc::new(())).await + | ----------- ^^^^^^ await occurs here, with `Rc::new(())` maybe used later + | | + | has type `Rc<()>` which is not `Send` +LL | }; + | - `Rc::new(())` is later dropped here +note: required by a bound in `is_send` + --> $DIR/async-await-let-else.rs:16:15 + | +LL | fn is_send(_: T) {} + | ^^^^ required by this bound in `is_send` + +error: future cannot be sent between threads safely + --> $DIR/async-await-let-else.rs:49:13 + | +LL | is_send(foo3(Some(true))); + | ^^^^^^^^^^^^^^^^ future returned by `foo3` is not `Send` + | + = help: within `impl Future`, the trait `Send` is not implemented for `Rc<()>` +note: future is not `Send` as this value is used across an await + --> $DIR/async-await-let-else.rs:30:28 + | +LL | (Rc::new(()), bar().await); + | ----------- ^^^^^^ await occurs here, with `Rc::new(())` maybe used later + | | + | has type `Rc<()>` which is not `Send` +note: `Rc::new(())` is later dropped here + --> $DIR/async-await-let-else.rs:30:35 + | +LL | (Rc::new(()), bar().await); + | ^ +note: required by a bound in `is_send` + --> $DIR/async-await-let-else.rs:16:15 + | +LL | fn is_send(_: T) {} + | ^^^^ required by this bound in `is_send` + +error: future cannot be sent between threads safely + --> $DIR/async-await-let-else.rs:51:13 + | +LL | is_send(foo4(Some(true))); + | ^^^^^^^^^^^^^^^^ future returned by `foo4` is not `Send` + | + = help: within `impl Future`, the trait `Send` is not implemented for `Rc<()>` +note: future is not `Send` as this value is used across an await + --> $DIR/async-await-let-else.rs:38:14 + | +LL | let r = Rc::new(()); + | - has type `Rc<()>` which is not `Send` +LL | bar().await; + | ^^^^^^ await occurs here, with `r` maybe used later +... +LL | }; + | - `r` is later dropped here +note: required by a bound in `is_send` + --> $DIR/async-await-let-else.rs:16:15 + | +LL | fn is_send(_: T) {} + | ^^^^ required by this bound in `is_send` + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-74713.rs b/src/test/ui/const-generics/generic_const_exprs/issue-74713.rs new file mode 100644 index 0000000000000..0bcb997d96cde --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/issue-74713.rs @@ -0,0 +1,8 @@ +fn bug<'a>() +where + [(); { //~ ERROR mismatched types + let _: &'a (); //~ ERROR a non-static lifetime is not allowed in a `const` + }]: +{} + +fn main() {} diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-74713.stderr b/src/test/ui/const-generics/generic_const_exprs/issue-74713.stderr new file mode 100644 index 0000000000000..e7673df0a025c --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/issue-74713.stderr @@ -0,0 +1,22 @@ +error[E0658]: a non-static lifetime is not allowed in a `const` + --> $DIR/issue-74713.rs:4:17 + | +LL | let _: &'a (); + | ^^ + | + = note: see issue #76560 for more information + = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable + +error[E0308]: mismatched types + --> $DIR/issue-74713.rs:3:10 + | +LL | [(); { + | __________^ +LL | | let _: &'a (); +LL | | }]: + | |_____^ expected `usize`, found `()` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0308, E0658. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/let-else/let-else-binding-explicit-mut-annotated.stderr b/src/test/ui/let-else/let-else-binding-explicit-mut-annotated.stderr index fdec7e7f6a753..065787cab08ff 100644 --- a/src/test/ui/let-else/let-else-binding-explicit-mut-annotated.stderr +++ b/src/test/ui/let-else/let-else-binding-explicit-mut-annotated.stderr @@ -2,7 +2,9 @@ error[E0308]: mismatched types --> $DIR/let-else-binding-explicit-mut-annotated.rs:9:37 | LL | let Some(n): &mut Option = &&Some(5i32) else { return }; - | ^^^^^^^^^^^^ types differ in mutability + | ---------------- ^^^^^^^^^^^^ types differ in mutability + | | + | expected due to this | = note: expected mutable reference `&mut Option` found reference `&&Option` @@ -11,7 +13,9 @@ error[E0308]: mismatched types --> $DIR/let-else-binding-explicit-mut-annotated.rs:13:37 | LL | let Some(n): &mut Option = &&mut Some(5i32) else { return }; - | ^^^^^^^^^^^^^^^^ types differ in mutability + | ---------------- ^^^^^^^^^^^^^^^^ types differ in mutability + | | + | expected due to this | = note: expected mutable reference `&mut Option` found reference `&&mut Option` diff --git a/src/test/ui/let-else/let-else-check.stderr b/src/test/ui/let-else/let-else-check.stderr index b3da412ec280e..3d647a4c05d86 100644 --- a/src/test/ui/let-else/let-else-check.stderr +++ b/src/test/ui/let-else/let-else-check.stderr @@ -1,8 +1,8 @@ error: unused variable: `x` - --> $DIR/let-else-check.rs:18:9 + --> $DIR/let-else-check.rs:14:13 | -LL | let x = 1; - | ^ help: if this is intentional, prefix it with an underscore: `_x` +LL | let x = 1; + | ^ help: if this is intentional, prefix it with an underscore: `_x` | note: the lint level is defined here --> $DIR/let-else-check.rs:3:9 @@ -11,10 +11,10 @@ LL | #![deny(unused_variables)] | ^^^^^^^^^^^^^^^^ error: unused variable: `x` - --> $DIR/let-else-check.rs:14:13 + --> $DIR/let-else-check.rs:18:9 | -LL | let x = 1; - | ^ help: if this is intentional, prefix it with an underscore: `_x` +LL | let x = 1; + | ^ help: if this is intentional, prefix it with an underscore: `_x` error: aborting due to 2 previous errors diff --git a/src/test/ui/let-else/let-else-non-diverging.stderr b/src/test/ui/let-else/let-else-non-diverging.stderr index b961b16b6f6ef..05e45f689890d 100644 --- a/src/test/ui/let-else/let-else-non-diverging.stderr +++ b/src/test/ui/let-else/let-else-non-diverging.stderr @@ -1,8 +1,11 @@ error[E0308]: `else` clause of `let...else` does not diverge - --> $DIR/let-else-non-diverging.rs:12:32 + --> $DIR/let-else-non-diverging.rs:4:32 | -LL | let Some(x) = Some(1) else { Some(2) }; - | ^^^^^^^^^^^ expected `!`, found enum `Option` +LL | let Some(x) = Some(1) else { + | ________________________________^ +LL | | Some(2) +LL | | }; + | |_____^ expected `!`, found enum `Option` | = note: expected type `!` found enum `Option<{integer}>` @@ -26,13 +29,10 @@ LL | | }; = help: ...or use `match` instead of `let...else` error[E0308]: `else` clause of `let...else` does not diverge - --> $DIR/let-else-non-diverging.rs:4:32 + --> $DIR/let-else-non-diverging.rs:12:32 | -LL | let Some(x) = Some(1) else { - | ________________________________^ -LL | | Some(2) -LL | | }; - | |_____^ expected `!`, found enum `Option` +LL | let Some(x) = Some(1) else { Some(2) }; + | ^^^^^^^^^^^ expected `!`, found enum `Option` | = note: expected type `!` found enum `Option<{integer}>` diff --git a/src/test/ui/let-else/let-else-ref-bindings.stderr b/src/test/ui/let-else/let-else-ref-bindings.stderr index 650f4ec5e779f..56b9e073330a6 100644 --- a/src/test/ui/let-else/let-else-ref-bindings.stderr +++ b/src/test/ui/let-else/let-else-ref-bindings.stderr @@ -20,7 +20,9 @@ error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:24:34 | LL | let Some(a): Option<&[u8]> = some else { return }; - | ^^^^ expected `&[u8]`, found struct `Vec` + | ------------- ^^^^ expected `&[u8]`, found struct `Vec` + | | + | expected due to this | = note: expected enum `Option<&[u8]>` found enum `Option>` @@ -29,7 +31,9 @@ error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:27:34 | LL | let Some(a): Option<&[u8]> = &some else { return }; - | ^^^^^ expected enum `Option`, found `&Option>` + | ------------- ^^^^^ expected enum `Option`, found `&Option>` + | | + | expected due to this | = note: expected enum `Option<&[u8]>` found reference `&Option>` @@ -56,7 +60,9 @@ error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:52:38 | LL | let Some(a): Option<&mut [u8]> = some else { return }; - | ^^^^ expected `&mut [u8]`, found struct `Vec` + | ----------------- ^^^^ expected `&mut [u8]`, found struct `Vec` + | | + | expected due to this | = note: expected enum `Option<&mut [u8]>` found enum `Option>` @@ -65,7 +71,9 @@ error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:55:38 | LL | let Some(a): Option<&mut [u8]> = &mut some else { return }; - | ^^^^^^^^^ expected enum `Option`, found mutable reference + | ----------------- ^^^^^^^^^ expected enum `Option`, found mutable reference + | | + | expected due to this | = note: expected enum `Option<&mut [u8]>` found mutable reference `&mut Option>` diff --git a/src/test/ui/let-else/let-else-temporary-lifetime.rs b/src/test/ui/let-else/let-else-temporary-lifetime.rs new file mode 100644 index 0000000000000..624c2ea37a70b --- /dev/null +++ b/src/test/ui/let-else/let-else-temporary-lifetime.rs @@ -0,0 +1,25 @@ +// run-pass +#![feature(let_else)] + +use std::sync::atomic::{AtomicU8, Ordering}; + +static TRACKER: AtomicU8 = AtomicU8::new(0); + +#[derive(Default)] +struct Droppy { + inner: u32, +} + +impl Drop for Droppy { + fn drop(&mut self) { + TRACKER.store(1, Ordering::Release); + println!("I've been dropped"); + } +} + +fn main() { + assert_eq!(TRACKER.load(Ordering::Acquire), 0); + let 0 = Droppy::default().inner else { return }; + assert_eq!(TRACKER.load(Ordering::Acquire), 1); + println!("Should have dropped 👆"); +} diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs index 45af6be2653f3..ca617859db49d 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs @@ -11,7 +11,7 @@ use rustc_lint::LateContext; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { let (init, has_trailing_exprs) = match (loop_block.stmts, loop_block.expr) { ([stmt, stmts @ ..], expr) => { - if let StmtKind::Local(&Local { init: Some(e), .. }) | StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind { + if let StmtKind::Local(&Local { init: Some(e), els: None, .. }) | StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind { (e, !stmts.is_empty() || expr.is_some()) } else { return; diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index b2a873ef5823c..3077b999f4ee4 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -1041,7 +1041,8 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { - self.infallible_destructuring_match_linted |= infallible_destructuring_match::check(cx, local); + self.infallible_destructuring_match_linted |= + local.els.is_none() && infallible_destructuring_match::check(cx, local); } fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 6598413c77ecc..819646bb6780e 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -92,6 +92,7 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { if_chain! { if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id); if let Some(init) = local.init; + if local.els.is_none(); if !local.pat.span.from_expansion(); if has_no_effect(cx, init); if let PatKind::Binding(_, _, ident, _) = local.pat.kind; diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index 5ae04947b82d0..1d9a2abf7066c 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -10,7 +10,6 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::Span; use rustc_span::sym; @@ -203,9 +202,7 @@ fn check_final_expr<'tcx>( check_block_return(cx, ifblock); } if let Some(else_clause) = else_clause_opt { - if expr.span.desugaring_kind() != Some(DesugaringKind::LetElse) { - check_final_expr(cx, else_clause, None, RetReplacement::Empty); - } + check_final_expr(cx, else_clause, None, RetReplacement::Empty); } }, // a match expr, check all arms diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 793e3cc58c21d..942f14ddd3d51 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -102,7 +102,7 @@ pub struct HirEqInterExpr<'a, 'b, 'tcx> { impl HirEqInterExpr<'_, '_, '_> { pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { match (&left.kind, &right.kind) { - (&StmtKind::Local(l), &StmtKind::Local(r)) => { + (&StmtKind::Local(l, ), &StmtKind::Local(r, )) => { // This additional check ensures that the type of the locals are equivalent even if the init // expression or type have some inferred parts. if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results { @@ -117,6 +117,7 @@ impl HirEqInterExpr<'_, '_, '_> { // these only get added if the init and type is equal. both(&l.init, &r.init, |l, r| self.eq_expr(l, r)) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) + && both(&l.els, &r.els, |l, r| self.eq_block(l, r)) && self.eq_pat(l.pat, r.pat) }, (&StmtKind::Expr(l), &StmtKind::Expr(r)) | (&StmtKind::Semi(l), &StmtKind::Semi(r)) => self.eq_expr(l, r), @@ -921,11 +922,14 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { std::mem::discriminant(&b.kind).hash(&mut self.s); match &b.kind { - StmtKind::Local(local) => { + StmtKind::Local(local, ) => { self.hash_pat(local.pat); if let Some(init) = local.init { self.hash_expr(init); } + if let Some(els) = local.els { + self.hash_block(els); + } }, StmtKind::Item(..) => {}, StmtKind::Expr(expr) | StmtKind::Semi(expr) => { diff --git a/triagebot.toml b/triagebot.toml index ba9ed20cc64a2..3842da566a91d 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -165,6 +165,19 @@ exclude_labels = [ "T-*", ] +[autolabel."A-bootstrap"] +trigger_files = [ + "x.py", + "src/bootstrap", + "src/tools/rust-installer", +] + +[autolabel."T-infra"] +trigger_files = [ + "src/ci", + "src/tools/bump-stage0", +] + [notify-zulip."I-prioritize"] zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts topic = "#{number} {title}" @@ -279,6 +292,9 @@ Examples of `T-libs-api` changes: * Changing observable runtime behavior of library APIs """ +[mentions."library/proc_macro/src/bridge"] +cc = ["@rust-lang/wg-rls-2"] + [mentions."src/librustdoc/clean/types.rs"] cc = ["@camelid"]