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 16, 2024
2 parents 4e1f5d9 + dc55e27 commit 035f40f
Show file tree
Hide file tree
Showing 30 changed files with 521 additions and 57 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 @@ -627,7 +627,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 @@ -1422,7 +1422,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
34 changes: 23 additions & 11 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1473,6 +1473,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}

#[instrument(level = "trace", skip(self, possibly_unsatisfied_predicates), ret)]
fn consider_probe(
&self,
self_ty: Ty<'tcx>,
Expand All @@ -1483,20 +1484,31 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
Option<ObligationCause<'tcx>>,
)>,
) -> ProbeResult {
debug!("consider_probe: self_ty={:?} probe={:?}", self_ty, probe);

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,
probe.xform_self_ty,
self_ty,
) {
Ok(InferOk { obligations, value: () }) => obligations,
Err(err) => {
debug!("--> cannot relate self-types {:?}", err);
return ProbeResult::NoMatch;
let sub_obligations = match self_ty.kind() {
// HACK: opaque types will match anything for which their bounds hold.
// Thus we need to prevent them from trying to match the `&_` autoref
// candidates that get created for `&self` trait methods.
ty::Alias(ty::Opaque, alias_ty)
if self.infcx.can_define_opaque_ty(alias_ty.def_id) =>
{
if !probe.xform_self_ty.is_ty_var() {
return ProbeResult::NoMatch;
}
vec![]
}
_ => match self.at(&ObligationCause::dummy(), self.param_env).sup(
DefineOpaqueTypes::Yes,
probe.xform_self_ty,
self_ty,
) {
Ok(InferOk { obligations, value: () }) => obligations,
Err(err) => {
debug!("--> cannot relate self-types {:?}", err);
return ProbeResult::NoMatch;
}
},
};

let mut result = ProbeResult::Match;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/traits/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ pub struct CandidateStep<'tcx> {

#[derive(Copy, Clone, Debug, HashStable)]
pub struct MethodAutoderefStepsResult<'tcx> {
/// The valid autoderef steps that could be find.
/// The valid autoderef steps that could be found.
pub steps: &'tcx [CandidateStep<'tcx>],
/// If Some(T), a type autoderef reported an error on.
pub opt_bad_ty: Option<&'tcx MethodAutoderefBadTy<'tcx>>,
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
26 changes: 26 additions & 0 deletions tests/ui/impl-trait/method-resolution.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//! Since there is only one possible `bar` method, we invoke it and subsequently
//! constrain `foo`'s RPIT to `u32`.

//@ revisions: current next
//@[next] compile-flags: -Znext-solver
//@ check-pass

trait Trait {}

impl Trait for u32 {}

struct Bar<T>(T);

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

fn foo(x: bool) -> Bar<impl Sized> {
if x {
let x = foo(false);
x.bar();
}
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 type 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() {}
20 changes: 20 additions & 0 deletions tests/ui/impl-trait/method-resolution3.current.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error[E0034]: multiple applicable items in scope
--> $DIR/method-resolution3.rs:21:11
|
LL | x.bar();
| ^^^ multiple `bar` found
|
note: candidate #1 is defined in an impl for the type `Bar<i32>`
--> $DIR/method-resolution3.rs:15:5
|
LL | fn bar(self) {}
| ^^^^^^^^^^^^
note: candidate #2 is defined in an impl for the type `Bar<u32>`
--> $DIR/method-resolution3.rs:11:5
|
LL | fn bar(self) {}
| ^^^^^^^^^^^^

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0034`.
20 changes: 20 additions & 0 deletions tests/ui/impl-trait/method-resolution3.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-resolution3.rs:21:11
|
LL | x.bar();
| ^^^ multiple `bar` found
|
note: candidate #1 is defined in an impl for the type `Bar<i32>`
--> $DIR/method-resolution3.rs:15:5
|
LL | fn bar(self) {}
| ^^^^^^^^^^^^
note: candidate #2 is defined in an impl for the type `Bar<u32>`
--> $DIR/method-resolution3.rs:11:5
|
LL | fn bar(self) {}
| ^^^^^^^^^^^^

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0034`.
27 changes: 27 additions & 0 deletions tests/ui/impl-trait/method-resolution3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//! 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> {
if x {
let x = foo(false);
x.bar();
//~^ ERROR: multiple applicable items in scope
}
todo!()
}

fn main() {}
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:13:9
|
LL | foo(false).next().unwrap();
| ^^^^^^^^^^ cannot infer type

error[E0308]: mismatched types
--> $DIR/method-resolution4.rs:16: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`.
20 changes: 20 additions & 0 deletions tests/ui/impl-trait/method-resolution4.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//! 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
//@[current] check-pass

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

15 changes: 15 additions & 0 deletions tests/ui/type-alias-impl-trait/method_resolution.current.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error[E0599]: no method named `bar` found for struct `Bar<u32>` in the current scope
--> $DIR/method_resolution.rs:20:14
|
LL | struct Bar<T>(T);
| ------------- method `bar` not found for this struct
...
LL | self.bar()
| ^^^ method not found in `Bar<u32>`
|
= note: the method was found for
- `Bar<Foo>`

error: aborting due to 1 previous error

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

0 comments on commit 035f40f

Please sign in to comment.