about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-07-18 18:53:42 +0000
committerbors <bors@rust-lang.org>2023-07-18 18:53:42 +0000
commitd3515155216e98c23440ea92c3f49c6a0d7101fc (patch)
treec641785b74f3f06d11423dc1dc2f0ed040ff82a8
parent42a982d36771288573359041e3252536d2926d8d (diff)
parent687558992430dc3992fab19a05fdb01b8af17708 (diff)
downloadrust-d3515155216e98c23440ea92c3f49c6a0d7101fc.tar.gz
rust-d3515155216e98c23440ea92c3f49c6a0d7101fc.zip
Auto merge of #113636 - compiler-errors:opaque-recursive-check-bad, r=oli-obk
Restrict recursive opaque type check

We have a recursive opaque check in writeback to avoid inferring the hidden of an opaque type to be itself:

https://github.com/rust-lang/rust/blob/33a2c2487ac5d9927830ea4c1844335c6b9f77db/compiler/rustc_hir_typeck/src/writeback.rs#L556-L575

Issue #113619 treats `make_option2` as not defining the TAIT `TestImpl` since it is inferred to have the definition `TestImpl := B<TestImpl>`, which fails this check. This regressed in #102700 (5d15beb5919e4ed481c44a1ac19c2ad04a36ee8a), I think due to the refactoring that made us record the hidden types of TAITs during writeback.

However, nothing actually seems to go bad if we relax this recursion checker to only check for directly recursive definitions. This PR fixes #113619 by changing this recursive check from being a visitor to just checking that the hidden type is exactly the same as the opaque being inferred.

Alternatively, we may be able to fix #113619 by restricting this recursion check only to RPITs/async fns. It seems to only be possible to use misuse the recursion check to cause ICEs for TAITs (though I didn't try too hard to create a bad RPIT example... may be possible, actually.)

r? `@oli-obk`

--

Fixes #113314
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs24
-rw-r--r--tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.rs21
-rw-r--r--tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.stderr14
-rw-r--r--tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.rs34
-rw-r--r--tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr14
5 files changed, 87 insertions, 20 deletions
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index cf6c7f70de3..2329a1f63ce 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -3,7 +3,6 @@
 // substitutions.
 
 use crate::FnCtxt;
-use hir::def_id::LocalDefId;
 use rustc_data_structures::unord::ExtendUnord;
 use rustc_errors::{ErrorGuaranteed, StashKey};
 use rustc_hir as hir;
@@ -11,13 +10,12 @@ use rustc_hir::intravisit::{self, Visitor};
 use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion};
 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
-use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt};
+use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 
 use std::mem;
-use std::ops::ControlFlow;
 
 ///////////////////////////////////////////////////////////////////////////
 // Entry point
@@ -565,23 +563,9 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
             let hidden_type = self.resolve(decl.hidden_type, &decl.hidden_type.span);
             let opaque_type_key = self.resolve(opaque_type_key, &decl.hidden_type.span);
 
-            struct RecursionChecker {
-                def_id: LocalDefId,
-            }
-            impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for RecursionChecker {
-                type BreakTy = ();
-                fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-                    if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *t.kind() {
-                        if def_id == self.def_id.to_def_id() {
-                            return ControlFlow::Break(());
-                        }
-                    }
-                    t.super_visit_with(self)
-                }
-            }
-            if hidden_type
-                .visit_with(&mut RecursionChecker { def_id: opaque_type_key.def_id })
-                .is_break()
+            if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind()
+                && alias_ty.def_id == opaque_type_key.def_id.to_def_id()
+                && alias_ty.args == opaque_type_key.args
             {
                 continue;
             }
diff --git a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.rs b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.rs
new file mode 100644
index 00000000000..10588398c9d
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.rs
@@ -0,0 +1,21 @@
+// issue: 113314
+
+#![feature(type_alias_impl_trait)]
+
+type Op = impl std::fmt::Display;
+fn foo() -> Op { &"hello world" }
+
+fn transform<S>() -> impl std::fmt::Display {
+    &0usize
+}
+fn bad() -> Op {
+    transform::<Op>()
+    //~^ ERROR concrete type differs from previous defining opaque type use
+}
+
+fn main() {
+    let mut x = foo();
+    println!("{x}");
+    x = bad();
+    println!("{x}");
+}
diff --git a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.stderr b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.stderr
new file mode 100644
index 00000000000..7481557fcba
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.stderr
@@ -0,0 +1,14 @@
+error: concrete type differs from previous defining opaque type use
+  --> $DIR/recursive-tait-conflicting-defn-2.rs:12:5
+   |
+LL |     transform::<Op>()
+   |     ^^^^^^^^^^^^^^^^^ expected `&'static &'static str`, got `impl std::fmt::Display`
+   |
+note: previous use here
+  --> $DIR/recursive-tait-conflicting-defn-2.rs:6:18
+   |
+LL | fn foo() -> Op { &"hello world" }
+   |                  ^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.rs b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.rs
new file mode 100644
index 00000000000..e221f4f3f55
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.rs
@@ -0,0 +1,34 @@
+// issue: 113596
+
+#![feature(type_alias_impl_trait)]
+
+trait Test {}
+
+struct A;
+
+impl Test for A {}
+
+struct B<T> {
+  inner: T,
+}
+
+impl<T: Test> Test for B<T> {}
+
+type TestImpl = impl Test;
+
+fn test() -> TestImpl {
+  A
+}
+
+fn make_option() -> Option<TestImpl> {
+  Some(test())
+}
+
+fn make_option2() -> Option<TestImpl> {
+  let inner = make_option().unwrap();
+
+  Some(B { inner })
+  //~^ ERROR concrete type differs from previous defining opaque type use
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr
new file mode 100644
index 00000000000..e4209643b7a
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr
@@ -0,0 +1,14 @@
+error: concrete type differs from previous defining opaque type use
+  --> $DIR/recursive-tait-conflicting-defn.rs:30:3
+   |
+LL |   Some(B { inner })
+   |   ^^^^^^^^^^^^^^^^^ expected `A`, got `B<TestImpl>`
+   |
+note: previous use here
+  --> $DIR/recursive-tait-conflicting-defn.rs:20:3
+   |
+LL |   A
+   |   ^
+
+error: aborting due to previous error
+