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
14 changes: 9 additions & 5 deletions compiler/rustc_next_trait_solver/src/solve/search_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,19 +99,23 @@ where
response_no_constraints(cx, input, Certainty::overflow(false))
}

fn is_ambiguous_result(result: QueryResult<I>) -> bool {
result.is_ok_and(|response| {
has_no_inference_or_external_constraints(response)
fn is_ambiguous_result(result: QueryResult<I>) -> Option<Certainty> {
result.ok().and_then(|response| {
if has_no_inference_or_external_constraints(response)
&& matches!(response.value.certainty, Certainty::Maybe { .. })
{
Some(response.value.certainty)
} else {
None
}
})
}

fn propagate_ambiguity(
cx: I,
for_input: CanonicalInput<I>,
from_result: QueryResult<I>,
certainty: Certainty,
) -> QueryResult<I> {
let certainty = from_result.unwrap().value.certainty;
response_no_constraints(cx, for_input, certainty)
}

Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_type_ir/src/interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::inherent::*;
use crate::ir_print::IrPrint;
use crate::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem};
use crate::relate::Relate;
use crate::solve::{CanonicalInput, ExternalConstraintsData, QueryResult, inspect};
use crate::solve::{CanonicalInput, Certainty, ExternalConstraintsData, QueryResult, inspect};
use crate::visit::{Flags, TypeVisitable};
use crate::{self as ty, CanonicalParamEnvCacheEntry, search_graph};

Expand Down Expand Up @@ -548,6 +548,7 @@ impl<T, R, E> CollectAndApply<T, R> for Result<T, E> {
impl<I: Interner> search_graph::Cx for I {
type Input = CanonicalInput<I>;
type Result = QueryResult<I>;
type AmbiguityInfo = Certainty;

type DepNodeIndex = I::DepNodeIndex;
type Tracked<T: Debug + Clone> = I::Tracked<T>;
Expand Down
34 changes: 21 additions & 13 deletions compiler/rustc_type_ir/src/search_graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub use global_cache::GlobalCache;
pub trait Cx: Copy {
type Input: Debug + Eq + Hash + Copy;
type Result: Debug + Eq + Hash + Copy;
type AmbiguityInfo: Debug + Eq + Hash + Copy;

type DepNodeIndex;
type Tracked<T: Debug + Clone>: Debug;
Expand Down Expand Up @@ -96,11 +97,13 @@ pub trait Delegate: Sized {
input: <Self::Cx as Cx>::Input,
) -> <Self::Cx as Cx>::Result;

fn is_ambiguous_result(result: <Self::Cx as Cx>::Result) -> bool;
fn is_ambiguous_result(
result: <Self::Cx as Cx>::Result,
) -> Option<<Self::Cx as Cx>::AmbiguityInfo>;
fn propagate_ambiguity(
cx: Self::Cx,
for_input: <Self::Cx as Cx>::Input,
from_result: <Self::Cx as Cx>::Result,
ambiguity_info: <Self::Cx as Cx>::AmbiguityInfo,
) -> <Self::Cx as Cx>::Result;

fn compute_goal(
Expand Down Expand Up @@ -913,9 +916,9 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
/// heads from the stack. This may not necessarily mean that we've actually
/// reached a fixpoint for that cycle head, which impacts the way we rebase
/// provisional cache entries.
enum RebaseReason {
enum RebaseReason<X: Cx> {
NoCycleUsages,
Ambiguity,
Ambiguity(X::AmbiguityInfo),
Overflow,
/// We've actually reached a fixpoint.
///
Expand Down Expand Up @@ -951,7 +954,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
&mut self,
cx: X,
stack_entry: &StackEntry<X>,
rebase_reason: RebaseReason,
rebase_reason: RebaseReason<X>,
) {
let popped_head_index = self.stack.next_index();
#[allow(rustc::potential_query_instability)]
Expand All @@ -969,10 +972,6 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
return true;
};

let Some(new_highest_head_index) = heads.opt_highest_cycle_head_index() else {
return false;
};

// We're rebasing an entry `e` over a head `p`. This head
// has a number of own heads `h` it depends on.
//
Expand Down Expand Up @@ -1033,8 +1032,8 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
// is not actually equal to the final provisional result. We
// need to discard the provisional cache entry in this case.
RebaseReason::NoCycleUsages => return false,
RebaseReason::Ambiguity => {
*result = D::propagate_ambiguity(cx, input, *result);
RebaseReason::Ambiguity(info) => {
*result = D::propagate_ambiguity(cx, input, info);
}
RebaseReason::Overflow => *result = D::fixpoint_overflow_result(cx, input),
RebaseReason::ReachedFixpoint(None) => {}
Expand All @@ -1046,6 +1045,10 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
};
}

let Some(new_highest_head_index) = heads.opt_highest_cycle_head_index() else {
return false;
};

// We now care about the path from the next highest cycle head to the
// provisional cache entry.
*path_from_head = path_from_head.extend(Self::cycle_path_kind(
Expand Down Expand Up @@ -1268,6 +1271,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
}

/// Whether we've reached a fixpoint when evaluating a cycle head.
#[instrument(level = "trace", skip(self, stack_entry), ret)]
fn reached_fixpoint(
&mut self,
stack_entry: &StackEntry<X>,
Expand Down Expand Up @@ -1355,8 +1359,12 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
// As we only get to this branch if we haven't yet reached a fixpoint,
// we also taint all provisional cache entries which depend on the
// current goal.
if D::is_ambiguous_result(result) {
self.rebase_provisional_cache_entries(cx, &stack_entry, RebaseReason::Ambiguity);
if let Some(info) = D::is_ambiguous_result(result) {
self.rebase_provisional_cache_entries(
cx,
&stack_entry,
RebaseReason::Ambiguity(info),
);
return EvaluationResult::finalize(stack_entry, encountered_overflow, result);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//@ compile-flags: -Znext-solver
#![feature(rustc_attrs)]
#![rustc_no_implicit_bounds]

// A regression test making sure that when forcing dependent
// provisional cache entries to ambiguous, we use the `MaybeCause`
// of the cycle head. We ended up trying to use the current result
// of the provisional cache entry, which is incorrect and caused an
// ICE when trying to unwrap it.

struct Root<T>(T);
struct Head<T>(T);
struct Error<T>(T);
struct NotImplemented<T>(T);

#[rustc_coinductive]
trait Trait {}
impl<T> Trait for Root<T>
where
Head<T>: Trait,
{}

impl<T> Trait for Head<T>
where
Root<T>: Trait,
T: Trait, // ambiguous
{}

impl<T> Trait for Head<T>
where
Error<T>: Trait,
NotImplemented<T>: Trait,
{}

impl<T> Trait for Error<T>
where
Head<T>: Trait,
NotImplemented<T>: Trait,
{}

fn impls_trait<T: Trait>() {}
fn main() {
impls_trait::<Root<_>>() //~ ERROR type annotations needed
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
error[E0283]: type annotations needed
--> $DIR/forced_ambiguity-use-head-maybe-cause.rs:43:19
|
LL | impls_trait::<Root<_>>()
| ^^^^^^^ cannot infer type for struct `Head<_>`
|
= note: cannot satisfy `Head<_>: Trait`
= help: the trait `Trait` is implemented for `Head<T>`
note: required for `Root<_>` to implement `Trait`
--> $DIR/forced_ambiguity-use-head-maybe-cause.rs:18:9
|
LL | impl<T> Trait for Root<T>
| ^^^^^ ^^^^^^^
LL | where
LL | Head<T>: Trait,
| ----- unsatisfied trait bound introduced here
= note: 8 redundant requirements hidden
= note: required for `Root<_>` to implement `Trait`
note: required by a bound in `impls_trait`
--> $DIR/forced_ambiguity-use-head-maybe-cause.rs:41:19
|
LL | fn impls_trait<T: Trait>() {}
| ^^^^^ required by this bound in `impls_trait`

error: aborting due to 1 previous error

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