Skip to content

Commit d95cc41

Browse files
committed
Tracking all the unsolved variables that was assigned ! type because of fallback
1 parent b42a3f1 commit d95cc41

File tree

11 files changed

+106
-8
lines changed

11 files changed

+106
-8
lines changed

compiler/rustc_middle/src/ty/context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ pub struct LocalTableInContext<'a, V> {
205205
/// stored/returned.
206206
fn validate_hir_id_for_typeck_results(hir_owner: LocalDefId, hir_id: hir::HirId) {
207207
if hir_id.owner != hir_owner {
208-
ty::tls::with(|tcx| {
208+
let _: () = ty::tls::with(|tcx| {
209209
bug!(
210210
"node {} with HirId::owner {:?} cannot be placed in TypeckResults with hir_owner {:?}",
211211
tcx.hir().node_to_string(hir_id),

compiler/rustc_middle/src/util/bug.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ fn opt_span_bug_fmt<S: Into<MultiSpan>>(
2626
args: fmt::Arguments<'_>,
2727
location: &Location<'_>,
2828
) -> ! {
29-
tls::with_opt(move |tcx| {
29+
let _: () = tls::with_opt(move |tcx| {
3030
let msg = format!("{}: {}", location, args);
3131
match (tcx, span) {
3232
(Some(tcx), Some(span)) => tcx.sess.diagnostic().span_bug(span, &msg),

compiler/rustc_parse/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ pub fn parse_in<'a, T>(
227227
let mut parser = Parser::new(sess, tts, false, Some(name));
228228
let result = f(&mut parser)?;
229229
if parser.token != token::Eof {
230-
parser.unexpected()?;
230+
let _: () = parser.unexpected()?;
231231
}
232232
Ok(result)
233233
}

compiler/rustc_session/src/lint/builtin.rs

+8
Original file line numberDiff line numberDiff line change
@@ -2555,6 +2555,13 @@ declare_lint! {
25552555
@feature_gate = sym::unsafe_block_in_unsafe_fn;
25562556
}
25572557

2558+
declare_lint! {
2559+
//TODO: Add explanation.
2560+
pub FALL_BACK_TO_NEVER_TYPE,
2561+
Deny,
2562+
"Unresolved variable might fall back to never_type `!`"
2563+
}
2564+
25582565
declare_lint! {
25592566
/// The `cenum_impl_drop_cast` lint detects an `as` cast of a field-less
25602567
/// `enum` that implements [`Drop`].
@@ -2770,6 +2777,7 @@ declare_lint_pass! {
27702777
MISSING_DOC_CODE_EXAMPLES,
27712778
INVALID_HTML_TAGS,
27722779
PRIVATE_DOC_TESTS,
2780+
FALL_BACK_TO_NEVER_TYPE,
27732781
WHERE_CLAUSES_OBJECT_SAFETY,
27742782
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
27752783
MACRO_USE_EXTERN_CRATE,

compiler/rustc_typeck/src/check/expr.rs

+5
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
215215
self.diverges.set(self.diverges.get() | old_diverges);
216216
self.has_errors.set(self.has_errors.get() | old_has_errors);
217217

218+
if self.diverges.get().is_always() {
219+
self.dead_nodes.borrow_mut().insert(expr.hir_id);
220+
debug!("expr with HIR id {:?} is dead on exit", expr.hir_id);
221+
}
222+
218223
debug!("type of {} is...", self.tcx.hir().node_to_string(expr.hir_id));
219224
debug!("... {:?}, expected is {:?}", ty, expected);
220225

compiler/rustc_typeck/src/check/fn_ctxt/checks.rs

+5
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
701701
self.diverges.set(prev_diverges);
702702
}
703703

704+
if self.diverges.get().is_always() {
705+
self.dead_nodes.borrow_mut().insert(blk.hir_id);
706+
debug!("expr with HIR id {:?} is dead on exit", blk.hir_id);
707+
}
708+
704709
let mut ty = ctxt.coerce.unwrap().complete(self);
705710

706711
if self.has_errors.get() || ty.references_error() {

compiler/rustc_typeck/src/check/inherited.rs

+6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc_trait_selection::infer::InferCtxtExt as _;
1414
use rustc_trait_selection::opaque_types::OpaqueTypeDecl;
1515
use rustc_trait_selection::traits::{self, TraitEngine, TraitEngineExt};
1616

17+
use rustc_data_structures::fx::FxHashSet;
1718
use std::cell::RefCell;
1819
use std::ops::Deref;
1920

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

7071
pub(super) body_id: Option<hir::BodyId>,
72+
73+
/// This keeps track of the dead nodes. We use this to determine
74+
/// if there are live nodes with the diverging fallback for linting.
75+
pub(super) dead_nodes: RefCell<FxHashSet<hir::HirId>>,
7176
}
7277

7378
impl<'a, 'tcx> Deref for Inherited<'a, 'tcx> {
@@ -126,6 +131,7 @@ impl Inherited<'a, 'tcx> {
126131
opaque_types: RefCell::new(Default::default()),
127132
opaque_types_vars: RefCell::new(Default::default()),
128133
body_id,
134+
dead_nodes: RefCell::new(FxHashSet::default()),
129135
}
130136
}
131137

compiler/rustc_typeck/src/check/mod.rs

+14-2
Original file line numberDiff line numberDiff line change
@@ -566,10 +566,12 @@ fn typeck_with_fallback<'tcx>(
566566
fcx.select_obligations_where_possible(false, |_| {});
567567
let mut fallback_has_occurred = false;
568568

569+
let unsolved_variables = fcx.unsolved_variables();
570+
569571
// We do fallback in two passes, to try to generate
570572
// better error messages.
571573
// The first time, we do *not* replace opaque types.
572-
for ty in &fcx.unsolved_variables() {
574+
for ty in &unsolved_variables {
573575
fallback_has_occurred |= fcx.fallback_if_possible(ty, FallbackMode::NoOpaque);
574576
}
575577
// We now see if we can make progress. This might
@@ -597,6 +599,16 @@ fn typeck_with_fallback<'tcx>(
597599
// refer to opaque types.
598600
fcx.select_obligations_where_possible(fallback_has_occurred, |_| {});
599601

602+
// We run through the list of `unsolved_variables` gathered earlier and
603+
// check if there are any that are marked `Diverging` at this point. if
604+
// so, this would imply, that they were assigned Divergent type because
605+
// of fallback. Any type in `unsolved_variables` that is now `!`, is `!`
606+
// as a result of fallback.
607+
let mut from_diverging_fallback = unsolved_variables;
608+
let diverging_default = fcx.tcx.mk_diverging_default();
609+
from_diverging_fallback
610+
.retain(|ty| fcx.infcx.resolve_vars_if_possible(ty) == diverging_default);
611+
600612
// We now run fallback again, but this time we allow it to replace
601613
// unconstrained opaque type variables, in addition to performing
602614
// other kinds of fallback.
@@ -630,7 +642,7 @@ fn typeck_with_fallback<'tcx>(
630642
fcx.regionck_expr(body);
631643
}
632644

633-
fcx.resolve_type_vars_in_body(body)
645+
fcx.resolve_type_vars_in_body(body, &from_diverging_fallback)
634646
});
635647

636648
// Consistency check our TypeckResults instance can hold all ItemLocalIds

compiler/rustc_typeck/src/check/writeback.rs

+39-3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3434
pub fn resolve_type_vars_in_body(
3535
&self,
3636
body: &'tcx hir::Body<'tcx>,
37+
from_diverging_fallback: &Vec<Ty<'tcx>>,
3738
) -> &'tcx ty::TypeckResults<'tcx> {
3839
let item_id = self.tcx.hir().body_owner(body.id());
3940
let item_def_id = self.tcx.hir().local_def_id(item_id);
@@ -43,7 +44,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4344
let rustc_dump_user_substs =
4445
self.tcx.has_attr(item_def_id.to_def_id(), sym::rustc_dump_user_substs);
4546

46-
let mut wbcx = WritebackCx::new(self, body, rustc_dump_user_substs);
47+
let mut wbcx =
48+
WritebackCx::new(self, body, rustc_dump_user_substs, from_diverging_fallback);
4749
for param in body.params {
4850
wbcx.visit_node_id(param.pat.span, param.hir_id);
4951
}
@@ -100,13 +102,19 @@ struct WritebackCx<'cx, 'tcx> {
100102
body: &'tcx hir::Body<'tcx>,
101103

102104
rustc_dump_user_substs: bool,
105+
106+
/// List of type variables which became the never type `!`
107+
/// as a result of fallback.
108+
/// This is used to issue lints and warnings for the user.
109+
from_diverging_fallback: &'cx Vec<Ty<'tcx>>,
103110
}
104111

105112
impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
106113
fn new(
107114
fcx: &'cx FnCtxt<'cx, 'tcx>,
108115
body: &'tcx hir::Body<'tcx>,
109116
rustc_dump_user_substs: bool,
117+
from_diverging_fallback: &'cx Vec<Ty<'tcx>>,
110118
) -> WritebackCx<'cx, 'tcx> {
111119
let owner = body.id().hir_id.owner;
112120

@@ -115,6 +123,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
115123
typeck_results: ty::TypeckResults::new(owner),
116124
body,
117125
rustc_dump_user_substs,
126+
from_diverging_fallback,
118127
}
119128
}
120129

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

523532
// Resolve the type of the node with id `node_id`
524-
let n_ty = self.fcx.node_ty(hir_id);
525-
let n_ty = self.resolve(&n_ty, &span);
533+
let n_ty_original = self.fcx.node_ty(hir_id);
534+
let n_ty = self.resolve(&n_ty_original, &span);
535+
536+
debug!("visit_node_id: {:?}", self.from_diverging_fallback);
537+
// check whether the node type contains any of the variables that
538+
// became `!` as a result of type fallback but they are not part of the
539+
// dead nodes. if so, warn. Note that, we concern ourselves with only
540+
// the `n_ty_original` and don't `walk()` the subparts of a type. So, for a
541+
// variable like `Foo<Bar<Bas<...<N>>>>` even if `N` is diverging type,
542+
// we will not generate a warning. This might be okay as sometimes we may
543+
// have things like `Result<i32, T> where even though `T` is diverging,
544+
// it might never be used and warning would be confusing for the user.
545+
if !self.from_diverging_fallback.is_empty() {
546+
debug!("hir_id:{}", &hir_id);
547+
debug!("n_ty_original:{}", &n_ty_original);
548+
if !self.fcx.dead_nodes.borrow().contains(&hir_id)
549+
&& self.from_diverging_fallback.contains(&n_ty_original)
550+
{
551+
self.tcx().struct_span_lint_hir(
552+
rustc_session::lint::builtin::FALL_BACK_TO_NEVER_TYPE,
553+
hir_id,
554+
span,
555+
|lint| {
556+
lint.build(&format!("resulted from diverging fallback: {:?}", n_ty)).emit()
557+
},
558+
);
559+
}
560+
}
561+
526562
self.write_ty_to_typeck_results(hir_id, n_ty);
527563
debug!("node {:?} has type {:?}", hir_id, n_ty);
528564

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![deny(fall_back_to_never_type)]
2+
3+
macro_rules! unreachable1 {
4+
() => {{ panic!("internal error: entered unreachable code") }};
5+
}
6+
7+
fn get_unchecked() {
8+
unreachable1!();
9+
}
10+
11+
fn main() {}
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#![feature(never_type_fallback)]
2+
3+
fn make_unit() {}
4+
5+
fn unconstrained_return<T>() -> T {
6+
unsafe {
7+
let make_unit_fn: fn() = make_unit;
8+
let ffi: fn() -> T = std::mem::transmute(make_unit_fn);
9+
ffi()
10+
}
11+
}
12+
13+
fn main() {
14+
let _ = if true { unconstrained_return() } else { panic!() };
15+
}

0 commit comments

Comments
 (0)