Skip to content

Commit

Permalink
Forbid impl Trait from referring to unnamable recursive types
Browse files Browse the repository at this point in the history
There is no type T, such that `T = [T; 2]`, we should not allow this
to be circumvented by impl Trait.
  • Loading branch information
matthewjasper committed Nov 18, 2018
1 parent 0d0d732 commit cdf5394
Show file tree
Hide file tree
Showing 6 changed files with 316 additions and 11 deletions.
70 changes: 68 additions & 2 deletions src/librustc/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ use hir::{self, Node};
use ich::NodeIdHashingMode;
use traits::{self, ObligationCause};
use ty::{self, Ty, TyCtxt, GenericParamDefKind, TypeFoldable};
use ty::subst::{Substs, UnpackedKind};
use ty::subst::{Subst, Substs, UnpackedKind};
use ty::query::TyCtxtAt;
use ty::TyKind::*;
use ty::layout::{Integer, IntegerExt};
use util::common::ErrorReported;
use middle::lang_items;

use rustc_data_structures::stable_hasher::{StableHasher, HashStable};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use std::{cmp, fmt};
use syntax::ast;
use syntax::attr::{self, SignedInt, UnsignedInt};
Expand Down Expand Up @@ -628,6 +628,72 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}
}
}

/// Expands the impl trait, stopping if the type is recursive.
pub fn try_expand_impl_trait_type(
self,
def_id: DefId,
substs: &'tcx Substs<'tcx>,
) -> Result<Ty<'tcx>, Ty<'tcx>> {
use crate::ty::fold::TypeFolder;

struct OpaqueTypeExpander<'a, 'gcx, 'tcx> {
seen_opaque_tys: FxHashSet<DefId>,
primary_def_id: DefId,
found_recursion: bool,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
}

impl<'a, 'gcx, 'tcx> OpaqueTypeExpander<'a, 'gcx, 'tcx> {
fn expand_opaque_ty(
&mut self,
def_id: DefId,
substs: &'tcx Substs<'tcx>,
) -> Option<Ty<'tcx>> {
if self.found_recursion {
None
} else if self.seen_opaque_tys.insert(def_id) {
let generic_ty = self.tcx.type_of(def_id);
let concrete_ty = generic_ty.subst(self.tcx, substs);
let expanded_ty = self.fold_ty(concrete_ty);
self.seen_opaque_tys.remove(&def_id);
Some(expanded_ty)
} else {
// If another opaque type that we contain is recursive, then it
// will report the error, so we don't have to.
self.found_recursion = def_id == self.primary_def_id;
None
}
}
}

impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for OpaqueTypeExpander<'a, 'gcx, 'tcx> {
fn tcx(&self) -> TyCtxt<'_, 'gcx, 'tcx> {
self.tcx
}

fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
if let ty::Opaque(def_id, substs) = t.sty {
self.expand_opaque_ty(def_id, substs).unwrap_or(t)
} else {
t.super_fold_with(self)
}
}
}

let mut visitor = OpaqueTypeExpander {
seen_opaque_tys: FxHashSet::default(),
primary_def_id: def_id,
found_recursion: false,
tcx: self,
};
let expanded_type = visitor.expand_opaque_ty(def_id, substs).unwrap();
if visitor.found_recursion {
Err(expanded_type)
} else {
Ok(expanded_type)
}
}
}

impl<'a, 'tcx> ty::TyS<'tcx> {
Expand Down
29 changes: 28 additions & 1 deletion src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,24 @@ fn check_union<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
check_packed(tcx, span, def_id);
}

fn check_opaque<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId,
substs: &'tcx Substs<'tcx>,
span: Span,
) {
if let Err(partially_expanded_type) = tcx.try_expand_impl_trait_type(def_id, substs) {
let mut err = tcx.sess.struct_span_err(span, "recursive opaque type");
err.span_label(span, "resolves to self-referential type");
if let ty::Opaque(..) = partially_expanded_type.sty {
err.note("type resolves to itself");
} else {
err.note(&format!("resolved type is `{}`", partially_expanded_type));
}
err.emit();
}
}

pub fn check_item_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &'tcx hir::Item) {
debug!(
"check_item_type(it.id={}, it.name={})",
Expand Down Expand Up @@ -1352,7 +1370,16 @@ pub fn check_item_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &'tcx hir::Ite
hir::ItemKind::Union(..) => {
check_union(tcx, it.id, it.span);
}
hir::ItemKind::Existential(..) | hir::ItemKind::Ty(..) => {
hir::ItemKind::Existential(..) => {
let def_id = tcx.hir.local_def_id(it.id);
let pty_ty = tcx.type_of(def_id);
let generics = tcx.generics_of(def_id);

check_bounds_are_used(tcx, &generics, pty_ty);
let substs = Substs::identity_for_item(tcx, def_id);
check_opaque(tcx, def_id, substs, it.span);
}
hir::ItemKind::Ty(..) => {
let def_id = tcx.hir.local_def_id(it.id);
let pty_ty = tcx.type_of(def_id);
let generics = tcx.generics_of(def_id);
Expand Down
6 changes: 2 additions & 4 deletions src/test/ui/impl-trait/infinite-impl-trait-issue-38064.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,15 @@
//
// Regression test for #38064.

// error-pattern:overflow evaluating the requirement `impl Quux`

trait Quux {}

fn foo() -> impl Quux {
fn foo() -> impl Quux { //~ recursive opaque type
struct Foo<T>(T);
impl<T> Quux for Foo<T> {}
Foo(bar())
}

fn bar() -> impl Quux {
fn bar() -> impl Quux { //~ recursive opaque type
struct Bar<T>(T);
impl<T> Quux for Bar<T> {}
Bar(foo())
Expand Down
19 changes: 15 additions & 4 deletions src/test/ui/impl-trait/infinite-impl-trait-issue-38064.stderr
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
error[E0275]: overflow evaluating the requirement `impl Quux`
error: recursive opaque type
--> $DIR/infinite-impl-trait-issue-38064.rs:18:13
|
= help: consider adding a `#![recursion_limit="128"]` attribute to your crate
LL | fn foo() -> impl Quux { //~ recursive opaque type
| ^^^^^^^^^ resolves to self-referential type
|
= note: resolved type is `foo::Foo<bar::Bar<impl Quux>>`

error: recursive opaque type
--> $DIR/infinite-impl-trait-issue-38064.rs:24:13
|
LL | fn bar() -> impl Quux { //~ recursive opaque type
| ^^^^^^^^^ resolves to self-referential type
|
= note: resolved type is `bar::Bar<foo::Foo<impl Quux>>`

error: aborting due to previous error
error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0275`.
81 changes: 81 additions & 0 deletions src/test/ui/impl-trait/recursive-impl-trait-type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Test that impl trait does not allow creating recursive types that are
// otherwise forbidden.

#![feature(await_macro, async_await, futures_api, generators)]

fn option(i: i32) -> impl Sized { //~ ERROR
if i < 0 {
None
} else {
Some((option(i - 1), i))
}
}

fn tuple() -> impl Sized { //~ ERROR
(tuple(),)
}

fn array() -> impl Sized { //~ ERROR
[array()]
}

fn ptr() -> impl Sized { //~ ERROR
&ptr() as *const _
}

fn fn_ptr() -> impl Sized { //~ ERROR
fn_ptr as fn() -> _
}

fn closure_capture() -> impl Sized { //~ ERROR
let x = closure_capture();
move || { x; }
}

fn closure_ref_capture() -> impl Sized { //~ ERROR
let x = closure_ref_capture();
move || { &x; }
}

fn closure_sig() -> impl Sized { //~ ERROR
|| closure_sig()
}

fn generator_sig() -> impl Sized { //~ ERROR
|| generator_sig()
}

fn generator_capture() -> impl Sized { //~ ERROR
let x = generator_capture();
move || { yield; x; }
}

fn substs_change<T>() -> impl Sized { //~ ERROR
(substs_change::<&T>(),)
}

fn generator_hold() -> impl Sized { //~ ERROR
move || {
let x = generator_hold();
yield;
x;
}
}

async fn recursive_async_function() -> () { //~ ERROR
await!(recursive_async_function());
}

fn use_fn_ptr() -> impl Sized { // OK, error already reported
fn_ptr()
}

fn mutual_recursion() -> impl Sync { //~ ERROR
mutual_recursion_b()
}

fn mutual_recursion_b() -> impl Sized { //~ ERROR
mutual_recursion()
}

fn main() {}
122 changes: 122 additions & 0 deletions src/test/ui/impl-trait/recursive-impl-trait-type.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
error: recursive opaque type
--> $DIR/recursive-impl-trait-type.rs:6:22
|
LL | fn option(i: i32) -> impl Sized { //~ ERROR
| ^^^^^^^^^^ resolves to self-referential type
|
= note: resolved type is `std::option::Option<(impl Sized, i32)>`

error: recursive opaque type
--> $DIR/recursive-impl-trait-type.rs:14:15
|
LL | fn tuple() -> impl Sized { //~ ERROR
| ^^^^^^^^^^ resolves to self-referential type
|
= note: resolved type is `(impl Sized,)`

error: recursive opaque type
--> $DIR/recursive-impl-trait-type.rs:18:15
|
LL | fn array() -> impl Sized { //~ ERROR
| ^^^^^^^^^^ resolves to self-referential type
|
= note: resolved type is `[impl Sized; 1]`

error: recursive opaque type
--> $DIR/recursive-impl-trait-type.rs:22:13
|
LL | fn ptr() -> impl Sized { //~ ERROR
| ^^^^^^^^^^ resolves to self-referential type
|
= note: resolved type is `*const impl Sized`

error: recursive opaque type
--> $DIR/recursive-impl-trait-type.rs:26:16
|
LL | fn fn_ptr() -> impl Sized { //~ ERROR
| ^^^^^^^^^^ resolves to self-referential type
|
= note: resolved type is `fn() -> impl Sized`

error: recursive opaque type
--> $DIR/recursive-impl-trait-type.rs:30:25
|
LL | fn closure_capture() -> impl Sized { //~ ERROR
| ^^^^^^^^^^ resolves to self-referential type
|
= note: resolved type is `[closure@$DIR/recursive-impl-trait-type.rs:32:5: 32:19 x:impl Sized]`

error: recursive opaque type
--> $DIR/recursive-impl-trait-type.rs:35:29
|
LL | fn closure_ref_capture() -> impl Sized { //~ ERROR
| ^^^^^^^^^^ resolves to self-referential type
|
= note: resolved type is `[closure@$DIR/recursive-impl-trait-type.rs:37:5: 37:20 x:impl Sized]`

error: recursive opaque type
--> $DIR/recursive-impl-trait-type.rs:40:21
|
LL | fn closure_sig() -> impl Sized { //~ ERROR
| ^^^^^^^^^^ resolves to self-referential type
|
= note: resolved type is `[closure@$DIR/recursive-impl-trait-type.rs:41:5: 41:21]`

error: recursive opaque type
--> $DIR/recursive-impl-trait-type.rs:44:23
|
LL | fn generator_sig() -> impl Sized { //~ ERROR
| ^^^^^^^^^^ resolves to self-referential type
|
= note: resolved type is `[closure@$DIR/recursive-impl-trait-type.rs:45:5: 45:23]`

error: recursive opaque type
--> $DIR/recursive-impl-trait-type.rs:48:27
|
LL | fn generator_capture() -> impl Sized { //~ ERROR
| ^^^^^^^^^^ resolves to self-referential type
|
= note: resolved type is `[generator@$DIR/recursive-impl-trait-type.rs:50:5: 50:26 x:impl Sized {()}]`

error: recursive opaque type
--> $DIR/recursive-impl-trait-type.rs:53:26
|
LL | fn substs_change<T>() -> impl Sized { //~ ERROR
| ^^^^^^^^^^ resolves to self-referential type
|
= note: resolved type is `(impl Sized,)`

error: recursive opaque type
--> $DIR/recursive-impl-trait-type.rs:57:24
|
LL | fn generator_hold() -> impl Sized { //~ ERROR
| ^^^^^^^^^^ resolves to self-referential type
|
= note: resolved type is `[generator@$DIR/recursive-impl-trait-type.rs:58:5: 62:6 {impl Sized, ()}]`

error: recursive opaque type
--> $DIR/recursive-impl-trait-type.rs:65:40
|
LL | async fn recursive_async_function() -> () { //~ ERROR
| ^^ resolves to self-referential type
|
= note: resolved type is `std::future::GenFuture<[static generator@$DIR/recursive-impl-trait-type.rs:65:43: 67:2 {impl std::future::Future, ()}]>`

error: recursive opaque type
--> $DIR/recursive-impl-trait-type.rs:73:26
|
LL | fn mutual_recursion() -> impl Sync { //~ ERROR
| ^^^^^^^^^ resolves to self-referential type
|
= note: type resolves to itself

error: recursive opaque type
--> $DIR/recursive-impl-trait-type.rs:77:28
|
LL | fn mutual_recursion_b() -> impl Sized { //~ ERROR
| ^^^^^^^^^^ resolves to self-referential type
|
= note: type resolves to itself

error: aborting due to 15 previous errors

0 comments on commit cdf5394

Please sign in to comment.