Skip to content

Commit

Permalink
Auto merge of rust-lang#123962 - oli-obk:define_opaque_types5, r=<try>
Browse files Browse the repository at this point in the history
change method resolution to constrain hidden types instead of rejecting method candidates

Some of these are in probes and may affect inference. This is therefore a breaking change.

This allows new code to compile on stable:

```rust
trait Trait {}

impl Trait for u32 {}

struct Bar<T>(T);

impl Bar<u32> {
    fn foo(self) {}
}

fn foo(x: bool) -> Bar<impl Sized> {
    if x {
        let x = foo(false);
        x.foo();
        //^ this used to not find the `foo` method, because while we did equate `x`'s type with possible candidates, we didn't allow opaque type inference while doing so
    }
    todo!()
}
```

But it is also a breaking change, since `&self` and `&mut self` method calls on recursive RPIT function calls now constrain the hidden type to `&_` and `&mut _` respectively. This is not what users really expect or want from this, but there's way around this.

r? `@compiler-errors`

fixes  rust-lang#121404

cc rust-lang#116652
  • Loading branch information
bors committed Apr 15, 2024
2 parents 0230848 + af980d5 commit fe30d25
Show file tree
Hide file tree
Showing 33 changed files with 580 additions and 46 deletions.
5 changes: 4 additions & 1 deletion compiler/rustc_data_structures/src/obligation_forest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,10 @@ impl<O: ForestObligation> ObligationForest<O> {
Entry::Vacant(v) => {
let obligation_tree_id = match parent {
Some(parent_index) => self.nodes[parent_index].obligation_tree_id,
None => self.obligation_tree_id_generator.next().unwrap(),
// FIXME(type_alias_impl_trait): with `#[defines]` attributes required to define hidden
// types we can convert this back to a `next` method call, as this function shouldn't be
// defining a hidden type anyway.
None => Iterator::next(&mut self.obligation_tree_id_generator).unwrap(),
};

let already_failed = parent.is_some()
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return;
};

let pick = self.confirm_method(
let pick = self.confirm_method_for_diagnostic(
call_expr.span,
callee_expr,
call_expr,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1448,7 +1448,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let impl_ty = self.normalize(span, tcx.type_of(impl_def_id).instantiate(tcx, args));
let self_ty = self.normalize(span, self_ty);
match self.at(&self.misc(span), self.param_env).eq(
DefineOpaqueTypes::No,
DefineOpaqueTypes::Yes,
impl_ty,
self_ty,
) {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/method/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
args,
})),
);
match self.at(&cause, self.param_env).sup(DefineOpaqueTypes::No, method_self_ty, self_ty) {
match self.at(&cause, self.param_env).sup(DefineOpaqueTypes::Yes, method_self_ty, self_ty) {
Ok(InferOk { obligations, value: () }) => {
self.register_predicates(obligations);
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1488,7 +1488,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
self.probe(|_| {
// First check that the self type can be related.
let sub_obligations = match self.at(&ObligationCause::dummy(), self.param_env).sup(
DefineOpaqueTypes::No,
DefineOpaqueTypes::Yes,
probe.xform_self_ty,
self_ty,
) {
Expand Down
4 changes: 3 additions & 1 deletion tests/ui/impl-trait/issues/issue-70877.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ fn ham() -> Foo {

fn oof(_: Foo) -> impl std::fmt::Debug {
let mut bar = ham();
let func = bar.next().unwrap();
// Need to UFC invoke `Iterator::next`,
// as otherwise the hidden type gets constrained to `&mut _`
let func = Iterator::next(&mut bar).unwrap();
return func(&"oof"); //~ ERROR opaque type's hidden type cannot be another opaque type
}

Expand Down
2 changes: 1 addition & 1 deletion tests/ui/impl-trait/issues/issue-70877.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: opaque type's hidden type cannot be another opaque type from the same scope
--> $DIR/issue-70877.rs:31:12
--> $DIR/issue-70877.rs:33:12
|
LL | return func(&"oof");
| ^^^^^^^^^^^^ one of the two opaque types used here has to be outside its defining scope
Expand Down
33 changes: 33 additions & 0 deletions tests/ui/impl-trait/method-resolution.current.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
error[E0599]: no method named `foo` found for struct `Bar` in the current scope
--> $DIR/method-resolution.rs:22:11
|
LL | struct Bar<T>(T);
| ------------- method `foo` not found for this struct
...
LL | x.foo();
| ^^^ method not found in `Bar<impl Sized>`

error[E0391]: cycle detected when computing type of opaque `foo::{opaque#0}`
--> $DIR/method-resolution.rs:18:24
|
LL | fn foo(x: bool) -> Bar<impl Sized> {
| ^^^^^^^^^^
|
note: ...which requires type-checking `foo`...
--> $DIR/method-resolution.rs:22:9
|
LL | x.foo();
| ^
= note: ...which requires evaluating trait selection obligation `Bar<foo::{opaque#0}>: core::marker::Unpin`...
= note: ...which again requires computing type of opaque `foo::{opaque#0}`, completing the cycle
note: cycle used when computing type of `foo::{opaque#0}`
--> $DIR/method-resolution.rs:18:24
|
LL | fn foo(x: bool) -> Bar<impl Sized> {
| ^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0391, E0599.
For more information about an error, try `rustc --explain E0391`.
12 changes: 12 additions & 0 deletions tests/ui/impl-trait/method-resolution.next.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error[E0599]: no method named `foo` found for struct `Bar` in the current scope
--> $DIR/method-resolution.rs:22:11
|
LL | struct Bar<T>(T);
| ------------- method `foo` not found for this struct
...
LL | x.foo();
| ^^^ method not found in `Bar<_>`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0599`.
28 changes: 28 additions & 0 deletions tests/ui/impl-trait/method-resolution.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//! Check that we do not constrain hidden types during method resolution.
//! Otherwise we'd pick up that calling `bar` can be satisfied iff `u32`
//! is the hidden type of the RPIT.
//@ revisions: current next
//@[next] compile-flags: -Znext-solver

trait Trait {}

impl Trait for u32 {}

struct Bar<T>(T);

impl Bar<u32> {
fn bar(self) {}
}

fn foo(x: bool) -> Bar<impl Sized> {
//[current]~^ ERROR: cycle
if x {
let x = foo(false);
x.foo();
//~^ ERROR: no method named `foo` found
}
todo!()
}

fn main() {}
20 changes: 20 additions & 0 deletions tests/ui/impl-trait/method-resolution2.next.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error[E0034]: multiple applicable items in scope
--> $DIR/method-resolution2.rs:25:11
|
LL | x.bar();
| ^^^ multiple `bar` found
|
note: candidate #1 is defined in an impl for the type `Bar<T>`
--> $DIR/method-resolution2.rs:19:5
|
LL | fn bar(self) {}
| ^^^^^^^^^^^^
note: candidate #2 is defined in an impl for the type `Bar<u32>`
--> $DIR/method-resolution2.rs:15:5
|
LL | fn bar(self) {}
| ^^^^^^^^^^^^

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0034`.
31 changes: 31 additions & 0 deletions tests/ui/impl-trait/method-resolution2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//! Check that the method call does not constrain the RPIT to `i32`, even though
//! `i32` is the only trait that satisfies the RPIT's trait bounds.
//@ revisions: current next
//@[next] compile-flags: -Znext-solver
//@[current] check-pass

trait Trait {}

impl Trait for i32 {}

struct Bar<T>(T);

impl Bar<u32> {
fn bar(self) {}
}

impl<T: Trait> Bar<T> {
fn bar(self) {}
}

fn foo(x: bool) -> Bar<impl Trait> {
if x {
let x = foo(false);
x.bar();
//[next]~^ ERROR: multiple applicable items in scope
}
Bar(42_i32)
}

fn main() {}
33 changes: 33 additions & 0 deletions tests/ui/impl-trait/method-resolution3.current.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
error[E0599]: no method named `foo` found for struct `Bar` in the current scope
--> $DIR/method-resolution3.rs:22:11
|
LL | struct Bar<T>(T);
| ------------- method `foo` not found for this struct
...
LL | x.foo();
| ^^^ method not found in `Bar<impl Sized>`

error[E0391]: cycle detected when computing type of opaque `foo::{opaque#0}`
--> $DIR/method-resolution3.rs:18:24
|
LL | fn foo(x: bool) -> Bar<impl Sized> {
| ^^^^^^^^^^
|
note: ...which requires type-checking `foo`...
--> $DIR/method-resolution3.rs:22:9
|
LL | x.foo();
| ^
= note: ...which requires evaluating trait selection obligation `Bar<foo::{opaque#0}>: core::marker::Unpin`...
= note: ...which again requires computing type of opaque `foo::{opaque#0}`, completing the cycle
note: cycle used when computing type of `foo::{opaque#0}`
--> $DIR/method-resolution3.rs:18:24
|
LL | fn foo(x: bool) -> Bar<impl Sized> {
| ^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0391, E0599.
For more information about an error, try `rustc --explain E0391`.
12 changes: 12 additions & 0 deletions tests/ui/impl-trait/method-resolution3.next.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error[E0599]: no method named `foo` found for struct `Bar` in the current scope
--> $DIR/method-resolution3.rs:22:11
|
LL | struct Bar<T>(T);
| ------------- method `foo` not found for this struct
...
LL | x.foo();
| ^^^ method not found in `Bar<_>`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0599`.
28 changes: 28 additions & 0 deletions tests/ui/impl-trait/method-resolution3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//! Check that we consider `Bar<impl Sized>` to successfully unify
//! with both `Bar<u32>` and `Bar<i32>` (in isolation), so we bail
//! out with ambiguity.
//@ revisions: current next
//@[next] compile-flags: -Znext-solver

struct Bar<T>(T);

impl Bar<u32> {
fn bar(self) {}
}

impl Bar<i32> {
fn bar(self) {}
}

fn foo(x: bool) -> Bar<impl Sized> {
//[current]~^ ERROR: cycle
if x {
let x = foo(false);
x.foo();
//~^ ERROR: no method named `foo` found
}
todo!()
}

fn main() {}
16 changes: 16 additions & 0 deletions tests/ui/impl-trait/method-resolution4.current.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error[E0308]: mismatched types
--> $DIR/method-resolution4.rs:15:5
|
LL | std::iter::empty()
| ^^^^^^^^^^^^^^^^^^ expected `&mut _`, found `Empty<_>`
|
= note: expected mutable reference `&mut _`
found struct `std::iter::Empty<_>`
help: consider mutably borrowing here
|
LL | &mut std::iter::empty()
| ++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0308`.
22 changes: 22 additions & 0 deletions tests/ui/impl-trait/method-resolution4.next.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
error[E0282]: type annotations needed
--> $DIR/method-resolution4.rs:12:9
|
LL | foo(false).next().unwrap();
| ^^^^^^^^^^ cannot infer type

error[E0308]: mismatched types
--> $DIR/method-resolution4.rs:15:5
|
LL | fn foo(b: bool) -> impl Iterator<Item = ()> {
| ------------------------ the expected opaque type
...
LL | std::iter::empty()
| ^^^^^^^^^^^^^^^^^^ types differ
|
= note: expected opaque type `impl Iterator<Item = ()>`
found struct `std::iter::Empty<_>`

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0282, E0308.
For more information about an error, try `rustc --explain E0282`.
19 changes: 19 additions & 0 deletions tests/ui/impl-trait/method-resolution4.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//! The recursive method call yields the opaque type. The
//! `next` method call then constrains the hidden type to `&mut _`
//! because `next` takes `&mut self`. We never resolve the inference
//! variable, but get a type mismatch when comparing `&mut _` with
//! `std::iter::Empty`.
//@ revisions: current next
//@[next] compile-flags: -Znext-solver

fn foo(b: bool) -> impl Iterator<Item = ()> {
if b {
foo(false).next().unwrap();
//[next]~^ type annotations needed
}
std::iter::empty()
//~^ mismatched types
}

fn main() {}
6 changes: 3 additions & 3 deletions tests/ui/methods/opaque_param_in_ufc.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#![feature(type_alias_impl_trait)]

//@ check-pass

struct Foo<T>(T);

impl Foo<u32> {
Expand All @@ -15,14 +18,11 @@ fn bar() -> Bar {
impl Foo<Bar> {
fn foo() -> Bar {
Self::method();
//~^ ERROR: no function or associated item named `method` found for struct `Foo<Bar>`
Foo::<Bar>::method();
//~^ ERROR: no function or associated item named `method` found for struct `Foo<Bar>`
let x = Foo(bar());
Foo::method2(x);
let x = Self(bar());
Self::method2(x);
//~^ ERROR: no function or associated item named `method2` found for struct `Foo<Bar>`
todo!()
}
}
Expand Down
36 changes: 0 additions & 36 deletions tests/ui/methods/opaque_param_in_ufc.stderr

This file was deleted.

Loading

0 comments on commit fe30d25

Please sign in to comment.