Skip to content

Commit da33dc0

Browse files
committed
Initial work for linting on bad never fallback
1 parent 689fca0 commit da33dc0

File tree

1 file changed

+121
-2
lines changed
  • src/librustc_typeck/check

1 file changed

+121
-2
lines changed

src/librustc_typeck/check/mod.rs

Lines changed: 121 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ use syntax::util::parser::ExprPrecedence;
135135

136136
use rustc_error_codes::*;
137137

138+
use std::borrow::Cow;
138139
use std::cell::{Cell, Ref, RefCell, RefMut};
139140
use std::cmp;
140141
use std::collections::hash_map::Entry;
@@ -254,6 +255,8 @@ pub struct Inherited<'a, 'tcx> {
254255
/// not clear.
255256
implicit_region_bound: Option<ty::Region<'tcx>>,
256257

258+
inferred_paths: RefCell<FxHashMap<hir::HirId, InferredPath<'tcx>>>,
259+
257260
body_id: Option<hir::BodyId>,
258261
}
259262

@@ -619,6 +622,13 @@ pub struct FnCtxt<'a, 'tcx> {
619622
inh: &'a Inherited<'a, 'tcx>,
620623
}
621624

625+
#[derive(Clone, Debug)]
626+
struct InferredPath<'tcx> {
627+
span: Span,
628+
ty: Option<Ty<'tcx>>,
629+
args: Option<Cow<'tcx, [Ty<'tcx>]>>,
630+
}
631+
622632
impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> {
623633
type Target = Inherited<'a, 'tcx>;
624634
fn deref(&self) -> &Self::Target {
@@ -685,6 +695,7 @@ impl Inherited<'a, 'tcx> {
685695
opaque_types: RefCell::new(Default::default()),
686696
opaque_types_vars: RefCell::new(Default::default()),
687697
implicit_region_bound,
698+
inferred_paths: RefCell::new(Default::default()),
688699
body_id,
689700
}
690701
}
@@ -1053,6 +1064,32 @@ fn typeck_tables_of_with_fallback<'tcx>(
10531064
// All type checking constraints were added, try to fallback unsolved variables.
10541065
fcx.select_obligations_where_possible(false, |_| {});
10551066
let mut fallback_has_occurred = false;
1067+
let unresolved_paths: FxHashMap<hir::HirId, InferredPath<'tcx>> = fcx
1068+
.inferred_paths
1069+
.borrow()
1070+
.iter()
1071+
.map(|(id, path)| (*id, path.clone()))
1072+
.filter(|(hir_id, path)| {
1073+
debug!(
1074+
"typeck_tables_of_with_fallback: inspecting path ({:?}, {:?})",
1075+
hir_id, path
1076+
);
1077+
let debug_resolved = fcx.infcx.resolve_vars_if_possible(&path.ty);
1078+
if fcx.infcx.unresolved_type_vars(&path.ty).is_some() {
1079+
debug!(
1080+
"typeck_tables_of_with_fallback: unresolved vars in ty: {:?}",
1081+
debug_resolved
1082+
);
1083+
true
1084+
} else {
1085+
debug!(
1086+
"typeck_tables_of_with_fallback: all vars resolved in ty: {:?}",
1087+
debug_resolved
1088+
);
1089+
false
1090+
}
1091+
})
1092+
.collect();
10561093

10571094
// We do fallback in two passes, to try to generate
10581095
// better error messages.
@@ -1095,6 +1132,42 @@ fn typeck_tables_of_with_fallback<'tcx>(
10951132
// See if we can make any more progress.
10961133
fcx.select_obligations_where_possible(fallback_has_occurred, |_| {});
10971134

1135+
for (call_id, path) in unresolved_paths {
1136+
debug!(
1137+
"Resolved ty: {:?} at span {:?} : expr={:?} parent={:?} path={:?}",
1138+
path.span,
1139+
path.ty,
1140+
tcx.hir().get(call_id),
1141+
tcx.hir().get(tcx.hir().get_parent_node(call_id)),
1142+
path
1143+
);
1144+
1145+
let ty = fcx.infcx.resolve_vars_if_possible(&path.ty);
1146+
debug!("Fully resolved ty: {:?}", ty);
1147+
1148+
let ty = ty.unwrap_or_else(|| bug!("Missing ty in path: {:?}", path));
1149+
1150+
if let ty::FnDef(_, substs) = ty.kind {
1151+
debug!("Got substs: {:?}", substs);
1152+
let mut inhabited = true;
1153+
for arg in &*path.args.unwrap() {
1154+
let resolved_arg = fcx.infcx.resolve_vars_if_possible(arg);
1155+
1156+
if resolved_arg.conservative_is_privately_uninhabited(tcx) {
1157+
debug!("Arg is uninhabited: {:?}", resolved_arg);
1158+
inhabited = false;
1159+
break;
1160+
} else {
1161+
debug!("Arg is inhabited: {:?}", resolved_arg);
1162+
}
1163+
}
1164+
1165+
if inhabited {
1166+
debug!("All arguments are inhabited!");
1167+
}
1168+
}
1169+
}
1170+
10981171
// Even though coercion casts provide type hints, we check casts after fallback for
10991172
// backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
11001173
fcx.check_casts();
@@ -3624,7 +3697,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
36243697
self.check_argument_types(
36253698
sp,
36263699
expr,
3627-
&err_inputs[..],
3700+
err_inputs,
36283701
&[],
36293702
args_no_rcvr,
36303703
false,
@@ -3732,13 +3805,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
37323805
&self,
37333806
sp: Span,
37343807
expr: &'tcx hir::Expr<'tcx>,
3735-
fn_inputs: &[Ty<'tcx>],
3808+
fn_inputs: impl Into<Cow<'tcx, [Ty<'tcx>]>>,
37363809
expected_arg_tys: &[Ty<'tcx>],
37373810
args: &'tcx [hir::Expr<'tcx>],
37383811
c_variadic: bool,
37393812
tuple_arguments: TupleArgumentsFlag,
37403813
def_span: Option<Span>,
37413814
) {
3815+
let fn_inputs = fn_inputs.into();
3816+
debug!("check_argument_types: storing arguments for expr {:?}", expr);
3817+
match self.inferred_paths.borrow_mut().entry(expr.hir_id) {
3818+
Entry::Vacant(e) => {
3819+
debug!("check_argument_types: making new entry for types {:?}", fn_inputs);
3820+
e.insert(InferredPath { span: sp, ty: None, args: Some(fn_inputs.clone()) });
3821+
}
3822+
Entry::Occupied(mut e) => {
3823+
debug!(
3824+
"check_argument_types: modifiying exsting entry {:?} with types {:?}",
3825+
e.get(),
3826+
fn_inputs
3827+
);
3828+
e.get_mut().args = Some(fn_inputs.clone());
3829+
}
3830+
}
3831+
37423832
let tcx = self.tcx;
37433833
// Grab the argument types, supplying fresh type variables
37443834
// if the wrong number of arguments were supplied
@@ -5425,6 +5515,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
54255515
// the referenced item.
54265516
let ty_substituted = self.instantiate_type_scheme(span, &substs, &ty);
54275517

5518+
if ty_substituted.has_infer_types() {
5519+
debug!(
5520+
"instantiate_value_path: saving path with infer: ({:?}, {:?})",
5521+
span, ty_substituted
5522+
);
5523+
let parent_id = tcx.hir().get_parent_node(hir_id);
5524+
let parent = tcx.hir().get(parent_id);
5525+
match parent {
5526+
Node::Expr(hir::Expr { span: p_span, kind: ExprKind::Call(..), .. })
5527+
| Node::Expr(hir::Expr { span: p_span, kind: ExprKind::MethodCall(..), .. }) => {
5528+
match self.inferred_paths.borrow_mut().entry(parent_id) {
5529+
Entry::Vacant(e) => {
5530+
debug!("instantiate_value_path: inserting new path");
5531+
e.insert(InferredPath {
5532+
span: *p_span,
5533+
ty: Some(ty_substituted),
5534+
args: None,
5535+
});
5536+
}
5537+
Entry::Occupied(mut e) => {
5538+
debug!("instantiate_value_path: updating existing path {:?}", e.get());
5539+
e.get_mut().ty = Some(ty_substituted);
5540+
}
5541+
}
5542+
}
5543+
_ => {}
5544+
}
5545+
}
5546+
54285547
if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
54295548
// In the case of `Foo<T>::method` and `<Foo<T>>::method`, if `method`
54305549
// is inherent, there is no `Self` parameter; instead, the impl needs

0 commit comments

Comments
 (0)