From 822b10d4288cc32e5914b1874b585e6f24ef0c28 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 24 Feb 2024 02:53:05 -0500 Subject: [PATCH] Use `ControlFlow` in HIR visitors --- .../src/diagnostics/mutability_errors.rs | 76 ++++++---------- compiler/rustc_borrowck/src/lib.rs | 1 + .../src/check/compare_impl_item.rs | 21 ++--- .../src/collect/resolve_bound_vars.rs | 22 ++--- .../rustc_hir_analysis/src/collect/type_of.rs | 16 ++-- .../rustc_hir_typeck/src/method/suggest.rs | 21 +++-- .../src/infer/error_reporting/mod.rs | 21 ++--- .../nice_region_error/find_anon_type.rs | 69 +++++++-------- .../src/infer/error_reporting/suggest.rs | 88 +++++++++---------- compiler/rustc_infer/src/lib.rs | 1 + .../error_reporting/type_err_ctxt_ext.rs | 27 +++--- tests/ui/issues/issue-20831-debruijn.stderr | 6 +- 12 files changed, 168 insertions(+), 201 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index c327e591f3cdd..ebc9f1d109ee7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1,6 +1,7 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +use core::ops::ControlFlow; use hir::ExprKind; use rustc_errors::{Applicability, Diag}; use rustc_hir as hir; @@ -727,30 +728,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { _ => local_decl.source_info.span, }; - struct BindingFinder { - span: Span, - hir_id: Option, - } - - impl<'tcx> Visitor<'tcx> for BindingFinder { - fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) { - if let hir::StmtKind::Local(local) = s.kind { - if local.pat.span == self.span { - self.hir_id = Some(local.hir_id); - } - } - hir::intravisit::walk_stmt(self, s); - } - } - let def_id = self.body.source.def_id(); let hir_id = if let Some(local_def_id) = def_id.as_local() && let Some(body_id) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id) { let body = self.infcx.tcx.hir().body(body_id); - let mut v = BindingFinder { span: pat_span, hir_id: None }; - v.visit_body(body); - v.hir_id + BindingFinder { span: pat_span }.visit_body(body).break_value() } else { None }; @@ -859,17 +842,18 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; let hir_map = self.infcx.tcx.hir(); - struct Finder<'tcx> { + struct Finder { span: Span, - expr: Option<&'tcx Expr<'tcx>>, } - impl<'tcx> Visitor<'tcx> for Finder<'tcx> { - fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { - if e.span == self.span && self.expr.is_none() { - self.expr = Some(e); + impl<'tcx> Visitor<'tcx> for Finder { + type Result = ControlFlow<&'tcx Expr<'tcx>>; + fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) -> Self::Result { + if e.span == self.span { + ControlFlow::Break(e) + } else { + hir::intravisit::walk_expr(self, e) } - hir::intravisit::walk_expr(self, e); } } if let Some(body_id) = hir_map.maybe_body_owned_by(self.mir_def_id()) @@ -878,9 +862,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // `span` corresponds to the expression being iterated, find the `for`-loop desugared // expression with that span in order to identify potential fixes when encountering a // read-only iterator that should be mutable. - let mut v = Finder { span, expr: None }; - v.visit_block(block); - if let Some(expr) = v.expr + if let ControlFlow::Break(expr) = (Finder { span }).visit_block(block) && let Call(_, [expr]) = expr.kind { match expr.kind { @@ -1179,29 +1161,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ); } Some((false, err_label_span, message)) => { - struct BindingFinder { - span: Span, - hir_id: Option, - } - - impl<'tcx> Visitor<'tcx> for BindingFinder { - fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) { - if let hir::StmtKind::Local(local) = s.kind { - if local.pat.span == self.span { - self.hir_id = Some(local.hir_id); - } - } - hir::intravisit::walk_stmt(self, s); - } - } let def_id = self.body.source.def_id(); let hir_id = if let Some(local_def_id) = def_id.as_local() && let Some(body_id) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id) { let body = self.infcx.tcx.hir().body(body_id); - let mut v = BindingFinder { span: err_label_span, hir_id: None }; - v.visit_body(body); - v.hir_id + BindingFinder { span: err_label_span }.visit_body(body).break_value() } else { None }; @@ -1333,6 +1298,23 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } +struct BindingFinder { + span: Span, +} + +impl<'tcx> Visitor<'tcx> for BindingFinder { + type Result = ControlFlow; + fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) -> Self::Result { + if let hir::StmtKind::Local(local) = s.kind + && local.pat.span == self.span + { + ControlFlow::Break(local.hir_id) + } else { + hir::intravisit::walk_stmt(self, s) + } + } +} + pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option) -> bool { debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind()); diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index ef582033c4ea1..840d07a897768 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -6,6 +6,7 @@ #![feature(assert_matches)] #![feature(associated_type_bounds)] #![feature(box_patterns)] +#![feature(control_flow_enum)] #![feature(let_chains)] #![feature(min_specialization)] #![feature(never_type)] diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 435e251b130ea..f708c66cfa444 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -1,5 +1,6 @@ use super::potentially_plural_count; use crate::errors::{LifetimesOrBoundsMismatchOnTrait, MethodShouldReturnFuture}; +use core::ops::ControlFlow; use hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, ErrorGuaranteed}; @@ -1565,24 +1566,24 @@ fn compare_synthetic_generics<'tcx>( let (sig, _) = impl_m.expect_fn(); let input_tys = sig.decl.inputs; - struct Visitor(Option, hir::def_id::LocalDefId); + struct Visitor(hir::def_id::LocalDefId); impl<'v> intravisit::Visitor<'v> for Visitor { - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { - intravisit::walk_ty(self, ty); + type Result = ControlFlow; + fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) -> Self::Result { if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = ty.kind && let Res::Def(DefKind::TyParam, def_id) = path.res - && def_id == self.1.to_def_id() + && def_id == self.0.to_def_id() { - self.0 = Some(ty.span); + ControlFlow::Break(ty.span) + } else { + intravisit::walk_ty(self, ty) } } } - let mut visitor = Visitor(None, impl_def_id); - for ty in input_tys { - intravisit::Visitor::visit_ty(&mut visitor, ty); - } - let span = visitor.0?; + let span = input_tys.iter().find_map(|ty| { + intravisit::Visitor::visit_ty(&mut Visitor(impl_def_id), ty).break_value() + })?; let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds; let bounds = bounds.first()?.span().to(bounds.last()?.span()); diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index ad8ec1036efee..368cde782193e 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -6,6 +6,7 @@ //! the types in HIR to identify late-bound lifetimes and assign their Debruijn indices. This file //! is also responsible for assigning their semantics to implicit lifetimes in trait objects. +use core::ops::ControlFlow; use rustc_ast::visit::walk_list; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::{codes::*, struct_span_code_err}; @@ -417,23 +418,18 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { { if let &hir::ClosureBinder::For { span: for_sp, .. } = binder { fn span_of_infer(ty: &hir::Ty<'_>) -> Option { - struct V(Option); - + struct V; impl<'v> Visitor<'v> for V { - fn visit_ty(&mut self, t: &'v hir::Ty<'v>) { - match t.kind { - _ if self.0.is_some() => (), - hir::TyKind::Infer => { - self.0 = Some(t.span); - } - _ => intravisit::walk_ty(self, t), + type Result = ControlFlow; + fn visit_ty(&mut self, t: &'v hir::Ty<'v>) -> Self::Result { + if matches!(t.kind, hir::TyKind::Infer) { + ControlFlow::Break(t.span) + } else { + intravisit::walk_ty(self, t) } } } - - let mut v = V(None); - v.visit_ty(ty); - v.0 + V.visit_ty(ty).break_value() } let infer_in_rt_sp = match fn_decl.output { diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 417f0fceaa81f..2335b0005b726 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -1,3 +1,4 @@ +use core::ops::ControlFlow; use rustc_errors::{Applicability, StashKey}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -666,19 +667,16 @@ pub fn type_alias_is_lazy<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool { if tcx.features().lazy_type_alias { return true; } - struct HasTait { - has_type_alias_impl_trait: bool, - } + struct HasTait; impl<'tcx> Visitor<'tcx> for HasTait { - fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { + type Result = ControlFlow<()>; + fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) -> Self::Result { if let hir::TyKind::OpaqueDef(..) = t.kind { - self.has_type_alias_impl_trait = true; + ControlFlow::Break(()) } else { - hir::intravisit::walk_ty(self, t); + hir::intravisit::walk_ty(self, t) } } } - let mut has_tait = HasTait { has_type_alias_impl_trait: false }; - has_tait.visit_ty(tcx.hir().expect_item(def_id).expect_ty_alias().0); - has_tait.has_type_alias_impl_trait + HasTait.visit_ty(tcx.hir().expect_item(def_id).expect_ty_alias().0).is_break() } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 893b3f9534de9..7f7629b1de976 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -6,6 +6,7 @@ use crate::errors::{self, CandidateTraitNote, NoAssociatedItem}; use crate::Expectation; use crate::FnCtxt; +use core::ops::ControlFlow; use rustc_ast::ast::Mutability; use rustc_attr::parse_confusables; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; @@ -2212,30 +2213,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let map = self.infcx.tcx.hir(); let body_id = self.tcx.hir().body_owned_by(self.body_id); let body = map.body(body_id); - struct LetVisitor<'a> { - result: Option<&'a hir::Expr<'a>>, + struct LetVisitor { ident_name: Symbol, } // FIXME: This really should be taking scoping, etc into account. - impl<'v> Visitor<'v> for LetVisitor<'v> { - fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) { - if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind + impl<'v> Visitor<'v> for LetVisitor { + type Result = ControlFlow>>; + fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result { + if let hir::StmtKind::Local(&hir::Local { pat, init, .. }) = ex.kind && let Binding(_, _, ident, ..) = pat.kind && ident.name == self.ident_name { - self.result = *init; + ControlFlow::Break(init) } else { - hir::intravisit::walk_stmt(self, ex); + hir::intravisit::walk_stmt(self, ex) } } } - let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name }; - visitor.visit_body(body); - if let Node::Expr(call_expr) = self.tcx.parent_hir_node(seg1.hir_id) - && let Some(expr) = visitor.result + && let ControlFlow::Break(Some(expr)) = + (LetVisitor { ident_name: seg1.ident.name }).visit_body(body) && let Some(self_ty) = self.node_ty_opt(expr.hir_id) { let probe = self.lookup_probe_for_diagnostic( diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 362ca3b4833ea..ea5c6b8c057ce 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -79,7 +79,7 @@ use rustc_middle::ty::{ use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span}; use rustc_target::spec::abi; use std::borrow::Cow; -use std::ops::Deref; +use std::ops::{ControlFlow, Deref}; use std::path::PathBuf; use std::{cmp, fmt, iter}; @@ -2129,15 +2129,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let tykind = match self.tcx.opt_hir_node_by_def_id(trace.cause.body_id) { Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) => { let body = hir.body(*body_id); - struct LetVisitor<'v> { + struct LetVisitor { span: Span, - result: Option<&'v hir::Ty<'v>>, } - impl<'v> Visitor<'v> for LetVisitor<'v> { - fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) { - if self.result.is_some() { - return; - } + impl<'v> Visitor<'v> for LetVisitor { + type Result = ControlFlow<&'v hir::TyKind<'v>>; + fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) -> Self::Result { // Find a local statement where the initializer has // the same span as the error and the type is specified. if let hir::Stmt { @@ -2151,13 +2148,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } = s && init_span == &self.span { - self.result = Some(*array_ty); + ControlFlow::Break(&array_ty.peel_refs().kind) + } else { + ControlFlow::Continue(()) } } } - let mut visitor = LetVisitor { span, result: None }; - visitor.visit_body(body); - visitor.result.map(|r| &r.peel_refs().kind) + LetVisitor { span }.visit_body(body).break_value() } Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _, _), .. })) => { Some(&ty.peel_refs().kind) diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs index 4f74365d06c47..265a315a55979 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs @@ -1,3 +1,4 @@ +use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::hir::map::Map; @@ -43,14 +44,9 @@ fn find_component_for_bound_region<'tcx>( arg: &'tcx hir::Ty<'tcx>, br: &ty::BoundRegionKind, ) -> Option<&'tcx hir::Ty<'tcx>> { - let mut nested_visitor = FindNestedTypeVisitor { - tcx, - bound_region: *br, - found_type: None, - current_index: ty::INNERMOST, - }; - nested_visitor.visit_ty(arg); - nested_visitor.found_type + FindNestedTypeVisitor { tcx, bound_region: *br, current_index: ty::INNERMOST } + .visit_ty(arg) + .break_value() } // The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the @@ -65,26 +61,24 @@ struct FindNestedTypeVisitor<'tcx> { // The bound_region corresponding to the Refree(freeregion) // associated with the anonymous region we are looking for. bound_region: ty::BoundRegionKind, - // The type where the anonymous lifetime appears - // for e.g., Vec<`&u8`> and <`&u8`> - found_type: Option<&'tcx hir::Ty<'tcx>>, current_index: ty::DebruijnIndex, } impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { + type Result = ControlFlow<&'tcx hir::Ty<'tcx>>; type NestedFilter = nested_filter::OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { self.tcx.hir() } - fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result { match arg.kind { hir::TyKind::BareFn(_) => { self.current_index.shift_in(1); intravisit::walk_ty(self, arg); self.current_index.shift_out(1); - return; + return ControlFlow::Continue(()); } hir::TyKind::TraitObject(bounds, ..) => { @@ -105,8 +99,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { (Some(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => { debug!("EarlyBound id={:?} def_id={:?}", id, def_id); if id == def_id { - self.found_type = Some(arg); - return; // we can stop visiting now + return ControlFlow::Break(arg); } } @@ -123,8 +116,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { ); debug!("LateBound id={:?} def_id={:?}", id, def_id); if debruijn_index == self.current_index && id == def_id { - self.found_type = Some(arg); - return; // we can stop visiting now + return ControlFlow::Break(arg); } } @@ -145,23 +137,30 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { } // Checks if it is of type `hir::TyKind::Path` which corresponds to a struct. hir::TyKind::Path(_) => { - let subvisitor = &mut TyPathVisitor { - tcx: self.tcx, - found_it: false, - bound_region: self.bound_region, - current_index: self.current_index, + // Prefer using the lifetime in type arguments rather than lifetime arguments. + intravisit::walk_ty(self, arg)?; + + // Call `walk_ty` as `visit_ty` is empty. + return if intravisit::walk_ty( + &mut TyPathVisitor { + tcx: self.tcx, + bound_region: self.bound_region, + current_index: self.current_index, + }, + arg, + ) + .is_break() + { + ControlFlow::Break(arg) + } else { + ControlFlow::Continue(()) }; - intravisit::walk_ty(subvisitor, arg); // call walk_ty; as visit_ty is empty, - // this will visit only outermost type - if subvisitor.found_it { - self.found_type = Some(arg); - } } _ => {} } // walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`, // go on to visit `&Foo` - intravisit::walk_ty(self, arg); + intravisit::walk_ty(self, arg) } } @@ -173,26 +172,25 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { // specific part of the type in the error message. struct TyPathVisitor<'tcx> { tcx: TyCtxt<'tcx>, - found_it: bool, bound_region: ty::BoundRegionKind, current_index: ty::DebruijnIndex, } impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { + type Result = ControlFlow<()>; type NestedFilter = nested_filter::OnlyBodies; fn nested_visit_map(&mut self) -> Map<'tcx> { self.tcx.hir() } - fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) { + fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) -> Self::Result { match (self.tcx.named_bound_var(lifetime.hir_id), self.bound_region) { // the lifetime of the TyPath! (Some(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => { debug!("EarlyBound id={:?} def_id={:?}", id, def_id); if id == def_id { - self.found_it = true; - return; // we can stop visiting now + return ControlFlow::Break(()); } } @@ -201,8 +199,7 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { debug!("id={:?}", id); debug!("def_id={:?}", def_id); if debruijn_index == self.current_index && id == def_id { - self.found_it = true; - return; // we can stop visiting now + return ControlFlow::Break(()); } } @@ -220,9 +217,10 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { debug!("no arg found"); } } + ControlFlow::Continue(()) } - fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result { // ignore nested types // // If you have a type like `Foo<'a, &Ty>` we @@ -231,5 +229,6 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { // Making `visit_ty` empty will ignore the `&Ty` embedded // inside, it will get reached by the outer visitor. debug!("`Ty` corresponding to a struct is {:?}", arg); + ControlFlow::Continue(()) } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index 472dab639d590..8cdf39b173987 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -1,4 +1,5 @@ use crate::infer::error_reporting::hir::Path; +use core::ops::ControlFlow; use hir::def::CtorKind; use hir::intravisit::{walk_expr, walk_stmt, Visitor}; use hir::{Local, QPath}; @@ -563,62 +564,55 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { cause: &ObligationCause<'_>, span: Span, ) -> Option { - let hir = self.tcx.hir(); - if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(cause.body_id) { - let body = hir.body(body_id); - - /// Find the if expression with given span - struct IfVisitor { - pub result: bool, - pub found_if: bool, - pub err_span: Span, - } - - impl<'v> Visitor<'v> for IfVisitor { - fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { - if self.result { - return; - } - match ex.kind { - hir::ExprKind::If(cond, _, _) => { - self.found_if = true; - walk_expr(self, cond); - self.found_if = false; - } - _ => walk_expr(self, ex), - } - } + /// Find the if expression with given span + struct IfVisitor { + pub found_if: bool, + pub err_span: Span, + } - fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) { - if let hir::StmtKind::Local(hir::Local { - span, - pat: hir::Pat { .. }, - ty: None, - init: Some(_), - .. - }) = &ex.kind - && self.found_if - && span.eq(&self.err_span) - { - self.result = true; + impl<'v> Visitor<'v> for IfVisitor { + type Result = ControlFlow<()>; + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result { + match ex.kind { + hir::ExprKind::If(cond, _, _) => { + self.found_if = true; + walk_expr(self, cond)?; + self.found_if = false; + ControlFlow::Continue(()) } - walk_stmt(self, ex); + _ => walk_expr(self, ex), } + } - fn visit_body(&mut self, body: &'v hir::Body<'v>) { - hir::intravisit::walk_body(self, body); + fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result { + if let hir::StmtKind::Local(hir::Local { + span, + pat: hir::Pat { .. }, + ty: None, + init: Some(_), + .. + }) = &ex.kind + && self.found_if + && span.eq(&self.err_span) + { + ControlFlow::Break(()) + } else { + walk_stmt(self, ex) } } - let mut visitor = IfVisitor { err_span: span, found_if: false, result: false }; - visitor.visit_body(body); - if visitor.result { - return Some(TypeErrorAdditionalDiags::AddLetForLetChains { - span: span.shrink_to_lo(), - }); + fn visit_body(&mut self, body: &'v hir::Body<'v>) -> Self::Result { + hir::intravisit::walk_body(self, body) } } - None + + self.tcx.hir().maybe_body_owned_by(cause.body_id).and_then(|body_id| { + let body = self.tcx.hir().body(body_id); + IfVisitor { err_span: span, found_if: false } + .visit_body(body) + .is_break() + .then(|| TypeErrorAdditionalDiags::AddLetForLetChains { span: span.shrink_to_lo() }) + }) } /// For "one type is more general than the other" errors on closures, suggest changing the lifetime diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 97f9a4b291d09..029bddda1e1c5 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -20,6 +20,7 @@ #![allow(rustc::untranslatable_diagnostic)] #![feature(associated_type_bounds)] #![feature(box_patterns)] +#![feature(control_flow_enum)] #![feature(extend_one)] #![feature(let_chains)] #![feature(if_let_guard)] diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 7a930937255b9..3d2e3115bdead 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -19,6 +19,7 @@ use crate::traits::{ ObligationCause, ObligationCauseCode, ObligationCtxt, Overflow, PredicateObligation, SelectionError, SignatureMismatch, TraitNotObjectSafe, }; +use core::ops::ControlFlow; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::codes::*; use rustc_errors::{pluralize, struct_span_code_err, Applicability, MultiSpan, StringPart}; @@ -1126,22 +1127,20 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { err: &mut Diag<'_>, ) -> bool { let span = obligation.cause.span; - struct V<'v> { + struct V { search_span: Span, - found: Option<&'v hir::Expr<'v>>, } - impl<'v> Visitor<'v> for V<'v> { - fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + impl<'v> Visitor<'v> for V { + type Result = ControlFlow<&'v hir::Expr<'v>>; + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result { if let hir::ExprKind::Match(expr, _arms, hir::MatchSource::TryDesugar(_)) = ex.kind + && ex.span.with_lo(ex.span.hi() - BytePos(1)).source_equal(self.search_span) + && let hir::ExprKind::Call(_, [expr, ..]) = expr.kind { - if ex.span.with_lo(ex.span.hi() - BytePos(1)).source_equal(self.search_span) { - if let hir::ExprKind::Call(_, [expr, ..]) = expr.kind { - self.found = Some(expr); - return; - } - } + ControlFlow::Break(expr) + } else { + hir::intravisit::walk_expr(self, ex) } - hir::intravisit::walk_expr(self, ex); } } let hir_id = self.tcx.local_def_id_to_hir_id(obligation.cause.body_id); @@ -1149,9 +1148,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) => body_id, _ => return false, }; - let mut v = V { search_span: span, found: None }; - v.visit_body(self.tcx.hir().body(*body_id)); - let Some(expr) = v.found else { + let ControlFlow::Break(expr) = + (V { search_span: span }).visit_body(self.tcx.hir().body(*body_id)) + else { return false; }; let Some(typeck) = &self.typeck_results else { diff --git a/tests/ui/issues/issue-20831-debruijn.stderr b/tests/ui/issues/issue-20831-debruijn.stderr index 7d1e19b7e4759..60721f001b738 100644 --- a/tests/ui/issues/issue-20831-debruijn.stderr +++ b/tests/ui/issues/issue-20831-debruijn.stderr @@ -4,11 +4,11 @@ error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` d LL | fn subscribe(&mut self, t : Box::Output> + 'a>) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: first, the lifetime cannot outlive the anonymous lifetime defined here... - --> $DIR/issue-20831-debruijn.rs:28:58 +note: first, the lifetime cannot outlive the anonymous lifetime as defined here... + --> $DIR/issue-20831-debruijn.rs:28:18 | LL | fn subscribe(&mut self, t : Box::Output> + 'a>) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ note: ...but the lifetime must also be valid for the lifetime `'a` as defined here... --> $DIR/issue-20831-debruijn.rs:26:6 |