Skip to content

Commit 3320b2a

Browse files
authored
Rollup merge of rust-lang#110432 - compiler-errors:unsatisfied-index-impl, r=cjgillot
Report more detailed reason why `Index` impl is not satisfied Fixes rust-lang#110373
2 parents da48140 + 8d75a8f commit 3320b2a

File tree

5 files changed

+183
-0
lines changed

5 files changed

+183
-0
lines changed

compiler/rustc_hir_typeck/src/expr.rs

Lines changed: 90 additions & 0 deletions
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,82 @@ 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+
let index_trait_output_def_id = self.tcx.get_diagnostic_item(sym::IndexOutput)?;
2875+
2876+
let mut relevant_impls = vec![];
2877+
self.tcx.for_each_relevant_impl(index_trait_def_id, base_ty, |impl_def_id| {
2878+
relevant_impls.push(impl_def_id);
2879+
});
2880+
let [impl_def_id] = relevant_impls[..] else {
2881+
// Only report unsatisfied impl predicates if there's one impl
2882+
return None;
2883+
};
2884+
2885+
self.commit_if_ok(|_| {
2886+
let ocx = ObligationCtxt::new_in_snapshot(self);
2887+
let impl_substs = self.fresh_substs_for_item(base_expr.span, impl_def_id);
2888+
let impl_trait_ref =
2889+
self.tcx.impl_trait_ref(impl_def_id).unwrap().subst(self.tcx, impl_substs);
2890+
let cause = self.misc(base_expr.span);
2891+
2892+
// Match the impl self type against the base ty. If this fails,
2893+
// we just skip this impl, since it's not particularly useful.
2894+
let impl_trait_ref = ocx.normalize(&cause, self.param_env, impl_trait_ref);
2895+
ocx.eq(&cause, self.param_env, impl_trait_ref.self_ty(), base_ty)?;
2896+
2897+
// Register the impl's predicates. One of these predicates
2898+
// must be unsatisfied, or else we wouldn't have gotten here
2899+
// in the first place.
2900+
ocx.register_obligations(traits::predicates_for_generics(
2901+
|idx, span| {
2902+
traits::ObligationCause::new(
2903+
base_expr.span,
2904+
self.body_id,
2905+
if span.is_dummy() {
2906+
traits::ExprItemObligation(impl_def_id, index_expr_hir_id, idx)
2907+
} else {
2908+
traits::ExprBindingObligation(impl_def_id, span, index_expr_hir_id, idx)
2909+
},
2910+
)
2911+
},
2912+
self.param_env,
2913+
self.tcx.predicates_of(impl_def_id).instantiate(self.tcx, impl_substs),
2914+
));
2915+
2916+
// Normalize the output type, which we can use later on as the
2917+
// return type of the index expression...
2918+
let element_ty = ocx.normalize(
2919+
&cause,
2920+
self.param_env,
2921+
self.tcx.mk_projection(index_trait_output_def_id, impl_trait_ref.substs),
2922+
);
2923+
2924+
let errors = ocx.select_where_possible();
2925+
// There should be at least one error reported. If not, we
2926+
// will still delay a span bug in `report_fulfillment_errors`.
2927+
Ok::<_, NoSolution>((
2928+
self.err_ctxt().report_fulfillment_errors(&errors),
2929+
impl_trait_ref.substs.type_at(1),
2930+
element_ty,
2931+
))
2932+
})
2933+
.ok()
2934+
}
2935+
28462936
fn point_at_index_if_possible(
28472937
&self,
28482938
errors: &mut Vec<traits::FulfillmentError<'tcx>>,

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ symbols! {
204204
HashSet,
205205
Hasher,
206206
Implied,
207+
IndexOutput,
207208
Input,
208209
Into,
209210
IntoDiagnostic,

library/core/src/ops/index.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
pub trait Index<Idx: ?Sized> {
6060
/// The returned type after indexing.
6161
#[stable(feature = "rust1", since = "1.0.0")]
62+
#[rustc_diagnostic_item = "IndexOutput"]
6263
type Output: ?Sized;
6364

6465
/// Performs the indexing (`container[index]`) operation.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use std::hash::Hash;
2+
use std::marker::PhantomData;
3+
use std::ops::Index;
4+
5+
struct HashMap<K, V>(PhantomData<(K, V)>);
6+
7+
impl<K, V> Index<&K> for HashMap<K, V>
8+
where
9+
K: Hash,
10+
V: Copy,
11+
{
12+
type Output = V;
13+
14+
fn index(&self, k: &K) -> &V {
15+
todo!()
16+
}
17+
}
18+
19+
fn index<'a, K, V>(map: &'a HashMap<K, V>, k: K) -> &'a V {
20+
map[k]
21+
//~^ ERROR the trait bound `K: Hash` is not satisfied
22+
//~| ERROR the trait bound `V: Copy` is not satisfied
23+
//~| ERROR mismatched types
24+
//~| ERROR mismatched types
25+
}
26+
27+
fn main() {}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
error[E0277]: the trait bound `K: Hash` is not satisfied
2+
--> $DIR/bad-index-due-to-nested.rs:20:5
3+
|
4+
LL | map[k]
5+
| ^^^ the trait `Hash` is not implemented for `K`
6+
|
7+
note: required by a bound in `<HashMap<K, V> as Index<&K>>`
8+
--> $DIR/bad-index-due-to-nested.rs:9:8
9+
|
10+
LL | K: Hash,
11+
| ^^^^ required by this bound in `<HashMap<K, V> as Index<&K>>`
12+
help: consider restricting type parameter `K`
13+
|
14+
LL | fn index<'a, K: std::hash::Hash, V>(map: &'a HashMap<K, V>, k: K) -> &'a V {
15+
| +++++++++++++++++
16+
17+
error[E0277]: the trait bound `V: Copy` is not satisfied
18+
--> $DIR/bad-index-due-to-nested.rs:20:5
19+
|
20+
LL | map[k]
21+
| ^^^ the trait `Copy` is not implemented for `V`
22+
|
23+
note: required by a bound in `<HashMap<K, V> as Index<&K>>`
24+
--> $DIR/bad-index-due-to-nested.rs:10:8
25+
|
26+
LL | V: Copy,
27+
| ^^^^ required by this bound in `<HashMap<K, V> as Index<&K>>`
28+
help: consider restricting type parameter `V`
29+
|
30+
LL | fn index<'a, K, V: std::marker::Copy>(map: &'a HashMap<K, V>, k: K) -> &'a V {
31+
| +++++++++++++++++++
32+
33+
error[E0308]: mismatched types
34+
--> $DIR/bad-index-due-to-nested.rs:20:9
35+
|
36+
LL | fn index<'a, K, V>(map: &'a HashMap<K, V>, k: K) -> &'a V {
37+
| - this type parameter
38+
LL | map[k]
39+
| ^
40+
| |
41+
| expected `&K`, found type parameter `K`
42+
| help: consider borrowing here: `&k`
43+
|
44+
= note: expected reference `&K`
45+
found type parameter `K`
46+
47+
error[E0308]: mismatched types
48+
--> $DIR/bad-index-due-to-nested.rs:20:5
49+
|
50+
LL | fn index<'a, K, V>(map: &'a HashMap<K, V>, k: K) -> &'a V {
51+
| - this type parameter ----- expected `&'a V` because of return type
52+
LL | map[k]
53+
| ^^^^^^
54+
| |
55+
| expected `&V`, found type parameter `V`
56+
| help: consider borrowing here: `&map[k]`
57+
|
58+
= note: expected reference `&'a V`
59+
found type parameter `V`
60+
61+
error: aborting due to 4 previous errors
62+
63+
Some errors have detailed explanations: E0277, E0308.
64+
For more information about an error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)