Skip to content

Commit ae167c9

Browse files
committed
Auto merge of #56074 - matthewjasper:forbid-recursive-impl-trait, r=nikomatsakis
Forbid recursive impl trait There is no type T, such that `T = [T; 2]`, but impl Trait could sometimes be to circumvented this. This patch makes it a hard error for an opaque type to resolve to such a "type". Before this can be merged it needs - [x] A better error message - it's good enough for now. - [x] A crater run (?) to see if this any real-world code closes #47659
2 parents a602f13 + 65c1f54 commit ae167c9

8 files changed

+342
-11
lines changed

src/librustc/ty/util.rs

+72-2
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ use hir::{self, Node};
77
use ich::NodeIdHashingMode;
88
use traits::{self, ObligationCause};
99
use ty::{self, Ty, TyCtxt, GenericParamDefKind, TypeFoldable};
10-
use ty::subst::{Substs, UnpackedKind};
10+
use ty::subst::{Subst, Substs, UnpackedKind};
1111
use ty::query::TyCtxtAt;
1212
use ty::TyKind::*;
1313
use ty::layout::{Integer, IntegerExt};
1414
use util::common::ErrorReported;
1515
use middle::lang_items;
1616

1717
use rustc_data_structures::stable_hasher::{StableHasher, HashStable};
18-
use rustc_data_structures::fx::FxHashMap;
18+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1919
use std::{cmp, fmt};
2020
use syntax::ast;
2121
use syntax::attr::{self, SignedInt, UnsignedInt};
@@ -618,6 +618,76 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
618618
}
619619
}
620620
}
621+
622+
/// Expands the given impl trait type, stopping if the type is recursive.
623+
pub fn try_expand_impl_trait_type(
624+
self,
625+
def_id: DefId,
626+
substs: &'tcx Substs<'tcx>,
627+
) -> Result<Ty<'tcx>, Ty<'tcx>> {
628+
use crate::ty::fold::TypeFolder;
629+
630+
struct OpaqueTypeExpander<'a, 'gcx, 'tcx> {
631+
// Contains the DefIds of the opaque types that are currently being
632+
// expanded. When we expand an opaque type we insert the DefId of
633+
// that type, and when we finish expanding that type we remove the
634+
// its DefId.
635+
seen_opaque_tys: FxHashSet<DefId>,
636+
primary_def_id: DefId,
637+
found_recursion: bool,
638+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
639+
}
640+
641+
impl<'a, 'gcx, 'tcx> OpaqueTypeExpander<'a, 'gcx, 'tcx> {
642+
fn expand_opaque_ty(
643+
&mut self,
644+
def_id: DefId,
645+
substs: &'tcx Substs<'tcx>,
646+
) -> Option<Ty<'tcx>> {
647+
if self.found_recursion {
648+
None
649+
} else if self.seen_opaque_tys.insert(def_id) {
650+
let generic_ty = self.tcx.type_of(def_id);
651+
let concrete_ty = generic_ty.subst(self.tcx, substs);
652+
let expanded_ty = self.fold_ty(concrete_ty);
653+
self.seen_opaque_tys.remove(&def_id);
654+
Some(expanded_ty)
655+
} else {
656+
// If another opaque type that we contain is recursive, then it
657+
// will report the error, so we don't have to.
658+
self.found_recursion = def_id == self.primary_def_id;
659+
None
660+
}
661+
}
662+
}
663+
664+
impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for OpaqueTypeExpander<'a, 'gcx, 'tcx> {
665+
fn tcx(&self) -> TyCtxt<'_, 'gcx, 'tcx> {
666+
self.tcx
667+
}
668+
669+
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
670+
if let ty::Opaque(def_id, substs) = t.sty {
671+
self.expand_opaque_ty(def_id, substs).unwrap_or(t)
672+
} else {
673+
t.super_fold_with(self)
674+
}
675+
}
676+
}
677+
678+
let mut visitor = OpaqueTypeExpander {
679+
seen_opaque_tys: FxHashSet::default(),
680+
primary_def_id: def_id,
681+
found_recursion: false,
682+
tcx: self,
683+
};
684+
let expanded_type = visitor.expand_opaque_ty(def_id, substs).unwrap();
685+
if visitor.found_recursion {
686+
Err(expanded_type)
687+
} else {
688+
Ok(expanded_type)
689+
}
690+
}
621691
}
622692

623693
impl<'a, 'tcx> ty::TyS<'tcx> {

src/librustc/util/ppaux.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1325,6 +1325,8 @@ define_print! {
13251325
}
13261326
if !is_sized {
13271327
write!(f, "{}?Sized", if first { " " } else { "+" })?;
1328+
} else if first {
1329+
write!(f, " Sized")?;
13281330
}
13291331
Ok(())
13301332
})

src/librustc_typeck/check/mod.rs

+31-1
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,27 @@ fn check_union<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
13051305
check_packed(tcx, span, def_id);
13061306
}
13071307

1308+
fn check_opaque<'a, 'tcx>(
1309+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
1310+
def_id: DefId,
1311+
substs: &'tcx Substs<'tcx>,
1312+
span: Span,
1313+
) {
1314+
if let Err(partially_expanded_type) = tcx.try_expand_impl_trait_type(def_id, substs) {
1315+
let mut err = struct_span_err!(
1316+
tcx.sess, span, E0720,
1317+
"opaque type expands to a recursive type",
1318+
);
1319+
err.span_label(span, "expands to self-referential type");
1320+
if let ty::Opaque(..) = partially_expanded_type.sty {
1321+
err.note("type resolves to itself");
1322+
} else {
1323+
err.note(&format!("expanded type is `{}`", partially_expanded_type));
1324+
}
1325+
err.emit();
1326+
}
1327+
}
1328+
13081329
pub fn check_item_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &'tcx hir::Item) {
13091330
debug!(
13101331
"check_item_type(it.id={}, it.name={})",
@@ -1351,7 +1372,16 @@ pub fn check_item_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &'tcx hir::Ite
13511372
hir::ItemKind::Union(..) => {
13521373
check_union(tcx, it.id, it.span);
13531374
}
1354-
hir::ItemKind::Existential(..) | hir::ItemKind::Ty(..) => {
1375+
hir::ItemKind::Existential(..) => {
1376+
let def_id = tcx.hir().local_def_id(it.id);
1377+
let pty_ty = tcx.type_of(def_id);
1378+
let generics = tcx.generics_of(def_id);
1379+
1380+
check_bounds_are_used(tcx, &generics, pty_ty);
1381+
let substs = Substs::identity_for_item(tcx, def_id);
1382+
check_opaque(tcx, def_id, substs, it.span);
1383+
}
1384+
hir::ItemKind::Ty(..) => {
13551385
let def_id = tcx.hir().local_def_id(it.id);
13561386
let pty_ty = tcx.type_of(def_id);
13571387
let generics = tcx.generics_of(def_id);

src/librustc_typeck/diagnostics.rs

+15
Original file line numberDiff line numberDiff line change
@@ -4816,6 +4816,21 @@ type, it's not allowed to override anything in those implementations, as it
48164816
would be ambiguous which override should actually be used.
48174817
"##,
48184818

4819+
4820+
E0720: r##"
4821+
An `impl Trait` type expands to a recursive type.
4822+
4823+
An `impl Trait` type must be expandable to a concrete type that contains no
4824+
`impl Trait` types. For example the following example tries to create an
4825+
`impl Trait` type `T` that is equal to `[T, T]`:
4826+
4827+
```compile_fail,E0720
4828+
fn make_recursive_type() -> impl Sized {
4829+
[make_recursive_type(), make_recursive_type()]
4830+
}
4831+
```
4832+
"##,
4833+
48194834
}
48204835

48214836
register_diagnostics! {

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

+2-4
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,15 @@
33
//
44
// Regression test for #38064.
55

6-
// error-pattern:overflow evaluating the requirement `impl Quux`
7-
86
trait Quux {}
97

10-
fn foo() -> impl Quux {
8+
fn foo() -> impl Quux { //~ opaque type expands to a recursive type
119
struct Foo<T>(T);
1210
impl<T> Quux for Foo<T> {}
1311
Foo(bar())
1412
}
1513

16-
fn bar() -> impl Quux {
14+
fn bar() -> impl Quux { //~ opaque type expands to a recursive type
1715
struct Bar<T>(T);
1816
impl<T> Quux for Bar<T> {}
1917
Bar(foo())
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
1-
error[E0275]: overflow evaluating the requirement `impl Quux`
1+
error[E0720]: opaque type expands to a recursive type
2+
--> $DIR/infinite-impl-trait-issue-38064.rs:8:13
23
|
3-
= help: consider adding a `#![recursion_limit="128"]` attribute to your crate
4+
LL | fn foo() -> impl Quux { //~ opaque type expands to a recursive type
5+
| ^^^^^^^^^ expands to self-referential type
6+
|
7+
= note: expanded type is `foo::Foo<bar::Bar<impl Quux>>`
8+
9+
error[E0720]: opaque type expands to a recursive type
10+
--> $DIR/infinite-impl-trait-issue-38064.rs:14:13
11+
|
12+
LL | fn bar() -> impl Quux { //~ opaque type expands to a recursive type
13+
| ^^^^^^^^^ expands to self-referential type
14+
|
15+
= note: expanded type is `bar::Bar<foo::Foo<impl Quux>>`
416

5-
error: aborting due to previous error
17+
error: aborting due to 2 previous errors
618

7-
For more information about this error, try `rustc --explain E0275`.
19+
For more information about this error, try `rustc --explain E0720`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Test that impl trait does not allow creating recursive types that are
2+
// otherwise forbidden.
3+
4+
#![feature(await_macro, async_await, futures_api, generators)]
5+
6+
fn option(i: i32) -> impl Sized { //~ ERROR
7+
if i < 0 {
8+
None
9+
} else {
10+
Some((option(i - 1), i))
11+
}
12+
}
13+
14+
fn tuple() -> impl Sized { //~ ERROR
15+
(tuple(),)
16+
}
17+
18+
fn array() -> impl Sized { //~ ERROR
19+
[array()]
20+
}
21+
22+
fn ptr() -> impl Sized { //~ ERROR
23+
&ptr() as *const _
24+
}
25+
26+
fn fn_ptr() -> impl Sized { //~ ERROR
27+
fn_ptr as fn() -> _
28+
}
29+
30+
fn closure_capture() -> impl Sized { //~ ERROR
31+
let x = closure_capture();
32+
move || { x; }
33+
}
34+
35+
fn closure_ref_capture() -> impl Sized { //~ ERROR
36+
let x = closure_ref_capture();
37+
move || { &x; }
38+
}
39+
40+
fn closure_sig() -> impl Sized { //~ ERROR
41+
|| closure_sig()
42+
}
43+
44+
fn generator_sig() -> impl Sized { //~ ERROR
45+
|| generator_sig()
46+
}
47+
48+
fn generator_capture() -> impl Sized { //~ ERROR
49+
let x = generator_capture();
50+
move || { yield; x; }
51+
}
52+
53+
fn substs_change<T>() -> impl Sized { //~ ERROR
54+
(substs_change::<&T>(),)
55+
}
56+
57+
fn generator_hold() -> impl Sized { //~ ERROR
58+
move || {
59+
let x = generator_hold();
60+
yield;
61+
x;
62+
}
63+
}
64+
65+
async fn recursive_async_function() -> () { //~ ERROR
66+
await!(recursive_async_function());
67+
}
68+
69+
fn use_fn_ptr() -> impl Sized { // OK, error already reported
70+
fn_ptr()
71+
}
72+
73+
fn mutual_recursion() -> impl Sync { //~ ERROR
74+
mutual_recursion_b()
75+
}
76+
77+
fn mutual_recursion_b() -> impl Sized { //~ ERROR
78+
mutual_recursion()
79+
}
80+
81+
fn main() {}

0 commit comments

Comments
 (0)