Skip to content

Commit 3f34590

Browse files
committed
Change type suggestion sorting heuristic and other cleanups
1 parent bb83e15 commit 3f34590

File tree

5 files changed

+87
-63
lines changed

5 files changed

+87
-63
lines changed

compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
342342
span: Span,
343343
arg: GenericArg<'tcx>,
344344
error_code: TypeAnnotationNeeded,
345-
turbofish_suggestions: Vec<String>,
345+
turbofish_suggestions: &[String],
346346
) -> DiagnosticBuilder<'tcx> {
347347
let arg = self.resolve_vars_if_possible(&arg);
348348
let arg_data = self.extract_inference_diagnostics_data(arg, None);
@@ -686,7 +686,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
686686
segment: &hir::PathSegment<'_>,
687687
e: &Expr<'_>,
688688
err: &mut DiagnosticBuilder<'_>,
689-
turbofish_suggestions: Vec<String>,
689+
turbofish_suggestions: &[String],
690690
) {
691691
if let (Some(typeck_results), None) = (self.in_progress_typeck_results, &segment.args) {
692692
let borrow = typeck_results.borrow();

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,7 +1505,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
15051505
// check upstream for type errors and don't add the obligations to
15061506
// begin with in those cases.
15071507
if self.tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) {
1508-
self.emit_inference_failure_err(body_id, span, subst, ErrorCode::E0282, vec![])
1508+
self.emit_inference_failure_err(body_id, span, subst, ErrorCode::E0282, &[])
15091509
.emit();
15101510
return;
15111511
}
@@ -1518,7 +1518,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
15181518
span,
15191519
subst,
15201520
ErrorCode::E0283,
1521-
turbofish_suggestions.clone(),
1521+
&turbofish_suggestions,
15221522
);
15231523
err.note(&format!("cannot satisfy `{}`", predicate));
15241524

@@ -1605,7 +1605,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
16051605
return;
16061606
}
16071607

1608-
self.emit_inference_failure_err(body_id, span, arg, ErrorCode::E0282, vec![])
1608+
self.emit_inference_failure_err(body_id, span, arg, ErrorCode::E0282, &[])
16091609
}
16101610

16111611
ty::PredicateAtom::Subtype(data) => {
@@ -1616,7 +1616,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
16161616
let SubtypePredicate { a_is_expected: _, a, b } = data;
16171617
// both must be type variables, or the other would've been instantiated
16181618
assert!(a.is_ty_var() && b.is_ty_var());
1619-
self.emit_inference_failure_err(body_id, span, a.into(), ErrorCode::E0282, vec![])
1619+
self.emit_inference_failure_err(body_id, span, a.into(), ErrorCode::E0282, &[])
16201620
}
16211621
ty::PredicateAtom::Projection(data) => {
16221622
let trait_ref = bound_predicate.rebind(data).to_poly_trait_ref(self.tcx);
@@ -1632,7 +1632,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
16321632
span,
16331633
self_ty.into(),
16341634
ErrorCode::E0284,
1635-
vec![],
1635+
&[],
16361636
);
16371637
err.note(&format!("cannot satisfy `{}`", predicate));
16381638
err

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

Lines changed: 77 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ use super::{
44
};
55

66
use crate::autoderef::Autoderef;
7-
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
87
use crate::infer::{InferCtxt, InferOk};
98
use crate::traits::{self, normalize_projection_type};
109

11-
use rustc_data_structures::fx::FxHashSet;
10+
use rustc_data_structures::fx::FxHashMap;
1211
use rustc_data_structures::stack::ensure_sufficient_stack;
1312
use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder, Style};
1413
use rustc_hir as hir;
@@ -336,64 +335,80 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
336335
data: ty::TraitPredicate<'tcx>,
337336
self_ty: Ty<'tcx>,
338337
) -> Vec<String> {
339-
let mut turbofish_suggestions = FxHashSet::default();
338+
let mut turbofish_suggestions = FxHashMap::default();
340339
self.tcx.for_each_relevant_impl(data.trait_ref.def_id, self_ty, |impl_def_id| {
341-
let param_env = ty::ParamEnv::empty();
342-
let param_env = param_env.subst(self.tcx, data.trait_ref.substs);
343-
let ty = self.next_ty_var(TypeVariableOrigin {
344-
kind: TypeVariableOriginKind::NormalizeProjectionType,
345-
span: DUMMY_SP,
346-
});
347-
348-
let impl_substs = self.fresh_substs_for_item(obligation.cause.span, impl_def_id);
349-
let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap();
350-
let trait_ref = trait_ref.subst(self.tcx, impl_substs);
351-
352-
// Require the type the impl is implemented on to match
353-
// our type, and ignore the impl if there was a mismatch.
354-
let cause = traits::ObligationCause::dummy();
355-
let eq_result = self.at(&cause, param_env).eq(trait_ref.self_ty(), ty);
356-
if let Ok(InferOk { value: (), obligations }) = eq_result {
357-
// FIXME: ignoring `obligations` might cause false positives.
358-
drop(obligations);
359-
360-
let can_impl = match self.evaluate_obligation(&Obligation::new(
361-
cause,
362-
obligation.param_env,
363-
trait_ref.without_const().to_predicate(self.tcx),
364-
)) {
365-
Ok(eval_result) => eval_result.may_apply(),
366-
Err(traits::OverflowError) => true, // overflow doesn't mean yes *or* no
367-
};
368-
if can_impl
369-
&& data.trait_ref.substs.iter().zip(trait_ref.substs.iter()).all(|(l, r)| {
370-
// FIXME: ideally we would use `can_coerce` here instead, but `typeck`
371-
// comes *after* in the dependency graph.
372-
match (l.unpack(), r.unpack()) {
373-
(GenericArgKind::Type(left_ty), GenericArgKind::Type(right_ty)) => {
374-
match (&left_ty.peel_refs().kind(), &right_ty.peel_refs().kind()) {
375-
(Infer(_), _) | (_, Infer(_)) => true,
376-
(left_kind, right_kind) => left_kind == right_kind,
377-
}
378-
}
379-
(GenericArgKind::Lifetime(_), GenericArgKind::Lifetime(_))
380-
| (GenericArgKind::Const(_), GenericArgKind::Const(_)) => true,
381-
_ => false,
340+
self.probe(|_| {
341+
let impl_substs = self.fresh_substs_for_item(DUMMY_SP, impl_def_id);
342+
let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap();
343+
let trait_ref = trait_ref.subst(self.tcx, impl_substs);
344+
345+
// Require the type the impl is implemented on to match
346+
// our type, and ignore the impl if there was a mismatch.
347+
let cause = traits::ObligationCause::dummy();
348+
let eq_result =
349+
self.at(&cause, obligation.param_env).eq(trait_ref.self_ty(), self_ty);
350+
if let Ok(InferOk { value: (), obligations: _ }) = eq_result {
351+
if let Ok(eval_result) = self.evaluate_obligation(&Obligation::new(
352+
cause.clone(),
353+
obligation.param_env,
354+
trait_ref.without_const().to_predicate(self.tcx),
355+
)) {
356+
// FIXME: We will also suggest cases where `eval_result` is
357+
// `EvaluatedToAmbig`, we just put them at the end of the sugg list.
358+
// Ideally we would only suggest types that would always apply cleanly.
359+
// This means that for `collect` we will suggest `PathBuf` as a valid type
360+
// whereas that `impl` has an extra requirement `T: AsRef<Path>` which we
361+
// don't evaluate.
362+
if eval_result.may_apply()
363+
&& data.trait_ref.substs.iter().zip(trait_ref.substs.iter()).all(
364+
// Here we'll have something like `[_, i32]` coming from our code
365+
// and `[Vec<_>, _]` from the probe. For cases with
366+
// inference variables we are left with ambiguous cases due to
367+
// trait bounds we couldn't evaluate, but we *can* filter out cases
368+
// like `[std::string::String, char]`, where we can `collect` a
369+
// `String` only if we have an `IntoIterator<Item = char>`, which
370+
// won't match the `i32` we have.
371+
|(l, r)| {
372+
// FIXME: ideally we would use `can_coerce` here instead, but `typeck`
373+
// comes *after* in the dependency graph.
374+
match (l.unpack(), r.unpack()) {
375+
(
376+
GenericArgKind::Type(left_ty),
377+
GenericArgKind::Type(right_ty),
378+
) => match (
379+
&left_ty.peel_refs().kind(),
380+
&right_ty.peel_refs().kind(),
381+
) {
382+
(Infer(_), _) | (_, Infer(_)) => true,
383+
(left_kind, right_kind) => left_kind == right_kind,
384+
},
385+
(
386+
GenericArgKind::Lifetime(_),
387+
GenericArgKind::Lifetime(_),
388+
)
389+
| (GenericArgKind::Const(_), GenericArgKind::Const(_)) => {
390+
true
391+
}
392+
_ => false,
393+
}
394+
},
395+
)
396+
&& !matches!(trait_ref.self_ty().kind(), ty::Infer(_))
397+
{
398+
turbofish_suggestions.insert(trait_ref.self_ty(), eval_result);
382399
}
383-
})
384-
&& !matches!(trait_ref.self_ty().kind(), ty::Infer(_))
385-
{
386-
turbofish_suggestions.insert(trait_ref.self_ty());
400+
};
387401
}
388-
}
402+
})
389403
});
390404
// Sort types by always suggesting `Vec<_>` and `String` first, as they are the
391-
// most likely desired types.
405+
// most likely desired types. Otherwise sort first by `EvaluationResult` and then by their
406+
// string representation.
392407
let mut turbofish_suggestions = turbofish_suggestions.into_iter().collect::<Vec<_>>();
393408
turbofish_suggestions.sort_by(|left, right| {
394409
let vec_type = self.tcx.get_diagnostic_item(sym::vec_type);
395410
let string_type = self.tcx.get_diagnostic_item(sym::string_type);
396-
match (&left.kind(), &right.kind()) {
411+
match (&left.0.kind(), &right.0.kind()) {
397412
(
398413
ty::Adt(ty::AdtDef { did: left, .. }, _),
399414
ty::Adt(ty::AdtDef { did: right, .. }, _),
@@ -416,10 +431,19 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
416431
{
417432
Ordering::Greater
418433
}
419-
_ => left.cmp(right),
434+
_ => {
435+
// We give preferential place in the suggestion list to types that will apply
436+
// without doubt, to push types with abiguities (may or may not apply depending
437+
// on other obligations we don't have access to here) later in the sugg list.
438+
if left.1 == right.1 {
439+
left.0.to_string().cmp(&right.0.to_string())
440+
} else {
441+
left.1.cmp(&right.1)
442+
}
443+
}
420444
}
421445
});
422-
turbofish_suggestions.into_iter().map(|ty| ty.to_string()).collect()
446+
turbofish_suggestions.into_iter().map(|(ty, _)| ty.to_string()).collect()
423447
}
424448

425449
fn suggest_restricting_param_bound(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1396,7 +1396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13961396
ty
13971397
} else {
13981398
if !self.is_tainted_by_errors() {
1399-
self.emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, vec![])
1399+
self.emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, &[])
14001400
.note("type must be known at this point")
14011401
.emit();
14021402
}

compiler/rustc_typeck/src/check/writeback.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
656656
self.span.to_span(self.tcx),
657657
t.into(),
658658
E0282,
659-
vec![],
659+
&[],
660660
)
661661
.emit();
662662
}
@@ -670,7 +670,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
670670
self.span.to_span(self.tcx),
671671
c.into(),
672672
E0282,
673-
vec![],
673+
&[],
674674
)
675675
.emit();
676676
}

0 commit comments

Comments
 (0)