Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Tracking the unsolved variables that was assigned ! type #78051

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ pub struct LocalTableInContext<'a, V> {
/// stored/returned.
fn validate_hir_id_for_typeck_results(hir_owner: LocalDefId, hir_id: hir::HirId) {
if hir_id.owner != hir_owner {
ty::tls::with(|tcx| {
let _: () = ty::tls::with(|tcx| {
bug!(
"node {} with HirId::owner {:?} cannot be placed in TypeckResults with hir_owner {:?}",
tcx.hir().node_to_string(hir_id),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/util/bug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fn opt_span_bug_fmt<S: Into<MultiSpan>>(
args: fmt::Arguments<'_>,
location: &Location<'_>,
) -> ! {
tls::with_opt(move |tcx| {
let _: () = tls::with_opt(move |tcx| {
let msg = format!("{}: {}", location, args);
match (tcx, span) {
(Some(tcx), Some(span)) => tcx.sess.diagnostic().span_bug(span, &msg),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ pub fn parse_in<'a, T>(
let mut parser = Parser::new(sess, tts, false, Some(name));
let result = f(&mut parser)?;
if parser.token != token::Eof {
parser.unexpected()?;
let _: () = parser.unexpected()?;
}
Ok(result)
}
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_session/src/lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2555,6 +2555,13 @@ declare_lint! {
@feature_gate = sym::unsafe_block_in_unsafe_fn;
}

declare_lint! {
//FIXME: Add explanation.
pub FALL_BACK_TO_NEVER_TYPE,
Deny,
"Unresolved variable might fall back to never_type `!`"
blitzerr marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: the style for user visible messages is not to have leading upper-case titling.

Suggested change
"Unresolved variable might fall back to never_type `!`"
"unresolved variable might fall back to never_type `!`"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot @estebank I will make the change.

}

declare_lint! {
/// The `cenum_impl_drop_cast` lint detects an `as` cast of a field-less
/// `enum` that implements [`Drop`].
Expand Down Expand Up @@ -2770,6 +2777,7 @@ declare_lint_pass! {
MISSING_DOC_CODE_EXAMPLES,
INVALID_HTML_TAGS,
PRIVATE_DOC_TESTS,
FALL_BACK_TO_NEVER_TYPE,
WHERE_CLAUSES_OBJECT_SAFETY,
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
MACRO_USE_EXTERN_CRATE,
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_typeck/src/check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.diverges.set(self.diverges.get() | old_diverges);
self.has_errors.set(self.has_errors.get() | old_has_errors);

if self.diverges.get().is_always() {
self.dead_nodes.borrow_mut().insert(expr.hir_id);
debug!("expr with HIR id {:?} is dead on exit", expr.hir_id);
}

debug!("type of {} is...", self.tcx.hir().node_to_string(expr.hir_id));
debug!("... {:?}, expected is {:?}", ty, expected);

Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.diverges.set(prev_diverges);
}

if self.diverges.get().is_always() {
self.dead_nodes.borrow_mut().insert(blk.hir_id);
debug!("expr with HIR id {:?} is dead on exit", blk.hir_id);
}

let mut ty = ctxt.coerce.unwrap().complete(self);

if self.has_errors.get() || ty.references_error() {
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_typeck/src/check/inherited.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::opaque_types::OpaqueTypeDecl;
use rustc_trait_selection::traits::{self, TraitEngine, TraitEngineExt};

use rustc_data_structures::fx::FxHashSet;
use std::cell::RefCell;
use std::ops::Deref;

Expand Down Expand Up @@ -68,6 +69,10 @@ pub struct Inherited<'a, 'tcx> {
pub(super) opaque_types_vars: RefCell<FxHashMap<Ty<'tcx>, Ty<'tcx>>>,

pub(super) body_id: Option<hir::BodyId>,

/// This keeps track of the dead nodes. We use this to determine
/// if there are live nodes with the diverging fallback for linting.
pub(super) dead_nodes: RefCell<FxHashSet<hir::HirId>>,
}

impl<'a, 'tcx> Deref for Inherited<'a, 'tcx> {
Expand Down Expand Up @@ -126,6 +131,7 @@ impl Inherited<'a, 'tcx> {
opaque_types: RefCell::new(Default::default()),
opaque_types_vars: RefCell::new(Default::default()),
body_id,
dead_nodes: RefCell::new(FxHashSet::default()),
}
}

Expand Down
16 changes: 14 additions & 2 deletions compiler/rustc_typeck/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -566,10 +566,12 @@ fn typeck_with_fallback<'tcx>(
fcx.select_obligations_where_possible(false, |_| {});
let mut fallback_has_occurred = false;

let unsolved_variables = fcx.unsolved_variables();

// We do fallback in two passes, to try to generate
// better error messages.
// The first time, we do *not* replace opaque types.
for ty in &fcx.unsolved_variables() {
for ty in &unsolved_variables {
fallback_has_occurred |= fcx.fallback_if_possible(ty, FallbackMode::NoOpaque);
}
// We now see if we can make progress. This might
Expand Down Expand Up @@ -597,6 +599,16 @@ fn typeck_with_fallback<'tcx>(
// refer to opaque types.
fcx.select_obligations_where_possible(fallback_has_occurred, |_| {});

// We run through the list of `unsolved_variables` gathered earlier and
// check if there are any that are marked `Diverging` at this point. if
// so, this would imply, that they were assigned Divergent type because
// of fallback. Any type in `unsolved_variables` that is now `!`, is `!`
// as a result of fallback.
let mut from_diverging_fallback = unsolved_variables;
let diverging_default = fcx.tcx.mk_diverging_default();
from_diverging_fallback
.retain(|ty| fcx.infcx.resolve_vars_if_possible(ty) == diverging_default);

// We now run fallback again, but this time we allow it to replace
// unconstrained opaque type variables, in addition to performing
// other kinds of fallback.
Expand Down Expand Up @@ -630,7 +642,7 @@ fn typeck_with_fallback<'tcx>(
fcx.regionck_expr(body);
}

fcx.resolve_type_vars_in_body(body)
fcx.resolve_type_vars_in_body(body, &from_diverging_fallback)
});

// Consistency check our TypeckResults instance can hold all ItemLocalIds
Expand Down
42 changes: 39 additions & 3 deletions compiler/rustc_typeck/src/check/writeback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn resolve_type_vars_in_body(
&self,
body: &'tcx hir::Body<'tcx>,
from_diverging_fallback: &Vec<Ty<'tcx>>,
) -> &'tcx ty::TypeckResults<'tcx> {
let item_id = self.tcx.hir().body_owner(body.id());
let item_def_id = self.tcx.hir().local_def_id(item_id);
Expand All @@ -43,7 +44,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let rustc_dump_user_substs =
self.tcx.has_attr(item_def_id.to_def_id(), sym::rustc_dump_user_substs);

let mut wbcx = WritebackCx::new(self, body, rustc_dump_user_substs);
let mut wbcx =
WritebackCx::new(self, body, rustc_dump_user_substs, from_diverging_fallback);
for param in body.params {
wbcx.visit_node_id(param.pat.span, param.hir_id);
}
Expand Down Expand Up @@ -100,13 +102,19 @@ struct WritebackCx<'cx, 'tcx> {
body: &'tcx hir::Body<'tcx>,

rustc_dump_user_substs: bool,

/// List of type variables which became the never type `!`
/// as a result of fallback.
/// This is used to issue lints and warnings for the user.
from_diverging_fallback: &'cx Vec<Ty<'tcx>>,
}

impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
fn new(
fcx: &'cx FnCtxt<'cx, 'tcx>,
body: &'tcx hir::Body<'tcx>,
rustc_dump_user_substs: bool,
from_diverging_fallback: &'cx Vec<Ty<'tcx>>,
) -> WritebackCx<'cx, 'tcx> {
let owner = body.id().hir_id.owner;

Expand All @@ -115,6 +123,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
typeck_results: ty::TypeckResults::new(owner),
body,
rustc_dump_user_substs,
from_diverging_fallback,
}
}

Expand Down Expand Up @@ -521,8 +530,35 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
self.visit_adjustments(span, hir_id);

// Resolve the type of the node with id `node_id`
let n_ty = self.fcx.node_ty(hir_id);
let n_ty = self.resolve(&n_ty, &span);
let n_ty_original = self.fcx.node_ty(hir_id);
let n_ty = self.resolve(&n_ty_original, &span);

debug!("visit_node_id: {:?}", self.from_diverging_fallback);
// check whether the node type contains any of the variables that
// became `!` as a result of type fallback but they are not part of the
// dead nodes. if so, warn. Note that, we concern ourselves with only
// the `n_ty_original` and don't `walk()` the subparts of a type. So, for a
// variable like `Foo<Bar<Bas<...<N>>>>` even if `N` is diverging type,
// we will not generate a warning. This might be okay as sometimes we may
// have things like `Result<i32, T> where even though `T` is diverging,
// it might never be used and warning would be confusing for the user.
if !self.from_diverging_fallback.is_empty() {
debug!("hir_id:{}", &hir_id);
debug!("n_ty_original:{}", &n_ty_original);
if !self.fcx.dead_nodes.borrow().contains(&hir_id)
&& self.from_diverging_fallback.contains(&n_ty_original)
{
self.tcx().struct_span_lint_hir(
rustc_session::lint::builtin::FALL_BACK_TO_NEVER_TYPE,
hir_id,
span,
|lint| {
lint.build(&format!("resulted from diverging fallback: {:?}", n_ty)).emit()
},
);
}
}

self.write_ty_to_typeck_results(hir_id, n_ty);
debug!("node {:?} has type {:?}", hir_id, n_ty);

Expand Down
13 changes: 13 additions & 0 deletions src/test/ui/issues/issue-75777.nll.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error: lifetime may not live long enough
--> $DIR/issue-75777.rs:13:5
|
LL | fn inject<'a, Env: 'a, A: 'a + Send>(v: A) -> Box<dyn FnOnce(&'a Env) -> BoxFuture<'a, A>> {
| -- lifetime `'a` defined here
LL | let fut: BoxFuture<'a, A> = Box::pin(future::ready(v));
LL | Box::new(move |_| fut)
| ^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
|
= help: consider replacing `'a` with `'static`

error: aborting due to previous error

17 changes: 17 additions & 0 deletions src/test/ui/issues/issue-75777.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Regression test for #75777.
// Checks that a boxed future can be properly constructed.

#![feature(future_readiness_fns)]

use std::future::{self, Future};
use std::pin::Pin;

type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a + Send>>;

fn inject<'a, Env: 'a, A: 'a + Send>(v: A) -> Box<dyn FnOnce(&'a Env) -> BoxFuture<'a, A>> {
let fut: BoxFuture<'a, A> = Box::pin(future::ready(v));
Box::new(move |_| fut)
//~^ ERROR: cannot infer an appropriate lifetime
}

fn main() {}
30 changes: 30 additions & 0 deletions src/test/ui/issues/issue-75777.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> $DIR/issue-75777.rs:13:14
|
LL | Box::new(move |_| fut)
| ^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 11:11...
--> $DIR/issue-75777.rs:11:11
|
LL | fn inject<'a, Env: 'a, A: 'a + Send>(v: A) -> Box<dyn FnOnce(&'a Env) -> BoxFuture<'a, A>> {
| ^^
note: ...so that the types are compatible
--> $DIR/issue-75777.rs:13:14
|
LL | Box::new(move |_| fut)
| ^^^^^^^^^^^^
= note: expected `(Pin<Box<dyn Future<Output = A> + Send>>,)`
found `(Pin<Box<(dyn Future<Output = A> + Send + 'a)>>,)`
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the expression is assignable
--> $DIR/issue-75777.rs:13:5
|
LL | Box::new(move |_| fut)
| ^^^^^^^^^^^^^^^^^^^^^^
= note: expected `Box<(dyn FnOnce(&'a Env) -> Pin<Box<(dyn Future<Output = A> + Send + 'a)>> + 'static)>`
found `Box<dyn FnOnce(&'a Env) -> Pin<Box<(dyn Future<Output = A> + Send + 'a)>>>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0495`.
11 changes: 11 additions & 0 deletions src/test/ui/never_type/never_type_false_warning_unreachable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![deny(fall_back_to_never_type)]

macro_rules! unreachable1 {
() => {{ panic!("internal error: entered unreachable code") }};
}

fn get_unchecked() {
unreachable1!();
}

fn main() {}
15 changes: 15 additions & 0 deletions src/test/ui/never_type/never_type_lint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#![feature(never_type_fallback)]

fn make_unit() {}

fn unconstrained_return<T>() -> T {
unsafe {
let make_unit_fn: fn() = make_unit;
let ffi: fn() -> T = std::mem::transmute(make_unit_fn);
ffi()
}
}

fn main() {
let _ = if true { unconstrained_return() } else { panic!() };
}