From 7f608eb9edb38ff271ff5be50dbbbb424cbf348f Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 1 Feb 2022 17:28:46 +0000 Subject: [PATCH] Prevent two opaque types in their defining scopes from being defined via the other --- .../rustc_infer/src/infer/opaque_types.rs | 104 +++++++++++------- src/test/ui/impl-trait/example-calendar.rs | 3 +- .../ui/impl-trait/example-calendar.stderr | 19 ++++ src/test/ui/impl-trait/issues/issue-70877.rs | 4 +- .../ui/impl-trait/issues/issue-70877.stderr | 19 ++++ .../issues/issue-88236-2.nll.stderr | 39 +++++++ .../two_tait_defining_each_other.rs | 3 +- .../two_tait_defining_each_other.stderr | 19 ++++ .../two_tait_defining_each_other2.rs | 2 +- .../two_tait_defining_each_other2.stderr | 15 ++- .../two_tait_defining_each_other3.rs | 3 +- .../two_tait_defining_each_other3.stderr | 19 ++++ .../nested_type_alias_impl_trait.rs | 4 +- .../nested_type_alias_impl_trait.stderr | 19 ++++ 14 files changed, 216 insertions(+), 56 deletions(-) create mode 100644 src/test/ui/impl-trait/example-calendar.stderr create mode 100644 src/test/ui/impl-trait/issues/issue-70877.stderr create mode 100644 src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr create mode 100644 src/test/ui/impl-trait/two_tait_defining_each_other.stderr create mode 100644 src/test/ui/impl-trait/two_tait_defining_each_other3.stderr create mode 100644 src/test/ui/type-alias-impl-trait/nested_type_alias_impl_trait.stderr diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index e704ac6dc78..35104c71bfc 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -80,47 +80,69 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } if self.defining_use_anchor.is_some() { let process = |a: Ty<'tcx>, b: Ty<'tcx>| match *a.kind() { - ty::Opaque(def_id, substs) => Some(self.register_hidden_type( - OpaqueTypeKey { def_id, substs }, - cause.clone(), - param_env, - b, - // Check that this is `impl Trait` type is - // declared by `parent_def_id` -- i.e., one whose - // value we are inferring. At present, this is - // always true during the first phase of - // type-check, but not always true later on during - // NLL. Once we support named opaque types more fully, - // this same scenario will be able to arise during all phases. - // - // Here is an example using type alias `impl Trait` - // that indicates the distinction we are checking for: - // - // ```rust - // mod a { - // pub type Foo = impl Iterator; - // pub fn make_foo() -> Foo { .. } - // } - // - // mod b { - // fn foo() -> a::Foo { a::make_foo() } - // } - // ``` - // - // Here, the return type of `foo` references an - // `Opaque` indeed, but not one whose value is - // presently being inferred. You can get into a - // similar situation with closure return types - // today: - // - // ```rust - // fn foo() -> impl Iterator { .. } - // fn bar() { - // let x = || foo(); // returns the Opaque assoc with `foo` - // } - // ``` - self.opaque_type_origin(def_id, cause.span)?, - )), + ty::Opaque(def_id, substs) => { + if let ty::Opaque(did2, _) = *b.kind() { + if self.opaque_type_origin(did2, cause.span).is_some() { + self.tcx + .sess + .struct_span_err( + cause.span, + "opaque type's hidden type cannot be another opaque type from the same scope", + ) + .span_label(cause.span, "one of the two opaque types used here has to be outside its defining scope") + .span_note( + self.tcx.def_span(def_id), + "opaque type whose hidden type is being assigned", + ) + .span_note( + self.tcx.def_span(did2), + "opaque type being used as hidden type", + ) + .emit(); + } + } + Some(self.register_hidden_type( + OpaqueTypeKey { def_id, substs }, + cause.clone(), + param_env, + b, + // Check that this is `impl Trait` type is + // declared by `parent_def_id` -- i.e., one whose + // value we are inferring. At present, this is + // always true during the first phase of + // type-check, but not always true later on during + // NLL. Once we support named opaque types more fully, + // this same scenario will be able to arise during all phases. + // + // Here is an example using type alias `impl Trait` + // that indicates the distinction we are checking for: + // + // ```rust + // mod a { + // pub type Foo = impl Iterator; + // pub fn make_foo() -> Foo { .. } + // } + // + // mod b { + // fn foo() -> a::Foo { a::make_foo() } + // } + // ``` + // + // Here, the return type of `foo` references an + // `Opaque` indeed, but not one whose value is + // presently being inferred. You can get into a + // similar situation with closure return types + // today: + // + // ```rust + // fn foo() -> impl Iterator { .. } + // fn bar() { + // let x = || foo(); // returns the Opaque assoc with `foo` + // } + // ``` + self.opaque_type_origin(def_id, cause.span)?, + )) + } _ => None, }; if let Some(res) = process(a, b) { diff --git a/src/test/ui/impl-trait/example-calendar.rs b/src/test/ui/impl-trait/example-calendar.rs index 45dcb74a6e0..26618eec1d7 100644 --- a/src/test/ui/impl-trait/example-calendar.rs +++ b/src/test/ui/impl-trait/example-calendar.rs @@ -1,4 +1,3 @@ -// run-pass // ignore-compare-mode-chalk #![feature(fn_traits, @@ -590,7 +589,7 @@ fn test_format_month() { fn format_months(it: impl Iterator) -> impl Iterator> { - it.map(format_month) + it.map(format_month) //~ ERROR opaque type's hidden type cannot be another opaque type } /// Takes an iterator of iterators of strings; the sub-iterators are consumed diff --git a/src/test/ui/impl-trait/example-calendar.stderr b/src/test/ui/impl-trait/example-calendar.stderr new file mode 100644 index 00000000000..3894285947f --- /dev/null +++ b/src/test/ui/impl-trait/example-calendar.stderr @@ -0,0 +1,19 @@ +error: opaque type's hidden type cannot be another opaque type from the same scope + --> $DIR/example-calendar.rs:592:5 + | +LL | it.map(format_month) + | ^^^^^^^^^^^^^^^^^^^^ one of the two opaque types used here has to be outside its defining scope + | +note: opaque type whose hidden type is being assigned + --> $DIR/example-calendar.rs:560:43 + | +LL | fn format_month(it: impl DateIterator) -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: opaque type being used as hidden type + --> $DIR/example-calendar.rs:590:39 + | +LL | -> impl Iterator> + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/impl-trait/issues/issue-70877.rs b/src/test/ui/impl-trait/issues/issue-70877.rs index 42e5436390c..8169cfafac7 100644 --- a/src/test/ui/impl-trait/issues/issue-70877.rs +++ b/src/test/ui/impl-trait/issues/issue-70877.rs @@ -1,7 +1,5 @@ #![feature(type_alias_impl_trait)] -// check-pass - type FooArg<'a> = &'a dyn ToString; type FooRet = impl std::fmt::Debug; @@ -30,7 +28,7 @@ fn ham() -> Foo { fn oof() -> impl std::fmt::Debug { let mut bar = ham(); let func = bar.next().unwrap(); - return func(&"oof"); + return func(&"oof"); //~ ERROR opaque type's hidden type cannot be another opaque type } fn main() { diff --git a/src/test/ui/impl-trait/issues/issue-70877.stderr b/src/test/ui/impl-trait/issues/issue-70877.stderr new file mode 100644 index 00000000000..8813bff3c35 --- /dev/null +++ b/src/test/ui/impl-trait/issues/issue-70877.stderr @@ -0,0 +1,19 @@ +error: opaque type's hidden type cannot be another opaque type from the same scope + --> $DIR/issue-70877.rs:31:12 + | +LL | return func(&"oof"); + | ^^^^^^^^^^^^ one of the two opaque types used here has to be outside its defining scope + | +note: opaque type whose hidden type is being assigned + --> $DIR/issue-70877.rs:28:13 + | +LL | fn oof() -> impl std::fmt::Debug { + | ^^^^^^^^^^^^^^^^^^^^ +note: opaque type being used as hidden type + --> $DIR/issue-70877.rs:4:15 + | +LL | type FooRet = impl std::fmt::Debug; + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr b/src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr new file mode 100644 index 00000000000..86323add779 --- /dev/null +++ b/src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr @@ -0,0 +1,39 @@ +error: higher-ranked subtype error + --> $DIR/issue-88236-2.rs:17:5 + | +LL | &() + | ^^^ + +error: higher-ranked subtype error + --> $DIR/issue-88236-2.rs:17:5 + | +LL | &() + | ^^^ + +error: lifetime may not live long enough + --> $DIR/issue-88236-2.rs:20:5 + | +LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> { + | -- lifetime `'b` defined here +LL | x + | ^ returning this value requires that `'b` must outlive `'static` + | +help: to allow this `impl Trait` to capture borrowed data with lifetime `'b`, add `'b` as a bound + | +LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> + 'b { + | ++++ + +error: higher-ranked subtype error + --> $DIR/issue-88236-2.rs:20:5 + | +LL | x + | ^ + +error: higher-ranked subtype error + --> $DIR/issue-88236-2.rs:20:5 + | +LL | x + | ^ + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/impl-trait/two_tait_defining_each_other.rs b/src/test/ui/impl-trait/two_tait_defining_each_other.rs index eb8d24832eb..6eb2a11b22c 100644 --- a/src/test/ui/impl-trait/two_tait_defining_each_other.rs +++ b/src/test/ui/impl-trait/two_tait_defining_each_other.rs @@ -1,7 +1,5 @@ #![feature(type_alias_impl_trait)] -// check-pass - type A = impl Foo; type B = impl Foo; @@ -12,6 +10,7 @@ fn muh(x: A) -> B { return Bar; // B's hidden type is Bar } x // A's hidden type is `Bar`, because all the hidden types of `B` are compared with each other + //~^ ERROR opaque type's hidden type cannot be another opaque type } struct Bar; diff --git a/src/test/ui/impl-trait/two_tait_defining_each_other.stderr b/src/test/ui/impl-trait/two_tait_defining_each_other.stderr new file mode 100644 index 00000000000..1a42ac525a6 --- /dev/null +++ b/src/test/ui/impl-trait/two_tait_defining_each_other.stderr @@ -0,0 +1,19 @@ +error: opaque type's hidden type cannot be another opaque type from the same scope + --> $DIR/two_tait_defining_each_other.rs:12:5 + | +LL | x // A's hidden type is `Bar`, because all the hidden types of `B` are compared with each other + | ^ one of the two opaque types used here has to be outside its defining scope + | +note: opaque type whose hidden type is being assigned + --> $DIR/two_tait_defining_each_other.rs:4:10 + | +LL | type B = impl Foo; + | ^^^^^^^^ +note: opaque type being used as hidden type + --> $DIR/two_tait_defining_each_other.rs:3:10 + | +LL | type A = impl Foo; + | ^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/impl-trait/two_tait_defining_each_other2.rs b/src/test/ui/impl-trait/two_tait_defining_each_other2.rs index 295e2375428..3b16d0f5e04 100644 --- a/src/test/ui/impl-trait/two_tait_defining_each_other2.rs +++ b/src/test/ui/impl-trait/two_tait_defining_each_other2.rs @@ -1,13 +1,13 @@ #![feature(type_alias_impl_trait)] type A = impl Foo; -//~^ ERROR unconstrained opaque type type B = impl Foo; trait Foo {} fn muh(x: A) -> B { x // B's hidden type is A (opaquely) + //~^ ERROR opaque type's hidden type cannot be another opaque type } struct Bar; diff --git a/src/test/ui/impl-trait/two_tait_defining_each_other2.stderr b/src/test/ui/impl-trait/two_tait_defining_each_other2.stderr index e48a5724a7f..ef2089a6c5b 100644 --- a/src/test/ui/impl-trait/two_tait_defining_each_other2.stderr +++ b/src/test/ui/impl-trait/two_tait_defining_each_other2.stderr @@ -1,10 +1,19 @@ -error: unconstrained opaque type +error: opaque type's hidden type cannot be another opaque type from the same scope + --> $DIR/two_tait_defining_each_other2.rs:9:5 + | +LL | x // B's hidden type is A (opaquely) + | ^ one of the two opaque types used here has to be outside its defining scope + | +note: opaque type whose hidden type is being assigned + --> $DIR/two_tait_defining_each_other2.rs:4:10 + | +LL | type B = impl Foo; + | ^^^^^^^^ +note: opaque type being used as hidden type --> $DIR/two_tait_defining_each_other2.rs:3:10 | LL | type A = impl Foo; | ^^^^^^^^ - | - = note: `A` must be used in combination with a concrete type within the same module error: aborting due to previous error diff --git a/src/test/ui/impl-trait/two_tait_defining_each_other3.rs b/src/test/ui/impl-trait/two_tait_defining_each_other3.rs index c289b43c425..37f8ae1b84b 100644 --- a/src/test/ui/impl-trait/two_tait_defining_each_other3.rs +++ b/src/test/ui/impl-trait/two_tait_defining_each_other3.rs @@ -1,7 +1,5 @@ #![feature(type_alias_impl_trait)] -// check-pass - type A = impl Foo; type B = impl Foo; @@ -10,6 +8,7 @@ trait Foo {} fn muh(x: A) -> B { if false { return x; // B's hidden type is A (opaquely) + //~^ ERROR opaque type's hidden type cannot be another opaque type } Bar // A's hidden type is `Bar`, because all the return types are compared with each other } diff --git a/src/test/ui/impl-trait/two_tait_defining_each_other3.stderr b/src/test/ui/impl-trait/two_tait_defining_each_other3.stderr new file mode 100644 index 00000000000..b06dc16d5e7 --- /dev/null +++ b/src/test/ui/impl-trait/two_tait_defining_each_other3.stderr @@ -0,0 +1,19 @@ +error: opaque type's hidden type cannot be another opaque type from the same scope + --> $DIR/two_tait_defining_each_other3.rs:10:16 + | +LL | return x; // B's hidden type is A (opaquely) + | ^ one of the two opaque types used here has to be outside its defining scope + | +note: opaque type whose hidden type is being assigned + --> $DIR/two_tait_defining_each_other3.rs:4:10 + | +LL | type B = impl Foo; + | ^^^^^^^^ +note: opaque type being used as hidden type + --> $DIR/two_tait_defining_each_other3.rs:3:10 + | +LL | type A = impl Foo; + | ^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/nested_type_alias_impl_trait.rs b/src/test/ui/type-alias-impl-trait/nested_type_alias_impl_trait.rs index 6282264d8fe..60b6e1aac62 100644 --- a/src/test/ui/type-alias-impl-trait/nested_type_alias_impl_trait.rs +++ b/src/test/ui/type-alias-impl-trait/nested_type_alias_impl_trait.rs @@ -1,5 +1,5 @@ #![feature(type_alias_impl_trait)] -// build-pass (FIXME(62277): could be check-pass?) + mod my_mod { use std::fmt::Debug; @@ -11,7 +11,7 @@ mod my_mod { } pub fn get_foot() -> Foot { - get_foo() + get_foo() //~ ERROR opaque type's hidden type cannot be another opaque type } } diff --git a/src/test/ui/type-alias-impl-trait/nested_type_alias_impl_trait.stderr b/src/test/ui/type-alias-impl-trait/nested_type_alias_impl_trait.stderr new file mode 100644 index 00000000000..fa6ecf68d28 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/nested_type_alias_impl_trait.stderr @@ -0,0 +1,19 @@ +error: opaque type's hidden type cannot be another opaque type from the same scope + --> $DIR/nested_type_alias_impl_trait.rs:14:9 + | +LL | get_foo() + | ^^^^^^^^^ one of the two opaque types used here has to be outside its defining scope + | +note: opaque type whose hidden type is being assigned + --> $DIR/nested_type_alias_impl_trait.rs:7:21 + | +LL | pub type Foot = impl Debug; + | ^^^^^^^^^^ +note: opaque type being used as hidden type + --> $DIR/nested_type_alias_impl_trait.rs:6:20 + | +LL | pub type Foo = impl Debug; + | ^^^^^^^^^^ + +error: aborting due to previous error +