Auto merge of #123939 - WaffleLapkin:never-fallback-unsafe-lint, r=compiler-errors

Add a lint against never type fallback affecting unsafe code

~~I'm not very happy with the code quality... `VecGraph` not allowing you to get predecessors is very annoying. This should work though, so there is that.~~ (ended up updating `VecGraph` to support getting predecessors)

~~First few commits are from https://github.com/rust-lang/rust/pull/123934 https://github.com/rust-lang/rust/pull/123980~~
This commit is contained in:
bors 2024-05-02 02:41:40 +00:00
commit fcc06c894b
9 changed files with 574 additions and 21 deletions

View File

@ -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)]

View File

@ -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<K: Eq + Hash, V> UnordMap<K, V> {
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<Q: ?Sized>(&self, k: &Q) -> bool
where

View File

@ -99,6 +99,17 @@ 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_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 ->
[true] {""}
*[other] {" "}in the current scope

View File

@ -164,6 +164,25 @@ pub struct MissingParenthesesInRange {
pub add_missing_parentheses: Option<AddMissingParenthesesInRange>,
}
#[derive(LintDiagnostic)]
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)]
#[multipart_suggestion(
hir_typeck_add_missing_parentheses_in_range,
@ -632,7 +651,6 @@ pub enum SuggestBoxingForReturnImplTrait {
ends: Vec<Span>,
},
}
#[derive(LintDiagnostic)]
#[diag(hir_typeck_dereferencing_mut_binding)]
pub struct DereferencingMutBinding {

View File

@ -1,11 +1,18 @@
use crate::FnCtxt;
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_hir as hir;
use rustc_hir::intravisit::Visitor;
use rustc_hir::HirId;
use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
use rustc_session::lint;
use rustc_span::DUMMY_SP;
use rustc_span::{def_id::LocalDefId, Span};
#[derive(Copy, Clone)]
pub enum DivergingFallbackBehavior {
@ -335,6 +342,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 +362,51 @@ 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::<Vec<_>>();
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,
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
}
},
);
}
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 +435,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 +449,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 +465,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<ty::TyVid> {
fn create_coercion_graph(&self) -> VecGraph<ty::TyVid, true> {
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
@ -436,17 +484,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)?;
@ -456,6 +499,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)
}
@ -464,3 +508,166 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
Some(self.root_var(self.shallow_resolve(ty).ty_vid()?))
}
}
#[derive(Debug, Copy, Clone)]
pub(crate) enum UnsafeUseReason {
Call,
Method,
Path,
UnionField,
Deref,
}
/// 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, reason
///
/// let y = core::mem::zeroed::<Option<_ /* ?Y */>>();
/// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- hir_id, span, reason
/// }
/// }
/// ```
///
/// `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<ty::TyVid, (HirId, Span, UnsafeUseReason)> {
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<ty::TyVid, (HirId, Span, UnsafeUseReason)>,
}
impl Visitor<'_> for UnsafeInferVarsVisitor<'_, '_, '_> {
fn visit_expr(&mut self, ex: &'_ hir::Expr<'_>) {
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, V> {
value: V,
res: &'r mut UnordMap<ty::TyVid, V>,
}
impl<'tcx, V: Copy> ty::TypeVisitor<TyCtxt<'tcx>> for InferVarCollector<'_, V> {
fn visit_ty(&mut self, t: Ty<'tcx>) {
if let Some(vid) = t.ty_vid() {
_ = self.res.try_insert(vid, self.value);
} else {
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
}

View File

@ -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;

View File

@ -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,85 @@ 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.
///
/// ### 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>(_: !) -> 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::<Fallback>(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 `!` 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 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:
/// ```
/// 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).
///
/// [`!`]: 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"
}
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

View File

@ -0,0 +1,141 @@
//@ check-pass
use std::{marker, mem, ptr};
fn main() {}
fn _zero() {
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 }
}
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<T: Copy> {
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<T>(_: Option<T>) {
let _ = mem::zeroed::<T>();
}
// 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<T>(_: Option<T>) {
let _ = mem::zeroed::<T>();
}
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<T>(marker::PhantomData<T>);
impl<T> S<T> {
#[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<R>() -> Result<R, ()> {
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!();
}
}

View File

@ -0,0 +1,87 @@
warning: never type fallback affects this call to an `unsafe` function
--> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:8: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: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 */>() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
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: 10 warnings emitted