Skip to content

Commit 755e108

Browse files
committed
Support enum structs; add many comments
1 parent 5e442fb commit 755e108

File tree

3 files changed

+218
-85
lines changed

3 files changed

+218
-85
lines changed

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

Lines changed: 164 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1991,107 +1991,197 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
19911991
}
19921992
}
19931993

1994+
/// We want to achieve the error span in the following example:
1995+
///
1996+
/// ```
1997+
/// struct Burrito<Filling> {
1998+
/// filling: Filling,
1999+
/// }
2000+
/// impl <Filling: Delicious> Delicious for Burrito<Filling> {}
2001+
/// fn eat_delicious_food<Food: Delicious>(food: Food) {}
2002+
///
2003+
/// fn will_type_error() {
2004+
/// eat_delicious_food(Burrito { filling: Kale });
2005+
/// } // ^--- The trait bound `Kale: Delicious` is not satisfied
2006+
/// ```
2007+
///
2008+
/// Without calling this function, the error span will cover the entire `Burrito { ... }` expression.
2009+
///
2010+
/// The strategy to determine this span involves connecting information about our generic `impl`
2011+
/// with information about our (struct) type and the (struct) literal expression:
2012+
/// - Find the `impl` (`impl <Filling: Delicious> Delicious for Burrito<Filling>`)
2013+
/// that links our obligation (`Kale: Delicious`) with the parent obligation (`Burrito<Kale>: Delicious`)
2014+
/// - Find the "original" predicate constraint in the impl (`Filling: Delicious`) which produced our obligation.
2015+
/// - Find all of the generics that are mentioned in the predicate (`Filling`).
2016+
/// - Examine the `Self` type in the `impl`, and see which of its type argument(s) mention any of those generics.
2017+
/// - Examing the definition for the `Self` type, and identify (for each of its variants) if there's a unique field
2018+
/// which uses those generic arguments.
2019+
/// - If there is a unique field mentioning the "blameable" arguments, use that field for the error span.
2020+
///
2021+
/// Before we do any of this logic, we recursively call `point_at_specific_expr_if_possible` on the parent
2022+
/// obligation. Hence we refine the `expr` "outwards-in" and bail at the first kind of expression/impl we don't recognize.
2023+
///
2024+
/// This function returns a `Result<&Expr, &Expr>` - either way, it returns the `Expr` whose span should be
2025+
/// reported as an error. If it is `Ok`, then it means it refined successfull. If it is `Err`, then it may be
2026+
/// only a partial success - but it cannot be refined even further.
2027+
///
2028+
/// `point_at_specific_expr_if_possible` will ultimately return the returned `Expr` whether it is `Ok` or `Err`;
2029+
/// this error state is only used for propagation within these recursive functions.
19942030
fn point_at_specific_expr_if_possible_for_derived_predicate_obligation(
19952031
&self,
19962032
obligation: &traits::ImplDerivedObligationCause<'tcx>,
19972033
expr: &'tcx hir::Expr<'tcx>,
19982034
) -> Result<&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>> {
1999-
if obligation.derived.parent_trait_pred.skip_binder().polarity != ty::ImplPolarity::Positive
2000-
{
2001-
// For now, ignore non-positive trait bounds.
2002-
return Err(expr);
2003-
}
2004-
2035+
// First, we attempt to refine the `expr` for our span using the parent obligation.
2036+
// If this cannot be done, then we are already stuck, so we stop early (hence the use
2037+
// of the `?` try operator here).
20052038
let expr = self.point_at_specific_expr_if_possible_for_obligation_cause_code(
20062039
&*obligation.derived.parent_code,
20072040
expr,
20082041
)?;
20092042

2043+
// These are the generic parameters used by the `impl` that produced our current obligation.
2044+
let impl_generics: &ty::Generics = self.tcx.generics_of(obligation.impl_def_id);
2045+
2046+
// This is the "trait" (meaning, the predicate "proved" by this `impl`) which provides the `Self` type we care about.
2047+
// For the purposes of this function, we hope that it is a `struct` type, and that our current `expr` is a literal of
2048+
// that struct type.
2049+
let impl_trait_self_ref: Option<ty::TraitRef<'tcx>> =
2050+
self.tcx.impl_trait_ref(obligation.impl_def_id);
2051+
2052+
let Some(impl_trait_self_ref) = impl_trait_self_ref else {
2053+
// It is possible that this is absent. In this case, we make no progress.
2054+
return Err(expr);
2055+
};
2056+
2057+
// We only really care about the `Self` type itself, which we extract from the ref.
2058+
let impl_self_ty: Ty<'tcx> = impl_trait_self_ref.self_ty();
2059+
2060+
// We can only handle Adt types for now.
2061+
// TODO: We could support blanket impls here as well.
2062+
// TODO: We could support tuple impls here as well.
2063+
// TODO: We could potentially support array/vec/slice impls here as well.
2064+
// TODO: We could support ref impls here as well.
2065+
// Note that there is no point in supporting "primitive" types like char/i32,
2066+
// since we cannot refine such a span any more anyway.
2067+
let ty::Adt(impl_self_ty_path, impl_self_ty_args) = impl_self_ty.kind() else {
2068+
return Err(expr);
2069+
};
2070+
2071+
// We want to find which of the generics in the `impl_generics` are relevant to
2072+
// the broken obligation predicate.
2073+
// A generic is relevant if it is mentioned in the "original" predicate.
2074+
// BUG: Oops, I am looking at the wrong thing here. We should find the original predicate, NOT the self type.
2075+
// This requires adding the impl_predicate_index to the obligation when it is created. TODO: do that.
2076+
let relevant_impl_generics: Vec<&ty::GenericParamDef> = impl_generics
2077+
.params
2078+
.iter()
2079+
.filter(|param| find_param_def_in_ty_walker(impl_self_ty.walk(), param))
2080+
.collect();
2081+
2082+
// We need to find which of the type's arguments are relevant to this obligation.
2083+
// For example, if we had an impl for `SomeTime<(A, C), B, Result<B, C>>` and the broken obligation
2084+
// was `B: Display` then we'd care about indexes `vec![1, 2]`, but if it was `C: PartialEq` then we'd
2085+
// care about index `vec![0, 2]`.
2086+
let relevant_ty_args_indices: Vec<usize> = impl_self_ty_args
2087+
.as_slice()
2088+
.iter()
2089+
.enumerate()
2090+
.filter(|(_index, ty_arg)| {
2091+
relevant_impl_generics
2092+
.iter()
2093+
.any(|param| find_param_def_in_ty_walker(ty_arg.walk(), param))
2094+
})
2095+
.map(|(index, _ty_arg)| index)
2096+
.collect();
2097+
2098+
eprintln!("@@@@@@@@@@@@ checing expr...");
2099+
2100+
// Different expression require different treatment. In general, we hope that we have a literal
2101+
// expression which produces the current `Self` type. Moreoever, we hope that it has a single
2102+
// field which can be "blamed" for the missing trait, so that it can be highlighted.
20102103
match expr.kind {
20112104
hir::ExprKind::Struct(
20122105
hir::QPath::Resolved(
20132106
None,
2014-
hir::Path {
2015-
res: hir::def::Res::Def(hir::def::DefKind::Struct, struct_def_id),
2016-
..
2017-
},
2107+
hir::Path { res: hir::def::Res::Def(struct_def_kind, struct_def_id), .. },
20182108
),
20192109
struct_fields,
20202110
_, // NOTE: "Rest" fields in structs are currently ignored by this function.
20212111
) => {
2022-
let impl_generics: &ty::Generics = self.tcx.generics_of(obligation.impl_def_id);
2023-
// The relevant generics occur in this predicate.
2112+
// We can directly support `Variant` and `Struct` struct expressions:
2113+
let (struct_variant_def, struct_def_generics): (&ty::VariantDef, &ty::Generics) =
2114+
match struct_def_kind {
2115+
hir::def::DefKind::Struct => {
2116+
if &impl_self_ty_path.did() != struct_def_id {
2117+
// If the struct is not the same as `Self`, we cannot refine.
2118+
return Err(expr);
2119+
}
20242120

2025-
// Now we look up the Self type for the trait impl.
2026-
let impl_trait_ref: Option<ty::TraitRef<'tcx>> =
2027-
self.tcx.impl_trait_ref(obligation.impl_def_id);
2121+
let struct_def_generics: &ty::Generics =
2122+
self.tcx.generics_of(struct_def_id);
20282123

2029-
let Some(impl_trait_ref) = impl_trait_ref else {
2030-
return Err(expr); // We must have a valid impl ref.
2031-
};
2032-
let impl_self_ty: Ty<'tcx> = impl_trait_ref.self_ty();
2124+
let struct_variant_defs: Vec<&ty::VariantDef> =
2125+
impl_self_ty_path.variants().iter().collect::<Vec<_>>();
20332126

2034-
let ty::Adt(impl_self_ty_path, impl_self_ty_args) = impl_self_ty.kind() else {
2035-
return Err(expr);
2036-
};
2127+
if struct_variant_defs.len() != 1 {
2128+
// We expect exactly one variant to exist, since it's a struct type.
2129+
return Err(expr);
2130+
}
20372131

2038-
if &impl_self_ty_path.did() != struct_def_id {
2039-
// If the struct in the impl is different from the struct constructor
2040-
// we're using, then we cannot make progress.
2041-
return Err(expr);
2042-
}
2132+
// Use the sole variant definition as our variant from now on:
2133+
(struct_variant_defs[0], struct_def_generics)
2134+
}
2135+
hir::def::DefKind::Variant => {
2136+
let variant_def_id = *struct_def_id;
2137+
let struct_def_id = self.tcx.parent(variant_def_id);
20432138

2044-
// The struct being constructed is the same as the struct in the impl.
2139+
if impl_self_ty_path.did() != struct_def_id {
2140+
// If the struct is not the same as `Self`, we cannot refine.
2141+
return Err(expr);
2142+
}
20452143

2046-
// Which of the self's type arguments use variables used in the predicate?
2047-
let mut relevant_impl_generics: Vec<&ty::GenericParamDef> = Vec::new();
2048-
for param in impl_generics.params.iter() {
2049-
if find_param_def_in_ty_walker(impl_self_ty.walk(), param) {
2050-
relevant_impl_generics.push(param);
2051-
}
2052-
}
2053-
let mut relevant_ty_args_indices: Vec<usize> = Vec::new();
2054-
for (index, ty_arg) in impl_self_ty_args.as_slice().iter().enumerate() {
2055-
let mut found = false;
2056-
for param in relevant_impl_generics.iter() {
2057-
if find_param_def_in_ty_walker(ty_arg.walk(), param) {
2058-
found = true;
2059-
}
2060-
}
2061-
if found {
2062-
relevant_ty_args_indices.push(index);
2063-
}
2064-
// in ty_arg, at least one of the type parameters mentioned above must be used.
2065-
}
2144+
let struct_def_generics: &ty::Generics =
2145+
self.tcx.generics_of(struct_def_id);
20662146

2067-
let struct_def_generics: &ty::Generics = self.tcx.generics_of(struct_def_id);
2147+
let struct_variant_def: Option<&ty::VariantDef> = impl_self_ty_path
2148+
.variants()
2149+
.iter()
2150+
.find(|variant: &&ty::VariantDef| variant.def_id == variant_def_id);
20682151

2069-
let struct_variant_defs: Vec<&ty::VariantDef> =
2070-
impl_self_ty_path.variants().iter().collect::<Vec<_>>();
2152+
let Some(struct_variant_def) = struct_variant_def else {
2153+
// Failed to find matching variant.
2154+
return Err(expr);
2155+
};
20712156

2072-
if struct_variant_defs.len() != 1 {
2073-
// We expect exactly one variant to exist, since it's a struct type.
2074-
return Err(expr);
2075-
}
2157+
// Use the sole variant definition as our variant from now on:
2158+
(struct_variant_def, struct_def_generics)
2159+
}
2160+
_ => {
2161+
return Err(expr);
2162+
}
2163+
};
20762164

2077-
let struct_variant_def: &ty::VariantDef = struct_variant_defs[0];
2165+
// Only retain the generics which are relevant (according to the `impl`).
2166+
let struct_def_generics: Vec<&ty::GenericParamDef> = relevant_ty_args_indices
2167+
.into_iter()
2168+
.filter_map(|index| struct_def_generics.params.get(index))
2169+
.collect();
20782170

2079-
let mut blameable_field_defs: Vec<&ty::FieldDef> = Vec::new();
2080-
for field_def in struct_variant_def.fields.iter() {
2081-
let field_type: Ty<'tcx> = self.tcx.type_of(field_def.did);
2082-
let mut is_blameable = false;
2171+
// The struct being constructed is the same as the struct in the impl.
20832172

2084-
for param in struct_def_generics.params.iter() {
2085-
if find_param_def_in_ty_walker(field_type.walk(), param) {
2086-
is_blameable = true;
2087-
break;
2088-
}
2089-
}
2173+
let blameable_field_defs: Vec<&ty::FieldDef> = struct_variant_def
2174+
.fields
2175+
.iter()
2176+
.filter(|field_def| {
2177+
let field_type: Ty<'tcx> = self.tcx.type_of(field_def.did);
20902178

2091-
if is_blameable {
2092-
blameable_field_defs.push(field_def);
2093-
}
2094-
}
2179+
// Only retain fields which mention the blameable generics.
2180+
struct_def_generics
2181+
.iter()
2182+
.any(|param| find_param_def_in_ty_walker(field_type.walk(), param))
2183+
})
2184+
.collect();
20952185

20962186
if blameable_field_defs.len() != 1 {
20972187
// We must have a single blameable field, in order to be able to blame it.
@@ -2110,7 +2200,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21102200
// We failed to find a matching field in the original struct expression.
21112201
Err(expr)
21122202
}
2113-
_ => Err(expr), // Stop propagating.
2203+
_ => {
2204+
eprintln!("!!!!!!!!!!!!! cannot handle expr {:#?}", expr);
2205+
Err(expr)
2206+
} // Stop propagating.
21142207
}
21152208
}
21162209

src/test/ui/tuple/blame-trait-error.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,37 @@ trait T2 {}
33
trait T3 {}
44
trait T4 {}
55

6-
impl<B: T2> T1 for Wrapper<B> {}
7-
86
impl T2 for i32 {}
97
impl T3 for i32 {}
108

11-
impl<A: T3> T2 for Burrito<A> {}
12-
139
struct Wrapper<W> {
1410
value: W,
1511
}
12+
impl<B: T2> T1 for Wrapper<B> {}
1613

1714
struct Burrito<F> {
15+
spicy: bool,
1816
filling: F,
1917
}
18+
impl<A: T3> T2 for Burrito<A> {}
19+
20+
struct BurritoTuple<F>(F);
21+
impl<C: T3> T2 for BurritoTuple<C> {}
22+
23+
enum BurritoKinds<G> {
24+
SmallBurrito { spicy: bool, small_filling: G },
25+
LargeBurrito { spicy: bool, large_filling: G },
26+
MultiBurrito { first_filling: G, second_filling: G },
27+
}
28+
impl<D: T3> T2 for BurritoKinds<D> {}
2029

2130
fn want<V: T1>(_x: V) {}
2231

2332
fn example<Q>(q: Q) {
24-
want(Wrapper { value: Burrito { filling: q } });
33+
want(Wrapper { value: Burrito { spicy: false, filling: q } });
34+
//~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
35+
36+
want(Wrapper { value: BurritoKinds::SmallBurrito { spicy: true, small_filling: q } });
2537
//~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
2638
}
2739

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1+
@@@@@@@@@@@@ checing expr...
2+
@@@@@@@@@@@@ checing expr...
13
error[E0277]: the trait bound `Q: T3` is not satisfied
2-
--> $DIR/blame-trait-error.rs:24:46
4+
--> $DIR/blame-trait-error.rs:33:60
35
|
4-
LL | want(Wrapper { value: Burrito { filling: q } });
5-
| ---- ^ the trait `T3` is not implemented for `Q`
6-
| |
7-
| required by a bound introduced by this call
6+
LL | want(Wrapper { value: Burrito { spicy: false, filling: q } });
7+
| ---- required by a bound introduced by this call ^ the trait `T3` is not implemented for `Q`
88
|
99
note: required for `Burrito<Q>` to implement `T2`
10-
--> $DIR/blame-trait-error.rs:11:13
10+
--> $DIR/blame-trait-error.rs:18:13
1111
|
1212
LL | impl<A: T3> T2 for Burrito<A> {}
1313
| ^^ ^^^^^^^^^^
1414
note: required for `Wrapper<Burrito<Q>>` to implement `T1`
15-
--> $DIR/blame-trait-error.rs:6:13
15+
--> $DIR/blame-trait-error.rs:12:13
1616
|
1717
LL | impl<B: T2> T1 for Wrapper<B> {}
1818
| ^^ ^^^^^^^^^^
1919
note: required by a bound in `want`
20-
--> $DIR/blame-trait-error.rs:21:12
20+
--> $DIR/blame-trait-error.rs:30:12
2121
|
2222
LL | fn want<V: T1>(_x: V) {}
2323
| ^^ required by this bound in `want`
@@ -26,6 +26,34 @@ help: consider restricting type parameter `Q`
2626
LL | fn example<Q: T3>(q: Q) {
2727
| ++++
2828

29-
error: aborting due to previous error
29+
@@@@@@@@@@@@ checing expr...
30+
@@@@@@@@@@@@ checing expr...
31+
error[E0277]: the trait bound `Q: T3` is not satisfied
32+
--> $DIR/blame-trait-error.rs:36:84
33+
|
34+
LL | want(Wrapper { value: BurritoKinds::SmallBurrito { spicy: true, small_filling: q } });
35+
| ---- required by a bound introduced by this call ^ the trait `T3` is not implemented for `Q`
36+
|
37+
note: required for `BurritoKinds<Q>` to implement `T2`
38+
--> $DIR/blame-trait-error.rs:28:13
39+
|
40+
LL | impl<D: T3> T2 for BurritoKinds<D> {}
41+
| ^^ ^^^^^^^^^^^^^^^
42+
note: required for `Wrapper<BurritoKinds<Q>>` to implement `T1`
43+
--> $DIR/blame-trait-error.rs:12:13
44+
|
45+
LL | impl<B: T2> T1 for Wrapper<B> {}
46+
| ^^ ^^^^^^^^^^
47+
note: required by a bound in `want`
48+
--> $DIR/blame-trait-error.rs:30:12
49+
|
50+
LL | fn want<V: T1>(_x: V) {}
51+
| ^^ required by this bound in `want`
52+
help: consider restricting type parameter `Q`
53+
|
54+
LL | fn example<Q: T3>(q: Q) {
55+
| ++++
56+
57+
error: aborting due to 2 previous errors
3058

3159
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)