Skip to content

Commit 45d033b

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 9d388d4 + 8f12485 commit 45d033b

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::{
@@ -1710,6 +1711,173 @@ fn check_opaque_for_inheriting_lifetimes(tcx: TyCtxt<'tcx>, def_id: LocalDefId,
17101711
}
17111712
}
17121713

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