From ff0bfea45f8e2f9f3081bd416b4610511b9a6384 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 14 Apr 2024 18:06:59 +0000 Subject: [PATCH 1/7] Convert `if` to `match` --- compiler/rustc_hir_typeck/src/fallback.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index c0b3984e3e17d..3b00c7353e588 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -436,17 +436,12 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // // In practice currently the two ways that this happens is // coercion and subtyping. - let (a, b) = if let ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) = atom { - (a, b) - } else if let ty::PredicateKind::Subtype(ty::SubtypePredicate { - a_is_expected: _, - a, - b, - }) = atom - { - (a, b) - } else { - return None; + let (a, b) = match atom { + ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => (a, b), + ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => { + (a, b) + } + _ => return None, }; let a_vid = self.root_vid(a)?; From aa0a916c81936ba725b7efb68804a4217b09b43a Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 14 Apr 2024 18:07:40 +0000 Subject: [PATCH 2/7] Add a lint against never type fallback affecting unsafe code --- compiler/rustc_hir_typeck/messages.ftl | 4 + compiler/rustc_hir_typeck/src/errors.rs | 6 +- compiler/rustc_hir_typeck/src/fallback.rs | 135 ++++++++++++++++-- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 4 +- compiler/rustc_lint_defs/src/builtin.rs | 44 ++++++ ...never-type-fallback-flowing-into-unsafe.rs | 35 +++++ ...r-type-fallback-flowing-into-unsafe.stderr | 23 +++ 7 files changed, 241 insertions(+), 10 deletions(-) create mode 100644 tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.rs create mode 100644 tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.stderr diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 07b4948872dd8..0caebf44a193e 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -99,6 +99,10 @@ hir_typeck_lossy_provenance_ptr2int = hir_typeck_missing_parentheses_in_range = can't call method `{$method_name}` on type `{$ty_str}` +hir_typeck_never_type_fallback_flowing_into_unsafe = + never type fallback affects this call to an `unsafe` function + .help = specify the type explicitly + hir_typeck_no_associated_item = no {$item_kind} named `{$item_name}` found for {$ty_prefix} `{$ty_str}`{$trait_missing_method -> [true] {""} *[other] {" "}in the current scope diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 1c4d5657b171e..fcad88f829e24 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -164,6 +164,11 @@ pub struct MissingParenthesesInRange { pub add_missing_parentheses: Option, } +#[derive(LintDiagnostic)] +#[diag(hir_typeck_never_type_fallback_flowing_into_unsafe)] +#[help] +pub struct NeverTypeFallbackFlowingIntoUnsafe {} + #[derive(Subdiagnostic)] #[multipart_suggestion( hir_typeck_add_missing_parentheses_in_range, @@ -632,7 +637,6 @@ pub enum SuggestBoxingForReturnImplTrait { ends: Vec, }, } - #[derive(LintDiagnostic)] #[diag(hir_typeck_dereferencing_mut_binding)] pub struct DereferencingMutBinding { diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 3b00c7353e588..86a75aa4d7873 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -1,10 +1,15 @@ -use crate::FnCtxt; +use std::cell::OnceCell; + +use crate::{errors, FnCtxt}; use rustc_data_structures::{ graph::{self, iterate::DepthFirstSearch, vec_graph::VecGraph}, unord::{UnordBag, UnordMap, UnordSet}, }; +use rustc_hir::HirId; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; +use rustc_session::lint; +use rustc_span::Span; use rustc_span::DUMMY_SP; #[derive(Copy, Clone)] @@ -335,6 +340,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // reach a member of N. If so, it falls back to `()`. Else // `!`. let mut diverging_fallback = UnordMap::with_capacity(diverging_vids.len()); + let unsafe_infer_vars = OnceCell::new(); for &diverging_vid in &diverging_vids { let diverging_ty = Ty::new_var(self.tcx, diverging_vid); let root_vid = self.root_var(diverging_vid); @@ -354,11 +360,35 @@ impl<'tcx> FnCtxt<'_, 'tcx> { output: infer_var_infos.items().any(|info| info.output), }; + let mut fallback_to = |ty| { + let unsafe_infer_vars = unsafe_infer_vars.get_or_init(|| { + let unsafe_infer_vars = compute_unsafe_infer_vars(self.root_ctxt, self.body_id); + debug!(?unsafe_infer_vars); + unsafe_infer_vars + }); + + let affected_unsafe_infer_vars = + graph::depth_first_search_as_undirected(&coercion_graph, root_vid) + .filter_map(|x| unsafe_infer_vars.get(&x).copied()) + .collect::>(); + + for (hir_id, span) in affected_unsafe_infer_vars { + self.tcx.emit_node_span_lint( + lint::builtin::NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, + hir_id, + span, + errors::NeverTypeFallbackFlowingIntoUnsafe {}, + ); + } + + diverging_fallback.insert(diverging_ty, ty); + }; + use DivergingFallbackBehavior::*; match behavior { FallbackToUnit => { debug!("fallback to () - legacy: {:?}", diverging_vid); - diverging_fallback.insert(diverging_ty, self.tcx.types.unit); + fallback_to(self.tcx.types.unit); } FallbackToNiko => { if found_infer_var_info.self_in_trait && found_infer_var_info.output { @@ -387,13 +417,13 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // set, see the relationship finding module in // compiler/rustc_trait_selection/src/traits/relationships.rs. debug!("fallback to () - found trait and projection: {:?}", diverging_vid); - diverging_fallback.insert(diverging_ty, self.tcx.types.unit); + fallback_to(self.tcx.types.unit); } else if can_reach_non_diverging { debug!("fallback to () - reached non-diverging: {:?}", diverging_vid); - diverging_fallback.insert(diverging_ty, self.tcx.types.unit); + fallback_to(self.tcx.types.unit); } else { debug!("fallback to ! - all diverging: {:?}", diverging_vid); - diverging_fallback.insert(diverging_ty, self.tcx.types.never); + fallback_to(self.tcx.types.never); } } FallbackToNever => { @@ -401,7 +431,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { "fallback to ! - `rustc_never_type_mode = \"fallback_to_never\")`: {:?}", diverging_vid ); - diverging_fallback.insert(diverging_ty, self.tcx.types.never); + fallback_to(self.tcx.types.never); } NoFallback => { debug!( @@ -417,7 +447,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { /// Returns a graph whose nodes are (unresolved) inference variables and where /// an edge `?A -> ?B` indicates that the variable `?A` is coerced to `?B`. - fn create_coercion_graph(&self) -> VecGraph { + fn create_coercion_graph(&self) -> VecGraph { let pending_obligations = self.fulfillment_cx.borrow_mut().pending_obligations(); debug!("create_coercion_graph: pending_obligations={:?}", pending_obligations); let coercion_edges: Vec<(ty::TyVid, ty::TyVid)> = pending_obligations @@ -451,6 +481,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { .collect(); debug!("create_coercion_graph: coercion_edges={:?}", coercion_edges); let num_ty_vars = self.num_ty_vars(); + VecGraph::new(num_ty_vars, coercion_edges) } @@ -459,3 +490,91 @@ impl<'tcx> FnCtxt<'_, 'tcx> { Some(self.root_var(self.shallow_resolve(ty).ty_vid()?)) } } + +/// Finds all type variables which are passed to an `unsafe` function. +/// +/// For example, for this function `f`: +/// ```ignore (demonstrative) +/// fn f() { +/// unsafe { +/// let x /* ?X */ = core::mem::zeroed(); +/// // ^^^^^^^^^^^^^^^^^^^ -- hir_id, span +/// +/// let y = core::mem::zeroed::>(); +/// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- hir_id, span +/// } +/// } +/// ``` +/// +/// Will return `{ id(?X) -> (hir_id, span) }` +fn compute_unsafe_infer_vars<'a, 'tcx>( + root_ctxt: &'a crate::TypeckRootCtxt<'tcx>, + body_id: rustc_span::def_id::LocalDefId, +) -> UnordMap { + use rustc_hir as hir; + + let tcx = root_ctxt.infcx.tcx; + let body_id = tcx.hir().maybe_body_owned_by(body_id).unwrap(); + let body = tcx.hir().body(body_id); + let mut res = <_>::default(); + + struct UnsafeInferVarsVisitor<'a, 'tcx, 'r> { + root_ctxt: &'a crate::TypeckRootCtxt<'tcx>, + res: &'r mut UnordMap, + } + + use hir::intravisit::Visitor; + impl hir::intravisit::Visitor<'_> for UnsafeInferVarsVisitor<'_, '_, '_> { + fn visit_expr(&mut self, ex: &'_ hir::Expr<'_>) { + // FIXME: method calls + if let hir::ExprKind::Call(func, ..) = ex.kind { + let typeck_results = self.root_ctxt.typeck_results.borrow(); + + let func_ty = typeck_results.expr_ty(func); + + // `is_fn` is required to ignore closures (which can't be unsafe) + if func_ty.is_fn() + && let sig = func_ty.fn_sig(self.root_ctxt.infcx.tcx) + && let hir::Unsafety::Unsafe = sig.unsafety() + { + let mut collector = + InferVarCollector { hir_id: ex.hir_id, call_span: ex.span, res: self.res }; + + // Collect generic arguments of the function which are inference variables + typeck_results + .node_args(ex.hir_id) + .types() + .for_each(|t| t.visit_with(&mut collector)); + + // Also check the return type, for cases like `(unsafe_fn::<_> as unsafe fn() -> _)()` + sig.output().visit_with(&mut collector); + } + } + + hir::intravisit::walk_expr(self, ex); + } + } + + struct InferVarCollector<'r> { + hir_id: HirId, + call_span: Span, + res: &'r mut UnordMap, + } + + impl<'tcx> ty::TypeVisitor> for InferVarCollector<'_> { + fn visit_ty(&mut self, t: Ty<'tcx>) { + if let Some(vid) = t.ty_vid() { + self.res.insert(vid, (self.hir_id, self.call_span)); + } else { + use ty::TypeSuperVisitable as _; + t.super_visit_with(self) + } + } + } + + UnsafeInferVarsVisitor { root_ctxt, res: &mut res }.visit_expr(&body.value); + + debug!(?res, "collected the following unsafe vars for {body_id:?}"); + + res +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 2f96cf9e373ce..794b854ca5f95 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -5,12 +5,14 @@ mod checks; mod inspect_obligations; mod suggestions; +use rustc_errors::ErrorGuaranteed; + use crate::coercion::DynamicCoerceMany; use crate::fallback::DivergingFallbackBehavior; use crate::fn_ctxt::checks::DivergingBlockBehavior; use crate::{CoroutineTypes, Diverges, EnclosingBreakables, TypeckRootCtxt}; use hir::def_id::CRATE_DEF_ID; -use rustc_errors::{DiagCtxt, ErrorGuaranteed}; +use rustc_errors::DiagCtxt; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 86a0f33a8d158..664c63da0fc1e 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -69,6 +69,7 @@ declare_lint_pass! { MISSING_FRAGMENT_SPECIFIER, MUST_NOT_SUSPEND, NAMED_ARGUMENTS_USED_POSITIONALLY, + NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, NON_CONTIGUOUS_RANGE_ENDPOINTS, NON_EXHAUSTIVE_OMITTED_PATTERNS, ORDER_DEPENDENT_TRAIT_OBJECTS, @@ -4245,6 +4246,49 @@ declare_lint! { "named arguments in format used positionally" } +declare_lint! { + /// The `never_type_fallback_flowing_into_unsafe` lint detects cases where never type fallback + /// affects unsafe function calls. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(never_type_fallback_flowing_into_unsafe)] + /// fn main() { + /// if true { + /// // return has type `!` (never) which, is some cases, causes never type fallback + /// return + /// } else { + /// // `zeroed` is an unsafe function, which returns an unbounded type + /// unsafe { std::mem::zeroed() } + /// }; + /// // depending on the fallback, `zeroed` may create `()` (which is completely sound), + /// // or `!` (which is instant undefined behavior) + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Due to historic reasons never type fallback were `()`, meaning that `!` got spontaneously + /// coerced to `()`. There are plans to change that, but they may make the code such as above + /// unsound. Instead of depending on the fallback, you should specify the type explicitly: + /// ``` + /// if true { + /// return + /// } else { + /// // type is explicitly specified, fallback can't hurt us no more + /// unsafe { std::mem::zeroed::<()>() } + /// }; + /// ``` + /// + /// See [Tracking Issue for making `!` fall back to `!`](https://github.com/rust-lang/rust/issues/123748). + pub NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, + Warn, + "never type fallback affecting unsafe function calls" +} + declare_lint! { /// The `byte_slice_in_packed_struct_with_derive` lint detects cases where a byte slice field /// (`[u8]`) or string slice field (`str`) is used in a `packed` struct that derives one or diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.rs b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.rs new file mode 100644 index 0000000000000..f13e20cc0f2f1 --- /dev/null +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.rs @@ -0,0 +1,35 @@ +//@ check-pass +use std::mem; + +fn main() { + if false { + unsafe { mem::zeroed() } + //~^ warn: never type fallback affects this call to an `unsafe` function + } else { + return; + }; + + // no ; -> type is inferred without fallback + if true { unsafe { mem::zeroed() } } else { return } +} + +// Minimization of the famous `objc` crate issue +fn _objc() { + pub unsafe fn send_message() -> Result { + Ok(unsafe { core::mem::zeroed() }) + } + + macro_rules! msg_send { + () => { + match send_message::<_ /* ?0 */>() { + //~^ warn: never type fallback affects this call to an `unsafe` function + Ok(x) => x, + Err(_) => loop {}, + } + }; + } + + unsafe { + msg_send!(); + } +} diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.stderr b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.stderr new file mode 100644 index 0000000000000..1610804c29b94 --- /dev/null +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.stderr @@ -0,0 +1,23 @@ +warning: never type fallback affects this call to an `unsafe` function + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:6:18 + | +LL | unsafe { mem::zeroed() } + | ^^^^^^^^^^^^^ + | + = help: specify the type explicitly + = note: `#[warn(never_type_fallback_flowing_into_unsafe)]` on by default + +warning: never type fallback affects this call to an `unsafe` function + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:24:19 + | +LL | match send_message::<_ /* ?0 */>() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | msg_send!(); + | ----------- in this macro invocation + | + = help: specify the type explicitly + = note: this warning originates in the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: 2 warnings emitted + From b63cb6fd81e6d58af308ede0241ecf7563d4723c Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 19 Apr 2024 17:33:55 +0000 Subject: [PATCH 3/7] Add an explanation about never type fallback --- compiler/rustc_lint_defs/src/builtin.rs | 40 +++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 664c63da0fc1e..c7996c27c2f09 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4250,13 +4250,46 @@ declare_lint! { /// The `never_type_fallback_flowing_into_unsafe` lint detects cases where never type fallback /// affects unsafe function calls. /// + /// ### Never type fallback + /// + /// When the compiler sees a value of type [`!`] it implicitly inserts a coercion (if possible), + /// to allow type check to infer any type: + /// + /// ```ignore (illustrative-and-has-placeholders) + /// // this + /// let x: u8 = panic!(); + /// + /// // is (essentially) turned by the compiler into + /// let x: u8 = absurd(panic!()); + /// + /// // where absurd is a function with the following signature + /// // (it's sound, because `!` always marks unreachable code): + /// fn absurd(_: !) -> T { ... } + // FIXME: use `core::convert::absurd` here instead, once it's merged + /// ``` + /// + /// While it's convenient to be able to use non-diverging code in one of the branches (like + /// `if a { b } else { return }`) this could lead to compilation errors: + /// + /// ```compile_fail + /// // this + /// { panic!() }; + /// + /// // gets turned into this + /// { absurd(panic!()) }; // error: can't infer the type of `absurd` + /// ``` + /// + /// To prevent such errors, compiler remembers where it inserted `absurd` calls, and if it + /// can't infer their type, it sets the type to fallback. `{ absurd::(panic!()) };`. + /// This is what is known as "never type fallback". + /// /// ### Example /// /// ```rust,compile_fail /// #![deny(never_type_fallback_flowing_into_unsafe)] /// fn main() { /// if true { - /// // return has type `!` (never) which, is some cases, causes never type fallback + /// // return has type `!` which, is some cases, causes never type fallback /// return /// } else { /// // `zeroed` is an unsafe function, which returns an unbounded type @@ -4271,7 +4304,7 @@ declare_lint! { /// /// ### Explanation /// - /// Due to historic reasons never type fallback were `()`, meaning that `!` got spontaneously + /// Due to historic reasons never type fallback was `()`, meaning that `!` got spontaneously /// coerced to `()`. There are plans to change that, but they may make the code such as above /// unsound. Instead of depending on the fallback, you should specify the type explicitly: /// ``` @@ -4284,6 +4317,9 @@ declare_lint! { /// ``` /// /// See [Tracking Issue for making `!` fall back to `!`](https://github.com/rust-lang/rust/issues/123748). + /// + /// [`!`]: https://doc.rust-lang.org/core/primitive.never.html + /// [`()`]: https://doc.rust-lang.org/core/primitive.unit.html pub NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, Warn, "never type fallback affecting unsafe function calls" From fe9c5e651092786565b077841f69fa5559701ae5 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Sun, 28 Apr 2024 19:26:23 +0200 Subject: [PATCH 4/7] Reorganize `use`s --- compiler/rustc_hir_typeck/src/fallback.rs | 24 +++++++++++------------ 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 86a75aa4d7873..52ece8887b972 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -1,16 +1,18 @@ use std::cell::OnceCell; -use crate::{errors, FnCtxt}; +use crate::{errors, FnCtxt, TypeckRootCtxt}; use rustc_data_structures::{ graph::{self, iterate::DepthFirstSearch, vec_graph::VecGraph}, unord::{UnordBag, UnordMap, UnordSet}, }; +use rustc_hir as hir; +use rustc_hir::intravisit::Visitor; use rustc_hir::HirId; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable}; use rustc_session::lint; -use rustc_span::Span; use rustc_span::DUMMY_SP; +use rustc_span::{def_id::LocalDefId, Span}; #[derive(Copy, Clone)] pub enum DivergingFallbackBehavior { @@ -508,23 +510,20 @@ impl<'tcx> FnCtxt<'_, 'tcx> { /// /// Will return `{ id(?X) -> (hir_id, span) }` fn compute_unsafe_infer_vars<'a, 'tcx>( - root_ctxt: &'a crate::TypeckRootCtxt<'tcx>, - body_id: rustc_span::def_id::LocalDefId, + root_ctxt: &'a TypeckRootCtxt<'tcx>, + body_id: LocalDefId, ) -> UnordMap { - use rustc_hir as hir; - let tcx = root_ctxt.infcx.tcx; - let body_id = tcx.hir().maybe_body_owned_by(body_id).unwrap(); + let body_id = tcx.hir().maybe_body_owned_by(body_id).expect("body id must have an owner"); let body = tcx.hir().body(body_id); - let mut res = <_>::default(); + let mut res = UnordMap::default(); struct UnsafeInferVarsVisitor<'a, 'tcx, 'r> { - root_ctxt: &'a crate::TypeckRootCtxt<'tcx>, + root_ctxt: &'a TypeckRootCtxt<'tcx>, res: &'r mut UnordMap, } - use hir::intravisit::Visitor; - impl hir::intravisit::Visitor<'_> for UnsafeInferVarsVisitor<'_, '_, '_> { + impl Visitor<'_> for UnsafeInferVarsVisitor<'_, '_, '_> { fn visit_expr(&mut self, ex: &'_ hir::Expr<'_>) { // FIXME: method calls if let hir::ExprKind::Call(func, ..) = ex.kind { @@ -566,7 +565,6 @@ fn compute_unsafe_infer_vars<'a, 'tcx>( if let Some(vid) = t.ty_vid() { self.res.insert(vid, (self.hir_id, self.call_span)); } else { - use ty::TypeSuperVisitable as _; t.super_visit_with(self) } } From 3c815a644cd1f9139efe960c0d2ba3accdc4092b Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Mon, 29 Apr 2024 01:53:54 +0200 Subject: [PATCH 5/7] Add `UnordMap::try_insert` --- compiler/rustc_data_structures/src/lib.rs | 1 + compiler/rustc_data_structures/src/unord.rs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 0c322939db97b..cf54e700e2b98 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -25,6 +25,7 @@ #![feature(lazy_cell)] #![feature(lint_reasons)] #![feature(macro_metavar_expr)] +#![feature(map_try_insert)] #![feature(maybe_uninit_uninit_array)] #![feature(min_specialization)] #![feature(negative_impls)] diff --git a/compiler/rustc_data_structures/src/unord.rs b/compiler/rustc_data_structures/src/unord.rs index ca66d58c1395e..1ccd22a56c959 100644 --- a/compiler/rustc_data_structures/src/unord.rs +++ b/compiler/rustc_data_structures/src/unord.rs @@ -4,6 +4,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; use rustc_macros::{Decodable_Generic, Encodable_Generic}; +use std::collections::hash_map::OccupiedError; use std::{ borrow::{Borrow, BorrowMut}, collections::hash_map::Entry, @@ -469,6 +470,11 @@ impl UnordMap { self.inner.insert(k, v) } + #[inline] + pub fn try_insert(&mut self, k: K, v: V) -> Result<&mut V, OccupiedError<'_, K, V>> { + self.inner.try_insert(k, v) + } + #[inline] pub fn contains_key(&self, k: &Q) -> bool where From 0df43db50c8afac0e6406a92721b1c669b882c03 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Mon, 29 Apr 2024 01:54:16 +0200 Subject: [PATCH 6/7] Add proper support for all kinds of unsafe ops to the lint (never_type_fallback_flowing_into_unsafe) --- compiler/rustc_hir_typeck/messages.ftl | 9 +- compiler/rustc_hir_typeck/src/errors.rs | 6 +- compiler/rustc_hir_typeck/src/fallback.rs | 175 ++++++++++++++---- ...never-type-fallback-flowing-into-unsafe.rs | 110 ++++++++++- ...r-type-fallback-flowing-into-unsafe.stderr | 70 ++++++- 5 files changed, 321 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 0caebf44a193e..ed288251021d2 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -100,7 +100,14 @@ hir_typeck_lossy_provenance_ptr2int = hir_typeck_missing_parentheses_in_range = can't call method `{$method_name}` on type `{$ty_str}` hir_typeck_never_type_fallback_flowing_into_unsafe = - never type fallback affects this call to an `unsafe` function + never type fallback affects this {$reason -> + [call] call to an `unsafe` function + [union_field] union access + [deref] raw pointer dereference + [path] `unsafe` function + [method] call to an `unsafe` method + *[other] THIS SHOULD NOT BE REACHABLE + } .help = specify the type explicitly hir_typeck_no_associated_item = no {$item_kind} named `{$item_name}` found for {$ty_prefix} `{$ty_str}`{$trait_missing_method -> diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index fcad88f829e24..2bb46c8d288cf 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -1,7 +1,7 @@ //! Errors emitted by `rustc_hir_typeck`. use std::borrow::Cow; -use crate::fluent_generated as fluent; +use crate::{fallback::UnsafeUseReason, fluent_generated as fluent}; use rustc_errors::{ codes::*, Applicability, Diag, DiagArgValue, EmissionGuarantee, IntoDiagArg, MultiSpan, SubdiagMessageOp, Subdiagnostic, @@ -167,7 +167,9 @@ pub struct MissingParenthesesInRange { #[derive(LintDiagnostic)] #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe)] #[help] -pub struct NeverTypeFallbackFlowingIntoUnsafe {} +pub struct NeverTypeFallbackFlowingIntoUnsafe { + pub reason: UnsafeUseReason, +} #[derive(Subdiagnostic)] #[multipart_suggestion( diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 52ece8887b972..7a89191545477 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -1,10 +1,11 @@ -use std::cell::OnceCell; +use std::{borrow::Cow, cell::OnceCell}; use crate::{errors, FnCtxt, TypeckRootCtxt}; use rustc_data_structures::{ graph::{self, iterate::DepthFirstSearch, vec_graph::VecGraph}, unord::{UnordBag, UnordMap, UnordSet}, }; +use rustc_errors::{DiagArgValue, IntoDiagArg}; use rustc_hir as hir; use rustc_hir::intravisit::Visitor; use rustc_hir::HirId; @@ -374,12 +375,12 @@ impl<'tcx> FnCtxt<'_, 'tcx> { .filter_map(|x| unsafe_infer_vars.get(&x).copied()) .collect::>(); - for (hir_id, span) in affected_unsafe_infer_vars { + for (hir_id, span, reason) in affected_unsafe_infer_vars { self.tcx.emit_node_span_lint( lint::builtin::NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, hir_id, span, - errors::NeverTypeFallbackFlowingIntoUnsafe {}, + errors::NeverTypeFallbackFlowingIntoUnsafe { reason }, ); } @@ -493,77 +494,169 @@ impl<'tcx> FnCtxt<'_, 'tcx> { } } -/// Finds all type variables which are passed to an `unsafe` function. +#[derive(Debug, Copy, Clone)] +pub(crate) enum UnsafeUseReason { + Call, + Method, + Path, + UnionField, + Deref, +} + +impl IntoDiagArg for UnsafeUseReason { + fn into_diag_arg(self) -> DiagArgValue { + let s = match self { + UnsafeUseReason::Call => "call", + UnsafeUseReason::Method => "method", + UnsafeUseReason::Path => "path", + UnsafeUseReason::UnionField => "union_field", + UnsafeUseReason::Deref => "deref", + }; + DiagArgValue::Str(Cow::Borrowed(s)) + } +} + +/// Finds all type variables which are passed to an `unsafe` operation. /// /// For example, for this function `f`: /// ```ignore (demonstrative) /// fn f() { /// unsafe { /// let x /* ?X */ = core::mem::zeroed(); -/// // ^^^^^^^^^^^^^^^^^^^ -- hir_id, span +/// // ^^^^^^^^^^^^^^^^^^^ -- hir_id, span, reason /// /// let y = core::mem::zeroed::>(); -/// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- hir_id, span +/// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- hir_id, span, reason /// } /// } /// ``` /// -/// Will return `{ id(?X) -> (hir_id, span) }` +/// `compute_unsafe_infer_vars` will return `{ id(?X) -> (hir_id, span, Call) }` fn compute_unsafe_infer_vars<'a, 'tcx>( root_ctxt: &'a TypeckRootCtxt<'tcx>, body_id: LocalDefId, -) -> UnordMap { - let tcx = root_ctxt.infcx.tcx; - let body_id = tcx.hir().maybe_body_owned_by(body_id).expect("body id must have an owner"); - let body = tcx.hir().body(body_id); +) -> UnordMap { + let body_id = + root_ctxt.tcx.hir().maybe_body_owned_by(body_id).expect("body id must have an owner"); + let body = root_ctxt.tcx.hir().body(body_id); let mut res = UnordMap::default(); struct UnsafeInferVarsVisitor<'a, 'tcx, 'r> { root_ctxt: &'a TypeckRootCtxt<'tcx>, - res: &'r mut UnordMap, + res: &'r mut UnordMap, } impl Visitor<'_> for UnsafeInferVarsVisitor<'_, '_, '_> { fn visit_expr(&mut self, ex: &'_ hir::Expr<'_>) { - // FIXME: method calls - if let hir::ExprKind::Call(func, ..) = ex.kind { - let typeck_results = self.root_ctxt.typeck_results.borrow(); - - let func_ty = typeck_results.expr_ty(func); - - // `is_fn` is required to ignore closures (which can't be unsafe) - if func_ty.is_fn() - && let sig = func_ty.fn_sig(self.root_ctxt.infcx.tcx) - && let hir::Unsafety::Unsafe = sig.unsafety() - { - let mut collector = - InferVarCollector { hir_id: ex.hir_id, call_span: ex.span, res: self.res }; - - // Collect generic arguments of the function which are inference variables - typeck_results - .node_args(ex.hir_id) - .types() - .for_each(|t| t.visit_with(&mut collector)); - - // Also check the return type, for cases like `(unsafe_fn::<_> as unsafe fn() -> _)()` - sig.output().visit_with(&mut collector); + let typeck_results = self.root_ctxt.typeck_results.borrow(); + + match ex.kind { + hir::ExprKind::MethodCall(..) => { + if let Some(def_id) = typeck_results.type_dependent_def_id(ex.hir_id) + && let method_ty = self.root_ctxt.tcx.type_of(def_id).instantiate_identity() + && let sig = method_ty.fn_sig(self.root_ctxt.tcx) + && let hir::Unsafety::Unsafe = sig.unsafety() + { + let mut collector = InferVarCollector { + value: (ex.hir_id, ex.span, UnsafeUseReason::Method), + res: self.res, + }; + + // Collect generic arguments (incl. `Self`) of the method + typeck_results + .node_args(ex.hir_id) + .types() + .for_each(|t| t.visit_with(&mut collector)); + } } - } + + hir::ExprKind::Call(func, ..) => { + let func_ty = typeck_results.expr_ty(func); + + if func_ty.is_fn() + && let sig = func_ty.fn_sig(self.root_ctxt.tcx) + && let hir::Unsafety::Unsafe = sig.unsafety() + { + let mut collector = InferVarCollector { + value: (ex.hir_id, ex.span, UnsafeUseReason::Call), + res: self.res, + }; + + // Try collecting generic arguments of the function. + // Note that we do this below for any paths (that don't have to be called), + // but there we do it with a different span/reason. + // This takes priority. + typeck_results + .node_args(func.hir_id) + .types() + .for_each(|t| t.visit_with(&mut collector)); + + // Also check the return type, for cases like `returns_unsafe_fn_ptr()()` + sig.output().visit_with(&mut collector); + } + } + + // Check paths which refer to functions. + // We do this, instead of only checking `Call` to make sure the lint can't be + // avoided by storing unsafe function in a variable. + hir::ExprKind::Path(_) => { + let ty = typeck_results.expr_ty(ex); + + // If this path refers to an unsafe function, collect inference variables which may affect it. + // `is_fn` excludes closures, but those can't be unsafe. + if ty.is_fn() + && let sig = ty.fn_sig(self.root_ctxt.tcx) + && let hir::Unsafety::Unsafe = sig.unsafety() + { + let mut collector = InferVarCollector { + value: (ex.hir_id, ex.span, UnsafeUseReason::Path), + res: self.res, + }; + + // Collect generic arguments of the function + typeck_results + .node_args(ex.hir_id) + .types() + .for_each(|t| t.visit_with(&mut collector)); + } + } + + hir::ExprKind::Unary(hir::UnOp::Deref, pointer) => { + if let ty::RawPtr(pointee, _) = typeck_results.expr_ty(pointer).kind() { + pointee.visit_with(&mut InferVarCollector { + value: (ex.hir_id, ex.span, UnsafeUseReason::Deref), + res: self.res, + }); + } + } + + hir::ExprKind::Field(base, _) => { + let base_ty = typeck_results.expr_ty(base); + + if base_ty.is_union() { + typeck_results.expr_ty(ex).visit_with(&mut InferVarCollector { + value: (ex.hir_id, ex.span, UnsafeUseReason::UnionField), + res: self.res, + }); + } + } + + _ => (), + }; hir::intravisit::walk_expr(self, ex); } } - struct InferVarCollector<'r> { - hir_id: HirId, - call_span: Span, - res: &'r mut UnordMap, + struct InferVarCollector<'r, V> { + value: V, + res: &'r mut UnordMap, } - impl<'tcx> ty::TypeVisitor> for InferVarCollector<'_> { + impl<'tcx, V: Copy> ty::TypeVisitor> for InferVarCollector<'_, V> { fn visit_ty(&mut self, t: Ty<'tcx>) { if let Some(vid) = t.ty_vid() { - self.res.insert(vid, (self.hir_id, self.call_span)); + _ = self.res.try_insert(vid, self.value); } else { t.super_visit_with(self) } diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.rs b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.rs index f13e20cc0f2f1..0ae498c134f09 100644 --- a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.rs +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.rs @@ -1,7 +1,9 @@ //@ check-pass -use std::mem; +use std::{marker, mem, ptr}; -fn main() { +fn main() {} + +fn _zero() { if false { unsafe { mem::zeroed() } //~^ warn: never type fallback affects this call to an `unsafe` function @@ -13,6 +15,110 @@ fn main() { if true { unsafe { mem::zeroed() } } else { return } } +fn _trans() { + if false { + unsafe { + struct Zst; + core::mem::transmute(Zst) + //~^ warn: never type fallback affects this call to an `unsafe` function + } + } else { + return; + }; +} + +fn _union() { + if false { + union Union { + a: (), + b: T, + } + + unsafe { Union { a: () }.b } + //~^ warn: never type fallback affects this union access + } else { + return; + }; +} + +fn _deref() { + if false { + unsafe { *ptr::from_ref(&()).cast() } + //~^ warn: never type fallback affects this raw pointer dereference + } else { + return; + }; +} + +fn _only_generics() { + if false { + unsafe fn internally_create(_: Option) { + let _ = mem::zeroed::(); + } + + // We need the option (and unwrap later) to call a function in a way, + // which makes it affected by the fallback, but without having it return anything + let x = None; + + unsafe { internally_create(x) } + //~^ warn: never type fallback affects this call to an `unsafe` function + + x.unwrap() + } else { + return; + }; +} + +fn _stored_function() { + if false { + let zeroed = mem::zeroed; + //~^ warn: never type fallback affects this `unsafe` function + + unsafe { zeroed() } + //~^ warn: never type fallback affects this call to an `unsafe` function + } else { + return; + }; +} + +fn _only_generics_stored_function() { + if false { + unsafe fn internally_create(_: Option) { + let _ = mem::zeroed::(); + } + + let x = None; + let f = internally_create; + //~^ warn: never type fallback affects this `unsafe` function + + unsafe { f(x) } + + x.unwrap() + } else { + return; + }; +} + +fn _method() { + struct S(marker::PhantomData); + + impl S { + #[allow(unused)] // FIXME: the unused lint is probably incorrect here + unsafe fn create_out_of_thin_air(&self) -> T { + todo!() + } + } + + if false { + unsafe { + S(marker::PhantomData).create_out_of_thin_air() + //~^ warn: never type fallback affects this call to an `unsafe` method + } + } else { + return; + }; +} + // Minimization of the famous `objc` crate issue fn _objc() { pub unsafe fn send_message() -> Result { diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.stderr b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.stderr index 1610804c29b94..84c9385fd139c 100644 --- a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.stderr +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.stderr @@ -1,5 +1,5 @@ warning: never type fallback affects this call to an `unsafe` function - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:6:18 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:8:18 | LL | unsafe { mem::zeroed() } | ^^^^^^^^^^^^^ @@ -8,7 +8,71 @@ LL | unsafe { mem::zeroed() } = note: `#[warn(never_type_fallback_flowing_into_unsafe)]` on by default warning: never type fallback affects this call to an `unsafe` function - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:24:19 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:22:13 + | +LL | core::mem::transmute(Zst) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: specify the type explicitly + +warning: never type fallback affects this union access + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:37:18 + | +LL | unsafe { Union { a: () }.b } + | ^^^^^^^^^^^^^^^^^ + | + = help: specify the type explicitly + +warning: never type fallback affects this raw pointer dereference + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:46:18 + | +LL | unsafe { *ptr::from_ref(&()).cast() } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: specify the type explicitly + +warning: never type fallback affects this call to an `unsafe` function + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:63:18 + | +LL | unsafe { internally_create(x) } + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: specify the type explicitly + +warning: never type fallback affects this call to an `unsafe` function + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:77:18 + | +LL | unsafe { zeroed() } + | ^^^^^^^^ + | + = help: specify the type explicitly + +warning: never type fallback affects this `unsafe` function + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:74:22 + | +LL | let zeroed = mem::zeroed; + | ^^^^^^^^^^^ + | + = help: specify the type explicitly + +warning: never type fallback affects this `unsafe` function + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:91:17 + | +LL | let f = internally_create; + | ^^^^^^^^^^^^^^^^^ + | + = help: specify the type explicitly + +warning: never type fallback affects this call to an `unsafe` method + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:114:13 + | +LL | S(marker::PhantomData).create_out_of_thin_air() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: specify the type explicitly + +warning: never type fallback affects this call to an `unsafe` function + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:130:19 | LL | match send_message::<_ /* ?0 */>() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -19,5 +83,5 @@ LL | msg_send!(); = help: specify the type explicitly = note: this warning originates in the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) -warning: 2 warnings emitted +warning: 10 warnings emitted From b5626172d13bed3f82ead73493e548d6df9bf63a Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Thu, 2 May 2024 03:29:18 +0200 Subject: [PATCH 7/7] Split `hir_typeck_never_type_fallback_flowing_into_unsafe` into 5 slugs --- compiler/rustc_hir_typeck/messages.ftl | 18 ++++++------ compiler/rustc_hir_typeck/src/errors.rs | 22 +++++++++++---- compiler/rustc_hir_typeck/src/fallback.rs | 34 ++++++++++++----------- 3 files changed, 44 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index ed288251021d2..0560d0d902a3f 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -99,15 +99,15 @@ hir_typeck_lossy_provenance_ptr2int = hir_typeck_missing_parentheses_in_range = can't call method `{$method_name}` on type `{$ty_str}` -hir_typeck_never_type_fallback_flowing_into_unsafe = - never type fallback affects this {$reason -> - [call] call to an `unsafe` function - [union_field] union access - [deref] raw pointer dereference - [path] `unsafe` function - [method] call to an `unsafe` method - *[other] THIS SHOULD NOT BE REACHABLE - } +hir_typeck_never_type_fallback_flowing_into_unsafe_call = never type fallback affects this call to an `unsafe` function + .help = specify the type explicitly +hir_typeck_never_type_fallback_flowing_into_unsafe_deref = never type fallback affects this raw pointer dereference + .help = specify the type explicitly +hir_typeck_never_type_fallback_flowing_into_unsafe_method = never type fallback affects this call to an `unsafe` method + .help = specify the type explicitly +hir_typeck_never_type_fallback_flowing_into_unsafe_path = never type fallback affects this `unsafe` function + .help = specify the type explicitly +hir_typeck_never_type_fallback_flowing_into_unsafe_union_field = never type fallback affects this union access .help = specify the type explicitly hir_typeck_no_associated_item = no {$item_kind} named `{$item_name}` found for {$ty_prefix} `{$ty_str}`{$trait_missing_method -> diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 2bb46c8d288cf..ba8f246fd8d24 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -1,7 +1,7 @@ //! Errors emitted by `rustc_hir_typeck`. use std::borrow::Cow; -use crate::{fallback::UnsafeUseReason, fluent_generated as fluent}; +use crate::fluent_generated as fluent; use rustc_errors::{ codes::*, Applicability, Diag, DiagArgValue, EmissionGuarantee, IntoDiagArg, MultiSpan, SubdiagMessageOp, Subdiagnostic, @@ -165,10 +165,22 @@ pub struct MissingParenthesesInRange { } #[derive(LintDiagnostic)] -#[diag(hir_typeck_never_type_fallback_flowing_into_unsafe)] -#[help] -pub struct NeverTypeFallbackFlowingIntoUnsafe { - pub reason: UnsafeUseReason, +pub enum NeverTypeFallbackFlowingIntoUnsafe { + #[help] + #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_call)] + Call, + #[help] + #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_method)] + Method, + #[help] + #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_path)] + Path, + #[help] + #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_union_field)] + UnionField, + #[help] + #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_deref)] + Deref, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 7a89191545477..f240a53a67974 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -1,11 +1,10 @@ -use std::{borrow::Cow, cell::OnceCell}; +use std::cell::OnceCell; use crate::{errors, FnCtxt, TypeckRootCtxt}; use rustc_data_structures::{ graph::{self, iterate::DepthFirstSearch, vec_graph::VecGraph}, unord::{UnordBag, UnordMap, UnordSet}, }; -use rustc_errors::{DiagArgValue, IntoDiagArg}; use rustc_hir as hir; use rustc_hir::intravisit::Visitor; use rustc_hir::HirId; @@ -380,7 +379,23 @@ impl<'tcx> FnCtxt<'_, 'tcx> { lint::builtin::NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, hir_id, span, - errors::NeverTypeFallbackFlowingIntoUnsafe { reason }, + match reason { + UnsafeUseReason::Call => { + errors::NeverTypeFallbackFlowingIntoUnsafe::Call + } + UnsafeUseReason::Method => { + errors::NeverTypeFallbackFlowingIntoUnsafe::Method + } + UnsafeUseReason::Path => { + errors::NeverTypeFallbackFlowingIntoUnsafe::Path + } + UnsafeUseReason::UnionField => { + errors::NeverTypeFallbackFlowingIntoUnsafe::UnionField + } + UnsafeUseReason::Deref => { + errors::NeverTypeFallbackFlowingIntoUnsafe::Deref + } + }, ); } @@ -503,19 +518,6 @@ pub(crate) enum UnsafeUseReason { Deref, } -impl IntoDiagArg for UnsafeUseReason { - fn into_diag_arg(self) -> DiagArgValue { - let s = match self { - UnsafeUseReason::Call => "call", - UnsafeUseReason::Method => "method", - UnsafeUseReason::Path => "path", - UnsafeUseReason::UnionField => "union_field", - UnsafeUseReason::Deref => "deref", - }; - DiagArgValue::Str(Cow::Borrowed(s)) - } -} - /// Finds all type variables which are passed to an `unsafe` operation. /// /// For example, for this function `f`: