Skip to content

Commit 2a67e99

Browse files
committed
Point at specific field in struct literal when trait fulfillment fails
1 parent c8e6a9e commit 2a67e99

File tree

21 files changed

+1151
-112
lines changed

21 files changed

+1151
-112
lines changed

compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs

+457
Large diffs are not rendered by default.

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+42-42
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}
3434

3535
use std::iter;
3636
use std::mem;
37-
use std::ops::ControlFlow;
3837
use std::slice;
3938

39+
use std::ops::ControlFlow;
40+
4041
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4142
pub(in super::super) fn check_casts(&mut self) {
4243
// don't hold the borrow to deferred_cast_checks while checking to avoid borrow checker errors
@@ -1837,7 +1838,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18371838
.into_iter()
18381839
.flatten()
18391840
{
1840-
if self.point_at_arg_if_possible(
1841+
if self.blame_specific_arg_if_possible(
18411842
error,
18421843
def_id,
18431844
param,
@@ -1867,7 +1868,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18671868
.into_iter()
18681869
.flatten()
18691870
{
1870-
if self.point_at_arg_if_possible(
1871+
if self.blame_specific_arg_if_possible(
18711872
error,
18721873
def_id,
18731874
param,
@@ -1892,16 +1893,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18921893
for param in
18931894
[param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
18941895
{
1895-
if let Some(param) = param
1896-
&& self.point_at_field_if_possible(
1897-
error,
1896+
if let Some(param) = param {
1897+
let refined_expr = self.point_at_field_if_possible(
18981898
def_id,
18991899
param,
19001900
variant_def_id,
19011901
fields,
1902-
)
1903-
{
1904-
return true;
1902+
);
1903+
1904+
match refined_expr {
1905+
None => {}
1906+
Some((refined_expr, _)) => {
1907+
error.obligation.cause.span = refined_expr
1908+
.span
1909+
.find_ancestor_in_same_ctxt(error.obligation.cause.span)
1910+
.unwrap_or(refined_expr.span);
1911+
return true;
1912+
}
1913+
}
19051914
}
19061915
}
19071916
}
@@ -1934,7 +1943,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
19341943
}
19351944
}
19361945

1937-
fn point_at_arg_if_possible(
1946+
/// - `blame_specific_*` means that the function will recursively traverse the expression,
1947+
/// looking for the most-specific-possible span to blame.
1948+
///
1949+
/// - `point_at_*` means that the function will only go "one level", pointing at the specific
1950+
/// expression mentioned.
1951+
///
1952+
/// `blame_specific_arg_if_possible` will find the most-specific expression anywhere inside
1953+
/// the provided function call expression, and mark it as responsible for the fullfillment
1954+
/// error.
1955+
fn blame_specific_arg_if_possible(
19381956
&self,
19391957
error: &mut traits::FulfillmentError<'tcx>,
19401958
def_id: DefId,
@@ -1953,13 +1971,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
19531971
.inputs()
19541972
.iter()
19551973
.enumerate()
1956-
.filter(|(_, ty)| find_param_in_ty(**ty, param_to_point_at))
1974+
.filter(|(_, ty)| Self::find_param_in_ty((**ty).into(), param_to_point_at))
19571975
.collect();
19581976
// If there's one field that references the given generic, great!
19591977
if let [(idx, _)] = args_referencing_param.as_slice()
19601978
&& let Some(arg) = receiver
19611979
.map_or(args.get(*idx), |rcvr| if *idx == 0 { Some(rcvr) } else { args.get(*idx - 1) }) {
1980+
19621981
error.obligation.cause.span = arg.span.find_ancestor_in_same_ctxt(error.obligation.cause.span).unwrap_or(arg.span);
1982+
1983+
if let hir::Node::Expr(arg_expr) = self.tcx.hir().get(arg.hir_id) {
1984+
// This is more specific than pointing at the entire argument.
1985+
self.blame_specific_expr_if_possible(error, arg_expr)
1986+
}
1987+
19631988
error.obligation.cause.map_code(|parent_code| {
19641989
ObligationCauseCode::FunctionArgumentObligation {
19651990
arg_hir_id: arg.hir_id,
@@ -1977,14 +2002,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
19772002
false
19782003
}
19792004

1980-
fn point_at_field_if_possible(
2005+
// FIXME: Make this private and move to mod adjust_fulfillment_errors
2006+
pub fn point_at_field_if_possible(
19812007
&self,
1982-
error: &mut traits::FulfillmentError<'tcx>,
19832008
def_id: DefId,
19842009
param_to_point_at: ty::GenericArg<'tcx>,
19852010
variant_def_id: DefId,
19862011
expr_fields: &[hir::ExprField<'tcx>],
1987-
) -> bool {
2012+
) -> Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)> {
19882013
let def = self.tcx.adt_def(def_id);
19892014

19902015
let identity_substs = ty::InternalSubsts::identity_for_item(self.tcx, def_id);
@@ -1994,7 +2019,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
19942019
.iter()
19952020
.filter(|field| {
19962021
let field_ty = field.ty(self.tcx, identity_substs);
1997-
find_param_in_ty(field_ty, param_to_point_at)
2022+
Self::find_param_in_ty(field_ty.into(), param_to_point_at)
19982023
})
19992024
.collect();
20002025

@@ -2004,17 +2029,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20042029
// same rules that check_expr_struct uses for macro hygiene.
20052030
if self.tcx.adjust_ident(expr_field.ident, variant_def_id) == field.ident(self.tcx)
20062031
{
2007-
error.obligation.cause.span = expr_field
2008-
.expr
2009-
.span
2010-
.find_ancestor_in_same_ctxt(error.obligation.cause.span)
2011-
.unwrap_or(expr_field.span);
2012-
return true;
2032+
return Some((expr_field.expr, self.tcx.type_of(field.did)));
20132033
}
20142034
}
20152035
}
20162036

2017-
false
2037+
None
20182038
}
20192039

20202040
fn point_at_path_if_possible(
@@ -2234,23 +2254,3 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22342254
}
22352255
}
22362256
}
2237-
2238-
fn find_param_in_ty<'tcx>(ty: Ty<'tcx>, param_to_point_at: ty::GenericArg<'tcx>) -> bool {
2239-
let mut walk = ty.walk();
2240-
while let Some(arg) = walk.next() {
2241-
if arg == param_to_point_at {
2242-
return true;
2243-
} else if let ty::GenericArgKind::Type(ty) = arg.unpack()
2244-
&& let ty::Alias(ty::Projection, ..) = ty.kind()
2245-
{
2246-
// This logic may seem a bit strange, but typically when
2247-
// we have a projection type in a function signature, the
2248-
// argument that's being passed into that signature is
2249-
// not actually constraining that projection's substs in
2250-
// a meaningful way. So we skip it, and see improvements
2251-
// in some UI tests.
2252-
walk.skip_current_subtree();
2253-
}
2254-
}
2255-
false
2256-
}

compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod _impl;
2+
mod adjust_fulfillment_errors;
23
mod arg_matrix;
34
mod checks;
45
mod suggestions;

compiler/rustc_hir_typeck/src/method/probe.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1568,6 +1568,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
15681568
traits::ImplDerivedObligationCause {
15691569
derived,
15701570
impl_def_id,
1571+
impl_def_predicate_index: None,
15711572
span,
15721573
},
15731574
))

compiler/rustc_infer/src/traits/util.rs

+26-24
Original file line numberDiff line numberDiff line change
@@ -145,30 +145,32 @@ impl<'tcx> Elaborator<'tcx> {
145145
// Get predicates declared on the trait.
146146
let predicates = tcx.super_predicates_of(data.def_id());
147147

148-
let obligations = predicates.predicates.iter().map(|&(mut pred, span)| {
149-
// when parent predicate is non-const, elaborate it to non-const predicates.
150-
if data.constness == ty::BoundConstness::NotConst {
151-
pred = pred.without_const(tcx);
152-
}
153-
154-
let cause = obligation.cause.clone().derived_cause(
155-
bound_predicate.rebind(data),
156-
|derived| {
157-
traits::ImplDerivedObligation(Box::new(
158-
traits::ImplDerivedObligationCause {
159-
derived,
160-
impl_def_id: data.def_id(),
161-
span,
162-
},
163-
))
164-
},
165-
);
166-
predicate_obligation(
167-
pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
168-
obligation.param_env,
169-
cause,
170-
)
171-
});
148+
let obligations =
149+
predicates.predicates.iter().enumerate().map(|(index, &(mut pred, span))| {
150+
// when parent predicate is non-const, elaborate it to non-const predicates.
151+
if data.constness == ty::BoundConstness::NotConst {
152+
pred = pred.without_const(tcx);
153+
}
154+
155+
let cause = obligation.cause.clone().derived_cause(
156+
bound_predicate.rebind(data),
157+
|derived| {
158+
traits::ImplDerivedObligation(Box::new(
159+
traits::ImplDerivedObligationCause {
160+
derived,
161+
impl_def_id: data.def_id(),
162+
impl_def_predicate_index: Some(index),
163+
span,
164+
},
165+
))
166+
},
167+
);
168+
predicate_obligation(
169+
pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
170+
obligation.param_env,
171+
cause,
172+
)
173+
});
172174
debug!(?data, ?obligations, "super_predicates");
173175

174176
// Only keep those bounds that we haven't already seen.

compiler/rustc_middle/src/traits/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,8 @@ pub enum WellFormedLoc {
467467
pub struct ImplDerivedObligationCause<'tcx> {
468468
pub derived: DerivedObligationCause<'tcx>,
469469
pub impl_def_id: DefId,
470+
/// The index of the derived predicate in the parent impl's predicates.
471+
pub impl_def_predicate_index: Option<usize>,
470472
pub span: Span,
471473
}
472474

compiler/rustc_trait_selection/src/traits/select/confirmation.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1224,6 +1224,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12241224
ImplDerivedObligation(Box::new(ImplDerivedObligationCause {
12251225
derived,
12261226
impl_def_id,
1227+
impl_def_predicate_index: None,
12271228
span: obligation.cause.span,
12281229
}))
12291230
});

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -2562,11 +2562,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
25622562
assert_eq!(predicates.parent, None);
25632563
let predicates = predicates.instantiate_own(tcx, substs);
25642564
let mut obligations = Vec::with_capacity(predicates.len());
2565-
for (predicate, span) in predicates {
2565+
for (index, (predicate, span)) in predicates.into_iter().enumerate() {
25662566
let cause = cause.clone().derived_cause(parent_trait_pred, |derived| {
25672567
ImplDerivedObligation(Box::new(ImplDerivedObligationCause {
25682568
derived,
25692569
impl_def_id: def_id,
2570+
impl_def_predicate_index: Some(index),
25702571
span,
25712572
}))
25722573
});

src/doc/book

src/tools/cargo

Submodule cargo updated 99 files

tests/ui/derives/deriving-copyclone.stderr

+12-12
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error[E0277]: the trait bound `B<C>: Copy` is not satisfied
2-
--> $DIR/deriving-copyclone.rs:31:13
2+
--> $DIR/deriving-copyclone.rs:31:26
33
|
44
LL | is_copy(B { a: 1, b: C });
5-
| ------- ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `B<C>`
5+
| ------- ^ the trait `Copy` is not implemented for `B<C>`
66
| |
77
| required by a bound introduced by this call
88
|
@@ -19,14 +19,14 @@ LL | fn is_copy<T: Copy>(_: T) {}
1919
= note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
2020
help: consider borrowing here
2121
|
22-
LL | is_copy(&B { a: 1, b: C });
23-
| +
22+
LL | is_copy(B { a: 1, b: &C });
23+
| +
2424

2525
error[E0277]: the trait bound `B<C>: Clone` is not satisfied
26-
--> $DIR/deriving-copyclone.rs:32:14
26+
--> $DIR/deriving-copyclone.rs:32:27
2727
|
2828
LL | is_clone(B { a: 1, b: C });
29-
| -------- ^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `B<C>`
29+
| -------- ^ the trait `Clone` is not implemented for `B<C>`
3030
| |
3131
| required by a bound introduced by this call
3232
|
@@ -43,14 +43,14 @@ LL | fn is_clone<T: Clone>(_: T) {}
4343
= note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
4444
help: consider borrowing here
4545
|
46-
LL | is_clone(&B { a: 1, b: C });
47-
| +
46+
LL | is_clone(B { a: 1, b: &C });
47+
| +
4848

4949
error[E0277]: the trait bound `B<D>: Copy` is not satisfied
50-
--> $DIR/deriving-copyclone.rs:35:13
50+
--> $DIR/deriving-copyclone.rs:35:26
5151
|
5252
LL | is_copy(B { a: 1, b: D });
53-
| ------- ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `B<D>`
53+
| ------- ^ the trait `Copy` is not implemented for `B<D>`
5454
| |
5555
| required by a bound introduced by this call
5656
|
@@ -67,8 +67,8 @@ LL | fn is_copy<T: Copy>(_: T) {}
6767
= note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
6868
help: consider borrowing here
6969
|
70-
LL | is_copy(&B { a: 1, b: D });
71-
| +
70+
LL | is_copy(B { a: 1, b: &D });
71+
| +
7272

7373
error: aborting due to 3 previous errors
7474

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
trait T1 {}
2+
trait T2 {}
3+
trait T3 {}
4+
trait T4 {}
5+
6+
impl<B: T2> T1 for Wrapper<B> {}
7+
8+
impl T2 for i32 {}
9+
impl T3 for i32 {}
10+
11+
impl<A: T3> T2 for Burrito<A> {}
12+
13+
struct Wrapper<W> {
14+
value: W,
15+
}
16+
17+
struct Burrito<F> {
18+
filling: F,
19+
}
20+
21+
fn want<V: T1>(_x: V) {}
22+
23+
fn example<Q>(q: Q) {
24+
want(Wrapper { value: Burrito { filling: q } });
25+
//~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
26+
}
27+
28+
fn main() {}

0 commit comments

Comments
 (0)