]> git.lizzy.rs Git - rust.git/commitdiff
Fix cycle error with existential types
authorAaron Hill <aa1ronham@gmail.com>
Fri, 5 Jul 2019 20:24:58 +0000 (16:24 -0400)
committerAaron Hill <aa1ronham@gmail.com>
Sun, 7 Jul 2019 21:22:06 +0000 (17:22 -0400)
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:

```
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')

src/librustc_mir/borrow_check/nll/type_check/mod.rs
src/librustc_typeck/check/writeback.rs
src/test/run-pass/existential_type_const.rs [new file with mode: 0644]
src/test/run-pass/existential_type_const.stderr [new file with mode: 0644]
src/test/run-pass/existential_type_fns.rs [new file with mode: 0644]
src/test/ui/existential_types/existential-types-with-cycle-error.rs
src/test/ui/existential_types/existential-types-with-cycle-error.stderr
src/test/ui/existential_types/existential-types-with-cycle-error2.rs
src/test/ui/existential_types/existential-types-with-cycle-error2.stderr
src/test/ui/existential_types/no_inferrable_concrete_type.rs
src/test/ui/existential_types/no_inferrable_concrete_type.stderr

index cdbbe1d02bd92edd54450409dd9ae907ef6b6a08..8bc377b401e87d77f3ee24256eac13500a82d968 100644 (file)
@@ -1253,17 +1253,38 @@ fn eq_opaque_type_and_type(
                             &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
+                    // type without 'revelaing' it. For example, code like this:
+                    //
+                    // existential type Foo: Debug;
+                    // fn foo1() -> Foo { ... }
+                    // fn foo2() -> Foo { foo1() }
+                    //
+                    // In 'foo2', we're not revealing the type of 'Foo' - we're
+                    // just treating it as the opaque type. All of the constraints
+                    // in our 'opaque_type_map' apply to the concrete type,
+                    // not to the opaque type itself. Therefore, it's enough
+                    // to simply equate the output and opque 'revealed_type',
+                    // as we do above
+                    if revealed_ty_is_opaque {
+                        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);
index a2632b20c2ecbca944f6bebedabb3f95d76c759f..14bd2f0fa7eb912cfe970b9f7d23ca3d2faaf1e5 100644 (file)
@@ -576,36 +576,38 @@ fn visit_opaque_types(&mut self, span: Span) {
                 })
             };
 
+            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 {
+                    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 {
diff --git a/src/test/run-pass/existential_type_const.rs b/src/test/run-pass/existential_type_const.rs
new file mode 100644 (file)
index 0000000..333e15f
--- /dev/null
@@ -0,0 +1,17 @@
+#![feature(existential_type)]
+#![feature(impl_trait_in_bindings)]
+//~^ WARN the feature `impl_trait_in_bindings` is incomplete and may cause the compiler to crash
+
+// Ensures that consts can constrain an existential type
+
+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() {
+}
diff --git a/src/test/run-pass/existential_type_const.stderr b/src/test/run-pass/existential_type_const.stderr
new file mode 100644 (file)
index 0000000..b6d83fb
--- /dev/null
@@ -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)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^
+
diff --git a/src/test/run-pass/existential_type_fns.rs b/src/test/run-pass/existential_type_fns.rs
new file mode 100644 (file)
index 0000000..e477dca
--- /dev/null
@@ -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() {}
index 3f0190892ebb3b54006b09167685948f62f10d5a..38fcabb5cc170fc360b8de518de23a80b10b5dba 100644 (file)
@@ -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
index 56057a9caa4a557bac5a984c77b5d0c6606344cd..98a269d5271a222691209e55992755f9e9990967 100644 (file)
@@ -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`.
index 29410309ef26ef596e4f0a65b0828fd25d38885c..f9e6bdb67d4dec6e1a0e94e6ff8b7e6fcb7c6f02 100644 (file)
@@ -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
index 8c7bf52470ab2ea1cf8f41d14d6b4d0d10e23279..830305d863119bc8d1e085e8b0fa9b1900c0c328 100644 (file)
@@ -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`.
index 6bbe8bdc0cd6d4e9470f1c1cb9fd4c894b276a8e..eec8a4be63d9808bebca26c107b73c1c52b91489 100644 (file)
@@ -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 }
index 4605332ef5b35d3b8d994398d56326b83570c6ec..bc9a883c8365c7b120531f3b7f504bddc9d35261 100644 (file)
@@ -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`.