From cdf5394f628f87e0d071e12488bc2950ccaa68bd Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 18 Nov 2018 18:33:44 +0000 Subject: [PATCH] Forbid impl Trait from referring to unnamable recursive types There is no type T, such that `T = [T; 2]`, we should not allow this to be circumvented by impl Trait. --- src/librustc/ty/util.rs | 70 +++++++++- src/librustc_typeck/check/mod.rs | 29 ++++- .../infinite-impl-trait-issue-38064.rs | 6 +- .../infinite-impl-trait-issue-38064.stderr | 19 ++- .../impl-trait/recursive-impl-trait-type.rs | 81 ++++++++++++ .../recursive-impl-trait-type.stderr | 122 ++++++++++++++++++ 6 files changed, 316 insertions(+), 11 deletions(-) create mode 100644 src/test/ui/impl-trait/recursive-impl-trait-type.rs create mode 100644 src/test/ui/impl-trait/recursive-impl-trait-type.stderr diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 3d0c54d6b0a5..7e5c21f1c907 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -17,7 +17,7 @@ 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}; @@ -25,7 +25,7 @@ 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}; @@ -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>> { + use crate::ty::fold::TypeFolder; + + struct OpaqueTypeExpander<'a, 'gcx, 'tcx> { + seen_opaque_tys: FxHashSet, + 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> { + 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> { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index eed5d909063b..2cb7daad947e 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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={})", @@ -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); diff --git a/src/test/ui/impl-trait/infinite-impl-trait-issue-38064.rs b/src/test/ui/impl-trait/infinite-impl-trait-issue-38064.rs index 653ef1723e0b..81cc112466a9 100644 --- a/src/test/ui/impl-trait/infinite-impl-trait-issue-38064.rs +++ b/src/test/ui/impl-trait/infinite-impl-trait-issue-38064.rs @@ -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); impl Quux for Foo {} Foo(bar()) } -fn bar() -> impl Quux { +fn bar() -> impl Quux { //~ recursive opaque type struct Bar(T); impl Quux for Bar {} Bar(foo()) diff --git a/src/test/ui/impl-trait/infinite-impl-trait-issue-38064.stderr b/src/test/ui/impl-trait/infinite-impl-trait-issue-38064.stderr index f260cce647bd..8b1d0490c4dd 100644 --- a/src/test/ui/impl-trait/infinite-impl-trait-issue-38064.stderr +++ b/src/test/ui/impl-trait/infinite-impl-trait-issue-38064.stderr @@ -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>` + +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>` -error: aborting due to previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0275`. diff --git a/src/test/ui/impl-trait/recursive-impl-trait-type.rs b/src/test/ui/impl-trait/recursive-impl-trait-type.rs new file mode 100644 index 000000000000..facb191a3708 --- /dev/null +++ b/src/test/ui/impl-trait/recursive-impl-trait-type.rs @@ -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() -> 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() {} diff --git a/src/test/ui/impl-trait/recursive-impl-trait-type.stderr b/src/test/ui/impl-trait/recursive-impl-trait-type.stderr new file mode 100644 index 000000000000..9f9fddb9c8f9 --- /dev/null +++ b/src/test/ui/impl-trait/recursive-impl-trait-type.stderr @@ -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() -> 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 +