Skip to content

Commit

Permalink
Auto merge of #124303 - compiler-errors:fulfill-slowdown, r=<try>
Browse files Browse the repository at this point in the history
[perf] Make fulfill in method probe less bad

r? `@lcnr`
  • Loading branch information
bors committed Apr 24, 2024
2 parents 7bb4f08 + b4427cd commit aeb7743
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 78 deletions.
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 => {
// 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()),
));
}
}
}
}
}
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 => {
// 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()),
));
}
}
}
}

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

0 comments on commit aeb7743

Please sign in to comment.