Skip to content
Merged
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
25 changes: 19 additions & 6 deletions compiler/rustc_trait_selection/src/solve/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use tracing::instrument;
use self::derive_errors::*;
use super::Certainty;
use super::delegate::SolverDelegate;
use crate::traits::{FulfillmentError, ScrubbedTraitError};
use crate::traits::{FulfillmentError, FulfillmentErrorCode, ScrubbedTraitError};

mod derive_errors;

Expand Down Expand Up @@ -191,7 +191,8 @@ where
// the other case.
TraitErrors::NoErrors
} else {
TraitErrors::HasErrors(collect_remaining_errors_impl(self, infcx))
let errors = collect_remaining_errors_impl(self, infcx);
TraitErrors::from_iter(errors.into_iter())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}
}

Expand Down Expand Up @@ -405,7 +406,9 @@ where
cx.obligations
.pending
.drain(..)
.map(|(obligation, _)| NextSolverError::Ambiguity(obligation))
.filter_map(|(obligation, _)| {
try_ambiguity_error_for_stalled(infcx, obligation).map(NextSolverError::Ambiguity)
})
.chain(
cx.obligations
.overflowed
Expand All @@ -416,9 +419,19 @@ where
.collect()
}

// We evaluate stalled obligations while collecting remaining errors because a
// previously ambiguous goal may have become successful. In that case we emit a
// delayed bug instead of producing a fulfillment error. Store the diagnostic
// information here so error conversion does not reevaluate the goal.
pub struct NextSolverAmbiguityError<'tcx> {
root_obligation: PredicateObligation<'tcx>,
code: FulfillmentErrorCode<'tcx>,
refine_obligation: bool,
}

pub enum NextSolverError<'tcx> {
TrueError(PredicateObligation<'tcx>),
Ambiguity(PredicateObligation<'tcx>),
Ambiguity(NextSolverAmbiguityError<'tcx>),
Overflow(PredicateObligation<'tcx>),
}

Expand All @@ -428,8 +441,8 @@ impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for FulfillmentError<'tc
NextSolverError::TrueError(obligation) => {
fulfillment_error_for_no_solution(infcx, obligation)
}
NextSolverError::Ambiguity(obligation) => {
fulfillment_error_for_stalled(infcx, obligation)
NextSolverError::Ambiguity(ambiguity) => {
fulfillment_error_for_stalled(infcx, ambiguity)
}
NextSolverError::Overflow(obligation) => {
fulfillment_error_for_overflow(infcx, obligation)
Expand Down
51 changes: 32 additions & 19 deletions compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use rustc_middle::{bug, span_bug};
use rustc_next_trait_solver::solve::{GoalEvaluation, MaybeInfo, SolverDelegateEvalExt as _};
use tracing::{instrument, trace};

use super::NextSolverAmbiguityError;
use crate::solve::delegate::SolverDelegate;
use crate::solve::inspect::{self, InferCtxtProofTreeExt, ProofTreeVisitor};
use crate::solve::{Certainty, deeply_normalize_for_diagnostics};
Expand Down Expand Up @@ -84,9 +85,24 @@ pub(super) fn fulfillment_error_for_no_solution<'tcx>(

pub(super) fn fulfillment_error_for_stalled<'tcx>(
infcx: &InferCtxt<'tcx>,
root_obligation: PredicateObligation<'tcx>,
ambiguity: NextSolverAmbiguityError<'tcx>,
) -> FulfillmentError<'tcx> {
let (code, refine_obligation) = infcx.probe(|_| {
let NextSolverAmbiguityError { root_obligation, code, refine_obligation } = ambiguity;

let obligation = if refine_obligation {
find_best_leaf_obligation(infcx, &root_obligation, true)
} else {
root_obligation.clone()
};

FulfillmentError { obligation, code, root_obligation }
}

pub(super) fn try_ambiguity_error_for_stalled<'tcx>(
infcx: &InferCtxt<'tcx>,
root_obligation: PredicateObligation<'tcx>,
) -> Option<NextSolverAmbiguityError<'tcx>> {
let evaluation = infcx.probe(|_| {
match <&SolverDelegate<'tcx>>::from(infcx).evaluate_root_goal(
root_obligation.as_goal(),
root_obligation.cause.span,
Expand All @@ -100,7 +116,7 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>(
stalled_on_coroutines: _,
}),
..
}) => (FulfillmentErrorCode::Ambiguity { overflow: None }, true),
}) => Some((FulfillmentErrorCode::Ambiguity { overflow: None }, true)),
Ok(GoalEvaluation {
certainty:
Certainty::Maybe(MaybeInfo {
Expand All @@ -110,7 +126,7 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>(
stalled_on_coroutines: _,
}),
..
}) => (
}) => Some((
FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) },
// Don't look into overflows because we treat overflows weirdly anyways.
// We discard the inference constraints from overflowing goals, so
Expand All @@ -119,14 +135,17 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>(
//
// FIXME: We should probably just look into overflows here.
false,
),
)),
Ok(GoalEvaluation { certainty: Certainty::Yes, .. }) => {
span_bug!(
infcx.dcx().span_delayed_bug(
root_obligation.cause.span,
"did not expect successful goal when collecting ambiguity errors for `{:?}`",
infcx.resolve_vars_if_possible(root_obligation.predicate),
)
}
format!(
"did not expect successful goal when collecting ambiguity errors for `{:?}`",
infcx.resolve_vars_if_possible(root_obligation.predicate),
),
);
None
},
Err(_) => {
span_bug!(
root_obligation.cause.span,
Expand All @@ -137,15 +156,9 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>(
}
});

FulfillmentError {
obligation: if refine_obligation {
find_best_leaf_obligation(infcx, &root_obligation, true)
} else {
root_obligation.clone()
},
code,
root_obligation,
}
let (code, refine_obligation) = evaluation?;

Some(NextSolverAmbiguityError { root_obligation, code, refine_obligation })
}

pub(super) fn fulfillment_error_for_overflow<'tcx>(
Expand Down

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the test file name should be changed as it's not actually about stale stalled.
And could you remove the issue number from the filename and add a comment like // Regression test for <https://github.com/rust-lang/rust/issues/161669> instead?

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Regression test for https://github.com/rust-lang/rust/issues/161669.
//@ edition:2024
//@ compile-flags: -Znext-solver

#![allow(dead_code)]
trait MyTrait {}
impl MyTrait for () {}

impl<'de> DeserTrait<'de> for &'de DeserStruct {}

trait DeserTrait<'de> {}

struct DeserStruct;

impl DeserTrait<'_> for &'static MyTrait {}
//~^ ERROR expected a type, found a trait

fn test() -> impl Send {
testfn(&DeserStruct)
}

fn testfn<'de, D: DeserTrait<'de>>(_deserializer: D) -> impl MyTrait + 'static {}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0782]: expected a type, found a trait
--> $DIR/successful-goal-during-ambiguity-reporting.rs:15:34
|
LL | impl DeserTrait<'_> for &'static MyTrait {}
| ^^^^^^^
|
help: you can add the `dyn` keyword if you want a trait object
|
LL | impl DeserTrait<'_> for &'static dyn MyTrait {}
| +++

error: aborting due to 1 previous error

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