mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Implement lint for obligations broken by never type fallback change
This commit is contained in:
parent
921645c737
commit
83f8f9f85d
@ -44,6 +44,9 @@ hir_typeck_convert_using_method = try using `{$sugg}` to convert `{$found}` to `
|
||||
|
||||
hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private
|
||||
|
||||
hir_typeck_dependency_on_unit_never_type_fallback = this function depends on never type fallback being `()`
|
||||
.help = specify the types explicitly
|
||||
|
||||
hir_typeck_deref_is_empty = this expression `Deref`s to `{$deref_ty}` which implements `is_empty`
|
||||
|
||||
hir_typeck_expected_default_return_type = expected `()` because of default return type
|
||||
|
@ -183,6 +183,11 @@ pub enum NeverTypeFallbackFlowingIntoUnsafe {
|
||||
Deref,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[help]
|
||||
#[diag(hir_typeck_dependency_on_unit_never_type_fallback)]
|
||||
pub struct DependencyOnUnitNeverTypeFallback {}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(
|
||||
hir_typeck_add_missing_parentheses_in_range,
|
||||
|
@ -8,12 +8,17 @@ use rustc_data_structures::{
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::HirId;
|
||||
use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
|
||||
use rustc_middle::bug;
|
||||
use rustc_infer::{
|
||||
infer::{DefineOpaqueTypes, InferOk},
|
||||
traits::ObligationCause,
|
||||
};
|
||||
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};
|
||||
use rustc_trait_selection::traits::ObligationCtxt;
|
||||
use rustc_type_ir::TyVid;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum DivergingFallbackBehavior {
|
||||
@ -344,6 +349,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
|
||||
// `!`.
|
||||
let mut diverging_fallback = UnordMap::with_capacity(diverging_vids.len());
|
||||
let unsafe_infer_vars = OnceCell::new();
|
||||
|
||||
self.lint_obligations_broken_by_never_type_fallback_change(behavior, &diverging_vids);
|
||||
|
||||
for &diverging_vid in &diverging_vids {
|
||||
let diverging_ty = Ty::new_var(self.tcx, diverging_vid);
|
||||
let root_vid = self.root_var(diverging_vid);
|
||||
@ -468,6 +476,46 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_obligations_broken_by_never_type_fallback_change(
|
||||
&self,
|
||||
behavior: DivergingFallbackBehavior,
|
||||
diverging_vids: &[TyVid],
|
||||
) {
|
||||
let DivergingFallbackBehavior::FallbackToUnit = behavior else { return };
|
||||
|
||||
// Returns errors which happen if fallback is set to `fallback`
|
||||
let try_out = |fallback| {
|
||||
self.probe(|_| {
|
||||
let obligations = self.fulfillment_cx.borrow().pending_obligations();
|
||||
let ocx = ObligationCtxt::new(&self.infcx);
|
||||
ocx.register_obligations(obligations.iter().cloned());
|
||||
|
||||
for &diverging_vid in diverging_vids {
|
||||
let diverging_ty = Ty::new_var(self.tcx, diverging_vid);
|
||||
|
||||
_ = ocx.eq(&ObligationCause::dummy(), self.param_env, diverging_ty, fallback);
|
||||
}
|
||||
|
||||
ocx.select_where_possible()
|
||||
})
|
||||
};
|
||||
|
||||
// If we have no errors with `fallback = ()`, but *do* have errors with `fallback = !`,
|
||||
// then this code will be broken by the never type fallback change.qba
|
||||
let unit_errors = try_out(self.tcx.types.unit);
|
||||
if unit_errors.is_empty()
|
||||
&& let never_errors = try_out(self.tcx.types.never)
|
||||
&& !never_errors.is_empty()
|
||||
{
|
||||
self.tcx.emit_node_span_lint(
|
||||
lint::builtin::DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK,
|
||||
self.tcx.local_def_id_to_hir_id(self.body_id),
|
||||
self.tcx.def_span(self.body_id),
|
||||
errors::DependencyOnUnitNeverTypeFallback {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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, true> {
|
||||
|
@ -34,6 +34,7 @@ declare_lint_pass! {
|
||||
CONST_EVALUATABLE_UNCHECKED,
|
||||
CONST_ITEM_MUTATION,
|
||||
DEAD_CODE,
|
||||
DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK,
|
||||
DEPRECATED,
|
||||
DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
|
||||
DEPRECATED_IN_FUTURE,
|
||||
@ -4199,6 +4200,62 @@ declare_lint! {
|
||||
report_in_external_macro
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `dependency_on_unit_never_type_fallback` lint detects cases where code compiles with
|
||||
/// [never type fallback] being [`()`], but will stop compiling with fallback being [`!`].
|
||||
///
|
||||
/// [never type fallback]: prim@never#never-type-fallback
|
||||
/// [`()`]: prim@unit
|
||||
/// [`!`]:
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// #![deny(dependency_on_unit_never_type_fallback)]
|
||||
/// fn main() {
|
||||
/// if true {
|
||||
/// // return has type `!` which, is some cases, causes never type fallback
|
||||
/// return
|
||||
/// } else {
|
||||
/// // the type produced by this call is not specified explicitly,
|
||||
/// // so it will be inferred from the previous branch
|
||||
/// Default::default()
|
||||
/// };
|
||||
/// // depending on the fallback, this may compile (because `()` implements `Default`),
|
||||
/// // or it may not (because `!` does not implement `Default`)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {{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
|
||||
/// not compile. 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
|
||||
/// <() as Default>::default()
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// 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 DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK,
|
||||
Warn,
|
||||
"never type fallback affecting unsafe function calls",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
|
||||
reference: "issue #123748 <https://github.com/rust-lang/rust/issues/123748>",
|
||||
};
|
||||
report_in_external_macro
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -1,5 +1,5 @@
|
||||
error[E0277]: the trait bound `!: ImplementedForUnitButNotNever` is not satisfied
|
||||
--> $DIR/defaulted-never-note.rs:30:9
|
||||
--> $DIR/defaulted-never-note.rs:32:9
|
||||
|
|
||||
LL | foo(_x);
|
||||
| --- ^^ the trait `ImplementedForUnitButNotNever` is not implemented for `!`
|
||||
|
13
tests/ui/never_type/defaulted-never-note.nofallback.stderr
Normal file
13
tests/ui/never_type/defaulted-never-note.nofallback.stderr
Normal file
@ -0,0 +1,13 @@
|
||||
warning: this function depends on never type fallback being `()`
|
||||
--> $DIR/defaulted-never-note.rs:28:1
|
||||
|
|
||||
LL | fn smeg() {
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748>
|
||||
= help: specify the types explicitly
|
||||
= note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
@ -26,6 +26,8 @@ fn foo<T: ImplementedForUnitButNotNever>(_t: T) {}
|
||||
//[fallback]~^ NOTE required by this bound in `foo`
|
||||
//[fallback]~| NOTE required by a bound in `foo`
|
||||
fn smeg() {
|
||||
//[nofallback]~^ warn: this function depends on never type fallback being `()`
|
||||
//[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
let _x = return;
|
||||
foo(_x);
|
||||
//[fallback]~^ ERROR the trait bound
|
||||
|
28
tests/ui/never_type/dependency-on-fallback-to-unit.rs
Normal file
28
tests/ui/never_type/dependency-on-fallback-to-unit.rs
Normal file
@ -0,0 +1,28 @@
|
||||
//@ check-pass
|
||||
|
||||
fn main() {
|
||||
def();
|
||||
_ = question_mark();
|
||||
}
|
||||
|
||||
fn def() {
|
||||
//~^ warn: this function depends on never type fallback being `()`
|
||||
//~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
match true {
|
||||
false => <_>::default(),
|
||||
true => return,
|
||||
};
|
||||
}
|
||||
|
||||
// <https://github.com/rust-lang/rust/issues/51125>
|
||||
// <https://github.com/rust-lang/rust/issues/39216>
|
||||
fn question_mark() -> Result<(), ()> {
|
||||
//~^ warn: this function depends on never type fallback being `()`
|
||||
//~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
deserialize()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize<T: Default>() -> Result<T, ()> {
|
||||
Ok(T::default())
|
||||
}
|
23
tests/ui/never_type/dependency-on-fallback-to-unit.stderr
Normal file
23
tests/ui/never_type/dependency-on-fallback-to-unit.stderr
Normal file
@ -0,0 +1,23 @@
|
||||
warning: this function depends on never type fallback being `()`
|
||||
--> $DIR/dependency-on-fallback-to-unit.rs:8:1
|
||||
|
|
||||
LL | fn def() {
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748>
|
||||
= help: specify the types explicitly
|
||||
= note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default
|
||||
|
||||
warning: this function depends on never type fallback being `()`
|
||||
--> $DIR/dependency-on-fallback-to-unit.rs:19:1
|
||||
|
|
||||
LL | fn question_mark() -> Result<(), ()> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748>
|
||||
= help: specify the types explicitly
|
||||
|
||||
warning: 2 warnings emitted
|
||||
|
@ -0,0 +1,23 @@
|
||||
warning: this function depends on never type fallback being `()`
|
||||
--> $DIR/diverging-fallback-control-flow.rs:30:1
|
||||
|
|
||||
LL | fn assignment() {
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748>
|
||||
= help: specify the types explicitly
|
||||
= note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default
|
||||
|
||||
warning: this function depends on never type fallback being `()`
|
||||
--> $DIR/diverging-fallback-control-flow.rs:42:1
|
||||
|
|
||||
LL | fn assignment_rev() {
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748>
|
||||
= help: specify the types explicitly
|
||||
|
||||
warning: 2 warnings emitted
|
||||
|
@ -28,6 +28,8 @@ impl UnitDefault for () {
|
||||
}
|
||||
|
||||
fn assignment() {
|
||||
//[nofallback]~^ warn: this function depends on never type fallback being `()`
|
||||
//[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
let x;
|
||||
|
||||
if true {
|
||||
@ -38,6 +40,8 @@ fn assignment() {
|
||||
}
|
||||
|
||||
fn assignment_rev() {
|
||||
//[nofallback]~^ warn: this function depends on never type fallback being `()`
|
||||
//[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
let x;
|
||||
|
||||
if true {
|
||||
|
@ -1,5 +1,5 @@
|
||||
error[E0277]: the trait bound `!: Test` is not satisfied
|
||||
--> $DIR/diverging-fallback-no-leak.rs:17:23
|
||||
--> $DIR/diverging-fallback-no-leak.rs:20:23
|
||||
|
|
||||
LL | unconstrained_arg(return);
|
||||
| ----------------- ^^^^^^ the trait `Test` is not implemented for `!`
|
||||
|
@ -0,0 +1,13 @@
|
||||
warning: this function depends on never type fallback being `()`
|
||||
--> $DIR/diverging-fallback-no-leak.rs:14:1
|
||||
|
|
||||
LL | fn main() {
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748>
|
||||
= help: specify the types explicitly
|
||||
= note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
@ -12,6 +12,9 @@ impl Test for () {}
|
||||
fn unconstrained_arg<T: Test>(_: T) {}
|
||||
|
||||
fn main() {
|
||||
//[nofallback]~^ warn: this function depends on never type fallback being `()`
|
||||
//[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
|
||||
// Here the type variable falls back to `!`,
|
||||
// and hence we get a type error.
|
||||
unconstrained_arg(return);
|
||||
|
@ -0,0 +1,13 @@
|
||||
warning: this function depends on never type fallback being `()`
|
||||
--> $DIR/diverging-fallback-unconstrained-return.rs:28:1
|
||||
|
|
||||
LL | fn main() {
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748>
|
||||
= help: specify the types explicitly
|
||||
= note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
@ -26,6 +26,9 @@ fn unconstrained_return<T: UnitReturn>() -> T {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
//[nofallback]~^ warn: this function depends on never type fallback being `()`
|
||||
//[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
|
||||
// In Ye Olde Days, the `T` parameter of `unconstrained_return`
|
||||
// winds up "entangled" with the `!` type that results from
|
||||
// `panic!`, and hence falls back to `()`. This is kind of unfortunate
|
||||
|
13
tests/ui/never_type/fallback-closure-ret.nofallback.stderr
Normal file
13
tests/ui/never_type/fallback-closure-ret.nofallback.stderr
Normal file
@ -0,0 +1,13 @@
|
||||
warning: this function depends on never type fallback being `()`
|
||||
--> $DIR/fallback-closure-ret.rs:21:1
|
||||
|
|
||||
LL | fn main() {
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748>
|
||||
= help: specify the types explicitly
|
||||
= note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
@ -12,12 +12,14 @@
|
||||
|
||||
#![cfg_attr(fallback, feature(never_type_fallback))]
|
||||
|
||||
trait Bar { }
|
||||
impl Bar for () { }
|
||||
impl Bar for u32 { }
|
||||
trait Bar {}
|
||||
impl Bar for () {}
|
||||
impl Bar for u32 {}
|
||||
|
||||
fn foo<R: Bar>(_: impl Fn() -> R) {}
|
||||
|
||||
fn main() {
|
||||
//[nofallback]~^ warn: this function depends on never type fallback being `()`
|
||||
//[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
foo(|| panic!());
|
||||
}
|
||||
|
@ -6,5 +6,7 @@ trait T {}
|
||||
impl T for () {}
|
||||
|
||||
fn should_ret_unit() -> impl T {
|
||||
//~^ warn: this function depends on never type fallback being `()`
|
||||
//~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
panic!()
|
||||
}
|
||||
|
13
tests/ui/never_type/impl_trait_fallback.stderr
Normal file
13
tests/ui/never_type/impl_trait_fallback.stderr
Normal file
@ -0,0 +1,13 @@
|
||||
warning: this function depends on never type fallback being `()`
|
||||
--> $DIR/impl_trait_fallback.rs:8:1
|
||||
|
|
||||
LL | fn should_ret_unit() -> impl T {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748>
|
||||
= help: specify the types explicitly
|
||||
= note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
Loading…
Reference in New Issue
Block a user