Skip to content

Commit 7e3af74

Browse files
authored
Rollup merge of #139419 - compiler-errors:recursive-opaque, r=lcnr
Error on recursive opaque ty in HIR typeck "Non-trivially recursive" opaques are opaques whose hidden types are inferred to be equal to something other than themselves. For example, if we have a TAIT like `type TAIT = impl Sized`, if we infer the hidden type to be `TAIT := (TAIT,)`, that would be a non-trivial recursive definition. We don't want to support opaques that are non-trivially recursive, since they will (almost!! -- see caveat below) always result in borrowck errors, and are generally a pain to deal with. On the contrary, trivially recursive opaques may occur today because the old solver overagerly uses `replace_opaque_types_with_inference_vars`. This infer var can then later be constrained to be equal to the opaque itself. These cases will not necessarily result in borrow-checker errors, since other uses of the opaque may properly constrain the opaque. If there are no other uses we may instead fall back to `()` today. The only weird case that we have to unfortunately deal with was discovered in #139406: ```rust #![allow(unconditional_recursion)] fn what1<T>(x: T) -> impl Sized { what1(x) } fn what2<T>(x: T) -> impl Sized { what2(what2(x)) } fn print_return_type<T, U>(_: impl Fn(T) -> U) { println!("{}", std::any::type_name::<U>()) } fn main() { print_return_type(what1::<i32>); // () print_return_type(what2::<i32>); // i32 } ``` > HIR typeck eagerly replaces the return type with an infer var, ending up with `RPIT<T> = RPIT<RPIT<T>>` in the storage. While we return this in the `TypeckResults`, it's never actually used anywhere. > > MIR building then results in the following statement > ```rust > let _0: impl RPIT<T> /* the return place */ = build<RPIT<T>>(_some_local); > ``` > Unlike HIR typeck MIR typeck now directly equates `RPIT<T>` with `RPIT<RPIT<T>>`. This does not try to define `RPIT` but instead relates its generic arguments https://github.com/rust-lang/rust/blob/b9856b6e400709392dd14599265b6fd52fc19f3e/compiler/rustc_infer/src/infer/relate/type_relating.rs#L185-L190 > > This means we relate `T` with `RPIT<T>`, which results in a defining use `RPIT<T> = T` I think it's pretty obvious that this is not desirable behavior, and according to the crater run there were no regressions, so let's break this so that we don't have any inference hazards in the new solver. In the future `what2` may end up compiling again by also falling back to `()`. However, that is not yet guaranteed and the transition to this state is made significantly harder by not temporarily breaking it on the way. It is also concerning to change the inferred hidden type like this without any notification to the user, even if likely not an issue in this concrete case.
2 parents 28174fc + 47b9e37 commit 7e3af74

22 files changed

+162
-218
lines changed

compiler/rustc_hir_typeck/src/writeback.rs

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,21 @@
99
//! which creates a new `TypeckResults` which doesn't contain any inference variables.
1010
1111
use std::mem;
12+
use std::ops::ControlFlow;
1213

14+
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
1315
use rustc_data_structures::unord::ExtendUnord;
14-
use rustc_errors::ErrorGuaranteed;
16+
use rustc_errors::{E0720, ErrorGuaranteed};
17+
use rustc_hir::def_id::LocalDefId;
1518
use rustc_hir::intravisit::{self, InferKind, Visitor};
1619
use rustc_hir::{self as hir, AmbigArg, HirId};
1720
use rustc_infer::traits::solve::Goal;
1821
use rustc_middle::traits::ObligationCause;
1922
use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion};
2023
use rustc_middle::ty::{
21-
self, DefiningScopeKind, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
22-
TypeVisitableExt, fold_regions,
24+
self, DefiningScopeKind, OpaqueHiddenType, Ty, TyCtxt, TypeFoldable, TypeFolder,
25+
TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
26+
fold_regions,
2327
};
2428
use rustc_span::{Span, sym};
2529
use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded;
@@ -595,6 +599,35 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
595599
entry.span = prev.span.substitute_dummy(hidden_type.span);
596600
}
597601
}
602+
603+
let recursive_opaques: Vec<_> = self
604+
.typeck_results
605+
.concrete_opaque_types
606+
.iter()
607+
.filter(|&(&def_id, hidden_ty)| {
608+
hidden_ty
609+
.ty
610+
.visit_with(&mut HasRecursiveOpaque {
611+
def_id,
612+
seen: Default::default(),
613+
opaques: &self.typeck_results.concrete_opaque_types,
614+
tcx,
615+
})
616+
.is_break()
617+
})
618+
.map(|(def_id, hidden_ty)| (*def_id, hidden_ty.span))
619+
.collect();
620+
for (def_id, span) in recursive_opaques {
621+
let guar = self
622+
.fcx
623+
.dcx()
624+
.struct_span_err(span, "cannot resolve opaque type")
625+
.with_code(E0720)
626+
.emit();
627+
self.typeck_results
628+
.concrete_opaque_types
629+
.insert(def_id, OpaqueHiddenType { span, ty: Ty::new_error(tcx, guar) });
630+
}
598631
}
599632

600633
fn visit_field_id(&mut self, hir_id: HirId) {
@@ -959,3 +992,34 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EagerlyNormalizeConsts<'tcx> {
959992
self.tcx.try_normalize_erasing_regions(self.typing_env, ct).unwrap_or(ct)
960993
}
961994
}
995+
996+
struct HasRecursiveOpaque<'a, 'tcx> {
997+
def_id: LocalDefId,
998+
seen: FxHashSet<LocalDefId>,
999+
opaques: &'a FxIndexMap<LocalDefId, ty::OpaqueHiddenType<'tcx>>,
1000+
tcx: TyCtxt<'tcx>,
1001+
}
1002+
1003+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasRecursiveOpaque<'_, 'tcx> {
1004+
type Result = ControlFlow<()>;
1005+
1006+
fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
1007+
if let ty::Alias(ty::Opaque, alias_ty) = *t.kind()
1008+
&& let Some(def_id) = alias_ty.def_id.as_local()
1009+
{
1010+
if self.def_id == def_id {
1011+
return ControlFlow::Break(());
1012+
}
1013+
1014+
if self.seen.insert(def_id)
1015+
&& let Some(hidden_ty) = self.opaques.get(&def_id)
1016+
{
1017+
ty::EarlyBinder::bind(hidden_ty.ty)
1018+
.instantiate(self.tcx, alias_ty.args)
1019+
.visit_with(self)?;
1020+
}
1021+
}
1022+
1023+
t.super_visit_with(self)
1024+
}
1025+
}

compiler/rustc_pattern_analysis/src/rustc.rs

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use std::fmt;
22
use std::iter::once;
3-
use std::ops::ControlFlow;
43

54
use rustc_abi::{FIRST_VARIANT, FieldIdx, Integer, VariantIdx};
65
use rustc_arena::DroplessArena;
@@ -12,8 +11,7 @@ use rustc_middle::mir::{self, Const};
1211
use rustc_middle::thir::{self, Pat, PatKind, PatRange, PatRangeBoundary};
1312
use rustc_middle::ty::layout::IntegerExt;
1413
use rustc_middle::ty::{
15-
self, FieldDef, OpaqueTypeKey, ScalarInt, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
16-
TypeVisitableExt, TypeVisitor, VariantDef,
14+
self, FieldDef, OpaqueTypeKey, ScalarInt, Ty, TyCtxt, TypeVisitableExt, VariantDef,
1715
};
1816
use rustc_middle::{bug, span_bug};
1917
use rustc_session::lint;
@@ -137,22 +135,11 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
137135
/// Returns the hidden type corresponding to this key if the body under analysis is allowed to
138136
/// know it.
139137
fn reveal_opaque_key(&self, key: OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>> {
140-
if let Some(hidden_ty) = self.typeck_results.concrete_opaque_types.get(&key.def_id) {
141-
let ty = ty::EarlyBinder::bind(hidden_ty.ty).instantiate(self.tcx, key.args);
142-
if ty.visit_with(&mut RecursiveOpaque { def_id: key.def_id.into() }).is_continue() {
143-
Some(ty)
144-
} else {
145-
// HACK: We skip revealing opaque types which recursively expand
146-
// to themselves. This is because we may infer hidden types like
147-
// `Opaque<T> = Opaque<Opaque<T>>` or `Opaque<T> = Opaque<(T,)>`
148-
// in hir typeck.
149-
None
150-
}
151-
} else {
152-
None
153-
}
138+
self.typeck_results
139+
.concrete_opaque_types
140+
.get(&key.def_id)
141+
.map(|x| ty::EarlyBinder::bind(x.ty).instantiate(self.tcx, key.args))
154142
}
155-
156143
// This can take a non-revealed `Ty` because it reveals opaques itself.
157144
pub fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
158145
!ty.inhabited_predicate(self.tcx).apply_revealing_opaque(
@@ -1177,20 +1164,3 @@ fn detect_mixed_deref_pat_ctors<'p, 'tcx>(
11771164
}
11781165
Ok(())
11791166
}
1180-
1181-
struct RecursiveOpaque {
1182-
def_id: DefId,
1183-
}
1184-
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for RecursiveOpaque {
1185-
type Result = ControlFlow<()>;
1186-
1187-
fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
1188-
if let ty::Alias(ty::Opaque, alias_ty) = t.kind() {
1189-
if alias_ty.def_id == self.def_id {
1190-
return ControlFlow::Break(());
1191-
}
1192-
}
1193-
1194-
if t.has_opaque_types() { t.super_visit_with(self) } else { ControlFlow::Continue(()) }
1195-
}
1196-
}
Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
error[E0720]: cannot resolve opaque type
2+
--> $DIR/issue-100075-2.rs:1:23
3+
|
4+
LL | fn opaque<T>(t: T) -> impl Sized {
5+
| ^^^^^^^^^^
6+
17
warning: function cannot return without recursing
28
--> $DIR/issue-100075-2.rs:1:1
39
|
@@ -10,15 +16,6 @@ LL | opaque(Some(t))
1016
= help: a `loop` may express intention better if this is on purpose
1117
= note: `#[warn(unconditional_recursion)]` on by default
1218

13-
error[E0720]: cannot resolve opaque type
14-
--> $DIR/issue-100075-2.rs:1:23
15-
|
16-
LL | fn opaque<T>(t: T) -> impl Sized {
17-
| ^^^^^^^^^^ recursive opaque type
18-
...
19-
LL | opaque(Some(t))
20-
| --------------- returning here with type `impl Sized`
21-
2219
error: aborting due to 1 previous error; 1 warning emitted
2320

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

tests/ui/impl-trait/issues/issue-100075.stderr

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ error[E0720]: cannot resolve opaque type
22
--> $DIR/issue-100075.rs:13:37
33
|
44
LL | fn _g<T>(t: &'static T) -> &'static impl Marker {
5-
| ^^^^^^^^^^^ recursive opaque type
6-
...
7-
LL | return _g(t);
8-
| ----- returning here with type `&impl Marker`
5+
| ^^^^^^^^^^^
96

107
error: aborting due to 1 previous error
118

tests/ui/impl-trait/issues/issue-103599.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
//@ check-pass
2-
31
trait T {}
42

53
fn wrap(x: impl T) -> impl T {
6-
//~^ WARN function cannot return without recursing
4+
//~^ ERROR cannot resolve opaque type
5+
//~| WARN function cannot return without recursing
76
wrap(wrap(x))
87
}
98

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
1+
error[E0720]: cannot resolve opaque type
2+
--> $DIR/issue-103599.rs:3:23
3+
|
4+
LL | fn wrap(x: impl T) -> impl T {
5+
| ^^^^^^
6+
17
warning: function cannot return without recursing
2-
--> $DIR/issue-103599.rs:5:1
8+
--> $DIR/issue-103599.rs:3:1
39
|
410
LL | fn wrap(x: impl T) -> impl T {
511
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
6-
LL |
12+
...
713
LL | wrap(wrap(x))
814
| ------- recursive call site
915
|
1016
= help: a `loop` may express intention better if this is on purpose
1117
= note: `#[warn(unconditional_recursion)]` on by default
1218

13-
warning: 1 warning emitted
19+
error: aborting due to 1 previous error; 1 warning emitted
1420

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

tests/ui/impl-trait/issues/issue-87450.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ fn bar() -> impl Fn() {
33
}
44

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

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
error[E0720]: cannot resolve opaque type
2+
--> $DIR/issue-87450.rs:5:13
3+
|
4+
LL | fn foo() -> impl Fn() {
5+
| ^^^^^^^^^
6+
17
warning: function cannot return without recursing
28
--> $DIR/issue-87450.rs:5:1
39
|
@@ -10,18 +16,6 @@ LL | wrap(wrap(wrap(wrap(wrap(wrap(wrap(foo())))))))
1016
= help: a `loop` may express intention better if this is on purpose
1117
= note: `#[warn(unconditional_recursion)]` on by default
1218

13-
error[E0720]: cannot resolve opaque type
14-
--> $DIR/issue-87450.rs:5:13
15-
|
16-
LL | fn foo() -> impl Fn() {
17-
| ^^^^^^^^^ recursive opaque type
18-
...
19-
LL | wrap(wrap(wrap(wrap(wrap(wrap(wrap(foo())))))))
20-
| ----------------------------------------------- returning here with type `impl Fn()`
21-
...
22-
LL | fn wrap(f: impl Fn()) -> impl Fn() {
23-
| --------- returning this opaque type `impl Fn()`
24-
2519
error: aborting due to 1 previous error; 1 warning emitted
2620

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

0 commit comments

Comments
 (0)