Skip to content

Commit 770c303

Browse files
Report reason why index impl is not satisfied deeply
1 parent 8a778ca commit 770c303

File tree

3 files changed

+141
-0
lines changed

3 files changed

+141
-0
lines changed

compiler/rustc_hir_typeck/src/expr.rs

+97
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ use rustc_infer::infer;
3838
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
3939
use rustc_infer::infer::DefineOpaqueTypes;
4040
use rustc_infer::infer::InferOk;
41+
use rustc_infer::traits::query::NoSolution;
4142
use rustc_infer::traits::ObligationCause;
4243
use rustc_middle::middle::stability;
4344
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
@@ -53,6 +54,8 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
5354
use rustc_target::abi::FieldIdx;
5455
use rustc_target::spec::abi::Abi::RustIntrinsic;
5556
use rustc_trait_selection::infer::InferCtxtExt;
57+
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
58+
use rustc_trait_selection::traits::ObligationCtxt;
5659
use rustc_trait_selection::traits::{self, ObligationCauseCode};
5760

5861
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -2800,6 +2803,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
28002803
element_ty
28012804
}
28022805
None => {
2806+
// Attempt to *shallowly* search for an impl which matches,
2807+
// but has nested obligations which are unsatisfied.
2808+
for (base_t, _) in self.autoderef(base.span, base_t).silence_errors() {
2809+
if let Some((_, index_ty, element_ty)) =
2810+
self.find_and_report_unsatisfied_index_impl(expr.hir_id, base, base_t)
2811+
{
2812+
self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No);
2813+
return element_ty;
2814+
}
2815+
}
2816+
28032817
let mut err = type_error_struct!(
28042818
self.tcx.sess,
28052819
expr.span,
@@ -2843,6 +2857,89 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
28432857
}
28442858
}
28452859

2860+
/// Try to match an implementation of `Index` against a self type, and report
2861+
/// the unsatisfied predicates that result from confirming this impl.
2862+
///
2863+
/// Given an index expression, sometimes the `Self` type shallowly but does not
2864+
/// deeply satisfy an impl predicate. Instead of simply saying that the type
2865+
/// does not support being indexed, we want to point out exactly what nested
2866+
/// predicates cause this to be, so that the user can add them to fix their code.
2867+
fn find_and_report_unsatisfied_index_impl(
2868+
&self,
2869+
index_expr_hir_id: HirId,
2870+
base_expr: &hir::Expr<'_>,
2871+
base_ty: Ty<'tcx>,
2872+
) -> Option<(ErrorGuaranteed, Ty<'tcx>, Ty<'tcx>)> {
2873+
let index_trait_def_id = self.tcx.lang_items().index_trait()?;
2874+
2875+
let mut relevant_impls = vec![];
2876+
self.tcx.for_each_relevant_impl(index_trait_def_id, base_ty, |impl_def_id| {
2877+
relevant_impls.push(impl_def_id);
2878+
});
2879+
let [impl_def_id] = relevant_impls[..] else {
2880+
// Only report unsatisfied impl predicates if there's one impl
2881+
return None;
2882+
};
2883+
2884+
self.commit_if_ok(|_| {
2885+
let ocx = ObligationCtxt::new_in_snapshot(self);
2886+
let impl_substs = self.fresh_substs_for_item(base_expr.span, impl_def_id);
2887+
let impl_trait_ref =
2888+
self.tcx.impl_trait_ref(impl_def_id).unwrap().subst(self.tcx, impl_substs);
2889+
let cause = self.misc(base_expr.span);
2890+
2891+
// Match the impl self type against the base ty. If this fails,
2892+
// we just skip this impl, since it's not particularly useful.
2893+
let impl_trait_ref = ocx.normalize(&cause, self.param_env, impl_trait_ref);
2894+
ocx.eq(&cause, self.param_env, impl_trait_ref.self_ty(), base_ty)?;
2895+
2896+
// Register the impl's predicates. One of these predicates
2897+
// must be unsatisfied, or else we wouldn't have gotten here
2898+
// in the first place.
2899+
ocx.register_obligations(traits::predicates_for_generics(
2900+
|idx, span| {
2901+
traits::ObligationCause::new(
2902+
base_expr.span,
2903+
self.body_id,
2904+
if span.is_dummy() {
2905+
traits::ExprItemObligation(impl_def_id, index_expr_hir_id, idx)
2906+
} else {
2907+
traits::ExprBindingObligation(impl_def_id, span, index_expr_hir_id, idx)
2908+
},
2909+
)
2910+
},
2911+
self.param_env,
2912+
self.tcx.predicates_of(impl_def_id).instantiate(self.tcx, impl_substs),
2913+
));
2914+
2915+
// Normalize the output type, which we can use later on as the
2916+
// return type of the index expression...
2917+
let element_ty = ocx.normalize(
2918+
&cause,
2919+
self.param_env,
2920+
self.tcx.mk_projection(
2921+
self.tcx
2922+
.associated_items(index_trait_def_id)
2923+
.filter_by_name_unhygienic(sym::Output)
2924+
.next()
2925+
.unwrap()
2926+
.def_id,
2927+
impl_trait_ref.substs,
2928+
),
2929+
);
2930+
2931+
let errors = ocx.select_where_possible();
2932+
// There should be at least one error reported. If not, we
2933+
// will still delay a span bug in `report_fulfillment_errors`.
2934+
Ok::<_, NoSolution>((
2935+
self.err_ctxt().report_fulfillment_errors(&errors),
2936+
impl_trait_ref.substs.type_at(1),
2937+
element_ty,
2938+
))
2939+
})
2940+
.ok()
2941+
}
2942+
28462943
fn point_at_index_if_possible(
28472944
&self,
28482945
errors: &mut Vec<traits::FulfillmentError<'tcx>>,
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use std::collections::HashMap;
2+
3+
pub struct Graph<V> {
4+
node_index_map: HashMap<V, usize>,
5+
}
6+
7+
impl<V> Graph<V> {
8+
pub fn node_index(&self, node: V) -> usize {
9+
self.node_index_map[&node]
10+
//~^ ERROR the trait bound `V: Eq` is not satisfied
11+
//~| ERROR the trait bound `V: Hash` is not satisfied
12+
}
13+
}
14+
15+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
error[E0277]: the trait bound `V: Eq` is not satisfied
2+
--> $DIR/bad-index-due-to-nested.rs:9:9
3+
|
4+
LL | self.node_index_map[&node]
5+
| ^^^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `V`
6+
|
7+
note: required by a bound in `<HashMap<K, V, S> as Index<&Q>>`
8+
--> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL
9+
help: consider restricting type parameter `V`
10+
|
11+
LL | impl<V: std::cmp::Eq> Graph<V> {
12+
| ++++++++++++++
13+
14+
error[E0277]: the trait bound `V: Hash` is not satisfied
15+
--> $DIR/bad-index-due-to-nested.rs:9:9
16+
|
17+
LL | self.node_index_map[&node]
18+
| ^^^^^^^^^^^^^^^^^^^ the trait `Hash` is not implemented for `V`
19+
|
20+
note: required by a bound in `<HashMap<K, V, S> as Index<&Q>>`
21+
--> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL
22+
help: consider restricting type parameter `V`
23+
|
24+
LL | impl<V: std::hash::Hash> Graph<V> {
25+
| +++++++++++++++++
26+
27+
error: aborting due to 2 previous errors
28+
29+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)