Skip to content

Error on recursive opaque ty in HIR typeck #139419

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 67 additions & 3 deletions compiler/rustc_hir_typeck/src/writeback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,21 @@
//! which creates a new `TypeckResults` which doesn't contain any inference variables.

use std::mem;
use std::ops::ControlFlow;

use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_data_structures::unord::ExtendUnord;
use rustc_errors::ErrorGuaranteed;
use rustc_errors::{E0720, ErrorGuaranteed};
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{self, InferKind, Visitor};
use rustc_hir::{self as hir, AmbigArg, HirId};
use rustc_infer::traits::solve::Goal;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion};
use rustc_middle::ty::{
self, DefiningScopeKind, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
TypeVisitableExt, fold_regions,
self, DefiningScopeKind, OpaqueHiddenType, Ty, TyCtxt, TypeFoldable, TypeFolder,
TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
fold_regions,
};
use rustc_span::{Span, sym};
use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded;
Expand Down Expand Up @@ -597,6 +601,35 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
entry.span = prev.span.substitute_dummy(hidden_type.span);
}
}

let recursive_opaques: Vec<_> = self
.typeck_results
.concrete_opaque_types
.iter()
.filter(|&(&def_id, hidden_ty)| {
hidden_ty
.ty
.visit_with(&mut HasRecursiveOpaque {
def_id,
seen: Default::default(),
opaques: &self.typeck_results.concrete_opaque_types,
tcx,
})
.is_break()
})
.map(|(def_id, hidden_ty)| (*def_id, hidden_ty.span))
.collect();
for (def_id, span) in recursive_opaques {
let guar = self
.fcx
.dcx()
.struct_span_err(span, "cannot resolve opaque type")
.with_code(E0720)
.emit();
self.typeck_results
.concrete_opaque_types
.insert(def_id, OpaqueHiddenType { span, ty: Ty::new_error(tcx, guar) });
}
}

fn visit_field_id(&mut self, hir_id: HirId) {
Expand Down Expand Up @@ -961,3 +994,34 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EagerlyNormalizeConsts<'tcx> {
self.tcx.try_normalize_erasing_regions(self.typing_env, ct).unwrap_or(ct)
}
}

struct HasRecursiveOpaque<'a, 'tcx> {
def_id: LocalDefId,
seen: FxHashSet<LocalDefId>,
opaques: &'a FxIndexMap<LocalDefId, ty::OpaqueHiddenType<'tcx>>,
tcx: TyCtxt<'tcx>,
}

impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasRecursiveOpaque<'_, 'tcx> {
type Result = ControlFlow<()>;

fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
if let ty::Alias(ty::Opaque, alias_ty) = *t.kind()
&& let Some(def_id) = alias_ty.def_id.as_local()
{
if self.def_id == def_id {
return ControlFlow::Break(());
}

if self.seen.insert(def_id)
&& let Some(hidden_ty) = self.opaques.get(&def_id)
{
ty::EarlyBinder::bind(hidden_ty.ty)
.instantiate(self.tcx, alias_ty.args)
.visit_with(self)?;
}
}

t.super_visit_with(self)
}
}
40 changes: 5 additions & 35 deletions compiler/rustc_pattern_analysis/src/rustc.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::fmt;
use std::iter::once;
use std::ops::ControlFlow;

use rustc_abi::{FIRST_VARIANT, FieldIdx, Integer, VariantIdx};
use rustc_arena::DroplessArena;
Expand All @@ -12,8 +11,7 @@ use rustc_middle::mir::{self, Const};
use rustc_middle::thir::{self, Pat, PatKind, PatRange, PatRangeBoundary};
use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::{
self, FieldDef, OpaqueTypeKey, ScalarInt, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
TypeVisitableExt, TypeVisitor, VariantDef,
self, FieldDef, OpaqueTypeKey, ScalarInt, Ty, TyCtxt, TypeVisitableExt, VariantDef,
};
use rustc_middle::{bug, span_bug};
use rustc_session::lint;
Expand Down Expand Up @@ -137,22 +135,11 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
/// Returns the hidden type corresponding to this key if the body under analysis is allowed to
/// know it.
fn reveal_opaque_key(&self, key: OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>> {
if let Some(hidden_ty) = self.typeck_results.concrete_opaque_types.get(&key.def_id) {
let ty = ty::EarlyBinder::bind(hidden_ty.ty).instantiate(self.tcx, key.args);
if ty.visit_with(&mut RecursiveOpaque { def_id: key.def_id.into() }).is_continue() {
Some(ty)
} else {
// HACK: We skip revealing opaque types which recursively expand
// to themselves. This is because we may infer hidden types like
// `Opaque<T> = Opaque<Opaque<T>>` or `Opaque<T> = Opaque<(T,)>`
// in hir typeck.
None
}
} else {
None
}
self.typeck_results
.concrete_opaque_types
.get(&key.def_id)
.map(|x| ty::EarlyBinder::bind(x.ty).instantiate(self.tcx, key.args))
}

// This can take a non-revealed `Ty` because it reveals opaques itself.
pub fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
!ty.inhabited_predicate(self.tcx).apply_revealing_opaque(
Expand Down Expand Up @@ -1118,20 +1105,3 @@ pub fn analyze_match<'p, 'tcx>(

Ok(report)
}

struct RecursiveOpaque {
def_id: DefId,
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for RecursiveOpaque {
type Result = ControlFlow<()>;

fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
if let ty::Alias(ty::Opaque, alias_ty) = t.kind() {
if alias_ty.def_id == self.def_id {
return ControlFlow::Break(());
}
}

if t.has_opaque_types() { t.super_visit_with(self) } else { ControlFlow::Continue(()) }
}
}
15 changes: 6 additions & 9 deletions tests/ui/impl-trait/issue-100075-2.stderr
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
error[E0720]: cannot resolve opaque type
--> $DIR/issue-100075-2.rs:1:23
|
LL | fn opaque<T>(t: T) -> impl Sized {
| ^^^^^^^^^^

warning: function cannot return without recursing
--> $DIR/issue-100075-2.rs:1:1
|
Expand All @@ -10,15 +16,6 @@ LL | opaque(Some(t))
= help: a `loop` may express intention better if this is on purpose
= note: `#[warn(unconditional_recursion)]` on by default

error[E0720]: cannot resolve opaque type
--> $DIR/issue-100075-2.rs:1:23
|
LL | fn opaque<T>(t: T) -> impl Sized {
| ^^^^^^^^^^ recursive opaque type
...
LL | opaque(Some(t))
| --------------- returning here with type `impl Sized`

error: aborting due to 1 previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0720`.
5 changes: 1 addition & 4 deletions tests/ui/impl-trait/issue-100075.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ error[E0720]: cannot resolve opaque type
--> $DIR/issue-100075.rs:13:37
|
LL | fn _g<T>(t: &'static T) -> &'static impl Marker {
| ^^^^^^^^^^^ recursive opaque type
...
LL | return _g(t);
| ----- returning here with type `&impl Marker`
| ^^^^^^^^^^^

error: aborting due to 1 previous error

Expand Down
5 changes: 2 additions & 3 deletions tests/ui/impl-trait/issue-103599.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
//@ check-pass

trait T {}

fn wrap(x: impl T) -> impl T {
//~^ WARN function cannot return without recursing
//~^ ERROR cannot resolve opaque type
//~| WARN function cannot return without recursing
wrap(wrap(x))
}

Expand Down
13 changes: 10 additions & 3 deletions tests/ui/impl-trait/issue-103599.stderr
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
error[E0720]: cannot resolve opaque type
--> $DIR/issue-103599.rs:3:23
|
LL | fn wrap(x: impl T) -> impl T {
| ^^^^^^

warning: function cannot return without recursing
--> $DIR/issue-103599.rs:5:1
--> $DIR/issue-103599.rs:3:1
|
LL | fn wrap(x: impl T) -> impl T {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
LL |
...
LL | wrap(wrap(x))
| ------- recursive call site
|
= help: a `loop` may express intention better if this is on purpose
= note: `#[warn(unconditional_recursion)]` on by default

warning: 1 warning emitted
error: aborting due to 1 previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0720`.
4 changes: 2 additions & 2 deletions tests/ui/impl-trait/issue-87450.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ fn bar() -> impl Fn() {
}

fn foo() -> impl Fn() {
//~^ WARNING 5:1: 5:22: function cannot return without recursing [unconditional_recursion]
//~| ERROR 5:13: 5:22: cannot resolve opaque type [E0720]
//~^ WARN function cannot return without recursing
//~| ERROR cannot resolve opaque type
wrap(wrap(wrap(wrap(wrap(wrap(wrap(foo())))))))
}

Expand Down
18 changes: 6 additions & 12 deletions tests/ui/impl-trait/issue-87450.stderr
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
error[E0720]: cannot resolve opaque type
--> $DIR/issue-87450.rs:5:13
|
LL | fn foo() -> impl Fn() {
| ^^^^^^^^^

warning: function cannot return without recursing
--> $DIR/issue-87450.rs:5:1
|
Expand All @@ -10,18 +16,6 @@ LL | wrap(wrap(wrap(wrap(wrap(wrap(wrap(foo())))))))
= help: a `loop` may express intention better if this is on purpose
= note: `#[warn(unconditional_recursion)]` on by default

error[E0720]: cannot resolve opaque type
--> $DIR/issue-87450.rs:5:13
|
LL | fn foo() -> impl Fn() {
| ^^^^^^^^^ recursive opaque type
...
LL | wrap(wrap(wrap(wrap(wrap(wrap(wrap(foo())))))))
| ----------------------------------------------- returning here with type `impl Fn()`
...
LL | fn wrap(f: impl Fn()) -> impl Fn() {
| --------- returning this opaque type `impl Fn()`

error: aborting due to 1 previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0720`.
Loading