Skip to content
Draft
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
13 changes: 8 additions & 5 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -731,10 +731,13 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) {
.map(|feat| feat.attr_sp)
{
#[allow(rustc::symbol_intern_string_literal)]
sess.dcx().emit_err(errors::IncompatibleFeatures {
spans: vec![gce_span],
f1: Symbol::intern("-Znext-solver=globally"),
f2: sym::generic_const_exprs,
});
sess.dcx()
.create_fatal(errors::IncompatibleFeatures {
spans: vec![gce_span],
f1: Symbol::intern("-Znext-solver=globally"),
f2: sym::generic_const_exprs,
})
.with_code(rustc_errors::E0001)
.emit();
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2699,7 +2699,7 @@ impl<'tcx> TyCtxt<'tcx> {
}

pub fn next_trait_solver_globally(self) -> bool {
self.sess.opts.unstable_opts.next_solver.globally
self.sess.opts.unstable_opts.next_solver.globally && !self.features().generic_const_exprs()
}

pub fn next_trait_solver_in_coherence(self) -> bool {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ where
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
todo!("Iterator is not yet const")
Err(NoSolution)
}

fn consider_builtin_fused_iterator_candidate(
Expand Down
14 changes: 3 additions & 11 deletions compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ where
span: I::Span,
stalled_on: Option<GoalStalledOn<I>>,
) -> Result<GoalEvaluation<I>, NoSolution> {
EvalCtxt::enter_root(self, self.cx().recursion_limit(), span, |ecx| {
EvalCtxt::enter_root(self, self.cx().recursion_limit() * 2, span, |ecx| {
ecx.evaluate_goal(GoalSource::Misc, goal, stalled_on)
})
}
Expand Down Expand Up @@ -284,18 +284,10 @@ where
// We currently only consider a cycle coinductive if it steps
// into a where-clause of a coinductive trait.
CurrentGoalKind::CoinductiveTrait => PathKind::Coinductive,
// While normalizing via an impl does step into a where-clause of
// an impl, accessing the associated item immediately steps out of
// it again. This means cycles/recursive calls are not guarded
// by impls used for normalization.
//
// See tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs
// for how this can go wrong.
CurrentGoalKind::NormalizesTo => PathKind::Inductive,
// We probably want to make all traits coinductive in the future,
// so we treat cycles involving where-clauses of not-yet coinductive
// traits as ambiguous for now.
CurrentGoalKind::Misc => PathKind::Unknown,
CurrentGoalKind::Misc | CurrentGoalKind::NormalizesTo => PathKind::Unknown,
},
// Relating types is always unproductive. If we were to map proof trees to
// corecursive functions as explained in #136824, relating types never
Expand Down Expand Up @@ -1491,7 +1483,7 @@ pub fn evaluate_root_goal_for_proof_tree_raw_provider<
let mut inspect = inspect::ProofTreeBuilder::new();
let canonical_result = SearchGraph::<D>::evaluate_root_goal_for_proof_tree(
cx,
cx.recursion_limit(),
cx.recursion_limit() * 2,
canonical_goal,
&mut inspect,
);
Expand Down
11 changes: 2 additions & 9 deletions compiler/rustc_next_trait_solver/src/solve/search_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,9 @@ where
response_no_constraints(cx, input, Certainty::overflow(true))
}

const FIXPOINT_OVERFLOW_AMBIGUITY_KIND: Certainty = Certainty::overflow(false);
fn fixpoint_overflow_result(cx: I, input: CanonicalInput<I>) -> QueryResult<I> {
response_no_constraints(cx, input, Certainty::overflow(false))
response_no_constraints(cx, input, Self::FIXPOINT_OVERFLOW_AMBIGUITY_KIND)
}

fn is_ambiguous_result(result: QueryResult<I>) -> Option<Certainty> {
Expand All @@ -111,14 +112,6 @@ where
})
}

fn propagate_ambiguity(
cx: I,
for_input: CanonicalInput<I>,
certainty: Certainty,
) -> QueryResult<I> {
response_no_constraints(cx, for_input, certainty)
}

fn compute_goal(
search_graph: &mut SearchGraph<D>,
cx: I,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_session/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -996,7 +996,7 @@ pub struct NextSolverConfig {
pub coherence: bool = true,
/// Whether the new trait solver should be enabled everywhere.
/// This is only `true` if `coherence` is also enabled.
pub globally: bool = false,
pub globally: bool = true,
}

#[derive(Clone)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_type_ir/src/interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,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 AmbiguityKind = Certainty;

type DepNodeIndex = I::DepNodeIndex;
type Tracked<T: Debug + Clone> = I::Tracked<T>;
Expand Down
55 changes: 24 additions & 31 deletions compiler/rustc_type_ir/src/search_graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +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 AmbiguityKind: Debug + Eq + Hash + Copy;

type DepNodeIndex;
type Tracked<T: Debug + Clone>: Debug;
Expand Down Expand Up @@ -92,19 +92,16 @@ pub trait Delegate: Sized {
cx: Self::Cx,
input: <Self::Cx as Cx>::Input,
) -> <Self::Cx as Cx>::Result;

const FIXPOINT_OVERFLOW_AMBIGUITY_KIND: <Self::Cx as Cx>::AmbiguityKind;
fn fixpoint_overflow_result(
cx: Self::Cx,
input: <Self::Cx as Cx>::Input,
) -> <Self::Cx as Cx>::Result;

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,
ambiguity_info: <Self::Cx as Cx>::AmbiguityInfo,
) -> <Self::Cx as Cx>::Result;
) -> Option<<Self::Cx as Cx>::AmbiguityKind>;

fn compute_goal(
search_graph: &mut SearchGraph<Self>,
Expand Down Expand Up @@ -929,8 +926,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
#[derive_where(Debug; X: Cx)]
enum RebaseReason<X: Cx> {
NoCycleUsages,
Ambiguity(X::AmbiguityInfo),
Overflow,
Ambiguity(X::AmbiguityKind),
/// We've actually reached a fixpoint.
///
/// This either happens in the first evaluation step for the cycle head.
Expand Down Expand Up @@ -961,10 +957,9 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
/// cache entries to also be ambiguous. This causes some undesirable ambiguity for nested
/// goals whose result doesn't actually depend on this cycle head, but that's acceptable
/// to me.
#[instrument(level = "trace", skip(self, cx))]
#[instrument(level = "trace", skip(self))]
fn rebase_provisional_cache_entries(
&mut self,
cx: X,
stack_entry: &StackEntry<X>,
rebase_reason: RebaseReason<X>,
) {
Expand Down Expand Up @@ -1039,18 +1034,22 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
}

// The provisional cache entry does depend on the provisional result
// of the popped cycle head. We need to mutate the result of our
// provisional cache entry in case we did not reach a fixpoint.
// of the popped cycle head. In case we didn't actually reach a fixpoint,
// we must not keep potentially incorrect provisional cache entries around.
match rebase_reason {
// If the cycle head does not actually depend on itself, then
// the provisional result used by the provisional cache entry
// 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(info) => {
*result = D::propagate_ambiguity(cx, input, info);
// If we avoid rerunning a goal due to ambiguity, we only keep provisional
// results which depend on that cycle head if these are already ambiguous
// themselves.
RebaseReason::Ambiguity(kind) => {
if !D::is_ambiguous_result(*result).is_some_and(|k| k == kind) {
return false;
}
}
RebaseReason::Overflow => *result = D::fixpoint_overflow_result(cx, input),
RebaseReason::ReachedFixpoint(None) => {}
RebaseReason::ReachedFixpoint(Some(path_kind)) => {
if !popped_head.usages.is_single(path_kind) {
Expand Down Expand Up @@ -1352,17 +1351,12 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
// final result is equal to the initial response for that case.
if let Ok(fixpoint) = self.reached_fixpoint(&stack_entry, usages, result) {
self.rebase_provisional_cache_entries(
cx,
&stack_entry,
RebaseReason::ReachedFixpoint(fixpoint),
);
return EvaluationResult::finalize(stack_entry, encountered_overflow, result);
} else if usages.is_empty() {
self.rebase_provisional_cache_entries(
cx,
&stack_entry,
RebaseReason::NoCycleUsages,
);
self.rebase_provisional_cache_entries(&stack_entry, RebaseReason::NoCycleUsages);
return EvaluationResult::finalize(stack_entry, encountered_overflow, result);
}

Expand All @@ -1371,19 +1365,15 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
// response in the next iteration in this case. These changes would
// likely either be caused by incompleteness or can change the maybe
// cause from ambiguity to overflow. Returning ambiguity always
// preserves soundness and completeness even if the goal is be known
// to succeed or fail.
// preserves soundness and completeness even if the goal could
// otherwise succeed or fail.
//
// This prevents exponential blowup affecting multiple major crates.
// 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 let Some(info) = D::is_ambiguous_result(result) {
self.rebase_provisional_cache_entries(
cx,
&stack_entry,
RebaseReason::Ambiguity(info),
);
if let Some(kind) = D::is_ambiguous_result(result) {
self.rebase_provisional_cache_entries(&stack_entry, RebaseReason::Ambiguity(kind));
return EvaluationResult::finalize(stack_entry, encountered_overflow, result);
};

Expand All @@ -1393,7 +1383,10 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
if i >= D::FIXPOINT_STEP_LIMIT {
debug!("canonical cycle overflow");
let result = D::fixpoint_overflow_result(cx, input);
self.rebase_provisional_cache_entries(cx, &stack_entry, RebaseReason::Overflow);
self.rebase_provisional_cache_entries(
&stack_entry,
RebaseReason::Ambiguity(D::FIXPOINT_OVERFLOW_AMBIGUITY_KIND),
);
return EvaluationResult::finalize(stack_entry, encountered_overflow, result);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ trait Foo {
trait Baz {}

impl<'a, T> Foo for &'a T //~ ERROR not all trait items implemented, missing: `Item`
//~| ERROR the trait bound `&'a T: Foo` is not satisfied
//~| ERROR type annotations needed: cannot satisfy `&'a T: Foo`
where
Self::Item: 'a, //~ ERROR the trait bound `&'a T: Foo` is not satisfied
Self::Item: 'a,
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,26 @@ LL | | where
LL | | Self::Item: 'a,
| |___________________^ missing `Item` in implementation

error[E0277]: the trait bound `&'a T: Foo` is not satisfied
error[E0283]: type annotations needed: cannot satisfy `&'a T: Foo`
--> $DIR/next-solver-region-resolution.rs:12:21
|
LL | impl<'a, T> Foo for &'a T
| ^^^^^ the trait `Foo` is not implemented for `&'a T`
| ^^^^^
|
help: the trait `Foo` is not implemented for `&'a _`
but it is implemented for `&_`
note: multiple `impl`s satisfying `&'a T: Foo` found
--> $DIR/next-solver-region-resolution.rs:12:1
|
LL | / impl<'a, T> Foo for &'a T
LL | |
LL | | where
LL | | Self::Item: 'a,
| |___________________^

error[E0277]: the trait bound `&'a T: Foo` is not satisfied
--> $DIR/next-solver-region-resolution.rs:15:17
|
LL | Self::Item: 'a,
| ^^ the trait `Foo` is not implemented for `&'a T`
|
help: the trait `Foo` is not implemented for `&'a _`
but it is implemented for `&_`
--> $DIR/next-solver-region-resolution.rs:12:1
|
LL | / impl<'a, T> Foo for &'a T
...
LL | / impl<'a, T> Foo for &T
LL | |
LL | | where
LL | | Self::Item: 'a,
| |___________________^
LL | | Self::Item: Baz,
| |____________________^

error[E0046]: not all trait items implemented, missing: `Item`
--> $DIR/next-solver-region-resolution.rs:19:1
Expand All @@ -63,7 +52,7 @@ LL | | where
LL | | Self::Item: Baz,
| |____________________^

error: aborting due to 5 previous errors
error: aborting due to 4 previous errors

Some errors have detailed explanations: E0046, E0277.
Some errors have detailed explanations: E0046, E0283.
For more information about an error, try `rustc --explain E0046`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error[E0283]: type annotations needed: cannot satisfy `<Vec<T> as IntoIterator>::IntoIter: Iterator`
--> $DIR/normalizes-to-is-not-productive-2.rs:21:41
|
LL | <Vec<T> as IntoIterator>::IntoIter: Iterator,
| ^^^^^^^^
|
= note: cannot satisfy `<Vec<T> as IntoIterator>::IntoIter: Iterator`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0283`.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
//@ check-pass
//@[current] check-pass

// Regression test for trait-system-refactor-initiative#176.
// Test from trait-system-refactor-initiative#176.
//
// Normalizing `<Vec<T> as IntoIterator>::IntoIter` has two candidates
// inside of the function:
Expand All @@ -13,11 +13,13 @@
// - where-clause requires `<Vec<T> as IntoIterator>::IntoIter eq Vec<T>`
// - normalize `<Vec<T> as IntoIterator>::IntoIter` again, cycle
//
// We need to treat this cycle as an error to be able to use the actual impl.
// The blanket impl is unfortunately also a productive cycle, so we have to
// break this code, see trait-system-refactor-initiative#273

fn test<T>()
where
<Vec<T> as IntoIterator>::IntoIter: Iterator,
//[next]~^ ERROR type annotations needed: cannot satisfy `<Vec<T> as IntoIterator>::IntoIter: Iterator`
{
}

Expand Down
Loading
Loading