Skip to content

[perf] Make fulfill in method probe less bad #124303

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 141 additions & 34 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use rustc_infer::infer::canonical::{Canonical, QueryResponse};
use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
use rustc_infer::infer::DefineOpaqueTypes;
use rustc_infer::infer::{self, InferOk, TyCtxtInferExt};
use rustc_infer::traits::EvaluationResult;
use rustc_middle::middle::stability;
use rustc_middle::query::Providers;
use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
Expand All @@ -37,6 +38,7 @@ use rustc_trait_selection::traits::query::method_autoderef::{
CandidateStep, MethodAutoderefStepsResult,
};
use rustc_trait_selection::traits::query::CanonicalTyGoal;
use rustc_trait_selection::traits::NormalizeExt;
use rustc_trait_selection::traits::ObligationCtxt;
use rustc_trait_selection::traits::{self, ObligationCause};
use std::cell::RefCell;
Expand Down Expand Up @@ -1375,7 +1377,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let impl_ty = self.tcx.type_of(impl_def_id).instantiate(self.tcx, impl_args);
(xform_self_ty, xform_ret_ty) =
self.xform_self_ty(probe.item, impl_ty, impl_args);
xform_self_ty = ocx.normalize(cause, self.param_env, xform_self_ty);
let InferOk { value: normalized, mut obligations } =
self.at(cause, self.param_env).normalize(xform_self_ty);
xform_self_ty = normalized;
// FIXME: Make this `ocx.sup` once we define opaques more eagerly.
match self.at(cause, self.param_env).sup(
DefineOpaqueTypes::No,
Expand All @@ -1397,8 +1401,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let impl_bounds =
self.tcx.predicates_of(impl_def_id).instantiate(self.tcx, impl_args);
let impl_bounds = ocx.normalize(cause, self.param_env, impl_bounds);
// Convert the bounds into obligations.
ocx.register_obligations(traits::predicates_for_generics(
obligations.extend(traits::predicates_for_generics(
|idx, span| {
let code = if span.is_dummy() {
traits::ExprItemObligation(impl_def_id, self.scope_expr_id, idx)
Expand All @@ -1415,6 +1418,32 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
self.param_env,
impl_bounds,
));

for obligation in obligations {
if self.infcx.next_trait_solver() {
ocx.register_obligation(obligation);
} else {
match self.infcx.evaluate_obligation_no_overflow(&obligation) {
EvaluationResult::EvaluatedToOk
| EvaluationResult::EvaluatedToOkModuloRegions => {
// No side-effects, no need to register obligations.
}
EvaluationResult::EvaluatedToOkModuloOpaqueTypes
| EvaluationResult::EvaluatedToAmbig
| EvaluationResult::EvaluatedToAmbigStackDependent => {
ocx.register_obligation(obligation);
}
EvaluationResult::EvaluatedToErr => {
result = ProbeResult::NoMatch;
possibly_unsatisfied_predicates.push((
self.resolve_vars_if_possible(obligation.predicate),
None,
Some(obligation.cause.clone()),
));
}
}
}
}
}
TraitCandidate(poly_trait_ref) => {
// Some trait methods are excluded for arrays before 2021.
Expand All @@ -1436,7 +1465,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let trait_ref = ocx.normalize(cause, self.param_env, trait_ref);
(xform_self_ty, xform_ret_ty) =
self.xform_self_ty(probe.item, trait_ref.self_ty(), trait_ref.args);
xform_self_ty = ocx.normalize(cause, self.param_env, xform_self_ty);

let InferOk { value: normalized, obligations: normalize_obligations } =
self.at(cause, self.param_env).normalize(xform_self_ty);
xform_self_ty = normalized;

// FIXME: Make this `ocx.sup` once we define opaques more eagerly.
match self.at(cause, self.param_env).sup(
DefineOpaqueTypes::No,
Expand All @@ -1451,28 +1484,70 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
return ProbeResult::NoMatch;
}
}

for obligation in normalize_obligations {
if self.infcx.next_trait_solver() {
ocx.register_obligation(obligation);
} else {
match self.infcx.evaluate_obligation_no_overflow(&obligation) {
EvaluationResult::EvaluatedToOk
| EvaluationResult::EvaluatedToOkModuloRegions => {
// No side-effects, no need to register obligations.
}
EvaluationResult::EvaluatedToOkModuloOpaqueTypes
| EvaluationResult::EvaluatedToAmbig
| EvaluationResult::EvaluatedToAmbigStackDependent => {
ocx.register_obligation(obligation);
}
EvaluationResult::EvaluatedToErr => {
result = ProbeResult::NoMatch;
possibly_unsatisfied_predicates.push((
self.resolve_vars_if_possible(obligation.predicate),
None,
Some(obligation.cause.clone()),
));
}
}
}
}

let obligation = traits::Obligation::new(
self.tcx,
cause.clone(),
self.param_env,
ty::Binder::dummy(trait_ref),
);

// FIXME(-Znext-solver): We only need this hack to deal with fatal
// overflow in the old solver.
if self.infcx.next_trait_solver() || self.infcx.predicate_may_hold(&obligation)
{
if self.infcx.next_trait_solver() {
ocx.register_obligation(obligation);
} else {
result = ProbeResult::NoMatch;
if let Ok(Some(candidate)) = self.select_trait_candidate(trait_ref) {
for nested_obligation in candidate.nested_obligations() {
if !self.infcx.predicate_may_hold(&nested_obligation) {
possibly_unsatisfied_predicates.push((
self.resolve_vars_if_possible(nested_obligation.predicate),
Some(self.resolve_vars_if_possible(obligation.predicate)),
Some(nested_obligation.cause),
));
match self.infcx.evaluate_obligation_no_overflow(&obligation) {
EvaluationResult::EvaluatedToOk => {
// No side-effects, no need to register obligations.
}
EvaluationResult::EvaluatedToOkModuloRegions
| EvaluationResult::EvaluatedToOkModuloOpaqueTypes
| EvaluationResult::EvaluatedToAmbig
| EvaluationResult::EvaluatedToAmbigStackDependent => {
ocx.register_obligation(obligation);
}
EvaluationResult::EvaluatedToErr => {
result = ProbeResult::NoMatch;
if let Ok(Some(candidate)) = self.select_trait_candidate(trait_ref)
{
for nested_obligation in candidate.nested_obligations() {
if !self.infcx.predicate_may_hold(&nested_obligation) {
possibly_unsatisfied_predicates.push((
self.resolve_vars_if_possible(
nested_obligation.predicate,
),
Some(self.resolve_vars_if_possible(
obligation.predicate,
)),
Some(nested_obligation.cause),
));
}
}
}
}
}
Expand All @@ -1488,7 +1563,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
);
(xform_self_ty, xform_ret_ty) =
self.xform_self_ty(probe.item, trait_ref.self_ty(), trait_ref.args);
xform_self_ty = ocx.normalize(cause, self.param_env, xform_self_ty);
let InferOk { value: normalized, obligations: normalize_obligations } =
self.at(cause, self.param_env).normalize(xform_self_ty);
xform_self_ty = normalized;

// FIXME: Make this `ocx.sup` once we define opaques more eagerly.
match self.at(cause, self.param_env).sup(
DefineOpaqueTypes::No,
Expand All @@ -1503,26 +1581,55 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
return ProbeResult::NoMatch;
}
}

for obligation in normalize_obligations {
if self.infcx.next_trait_solver() {
ocx.register_obligation(obligation);
} else {
match self.infcx.evaluate_obligation_no_overflow(&obligation) {
EvaluationResult::EvaluatedToOk => {
// No side-effects, no need to register obligations.
}
EvaluationResult::EvaluatedToOkModuloRegions
| EvaluationResult::EvaluatedToOkModuloOpaqueTypes
| EvaluationResult::EvaluatedToAmbig
| EvaluationResult::EvaluatedToAmbigStackDependent => {
ocx.register_obligation(obligation);
}
EvaluationResult::EvaluatedToErr => {
result = ProbeResult::NoMatch;
possibly_unsatisfied_predicates.push((
self.resolve_vars_if_possible(obligation.predicate),
None,
Some(obligation.cause.clone()),
));
}
}
}
}
}
}

// Evaluate those obligations to see if they might possibly hold.
for error in ocx.select_where_possible() {
result = ProbeResult::NoMatch;
let nested_predicate = self.resolve_vars_if_possible(error.obligation.predicate);
if let Some(trait_predicate) = trait_predicate
&& nested_predicate == self.resolve_vars_if_possible(trait_predicate)
{
// Don't report possibly unsatisfied predicates if the root
// trait obligation from a `TraitCandidate` is unsatisfied.
// That just means the candidate doesn't hold.
} else {
possibly_unsatisfied_predicates.push((
nested_predicate,
Some(self.resolve_vars_if_possible(error.root_obligation.predicate))
.filter(|root_predicate| *root_predicate != nested_predicate),
Some(error.obligation.cause),
));
if let ProbeResult::Match = result {
for error in ocx.select_where_possible() {
result = ProbeResult::NoMatch;
let nested_predicate =
self.resolve_vars_if_possible(error.obligation.predicate);
if let Some(trait_predicate) = trait_predicate
&& nested_predicate == self.resolve_vars_if_possible(trait_predicate)
{
// Don't report possibly unsatisfied predicates if the root
// trait obligation from a `TraitCandidate` is unsatisfied.
// That just means the candidate doesn't hold.
} else {
possibly_unsatisfied_predicates.push((
nested_predicate,
Some(self.resolve_vars_if_possible(error.root_obligation.predicate))
.filter(|root_predicate| *root_predicate != nested_predicate),
Some(error.obligation.cause),
));
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions tests/ui/impl-trait/issues/issue-84073.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0275]: overflow assigning `_` to `Option<_>`
--> $DIR/issue-84073.rs:32:27
--> $DIR/issue-84073.rs:32:22
|
LL | Race::new(|race| race.when());
| ^^^^
| ^^^^

error: aborting due to 1 previous error

Expand Down
11 changes: 0 additions & 11 deletions tests/ui/missing-trait-bounds/issue-35677.fixed

This file was deleted.

1 change: 0 additions & 1 deletion tests/ui/missing-trait-bounds/issue-35677.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
//@ run-rustfix
#![allow(dead_code)]
use std::collections::HashSet;
use std::hash::Hash;
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/missing-trait-bounds/issue-35677.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0599]: the method `is_subset` exists for reference `&HashSet<T>`, but its trait bounds were not satisfied
--> $DIR/issue-35677.rs:7:10
--> $DIR/issue-35677.rs:6:10
|
LL | this.is_subset(other)
| ^^^^^^^^^ method cannot be called on `&HashSet<T>` due to unsatisfied trait bounds
Expand Down
24 changes: 8 additions & 16 deletions tests/ui/suggestions/derive-trait-for-method-call.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -74,30 +74,22 @@ LL | struct Struct {
error[E0599]: the method `test` exists for struct `Foo<Vec<Enum>, Instant>`, but its trait bounds were not satisfied
--> $DIR/derive-trait-for-method-call.rs:40:15
|
LL | enum Enum {
| --------- doesn't satisfy `Enum: Clone`
...
LL | struct Foo<X, Y> (X, Y);
| ---------------- method `test` not found for this struct
...
LL | let y = x.test();
| ^^^^ method cannot be called on `Foo<Vec<Enum>, Instant>` due to unsatisfied trait bounds
|
note: trait bound `Instant: Default` was not satisfied
--> $DIR/derive-trait-for-method-call.rs:20:40
note: the following trait bounds were not satisfied:
`Instant: Default`
`Vec<Enum>: Clone`
--> $DIR/derive-trait-for-method-call.rs:20:9
|
LL | impl<X: Clone + Default + , Y: Clone + Default> Foo<X, Y> {
| ^^^^^^^ ---------
| |
| unsatisfied trait bound introduced here
= note: the following trait bounds were not satisfied:
`Enum: Clone`
which is required by `Vec<Enum>: Clone`
help: consider annotating `Enum` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
LL | enum Enum {
|
| ^^^^^ ^^^^^^^ ---------
| | |
| | unsatisfied trait bound introduced here
| unsatisfied trait bound introduced here

error: aborting due to 3 previous errors

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ LL | struct Foo<I>(I);
LL | Foo::<()>::f()
| ^ function or associated item cannot be called on `Foo<()>` due to unsatisfied trait bounds
|
note: trait bound `(): Iterator` was not satisfied
--> $DIR/issue-108132-unmet-trait-alias-bound-on-generic-impl.rs:5:23
note: trait bound `(): IteratorAlias` was not satisfied
--> $DIR/issue-108132-unmet-trait-alias-bound-on-generic-impl.rs:9:9
|
LL | trait IteratorAlias = Iterator;
| ------------- ^^^^^^^^ unsatisfied trait bound introduced here
LL | impl<I: IteratorAlias> Foo<I> {
| ^^^^^^^^^^^^^ ------
| |
| unsatisfied trait bound introduced here

error: aborting due to 1 previous error

Expand Down
21 changes: 12 additions & 9 deletions tests/ui/traits/track-obligations.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,26 @@ error[E0599]: the method `check` exists for struct `Client<()>`, but its trait b
LL | struct ALayer<C>(C);
| ---------------- doesn't satisfy `ALayer<()>: ParticularServiceLayer<()>`
...
LL | struct AService;
| --------------- doesn't satisfy `<AService as Service<Req>>::Response = Res`
...
LL | struct Client<C>(C);
| ---------------- method `check` not found for this struct
...
LL | Client(()).check();
| ^^^^^ method cannot be called on `Client<()>` due to unsatisfied trait bounds
|
note: trait bound `<AService as Service<Req>>::Response = Res` was not satisfied
--> $DIR/track-obligations.rs:24:21
note: trait bound `ALayer<()>: ParticularServiceLayer<()>` was not satisfied
--> $DIR/track-obligations.rs:71:16
|
LL | impl<T> ParticularService for T
| ----------------- -
LL | impl<C> Client<C>
| ---------
LL | where
LL | T: Service<Req, Response = Res>,
| ^^^^^^^^^^^^^^ unsatisfied trait bound introduced here
LL | ALayer<C>: ParticularServiceLayer<C>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound introduced here
note: the trait `ParticularServiceLayer` must be implemented
--> $DIR/track-obligations.rs:34:1
|
LL | / pub trait ParticularServiceLayer<C>:
LL | | Layer<C, Service = <Self as ParticularServiceLayer<C>>::Service>
| |____________________________________________________________________^

error[E0271]: type mismatch resolving `<AService as Service<Req>>::Response == Res`
--> $DIR/track-obligations.rs:87:11
Expand Down
Loading