Skip to content
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

Fix cycle error with existential types #62423

Merged
merged 7 commits into from
Jul 27, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
25 changes: 23 additions & 2 deletions src/librustc_mir/borrow_check/nll/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1253,17 +1253,38 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
&anon_ty,
locations.span(body),
));

let revealed_ty_is_opaque = revealed_ty.is_impl_trait();

debug!(
"eq_opaque_type_and_type: \
instantiated output_ty={:?} \
opaque_type_map={:#?} \
revealed_ty={:?}",
output_ty, opaque_type_map, revealed_ty
revealed_ty={:?} \
revealed_ty_is_opaque={}",
output_ty, opaque_type_map, revealed_ty, revealed_ty_is_opaque
);
obligations.add(infcx
.at(&ObligationCause::dummy(), param_env)
.eq(output_ty, revealed_ty)?);

// This is 'true' when we're using an existential
Centril marked this conversation as resolved.
Show resolved Hide resolved
// type without 'revelaing' it. For example, code like this:
Centril marked this conversation as resolved.
Show resolved Hide resolved
//
// existential type Foo: Debug;
// fn foo1() -> Foo { ... }
// fn foo2() -> Foo { foo1() }
//
// In 'foo2', we're not revealing the type of 'Foo' - we're
Centril marked this conversation as resolved.
Show resolved Hide resolved
// just treating it as the opaque type. All of the constraints
// in our 'opaque_type_map' apply to the concrete type,
Centril marked this conversation as resolved.
Show resolved Hide resolved
// not to the opaque type itself. Therefore, it's enough
// to simply equate the output and opque 'revealed_type',
Centril marked this conversation as resolved.
Show resolved Hide resolved
// as we do above
Centril marked this conversation as resolved.
Show resolved Hide resolved
if revealed_ty_is_opaque {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just testing that the revealed type is opaque doesn't seem like enough

return Ok(InferOk { value: None, obligations: obligations.into_vec() });
}

for (&opaque_def_id, opaque_decl) in &opaque_type_map {
let opaque_defn_ty = tcx.type_of(opaque_def_id);
let opaque_defn_ty = opaque_defn_ty.subst(tcx, opaque_decl.substs);
Expand Down
50 changes: 26 additions & 24 deletions src/librustc_typeck/check/writeback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,36 +576,38 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
})
};

let mut skip_add = false;

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

if !opaque_defn.substs.has_local_value() {
let new = ty::ResolvedOpaqueTy {
concrete_type: definition_ty,
substs: opaque_defn.substs,
};

let old = self.tables
.concrete_existential_types
.insert(def_id, new);
if let Some(old) = old {
if old.concrete_type != definition_ty || old.substs != opaque_defn.substs {
span_bug!(
span,
"visit_opaque_types tried to write \
different types for the same existential type: {:?}, {:?}, {:?}, {:?}",
def_id,
definition_ty,
opaque_defn,
old,
);
if !skip_add {
Centril marked this conversation as resolved.
Show resolved Hide resolved
let new = ty::ResolvedOpaqueTy {
concrete_type: definition_ty,
substs: opaque_defn.substs,
};

let old = self.tables
.concrete_existential_types
.insert(def_id, new);
if let Some(old) = old {
if old.concrete_type != definition_ty || old.substs != opaque_defn.substs {
span_bug!(
span,
"visit_opaque_types tried to write different types for the same \
existential type: {:?}, {:?}, {:?}, {:?}",
def_id,
definition_ty,
opaque_defn,
old,
);
}
}
}
} else {
Expand Down
17 changes: 17 additions & 0 deletions src/test/run-pass/existential_type_const.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#![feature(existential_type)]
Centril marked this conversation as resolved.
Show resolved Hide resolved
#![feature(impl_trait_in_bindings)]
Centril marked this conversation as resolved.
Show resolved Hide resolved
//~^ WARN the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash

// Ensures that consts can constrain an existential type
Aaron1011 marked this conversation as resolved.
Show resolved Hide resolved

use std::fmt::Debug;

// Type `Foo` refers to a type that implements the `Debug` trait.
// The concrete type to which `Foo` refers is inferred from this module,
// and this concrete type is hidden from outer modules (but not submodules).
pub existential type Foo: Debug;

const _FOO: Foo = 5;

fn main() {
}
6 changes: 6 additions & 0 deletions src/test/run-pass/existential_type_const.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
warning: the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash
--> $DIR/existential_type_const.rs:2:12
|
LL | #![feature(impl_trait_in_bindings)]
| ^^^^^^^^^^^^^^^^^^^^^^

25 changes: 25 additions & 0 deletions src/test/run-pass/existential_type_fns.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#![feature(existential_type)]

// Regression test for issue #61863

pub trait MyTrait {}

#[derive(Debug)]
pub struct MyStruct {
v: u64
}

impl MyTrait for MyStruct {}

pub fn bla() -> TE {
return MyStruct {v:1}
}

pub fn bla2() -> TE {
bla()
}


existential type TE: MyTrait;

fn main() {}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![feature(existential_type)]

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

fn crash(x: Foo) -> Foo {
x
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,8 @@
error[E0391]: cycle detected when processing `Foo`
error: could not find defining uses
--> $DIR/existential-types-with-cycle-error.rs:3:1
|
LL | existential type Foo: Fn() -> Foo;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: ...which requires processing `crash`...
--> $DIR/existential-types-with-cycle-error.rs:6:25
|
LL | fn crash(x: Foo) -> Foo {
| _________________________^
LL | | x
LL | | }
| |_^
= note: ...which again requires processing `Foo`, completing the cycle
note: cycle used when collecting item types in top-level module
--> $DIR/existential-types-with-cycle-error.rs:1:1
|
LL | / #![feature(existential_type)]
LL | |
LL | | existential type Foo: Fn() -> Foo;
LL | |
... |
LL | |
LL | | }
| |_^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0391`.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub trait Bar<T> {
}

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

fn crash(x: Foo) -> Foo {
x
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,8 @@
error[E0391]: cycle detected when processing `Foo`
error: could not find defining uses
--> $DIR/existential-types-with-cycle-error2.rs:7:1
|
LL | existential type Foo: Bar<Foo, Item = Foo>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: ...which requires processing `crash`...
--> $DIR/existential-types-with-cycle-error2.rs:10:25
|
LL | fn crash(x: Foo) -> Foo {
| _________________________^
LL | | x
LL | | }
| |_^
= note: ...which again requires processing `Foo`, completing the cycle
note: cycle used when collecting item types in top-level module
--> $DIR/existential-types-with-cycle-error2.rs:1:1
|
LL | / #![feature(existential_type)]
LL | |
LL | | pub trait Bar<T> {
LL | | type Item;
... |
LL | |
LL | | }
| |_^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0391`.
6 changes: 3 additions & 3 deletions src/test/ui/existential_types/no_inferrable_concrete_type.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Issue 52985: Cause cycle error if user code provides no use case that allows an existential type
// to be inferred to a concrete type. This results in an infinite cycle during type normalization.
// Issue 52985: user code provides no use case that allows an existential type
// We now emit a 'could not find defining uses' error

#![feature(existential_type)]

existential type Foo: Copy; //~ cycle detected
existential type Foo: Copy; //~ could not find defining uses

// make compiler happy about using 'Foo'
fn bar(x: Foo) -> Foo { x }
Expand Down
21 changes: 1 addition & 20 deletions src/test/ui/existential_types/no_inferrable_concrete_type.stderr
Original file line number Diff line number Diff line change
@@ -1,27 +1,8 @@
error[E0391]: cycle detected when processing `Foo`
error: could not find defining uses
--> $DIR/no_inferrable_concrete_type.rs:6:1
|
LL | existential type Foo: Copy;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: ...which requires processing `bar`...
--> $DIR/no_inferrable_concrete_type.rs:9:23
|
LL | fn bar(x: Foo) -> Foo { x }
| ^^^^^
= note: ...which again requires processing `Foo`, completing the cycle
note: cycle used when collecting item types in top-level module
--> $DIR/no_inferrable_concrete_type.rs:4:1
|
LL | / #![feature(existential_type)]
LL | |
LL | | existential type Foo: Copy;
LL | |
... |
LL | | let _: Foo = std::mem::transmute(0u8);
LL | | }
| |_^

error: aborting due to previous error

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