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 22, 2024
2 parents 7f2fc33 + 919500f commit 384ba20
Show file tree
Hide file tree
Showing 31 changed files with 569 additions and 63 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 @@ -1435,7 +1435,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
46 changes: 29 additions & 17 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -651,8 +651,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}

#[instrument(level = "debug", skip(self))]
fn assemble_probe(&mut self, self_ty: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>) {
debug!("assemble_probe: self_ty={:?}", self_ty);
let raw_self_ty = self_ty.value.value;
match *raw_self_ty.kind() {
ty::Dynamic(data, ..) if let Some(p) = data.principal() => {
Expand Down Expand Up @@ -730,13 +730,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}

#[instrument(level = "debug", skip(self))]
fn assemble_inherent_impl_probe(&mut self, impl_def_id: DefId) {
if !self.impl_dups.insert(impl_def_id) {
return; // already visited
}

debug!("assemble_inherent_impl_probe {:?}", impl_def_id);

for item in self.impl_or_trait_item(impl_def_id) {
if !self.has_applicable_self(&item) {
// No receiver declared. Not a candidate.
Expand Down Expand Up @@ -786,9 +785,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}

#[instrument(level = "debug", skip(self))]
fn assemble_inherent_candidates_from_object(&mut self, self_ty: Ty<'tcx>) {
debug!("assemble_inherent_candidates_from_object(self_ty={:?})", self_ty);

let principal = match self_ty.kind() {
ty::Dynamic(ref data, ..) => Some(data),
_ => None,
Expand Down Expand Up @@ -835,9 +833,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
});
}

#[instrument(level = "debug", skip(self))]
fn assemble_inherent_candidates_from_param(&mut self, param_ty: ty::ParamTy) {
// FIXME: do we want to commit to this behavior for param bounds?
debug!("assemble_inherent_candidates_from_param(param_ty={:?})", param_ty);

let bounds = self.param_env.caller_bounds().iter().filter_map(|predicate| {
let bound_predicate = predicate.kind();
Expand Down Expand Up @@ -904,6 +902,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}

#[instrument(level = "debug", skip(self))]
fn assemble_extension_candidates_for_traits_in_scope(&mut self) {
let mut duplicates = FxHashSet::default();
let opt_applicable_traits = self.tcx.in_scope_traits(self.scope_expr_id);
Expand All @@ -920,6 +919,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}

#[instrument(level = "debug", skip(self))]
fn assemble_extension_candidates_for_all_traits(&mut self) {
let mut duplicates = FxHashSet::default();
for trait_info in suggest::all_traits(self.tcx) {
Expand Down Expand Up @@ -956,12 +956,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}

#[instrument(level = "debug", skip(self))]
fn assemble_extension_candidates_for_trait(
&mut self,
import_ids: &SmallVec<[LocalDefId; 1]>,
trait_def_id: DefId,
) {
debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})", trait_def_id);
let trait_args = self.fresh_args_for_item(self.span, trait_def_id);
let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, trait_args);

Expand Down Expand Up @@ -1066,6 +1066,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
///////////////////////////////////////////////////////////////////////////
// THE ACTUAL SEARCH

#[instrument(level = "debug", skip(self))]
fn pick(mut self) -> PickResult<'tcx> {
assert!(self.method_name.is_some());

Expand Down Expand Up @@ -1474,6 +1475,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 @@ -1484,20 +1486,29 @@ 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);
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)
&& !probe.xform_self_ty.is_ty_var() =>
{
return ProbeResult::NoMatch;
}
_ => 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 Expand Up @@ -1755,6 +1766,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
/// Similarly to `probe_for_return_type`, this method attempts to find the best matching
/// candidate method where the method name may have been misspelled. Similarly to other
/// edit distance based suggestions, we provide at most one such suggestion.
#[instrument(level = "debug", skip(self))]
pub(crate) fn probe_for_similar_candidate(
&mut self,
) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> {
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`.
Loading

0 comments on commit 384ba20

Please sign in to comment.