Skip to content

Commit c0d3f22

Browse files
authored
Rollup merge of rust-lang#71338 - estebank:recursive-impl-trait, r=nikomatsakis
Expand "recursive opaque type" diagnostic Fix rust-lang#70968, partially address rust-lang#66523.
2 parents 2935d29 + 8f12485 commit c0d3f22

13 files changed

+366
-115
lines changed

src/librustc_hir/hir.rs

+12
Original file line numberDiff line numberDiff line change
@@ -2726,6 +2726,18 @@ impl Node<'_> {
27262726
}
27272727
}
27282728

2729+
pub fn body_id(&self) -> Option<BodyId> {
2730+
match self {
2731+
Node::TraitItem(TraitItem {
2732+
kind: TraitItemKind::Fn(_, TraitFn::Provided(body_id)),
2733+
..
2734+
})
2735+
| Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(_, body_id), .. })
2736+
| Node::Item(Item { kind: ItemKind::Fn(.., body_id), .. }) => Some(*body_id),
2737+
_ => None,
2738+
}
2739+
}
2740+
27292741
pub fn generics(&self) -> Option<&Generics<'_>> {
27302742
match self {
27312743
Node::TraitItem(TraitItem { generics, .. })

src/librustc_trait_selection/traits/error_reporting/suggestions.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1992,8 +1992,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
19921992
/// Collect all the returned expressions within the input expression.
19931993
/// Used to point at the return spans when we want to suggest some change to them.
19941994
#[derive(Default)]
1995-
struct ReturnsVisitor<'v> {
1996-
returns: Vec<&'v hir::Expr<'v>>,
1995+
pub struct ReturnsVisitor<'v> {
1996+
pub returns: Vec<&'v hir::Expr<'v>>,
19971997
in_block_tail: bool,
19981998
}
19991999

src/librustc_typeck/check/mod.rs

+173-14
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ use rustc_target::spec::abi::Abi;
138138
use rustc_trait_selection::infer::InferCtxtExt as _;
139139
use rustc_trait_selection::opaque_types::{InferCtxtExt as _, OpaqueTypeDecl};
140140
use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error;
141+
use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor;
141142
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
142143
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
143144
use rustc_trait_selection::traits::{
@@ -1711,6 +1712,173 @@ fn check_opaque_for_inheriting_lifetimes(tcx: TyCtxt<'tcx>, def_id: LocalDefId,
17111712
}
17121713
}
17131714

1715+
/// Given a `DefId` for an opaque type in return position, find its parent item's return
1716+
/// expressions.
1717+
fn get_owner_return_paths(
1718+
tcx: TyCtxt<'tcx>,
1719+
def_id: LocalDefId,
1720+
) -> Option<(hir::HirId, ReturnsVisitor<'tcx>)> {
1721+
let hir_id = tcx.hir().as_local_hir_id(def_id);
1722+
let id = tcx.hir().get_parent_item(hir_id);
1723+
tcx.hir()
1724+
.find(id)
1725+
.map(|n| (id, n))
1726+
.and_then(|(hir_id, node)| node.body_id().map(|b| (hir_id, b)))
1727+
.map(|(hir_id, body_id)| {
1728+
let body = tcx.hir().body(body_id);
1729+
let mut visitor = ReturnsVisitor::default();
1730+
visitor.visit_body(body);
1731+
(hir_id, visitor)
1732+
})
1733+
}
1734+
1735+
/// Emit an error for recursive opaque types.
1736+
///
1737+
/// If this is a return `impl Trait`, find the item's return expressions and point at them. For
1738+
/// direct recursion this is enough, but for indirect recursion also point at the last intermediary
1739+
/// `impl Trait`.
1740+
///
1741+
/// If all the return expressions evaluate to `!`, then we explain that the error will go away
1742+
/// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder.
1743+
fn opaque_type_cycle_error(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) {
1744+
let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type");
1745+
1746+
let mut label = false;
1747+
if let Some((hir_id, visitor)) = get_owner_return_paths(tcx, def_id) {
1748+
let tables = tcx.typeck_tables_of(tcx.hir().local_def_id(hir_id));
1749+
if visitor
1750+
.returns
1751+
.iter()
1752+
.filter_map(|expr| tables.node_type_opt(expr.hir_id))
1753+
.all(|ty| matches!(ty.kind, ty::Never))
1754+
{
1755+
let spans = visitor
1756+
.returns
1757+
.iter()
1758+
.filter(|expr| tables.node_type_opt(expr.hir_id).is_some())
1759+
.map(|expr| expr.span)
1760+
.collect::<Vec<Span>>();
1761+
let span_len = spans.len();
1762+
if span_len == 1 {
1763+
err.span_label(spans[0], "this returned value is of `!` type");
1764+
} else {
1765+
let mut multispan: MultiSpan = spans.clone().into();
1766+
for span in spans {
1767+
multispan
1768+
.push_span_label(span, "this returned value is of `!` type".to_string());
1769+
}
1770+
err.span_note(multispan, "these returned values have a concrete \"never\" type");
1771+
}
1772+
err.help("this error will resolve once the item's body returns a concrete type");
1773+
} else {
1774+
let mut seen = FxHashSet::default();
1775+
seen.insert(span);
1776+
err.span_label(span, "recursive opaque type");
1777+
label = true;
1778+
for (sp, ty) in visitor
1779+
.returns
1780+
.iter()
1781+
.filter_map(|e| tables.node_type_opt(e.hir_id).map(|t| (e.span, t)))
1782+
.filter(|(_, ty)| !matches!(ty.kind, ty::Never))
1783+
{
1784+
struct VisitTypes(Vec<DefId>);
1785+
impl<'tcx> ty::fold::TypeVisitor<'tcx> for VisitTypes {
1786+
fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
1787+
match t.kind {
1788+
ty::Opaque(def, _) => {
1789+
self.0.push(def);
1790+
false
1791+
}
1792+
_ => t.super_visit_with(self),
1793+
}
1794+
}
1795+
}
1796+
let mut visitor = VisitTypes(vec![]);
1797+
ty.visit_with(&mut visitor);
1798+
for def_id in visitor.0 {
1799+
let ty_span = tcx.def_span(def_id);
1800+
if !seen.contains(&ty_span) {
1801+
err.span_label(ty_span, &format!("returning this opaque type `{}`", ty));
1802+
seen.insert(ty_span);
1803+
}
1804+
err.span_label(sp, &format!("returning here with type `{}`", ty));
1805+
}
1806+
}
1807+
}
1808+
}
1809+
if !label {
1810+
err.span_label(span, "cannot resolve opaque type");
1811+
}
1812+
err.emit();
1813+
}
1814+
1815+
/// Emit an error for recursive opaque types in a `let` binding.
1816+
fn binding_opaque_type_cycle_error(
1817+
tcx: TyCtxt<'tcx>,
1818+
def_id: LocalDefId,
1819+
span: Span,
1820+
partially_expanded_type: Ty<'tcx>,
1821+
) {
1822+
let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type");
1823+
err.span_label(span, "cannot resolve opaque type");
1824+
// Find the the owner that declared this `impl Trait` type.
1825+
let hir_id = tcx.hir().as_local_hir_id(def_id);
1826+
let mut prev_hir_id = hir_id;
1827+
let mut hir_id = tcx.hir().get_parent_node(hir_id);
1828+
while let Some(node) = tcx.hir().find(hir_id) {
1829+
match node {
1830+
hir::Node::Local(hir::Local {
1831+
pat,
1832+
init: None,
1833+
ty: Some(ty),
1834+
source: hir::LocalSource::Normal,
1835+
..
1836+
}) => {
1837+
err.span_label(pat.span, "this binding might not have a concrete type");
1838+
err.span_suggestion_verbose(
1839+
ty.span.shrink_to_hi(),
1840+
"set the binding to a value for a concrete type to be resolved",
1841+
" = /* value */".to_string(),
1842+
Applicability::HasPlaceholders,
1843+
);
1844+
}
1845+
hir::Node::Local(hir::Local {
1846+
init: Some(expr),
1847+
source: hir::LocalSource::Normal,
1848+
..
1849+
}) => {
1850+
let hir_id = tcx.hir().as_local_hir_id(def_id);
1851+
let tables =
1852+
tcx.typeck_tables_of(tcx.hir().local_def_id(tcx.hir().get_parent_item(hir_id)));
1853+
if let Some(ty) = tables.node_type_opt(expr.hir_id) {
1854+
err.span_label(
1855+
expr.span,
1856+
&format!(
1857+
"this is of type `{}`, which doesn't constrain \
1858+
`{}` enough to arrive to a concrete type",
1859+
ty, partially_expanded_type
1860+
),
1861+
);
1862+
}
1863+
}
1864+
_ => {}
1865+
}
1866+
if prev_hir_id == hir_id {
1867+
break;
1868+
}
1869+
prev_hir_id = hir_id;
1870+
hir_id = tcx.hir().get_parent_node(hir_id);
1871+
}
1872+
err.emit();
1873+
}
1874+
1875+
fn async_opaque_type_cycle_error(tcx: TyCtxt<'tcx>, span: Span) {
1876+
struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing")
1877+
.span_label(span, "recursive `async fn`")
1878+
.note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`")
1879+
.emit();
1880+
}
1881+
17141882
/// Checks that an opaque type does not contain cycles.
17151883
fn check_opaque_for_cycles<'tcx>(
17161884
tcx: TyCtxt<'tcx>,
@@ -1721,21 +1889,12 @@ fn check_opaque_for_cycles<'tcx>(
17211889
) {
17221890
if let Err(partially_expanded_type) = tcx.try_expand_impl_trait_type(def_id.to_def_id(), substs)
17231891
{
1724-
if let hir::OpaqueTyOrigin::AsyncFn = origin {
1725-
struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing",)
1726-
.span_label(span, "recursive `async fn`")
1727-
.note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`")
1728-
.emit();
1729-
} else {
1730-
let mut err =
1731-
struct_span_err!(tcx.sess, span, E0720, "opaque type expands to a recursive type",);
1732-
err.span_label(span, "expands to a recursive type");
1733-
if let ty::Opaque(..) = partially_expanded_type.kind {
1734-
err.note("type resolves to itself");
1735-
} else {
1736-
err.note(&format!("expanded type is `{}`", partially_expanded_type));
1892+
match origin {
1893+
hir::OpaqueTyOrigin::AsyncFn => async_opaque_type_cycle_error(tcx, span),
1894+
hir::OpaqueTyOrigin::Binding => {
1895+
binding_opaque_type_cycle_error(tcx, def_id, span, partially_expanded_type)
17371896
}
1738-
err.emit();
1897+
_ => opaque_type_cycle_error(tcx, def_id, span),
17391898
}
17401899
}
17411900
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#![allow(incomplete_features)]
2+
#![feature(impl_trait_in_bindings)]
3+
4+
fn foo() {
5+
let _ : impl Copy;
6+
//~^ ERROR cannot resolve opaque type
7+
}
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0720]: cannot resolve opaque type
2+
--> $DIR/binding-without-value.rs:5:13
3+
|
4+
LL | let _ : impl Copy;
5+
| - ^^^^^^^^^ cannot resolve opaque type
6+
| |
7+
| this binding might not have a concrete type
8+
|
9+
help: set the binding to a value for a concrete type to be resolved
10+
|
11+
LL | let _ : impl Copy = /* value */;
12+
| ^^^^^^^^^^^^^
13+
14+
error: aborting due to previous error
15+
16+
For more information about this error, try `rustc --explain E0720`.

src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55

66
trait Quux {}
77

8-
fn foo() -> impl Quux { //~ opaque type expands to a recursive type
8+
fn foo() -> impl Quux { //~ ERROR cannot resolve opaque type
99
struct Foo<T>(T);
1010
impl<T> Quux for Foo<T> {}
1111
Foo(bar())
1212
}
1313

14-
fn bar() -> impl Quux { //~ opaque type expands to a recursive type
14+
fn bar() -> impl Quux { //~ ERROR cannot resolve opaque type
1515
struct Bar<T>(T);
1616
impl<T> Quux for Bar<T> {}
1717
Bar(foo())

src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.stderr

+16-8
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
1-
error[E0720]: opaque type expands to a recursive type
1+
error[E0720]: cannot resolve opaque type
22
--> $DIR/infinite-impl-trait-issue-38064.rs:8:13
33
|
44
LL | fn foo() -> impl Quux {
5-
| ^^^^^^^^^ expands to a recursive type
6-
|
7-
= note: expanded type is `foo::Foo<bar::Bar<impl Quux>>`
5+
| ^^^^^^^^^ recursive opaque type
6+
...
7+
LL | Foo(bar())
8+
| ---------- returning here with type `foo::Foo<impl Quux>`
9+
...
10+
LL | fn bar() -> impl Quux {
11+
| --------- returning this opaque type `foo::Foo<impl Quux>`
812

9-
error[E0720]: opaque type expands to a recursive type
13+
error[E0720]: cannot resolve opaque type
1014
--> $DIR/infinite-impl-trait-issue-38064.rs:14:13
1115
|
16+
LL | fn foo() -> impl Quux {
17+
| --------- returning this opaque type `bar::Bar<impl Quux>`
18+
...
1219
LL | fn bar() -> impl Quux {
13-
| ^^^^^^^^^ expands to a recursive type
14-
|
15-
= note: expanded type is `bar::Bar<foo::Foo<impl Quux>>`
20+
| ^^^^^^^^^ recursive opaque type
21+
...
22+
LL | Bar(foo())
23+
| ---------- returning here with type `bar::Bar<impl Quux>`
1624

1725
error: aborting due to 2 previous errors
1826

src/test/ui/impl-trait/recursive-impl-trait-type-direct.stderr

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
error[E0720]: opaque type expands to a recursive type
1+
error[E0720]: cannot resolve opaque type
22
--> $DIR/recursive-impl-trait-type-direct.rs:5:14
33
|
44
LL | fn test() -> impl Sized {
5-
| ^^^^^^^^^^ expands to a recursive type
6-
|
7-
= note: type resolves to itself
5+
| ^^^^^^^^^^ recursive opaque type
6+
LL |
7+
LL | test()
8+
| ------ returning here with type `impl Sized`
89

910
error: aborting due to previous error
1011

0 commit comments

Comments
 (0)