Skip to content

Commit b0085c8

Browse files
committed
Suggest constraint on impl Trait in return type
Fix #71035.
1 parent 6648a08 commit b0085c8

File tree

3 files changed

+100
-21
lines changed

3 files changed

+100
-21
lines changed

src/librustc_middle/ty/error.rs

+47-21
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ impl<T> Trait<T> for X {
552552
continue;
553553
}
554554

555-
if self.constrain_associated_type_structured_suggestion(
555+
if self.constrain_generic_bound_associated_type_structured_suggestion(
556556
db,
557557
&trait_ref,
558558
pred.bounds,
@@ -569,7 +569,7 @@ impl<T> Trait<T> for X {
569569
== Some(def_id)
570570
{
571571
// This is type param `A` in `<A as T>::Foo`.
572-
return self.constrain_associated_type_structured_suggestion(
572+
return self.constrain_generic_bound_associated_type_structured_suggestion(
573573
db,
574574
&trait_ref,
575575
param.bounds,
@@ -629,15 +629,16 @@ impl<T> Trait<T> for X {
629629
| ObligationCauseCode::CompareImplTypeObligation { .. }
630630
| ObligationCauseCode::CompareImplConstObligation
631631
);
632+
let assoc = self.associated_item(proj_ty.item_def_id);
632633
if !callable_scope || impl_comparison {
633634
// We do not want to suggest calling functions when the reason of the
634635
// type error is a comparison of an `impl` with its `trait` or when the
635636
// scope is outside of a `Body`.
636637
} else {
637-
let assoc = self.associated_item(proj_ty.item_def_id);
638638
let items = self.associated_items(assoc.container.id());
639639
// Find all the methods in the trait that could be called to construct the
640640
// expected associated type.
641+
// FIXME: consider suggesting the use of associated `const`s.
641642
let methods: Vec<(Span, String)> = items
642643
.items
643644
.iter()
@@ -739,6 +740,18 @@ impl<T> Trait<T> for X {
739740
_ => {}
740741
}
741742
}
743+
if let ty::Opaque(def_id, _) = proj_ty.self_ty().kind {
744+
// When the expected `impl Trait` is not defined in the current item, it will come from
745+
// a return type. This can occur when dealing with `TryStream` (#71035).
746+
suggested |= self.constrain_associated_type_structured_suggestion(
747+
db,
748+
self.def_span(def_id),
749+
&assoc,
750+
values.found,
751+
&msg,
752+
);
753+
}
754+
742755
if !suggested && !impl_comparison {
743756
// Generic suggestion when we can't be more specific.
744757
if callable_scope {
@@ -771,7 +784,7 @@ fn foo(&self) -> Self::T { String::new() }
771784
}
772785
}
773786

774-
fn constrain_associated_type_structured_suggestion(
787+
fn constrain_generic_bound_associated_type_structured_suggestion(
775788
&self,
776789
db: &mut DiagnosticBuilder<'_>,
777790
trait_ref: &ty::TraitRef<'tcx>,
@@ -785,28 +798,41 @@ fn foo(&self) -> Self::T { String::new() }
785798
match bound {
786799
hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => {
787800
// Relate the type param against `T` in `<A as T>::Foo`.
788-
if ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id) {
789-
if let Ok(has_params) = self
790-
.sess
791-
.source_map()
792-
.span_to_snippet(ptr.span)
793-
.map(|snippet| snippet.ends_with('>'))
794-
{
795-
let (span, sugg) = if has_params {
796-
let pos = ptr.span.hi() - BytePos(1);
797-
let span = Span::new(pos, pos, ptr.span.ctxt());
798-
(span, format!(", {} = {}", assoc.ident, ty))
799-
} else {
800-
(ptr.span.shrink_to_hi(), format!("<{} = {}>", assoc.ident, ty))
801-
};
802-
db.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect);
803-
return true;
804-
}
801+
if ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id)
802+
&& self.constrain_associated_type_structured_suggestion(
803+
db, ptr.span, assoc, ty, msg,
804+
)
805+
{
806+
return true;
805807
}
806808
}
807809
_ => {}
808810
}
809811
}
810812
false
811813
}
814+
815+
fn constrain_associated_type_structured_suggestion(
816+
&self,
817+
db: &mut DiagnosticBuilder<'_>,
818+
span: Span,
819+
assoc: &ty::AssocItem,
820+
ty: Ty<'tcx>,
821+
msg: &str,
822+
) -> bool {
823+
if let Ok(has_params) =
824+
self.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>'))
825+
{
826+
let (span, sugg) = if has_params {
827+
let pos = span.hi() - BytePos(1);
828+
let span = Span::new(pos, pos, span.ctxt());
829+
(span, format!(", {} = {}", assoc.ident, ty))
830+
} else {
831+
(span.shrink_to_hi(), format!("<{} = {}>", assoc.ident, ty))
832+
};
833+
db.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect);
834+
return true;
835+
}
836+
false
837+
}
812838
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
trait Foo {
2+
type Item;
3+
}
4+
5+
trait Bar: Foo {}
6+
7+
struct S;
8+
9+
impl Foo for S {
10+
type Item = i32;
11+
}
12+
impl Bar for S {}
13+
14+
struct T;
15+
16+
impl Foo for T {
17+
type Item = u32;
18+
}
19+
impl Bar for T {}
20+
21+
fn bar() -> impl Bar {
22+
T
23+
}
24+
25+
fn baz() -> impl Bar<Item = i32> {
26+
//~^ ERROR type mismatch resolving `<impl Bar as Foo>::Item == i32`
27+
bar()
28+
}
29+
30+
fn main() {
31+
let _ = baz();
32+
}
33+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0271]: type mismatch resolving `<impl Bar as Foo>::Item == i32`
2+
--> $DIR/impl-trait-return-missing-constraint.rs:25:13
3+
|
4+
LL | fn bar() -> impl Bar {
5+
| -------- the expected opaque type
6+
...
7+
LL | fn baz() -> impl Bar<Item = i32> {
8+
| ^^^^^^^^^^^^^^^^^^^^ expected associated type, found `i32`
9+
|
10+
= note: expected associated type `<impl Bar as Foo>::Item`
11+
found type `i32`
12+
= note: the return type of a function must have a statically known size
13+
help: consider constraining the associated type `<impl Bar as Foo>::Item` to `i32`
14+
|
15+
LL | fn bar() -> impl Bar<Item = i32> {
16+
| ^^^^^^^^^^^^
17+
18+
error: aborting due to previous error
19+
20+
For more information about this error, try `rustc --explain E0271`.

0 commit comments

Comments
 (0)