Skip to content

Commit a09fc01

Browse files
committed
Avoid forcing index operand inference
Adjust ambiguous index diagnostics without adding new index operand resolution points. Keep invalid operator and mismatched operand diagnostics on the operator while still pointing valid ambiguous index cases at the index operand.
1 parent d061dcb commit a09fc01

5 files changed

Lines changed: 208 additions & 40 deletions

File tree

compiler/rustc_hir_typeck/src/cast.rs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -725,23 +725,21 @@ impl<'a, 'tcx> CastCheck<'tcx> {
725725
);
726726
}
727727

728-
#[instrument(skip(fcx), level = "debug")]
729-
pub(crate) fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) {
728+
fn expr_span_for_type_resolution(&self, fcx: &FnCtxt<'a, 'tcx>) -> Span {
730729
if let hir::ExprKind::Index(_, idx, _) = self.expr.kind
731-
&& self.expr_ty.has_infer()
730+
&& fcx.resolve_vars_if_possible(self.expr_ty).is_ty_var()
731+
&& fcx.resolve_vars_if_possible(fcx.node_ty(idx.hir_id)).is_ty_var()
732732
{
733-
let idx_ty = fcx.resolve_vars_if_possible(fcx.node_ty(idx.hir_id));
734-
if idx_ty.is_ty_var() {
735-
let resolved = fcx.structurally_resolve_type(idx.span, idx_ty);
736-
if resolved.references_error() {
737-
self.expr_ty = resolved;
738-
}
739-
}
740-
}
741-
// Skip if idx resolution above already emitted a diagnostic and set expr_ty to error.
742-
if !self.expr_ty.references_error() {
743-
self.expr_ty = fcx.structurally_resolve_type(self.expr_span, self.expr_ty);
733+
index_operand_ambiguity_span(idx)
734+
} else {
735+
self.expr_span
744736
}
737+
}
738+
739+
#[instrument(skip(fcx), level = "debug")]
740+
pub(crate) fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) {
741+
let expr_span = self.expr_span_for_type_resolution(fcx);
742+
self.expr_ty = fcx.structurally_resolve_type(expr_span, self.expr_ty);
745743
self.cast_ty = fcx.structurally_resolve_type(self.cast_span, self.cast_ty);
746744

747745
debug!("check_cast({}, {:?} as {:?})", self.expr.hir_id, self.expr_ty, self.cast_ty);
@@ -1222,3 +1220,10 @@ impl<'a, 'tcx> CastCheck<'tcx> {
12221220
}
12231221
}
12241222
}
1223+
1224+
fn index_operand_ambiguity_span(expr: &hir::Expr<'_>) -> Span {
1225+
match expr.kind {
1226+
hir::ExprKind::MethodCall(segment, ..) => segment.ident.span,
1227+
_ => expr.span,
1228+
}
1229+
}

compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ use rustc_hir as hir;
44
use rustc_hir::def::{DefKind, Res};
55
use rustc_hir::def_id::DefId;
66
use rustc_infer::traits::ObligationCauseCode;
7-
use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
7+
use rustc_middle::ty::{
8+
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
9+
};
810
use rustc_span::{Span, kw};
11+
use rustc_trait_selection::infer::InferCtxtExt;
912
use rustc_trait_selection::traits;
1013

1114
use crate::FnCtxt;
@@ -37,6 +40,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3740
&self,
3841
error: &mut traits::FulfillmentError<'tcx>,
3942
) -> bool {
43+
if self.adjust_binop_index_operand(error) {
44+
return true;
45+
}
46+
4047
let (def_id, hir_id, idx, flavor) = match *error.obligation.cause.code().peel_derives() {
4148
ObligationCauseCode::WhereClauseInExpr(def_id, _, hir_id, idx) => {
4249
(def_id, hir_id, idx, ClauseFlavor::Where)
@@ -165,6 +172,119 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
165172
}
166173
}
167174

175+
fn adjust_binop_index_operand(&self, error: &mut traits::FulfillmentError<'tcx>) -> bool {
176+
let ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. } =
177+
*error.obligation.cause.code().peel_derives()
178+
else {
179+
return false;
180+
};
181+
if !matches!(error.code, traits::FulfillmentErrorCode::Ambiguity { .. })
182+
|| !error.obligation.predicate.has_infer()
183+
{
184+
return false;
185+
}
186+
187+
let hir::Node::Expr(lhs_expr) = self.tcx.hir_node(lhs_hir_id) else {
188+
return false;
189+
};
190+
let hir::Node::Expr(rhs_expr) = self.tcx.hir_node(rhs_hir_id) else {
191+
return false;
192+
};
193+
let Some(binop) = self.binop_for_operands(lhs_hir_id, rhs_hir_id) else {
194+
return false;
195+
};
196+
let hir::ExprKind::Index(indexed_expr, idx, _) = rhs_expr.kind else {
197+
return false;
198+
};
199+
if !self.resolve_vars_if_possible(self.node_ty(idx.hir_id)).is_ty_var() {
200+
return false;
201+
}
202+
let lhs_ty = self.resolve_vars_if_possible(self.node_ty(lhs_expr.hir_id));
203+
let indexed_ty = self.resolve_vars_if_possible(self.node_ty(indexed_expr.hir_id));
204+
let rhs_ty = match *indexed_ty.kind() {
205+
ty::Array(element_ty, _) | ty::Slice(element_ty) => element_ty,
206+
ty::Ref(_, pointee_ty, _) => match *pointee_ty.kind() {
207+
ty::Array(element_ty, _) | ty::Slice(element_ty) => element_ty,
208+
_ => self.resolve_vars_if_possible(self.node_ty(rhs_expr.hir_id)),
209+
},
210+
_ => self.resolve_vars_if_possible(self.node_ty(rhs_expr.hir_id)),
211+
};
212+
if !self.binop_accepts_types(binop.node, lhs_ty, rhs_ty) {
213+
return false;
214+
}
215+
216+
error.obligation.cause.span = match idx.kind {
217+
hir::ExprKind::MethodCall(segment, ..) => segment.ident.span,
218+
_ => idx.span,
219+
};
220+
true
221+
}
222+
223+
fn binop_for_operands(
224+
&self,
225+
lhs_hir_id: hir::HirId,
226+
rhs_hir_id: hir::HirId,
227+
) -> Option<hir::BinOp> {
228+
let hir::Node::Expr(parent_expr) = self.tcx.parent_hir_node(rhs_hir_id) else {
229+
return None;
230+
};
231+
let hir::ExprKind::Binary(binop, lhs_expr, rhs_expr) = parent_expr.kind else {
232+
return None;
233+
};
234+
(lhs_expr.hir_id == lhs_hir_id && rhs_expr.hir_id == rhs_hir_id).then_some(binop)
235+
}
236+
237+
fn binop_accepts_types(
238+
&self,
239+
binop: hir::BinOpKind,
240+
lhs_ty: Ty<'tcx>,
241+
rhs_ty: Ty<'tcx>,
242+
) -> bool {
243+
let lhs_ty = self.deref_ty_if_possible(lhs_ty);
244+
let rhs_ty = self.deref_ty_if_possible(rhs_ty);
245+
if lhs_ty.references_error() || rhs_ty.references_error() {
246+
return true;
247+
}
248+
249+
match binop {
250+
hir::BinOpKind::Shl | hir::BinOpKind::Shr => {
251+
lhs_ty.is_integral() && rhs_ty.is_integral()
252+
}
253+
hir::BinOpKind::Add
254+
| hir::BinOpKind::Sub
255+
| hir::BinOpKind::Mul
256+
| hir::BinOpKind::Div
257+
| hir::BinOpKind::Rem => {
258+
self.can_eq(self.param_env, lhs_ty, rhs_ty)
259+
&& (lhs_ty.is_integral() || lhs_ty.is_floating_point())
260+
&& (rhs_ty.is_integral() || rhs_ty.is_floating_point())
261+
}
262+
hir::BinOpKind::BitXor | hir::BinOpKind::BitAnd | hir::BinOpKind::BitOr => {
263+
self.can_eq(self.param_env, lhs_ty, rhs_ty)
264+
&& ((lhs_ty.is_integral() && rhs_ty.is_integral())
265+
|| (lhs_ty.is_bool() && rhs_ty.is_bool()))
266+
}
267+
hir::BinOpKind::Eq
268+
| hir::BinOpKind::Ne
269+
| hir::BinOpKind::Lt
270+
| hir::BinOpKind::Le
271+
| hir::BinOpKind::Ge
272+
| hir::BinOpKind::Gt => {
273+
self.can_eq(self.param_env, lhs_ty, rhs_ty)
274+
&& lhs_ty.is_scalar()
275+
&& rhs_ty.is_scalar()
276+
}
277+
hir::BinOpKind::And | hir::BinOpKind::Or => lhs_ty.is_bool() && rhs_ty.is_bool(),
278+
}
279+
}
280+
281+
fn deref_ty_if_possible(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
282+
match ty.kind() {
283+
ty::Ref(_, ty, hir::Mutability::Not) => *ty,
284+
_ => ty,
285+
}
286+
}
287+
168288
fn point_at_expr_if_possible(
169289
&self,
170290
error: &mut traits::FulfillmentError<'tcx>,

compiler/rustc_hir_typeck/src/op.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -314,15 +314,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
314314

315315
self.write_method_call_and_enforce_effects(expr.hir_id, expr.span, method);
316316

317-
if method.sig.output().has_infer()
318-
&& let hir::ExprKind::Index(_, idx, _) = rhs_expr.kind
319-
{
320-
let idx_ty = self.resolve_vars_if_possible(self.node_ty(idx.hir_id));
321-
if idx_ty.is_ty_var() {
322-
self.structurally_resolve_type(idx.span, idx_ty);
323-
}
324-
}
325-
326317
method.sig.output()
327318
}
328319
// error types are considered "builtin"

tests/ui/type-inference/index-expr-ambiguous-type.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,28 @@ fn with_known_index_type() {
2727
let _foo = [1, 2, 3][Into::<usize>::into(bad_idx)] as i32;
2828
}
2929

30+
fn invalid_operator_with_ambiguous_index() {
31+
let bad_idx = 0u8;
32+
let _foo = true + [1, 2, 3][bad_idx.into()];
33+
//~^ ERROR cannot add
34+
}
35+
36+
fn mismatched_numeric_binop_with_ambiguous_index() {
37+
let bad_idx = 0u8;
38+
let _foo = 0u64 + [1i32, 2, 3][bad_idx.into()];
39+
//~^ ERROR type annotations needed
40+
}
41+
42+
fn shift_with_ambiguous_index() {
43+
let bad_idx = 0u8;
44+
let _foo = 1u32 << [0u8][bad_idx.into()];
45+
//~^ ERROR type annotations needed
46+
}
47+
48+
fn string_add_with_ambiguous_index() {
49+
let bad_idx = 0u8;
50+
let _foo = String::new() + [""][bad_idx.into()];
51+
//~^ ERROR type annotations needed
52+
}
53+
3054
fn main() {}

tests/ui/type-inference/index-expr-ambiguous-type.stderr

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,15 @@ error[E0282]: type annotations needed
22
--> $DIR/index-expr-ambiguous-type.rs:9:34
33
|
44
LL | let _foo = [1, 2, 3][bad_idx.into()] as i32;
5-
| ^^^^
6-
|
7-
help: try using a fully qualified path to specify the expected types
8-
|
9-
LL - let _foo = [1, 2, 3][bad_idx.into()] as i32;
10-
LL + let _foo = [1, 2, 3][<u8 as Into<T>>::into(bad_idx)] as i32;
11-
|
5+
| ^^^^ cannot infer type
126

13-
error[E0282]: type annotations needed
7+
error[E0284]: type annotations needed
148
--> $DIR/index-expr-ambiguous-type.rs:15:38
159
|
1610
LL | let _foo = 0 + [1, 2, 3][bad_idx.into()];
17-
| ^^^^
18-
|
19-
help: try using a fully qualified path to specify the expected types
20-
|
21-
LL - let _foo = 0 + [1, 2, 3][bad_idx.into()];
22-
LL + let _foo = 0 + [1, 2, 3][<u8 as Into<T>>::into(bad_idx)];
11+
| ^^^^ cannot infer type
2312
|
13+
= note: cannot satisfy `<i32 as Add<_>>::Output == _`
2414

2515
error[E0283]: type annotations needed
2616
--> $DIR/index-expr-ambiguous-type.rs:21:34
@@ -36,7 +26,45 @@ LL - let _foo = [1, 2, 3][bad_idx.into()];
3626
LL + let _foo = [1, 2, 3][<u8 as Into<T>>::into(bad_idx)];
3727
|
3828

39-
error: aborting due to 3 previous errors
29+
error[E0369]: cannot add `_` to `bool`
30+
--> $DIR/index-expr-ambiguous-type.rs:32:21
31+
|
32+
LL | let _foo = true + [1, 2, 3][bad_idx.into()];
33+
| ---- ^ ------------------------- _
34+
| |
35+
| bool
36+
37+
error[E0284]: type annotations needed
38+
--> $DIR/index-expr-ambiguous-type.rs:38:21
39+
|
40+
LL | let _foo = 0u64 + [1i32, 2, 3][bad_idx.into()];
41+
| ^ cannot infer type
42+
|
43+
= note: cannot satisfy `<u64 as Add<_>>::Output == _`
44+
45+
error[E0284]: type annotations needed
46+
--> $DIR/index-expr-ambiguous-type.rs:44:38
47+
|
48+
LL | let _foo = 1u32 << [0u8][bad_idx.into()];
49+
| ^^^^ cannot infer type
50+
|
51+
= note: cannot satisfy `<u32 as Shl<_>>::Output == _`
52+
53+
error[E0283]: type annotations needed
54+
--> $DIR/index-expr-ambiguous-type.rs:50:45
55+
|
56+
LL | let _foo = String::new() + [""][bad_idx.into()];
57+
| ^^^^
58+
|
59+
= note: the type must implement `From<u8>`
60+
= note: required for `u8` to implement `Into<_>`
61+
help: try using a fully qualified path to specify the expected types
62+
|
63+
LL - let _foo = String::new() + [""][bad_idx.into()];
64+
LL + let _foo = String::new() + [""][<u8 as Into<T>>::into(bad_idx)];
65+
|
66+
67+
error: aborting due to 7 previous errors
4068

41-
Some errors have detailed explanations: E0282, E0283.
69+
Some errors have detailed explanations: E0282, E0283, E0284, E0369.
4270
For more information about an error, try `rustc --explain E0282`.

0 commit comments

Comments
 (0)