Skip to content

Commit 8ba9b10

Browse files
committed
Fix cycle error with existential types
Fixes rust-lang#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: ``` 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')
1 parent dd2e804 commit 8ba9b10

11 files changed

+105
-97
lines changed

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

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,17 +1253,38 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
12531253
&anon_ty,
12541254
locations.span(body),
12551255
));
1256+
1257+
let revealed_ty_is_opaque = revealed_ty.is_impl_trait();
1258+
12561259
debug!(
12571260
"eq_opaque_type_and_type: \
12581261
instantiated output_ty={:?} \
12591262
opaque_type_map={:#?} \
1260-
revealed_ty={:?}",
1261-
output_ty, opaque_type_map, revealed_ty
1263+
revealed_ty={:?} \
1264+
revealed_ty_is_opaque={}",
1265+
output_ty, opaque_type_map, revealed_ty, revealed_ty_is_opaque
12621266
);
12631267
obligations.add(infcx
12641268
.at(&ObligationCause::dummy(), param_env)
12651269
.eq(output_ty, revealed_ty)?);
12661270

1271+
// This is 'true' when we're using an existential
1272+
// type without 'revelaing' it. For example, code like this:
1273+
//
1274+
// existential type Foo: Debug;
1275+
// fn foo1() -> Foo { ... }
1276+
// fn foo2() -> Foo { foo1() }
1277+
//
1278+
// In 'foo2', we're not revealing the type of 'Foo' - we're
1279+
// just treating it as the opaque type. All of the constraints
1280+
// in our 'opaque_type_map' apply to the concrete type,
1281+
// not to the opaque type itself. Therefore, it's enough
1282+
// to simply equate the output and opque 'revealed_type',
1283+
// as we do above
1284+
if revealed_ty_is_opaque {
1285+
return Ok(InferOk { value: None, obligations: obligations.into_vec() });
1286+
}
1287+
12671288
for (&opaque_def_id, opaque_decl) in &opaque_type_map {
12681289
let opaque_defn_ty = tcx.type_of(opaque_def_id);
12691290
let opaque_defn_ty = opaque_defn_ty.subst(tcx, opaque_decl.substs);

src/librustc_typeck/check/writeback.rs

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -576,36 +576,38 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
576576
})
577577
};
578578

579+
let mut skip_add = false;
580+
579581
if let ty::Opaque(defin_ty_def_id, _substs) = definition_ty.sty {
580582
if def_id == defin_ty_def_id {
581-
// Concrete type resolved to the existential type itself.
582-
// Force a cycle error.
583-
// FIXME(oli-obk): we could just not insert it into `concrete_existential_types`
584-
// which simply would make this use not a defining use.
585-
self.tcx().at(span).type_of(defin_ty_def_id);
583+
debug!("Skipping adding concrete definition for opaque type {:?} {:?}",
584+
opaque_defn, defin_ty_def_id);
585+
skip_add = true;
586586
}
587587
}
588588

589589
if !opaque_defn.substs.has_local_value() {
590-
let new = ty::ResolvedOpaqueTy {
591-
concrete_type: definition_ty,
592-
substs: opaque_defn.substs,
593-
};
594-
595-
let old = self.tables
596-
.concrete_existential_types
597-
.insert(def_id, new);
598-
if let Some(old) = old {
599-
if old.concrete_type != definition_ty || old.substs != opaque_defn.substs {
600-
span_bug!(
601-
span,
602-
"visit_opaque_types tried to write \
603-
different types for the same existential type: {:?}, {:?}, {:?}, {:?}",
604-
def_id,
605-
definition_ty,
606-
opaque_defn,
607-
old,
608-
);
590+
if !skip_add {
591+
let new = ty::ResolvedOpaqueTy {
592+
concrete_type: definition_ty,
593+
substs: opaque_defn.substs,
594+
};
595+
596+
let old = self.tables
597+
.concrete_existential_types
598+
.insert(def_id, new);
599+
if let Some(old) = old {
600+
if old.concrete_type != definition_ty || old.substs != opaque_defn.substs {
601+
span_bug!(
602+
span,
603+
"visit_opaque_types tried to write different types for the same \
604+
existential type: {:?}, {:?}, {:?}, {:?}",
605+
def_id,
606+
definition_ty,
607+
opaque_defn,
608+
old,
609+
);
610+
}
609611
}
610612
}
611613
} else {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#![feature(existential_type)]
2+
#![feature(impl_trait_in_bindings)]
3+
//~^ WARN the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash
4+
5+
// Ensures that consts can constrain an existential type
6+
7+
use std::fmt::Debug;
8+
9+
// Type `Foo` refers to a type that implements the `Debug` trait.
10+
// The concrete type to which `Foo` refers is inferred from this module,
11+
// and this concrete type is hidden from outer modules (but not submodules).
12+
pub existential type Foo: Debug;
13+
14+
const _FOO: Foo = 5;
15+
16+
fn main() {
17+
}
Lines changed: 6 additions & 0 deletions
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:2:12
3+
|
4+
LL | #![feature(impl_trait_in_bindings)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^
6+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#![feature(existential_type)]
2+
3+
// Regression test for issue #61863
4+
5+
pub trait MyTrait {}
6+
7+
#[derive(Debug)]
8+
pub struct MyStruct {
9+
v: u64
10+
}
11+
12+
impl MyTrait for MyStruct {}
13+
14+
pub fn bla() -> TE {
15+
return MyStruct {v:1}
16+
}
17+
18+
pub fn bla2() -> TE {
19+
bla()
20+
}
21+
22+
23+
existential type TE: MyTrait;
24+
25+
fn main() {}

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

Lines changed: 1 addition & 1 deletion
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
Lines changed: 1 addition & 23 deletions
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

Lines changed: 1 addition & 1 deletion
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
Lines changed: 1 addition & 23 deletions
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`.

src/test/ui/existential_types/no_inferrable_concrete_type.rs

Lines changed: 3 additions & 3 deletions
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 }
Lines changed: 1 addition & 20 deletions
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)