Skip to content

Commit 4b8031c

Browse files
authored
Rollup merge of #62423 - Aaron1011:fix/existential-cycle, r=oli-obk
Fix cycle error with existential types Fixes #61863 We now allow uses of `existential type`'s that aren't defining uses - that is, uses which don't constrain the underlying concrete type. To make this work correctly, we also modify `eq_opaque_type_and_type` to not try to apply additional constraints to an opaque type. If we have code like this: ```rust existential type Foo; fn foo1() -> Foo { ... } fn foo2() -> Foo { foo1() } ``` then `foo2` doesn't end up constraining `Foo`, which means that `foo2` will end up using the type `Foo` internally - that is, an actual `TyKind::Opaque`. We don't want to equate this to the underlying concrete type - we just need to enforce the basic equality constraint between the two types (here, the return type of `foo1` and the return type of `foo2`)
2 parents 0e9b465 + 2f41962 commit 4b8031c

12 files changed

+158
-100
lines changed

src/librustc_mir/borrow_check/nll/type_check/mod.rs

+33-5
Original file line numberDiff line numberDiff line change
@@ -1281,15 +1281,43 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
12811281
let opaque_defn_ty = tcx.type_of(opaque_def_id);
12821282
let opaque_defn_ty = opaque_defn_ty.subst(tcx, opaque_decl.substs);
12831283
let opaque_defn_ty = renumber::renumber_regions(infcx, &opaque_defn_ty);
1284+
let concrete_is_opaque = infcx
1285+
.resolve_vars_if_possible(&opaque_decl.concrete_ty).is_impl_trait();
1286+
12841287
debug!(
1285-
"eq_opaque_type_and_type: concrete_ty={:?}={:?} opaque_defn_ty={:?}",
1288+
"eq_opaque_type_and_type: concrete_ty={:?}={:?} opaque_defn_ty={:?} \
1289+
concrete_is_opaque={}",
12861290
opaque_decl.concrete_ty,
12871291
infcx.resolve_vars_if_possible(&opaque_decl.concrete_ty),
1288-
opaque_defn_ty
1292+
opaque_defn_ty,
1293+
concrete_is_opaque
12891294
);
1290-
obligations.add(infcx
1291-
.at(&ObligationCause::dummy(), param_env)
1292-
.eq(opaque_decl.concrete_ty, opaque_defn_ty)?);
1295+
1296+
// concrete_is_opaque is `true` when we're using an existential
1297+
// type without 'revealing' it. For example, code like this:
1298+
//
1299+
// existential type Foo: Debug;
1300+
// fn foo1() -> Foo { ... }
1301+
// fn foo2() -> Foo { foo1() }
1302+
//
1303+
// In `foo2`, we're not revealing the type of `Foo` - we're
1304+
// just treating it as the opaque type.
1305+
//
1306+
// When this occurs, we do *not* want to try to equate
1307+
// the concrete type with the underlying defining type
1308+
// of the existential type - this will always fail, since
1309+
// the defining type of an existential type is always
1310+
// some other type (e.g. not itself)
1311+
// Essentially, none of the normal obligations apply here -
1312+
// we're just passing around some unknown opaque type,
1313+
// without actually looking at the underlying type it
1314+
// gets 'revealed' into
1315+
1316+
if !concrete_is_opaque {
1317+
obligations.add(infcx
1318+
.at(&ObligationCause::dummy(), param_env)
1319+
.eq(opaque_decl.concrete_ty, opaque_defn_ty)?);
1320+
}
12931321
}
12941322

12951323
debug!("eq_opaque_type_and_type: equated");

src/librustc_typeck/check/writeback.rs

+31-24
Original file line numberDiff line numberDiff line change
@@ -453,36 +453,43 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
453453
let definition_ty = self.fcx.infer_opaque_definition_from_instantiation(
454454
def_id, opaque_defn, instantiated_ty, span);
455455

456+
let mut skip_add = false;
457+
456458
if let ty::Opaque(defin_ty_def_id, _substs) = definition_ty.sty {
457459
if def_id == defin_ty_def_id {
458-
// Concrete type resolved to the existential type itself.
459-
// Force a cycle error.
460-
// FIXME(oli-obk): we could just not insert it into `concrete_existential_types`
461-
// which simply would make this use not a defining use.
462-
self.tcx().at(span).type_of(defin_ty_def_id);
460+
debug!("Skipping adding concrete definition for opaque type {:?} {:?}",
461+
opaque_defn, defin_ty_def_id);
462+
skip_add = true;
463463
}
464464
}
465465

466466
if !opaque_defn.substs.has_local_value() {
467-
let new = ty::ResolvedOpaqueTy {
468-
concrete_type: definition_ty,
469-
substs: opaque_defn.substs,
470-
};
471-
472-
let old = self.tables
473-
.concrete_existential_types
474-
.insert(def_id, new);
475-
if let Some(old) = old {
476-
if old.concrete_type != definition_ty || old.substs != opaque_defn.substs {
477-
span_bug!(
478-
span,
479-
"visit_opaque_types tried to write \
480-
different types for the same existential type: {:?}, {:?}, {:?}, {:?}",
481-
def_id,
482-
definition_ty,
483-
opaque_defn,
484-
old,
485-
);
467+
// We only want to add an entry into `concrete_existential_types`
468+
// if we actually found a defining usage of this existential type.
469+
// Otherwise, we do nothing - we'll either find a defining usage
470+
// in some other location, or we'll end up emitting an error due
471+
// to the lack of defining usage
472+
if !skip_add {
473+
let new = ty::ResolvedOpaqueTy {
474+
concrete_type: definition_ty,
475+
substs: opaque_defn.substs,
476+
};
477+
478+
let old = self.tables
479+
.concrete_existential_types
480+
.insert(def_id, new);
481+
if let Some(old) = old {
482+
if old.concrete_type != definition_ty || old.substs != opaque_defn.substs {
483+
span_bug!(
484+
span,
485+
"visit_opaque_types tried to write different types for the same \
486+
existential type: {:?}, {:?}, {:?}, {:?}",
487+
def_id,
488+
definition_ty,
489+
opaque_defn,
490+
old,
491+
);
492+
}
486493
}
487494
}
488495
} else {

src/test/ui/existential_types/existential-types-with-cycle-error.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![feature(existential_type)]
22

33
existential type Foo: Fn() -> Foo;
4-
//~^ ERROR: cycle detected when processing `Foo`
4+
//~^ ERROR: could not find defining uses
55

66
fn crash(x: Foo) -> Foo {
77
x
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,8 @@
1-
error[E0391]: cycle detected when processing `Foo`
1+
error: could not find defining uses
22
--> $DIR/existential-types-with-cycle-error.rs:3:1
33
|
44
LL | existential type Foo: Fn() -> Foo;
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6-
|
7-
note: ...which requires processing `crash`...
8-
--> $DIR/existential-types-with-cycle-error.rs:6:25
9-
|
10-
LL | fn crash(x: Foo) -> Foo {
11-
| _________________________^
12-
LL | | x
13-
LL | | }
14-
| |_^
15-
= note: ...which again requires processing `Foo`, completing the cycle
16-
note: cycle used when collecting item types in top-level module
17-
--> $DIR/existential-types-with-cycle-error.rs:1:1
18-
|
19-
LL | / #![feature(existential_type)]
20-
LL | |
21-
LL | | existential type Foo: Fn() -> Foo;
22-
LL | |
23-
... |
24-
LL | |
25-
LL | | }
26-
| |_^
276

287
error: aborting due to previous error
298

30-
For more information about this error, try `rustc --explain E0391`.

src/test/ui/existential_types/existential-types-with-cycle-error2.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ pub trait Bar<T> {
55
}
66

77
existential type Foo: Bar<Foo, Item = Foo>;
8-
//~^ ERROR: cycle detected when processing `Foo`
8+
//~^ ERROR: could not find defining uses
99

1010
fn crash(x: Foo) -> Foo {
1111
x
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,8 @@
1-
error[E0391]: cycle detected when processing `Foo`
1+
error: could not find defining uses
22
--> $DIR/existential-types-with-cycle-error2.rs:7:1
33
|
44
LL | existential type Foo: Bar<Foo, Item = Foo>;
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6-
|
7-
note: ...which requires processing `crash`...
8-
--> $DIR/existential-types-with-cycle-error2.rs:10:25
9-
|
10-
LL | fn crash(x: Foo) -> Foo {
11-
| _________________________^
12-
LL | | x
13-
LL | | }
14-
| |_^
15-
= note: ...which again requires processing `Foo`, completing the cycle
16-
note: cycle used when collecting item types in top-level module
17-
--> $DIR/existential-types-with-cycle-error2.rs:1:1
18-
|
19-
LL | / #![feature(existential_type)]
20-
LL | |
21-
LL | | pub trait Bar<T> {
22-
LL | | type Item;
23-
... |
24-
LL | |
25-
LL | | }
26-
| |_^
276

287
error: aborting due to previous error
298

30-
For more information about this error, try `rustc --explain E0391`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// check-pass
2+
3+
#![feature(existential_type)]
4+
// Currently, the `existential_type` feature implicitly
5+
// depends on `impl_trait_in_bindings` in order to work properly.
6+
// Specifically, this line requires `impl_trait_in_bindings` to be enabled:
7+
// https://github.com/rust-lang/rust/blob/481068a707679257e2a738b40987246e0420e787/src/librustc_typeck/check/mod.rs#L856
8+
#![feature(impl_trait_in_bindings)]
9+
//~^ WARN the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash
10+
11+
// Ensures that `const` items can constrain an `existential type`.
12+
13+
use std::fmt::Debug;
14+
15+
pub existential type Foo: Debug;
16+
17+
const _FOO: Foo = 5;
18+
19+
fn main() {
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
warning: the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash
2+
--> $DIR/existential_type_const.rs:8:12
3+
|
4+
LL | #![feature(impl_trait_in_bindings)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^
6+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// check-pass
2+
3+
#![feature(existential_type)]
4+
5+
// Regression test for issue #61863
6+
7+
pub trait MyTrait {}
8+
9+
#[derive(Debug)]
10+
pub struct MyStruct {
11+
v: u64
12+
}
13+
14+
impl MyTrait for MyStruct {}
15+
16+
pub fn bla() -> TE {
17+
return MyStruct {v:1}
18+
}
19+
20+
pub fn bla2() -> TE {
21+
bla()
22+
}
23+
24+
25+
existential type TE: MyTrait;
26+
27+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// check-pass
2+
3+
#![feature(existential_type)]
4+
#![allow(dead_code)]
5+
6+
pub trait MyTrait {}
7+
8+
impl MyTrait for bool {}
9+
10+
struct Blah {
11+
my_foo: Foo,
12+
my_u8: u8
13+
}
14+
15+
impl Blah {
16+
fn new() -> Blah {
17+
Blah {
18+
my_foo: make_foo(),
19+
my_u8: 12
20+
}
21+
}
22+
fn into_inner(self) -> (Foo, u8) {
23+
(self.my_foo, self.my_u8)
24+
}
25+
}
26+
27+
fn make_foo() -> Foo {
28+
true
29+
}
30+
31+
existential type Foo: MyTrait;
32+
33+
fn main() {}

src/test/ui/existential_types/no_inferrable_concrete_type.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
// Issue 52985: Cause cycle error if user code provides no use case that allows an existential type
2-
// to be inferred to a concrete type. This results in an infinite cycle during type normalization.
1+
// Issue 52985: user code provides no use case that allows an existential type
2+
// We now emit a 'could not find defining uses' error
33

44
#![feature(existential_type)]
55

6-
existential type Foo: Copy; //~ cycle detected
6+
existential type Foo: Copy; //~ could not find defining uses
77

88
// make compiler happy about using 'Foo'
99
fn bar(x: Foo) -> Foo { x }
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,8 @@
1-
error[E0391]: cycle detected when processing `Foo`
1+
error: could not find defining uses
22
--> $DIR/no_inferrable_concrete_type.rs:6:1
33
|
44
LL | existential type Foo: Copy;
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
6-
|
7-
note: ...which requires processing `bar`...
8-
--> $DIR/no_inferrable_concrete_type.rs:9:23
9-
|
10-
LL | fn bar(x: Foo) -> Foo { x }
11-
| ^^^^^
12-
= note: ...which again requires processing `Foo`, completing the cycle
13-
note: cycle used when collecting item types in top-level module
14-
--> $DIR/no_inferrable_concrete_type.rs:4:1
15-
|
16-
LL | / #![feature(existential_type)]
17-
LL | |
18-
LL | | existential type Foo: Copy;
19-
LL | |
20-
... |
21-
LL | | let _: Foo = std::mem::transmute(0u8);
22-
LL | | }
23-
| |_^
246

257
error: aborting due to previous error
268

27-
For more information about this error, try `rustc --explain E0391`.

0 commit comments

Comments
 (0)