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,11 +731,14 @@ 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(diagnostics::IncompatibleFeatures {
spans: vec![gce_span],
f1: Symbol::intern("-Znext-solver=globally"),
f2: sym::generic_const_exprs,
});
sess.dcx()
.create_fatal(diagnostics::IncompatibleFeatures {
spans: vec![gce_span],
f1: Symbol::intern("-Znext-solver=globally"),
f2: sym::generic_const_exprs,
})
.with_code(rustc_errors::E0001)
.emit();
}
}

Expand Down
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 @@ -2681,7 +2681,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
16 changes: 5 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 @@ -246,7 +246,7 @@ where
return Ok(res);
}

let result = EvalCtxt::enter_root(self, self.cx().recursion_limit(), span, |ecx| {
let result = EvalCtxt::enter_root(self, self.cx().recursion_limit() * 2, span, |ecx| {
// Fast paths handled above
ecx.evaluate_goal_no_fast_paths(GoalSource::Misc, goal)
});
Expand Down Expand Up @@ -338,18 +338,12 @@ 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::ProjectionComputeAssocTermCandidate => 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::ProjectionComputeAssocTermCandidate => {
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 @@ -1705,7 +1699,7 @@ pub fn evaluate_root_goal_for_proof_tree_raw_provider<
let mut inspect = inspect::ProofTreeBuilder::new();
let (canonical_result, accessed_opaques) = 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 @@ -106,11 +106,12 @@ 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>, AccessedOpaques<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>, AccessedOpaques<I>)) -> Option<Certainty> {
Expand All @@ -125,14 +126,6 @@ where
})
}

fn propagate_ambiguity(
cx: I,
for_input: CanonicalInput<I>,
certainty: Certainty,
) -> (QueryResult<I>, AccessedOpaques<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 @@ -1005,7 +1005,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_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ fn do_normalize_predicates<'tcx>(
let elaborated_env = if tcx.next_trait_solver_globally()
&& !tcx.disable_param_env_normalization_hack()
{
let elaborated_env = ty::set_aliases_to_rigid(tcx, elaborated_env);
let elaborated_env = ty::set_type_aliases_to_rigid(tcx, elaborated_env);
let elaborated_env = set_projection_term_to_non_rigid(tcx, elaborated_env.caller_bounds());
ty::ParamEnv::new(tcx.mk_clauses_from_iter(elaborated_env))
} else {
Expand Down
18 changes: 15 additions & 3 deletions compiler/rustc_type_ir/src/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,13 @@ where
set_aliases_rigidness_with_mode(cx, value, RigidnessFoldMode::AllToRigid)
}

pub fn set_type_aliases_to_rigid<I: Interner, T>(cx: I, value: T) -> T
where
T: TypeFoldable<I>,
{
set_aliases_rigidness_with_mode(cx, value, RigidnessFoldMode::TypeToRigid)
}

fn set_aliases_rigidness_with_mode<I: Interner, T>(cx: I, value: T, mode: RigidnessFoldMode) -> T
where
T: TypeFoldable<I>,
Expand All @@ -597,8 +604,9 @@ where
}

enum RigidnessFoldMode {
AllToNonRigid,
AllToRigid,
AllToNonRigid,
TypeToRigid,
OpaqueToNonRigid,
}

Expand All @@ -607,6 +615,10 @@ impl RigidnessFoldMode {
match self {
RigidnessFoldMode::AllToRigid => v.has_non_rigid_aliases(),
RigidnessFoldMode::AllToNonRigid => v.has_rigid_aliases(),
RigidnessFoldMode::TypeToRigid => {
v.has_non_rigid_aliases()
&& v.has_type_flags(ty::TypeFlags::HAS_ALIAS - ty::TypeFlags::HAS_CONST_ALIAS)
}
RigidnessFoldMode::OpaqueToNonRigid => v.has_rigid_aliases() && v.has_opaque_types(),
}
}
Expand Down Expand Up @@ -637,7 +649,7 @@ impl<I: Interner> TypeFolder<I> for RigidnessFolder<I> {
ty::Alias(is_rigid, alias_ty) => {
let alias_ty = alias_ty.fold_with(self);
match self.mode {
RigidnessFoldMode::AllToRigid => {
RigidnessFoldMode::AllToRigid | RigidnessFoldMode::TypeToRigid => {
I::Ty::new_alias(self.cx(), ty::IsRigid::Yes, alias_ty)
}
RigidnessFoldMode::AllToNonRigid => {
Expand Down Expand Up @@ -671,7 +683,7 @@ impl<I: Interner> TypeFolder<I> for RigidnessFolder<I> {
RigidnessFoldMode::AllToNonRigid => {
I::Const::new_alias(self.cx(), ty::IsRigid::No, alias_const)
}
RigidnessFoldMode::OpaqueToNonRigid => {
RigidnessFoldMode::OpaqueToNonRigid | RigidnessFoldMode::TypeToRigid => {
I::Const::new_alias(self.cx(), is_rigid, alias_const)
}
}
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 @@ -654,7 +654,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>, AccessedOpaques<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 @@ -955,8 +952,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 @@ -987,10 +983,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 @@ -1065,18 +1060,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 @@ -1380,17 +1379,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 @@ -1399,19 +1393,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 @@ -1421,7 +1411,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 @@ -9,7 +9,8 @@ trait Foo {

trait Baz {}

impl<'a, T> Foo for &'a T
impl<'a, T> Foo for &'a T //~ ERROR not all trait items implemented, missing: `Item`
//~| ERROR type annotations needed: cannot satisfy `&'a T: Foo`
where
Self::Item: 'a,
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,58 @@
error[E0391]: cycle detected when computing normalized predicates of `<impl at $DIR/next-solver-region-resolution.rs:18:1: 21:21>`
--> $DIR/next-solver-region-resolution.rs:18:1
error[E0046]: not all trait items implemented, missing: `Item`
--> $DIR/next-solver-region-resolution.rs:12:1
|
LL | / impl<'a, T> Foo for &T
LL | type Item;
| --------- `Item` from trait
...
LL | / impl<'a, T> Foo for &'a T
LL | |
LL | | where
LL | | Self::Item: Baz,
| |____________________^
LL | | Self::Item: 'a,
| |___________________^ missing `Item` in implementation

error[E0283]: type annotations needed: cannot satisfy `&'a T: Foo`
--> $DIR/next-solver-region-resolution.rs:12:21
|
= note: ...which immediately requires computing normalized predicates of `<impl at $DIR/next-solver-region-resolution.rs:18:1: 21:21>` again
note: cycle used when computing whether impls specialize one another
LL | impl<'a, T> Foo for &'a T
| ^^^^^
|
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,
| |___________________^
= note: for more information, see <https://rustc-dev-guide.rust-lang.org/overview.html#queries> and <https://rustc-dev-guide.rust-lang.org/query.html>
...
LL | / impl<'a, T> Foo for &T
LL | |
LL | | where
LL | | Self::Item: Baz,
| |____________________^

error[E0046]: not all trait items implemented, missing: `Item`
--> $DIR/next-solver-region-resolution.rs:19:1
|
LL | type Item;
| --------- `Item` from trait
...
LL | / impl<'a, T> Foo for &T
LL | |
LL | | where
LL | | Self::Item: Baz,
| |____________________^ missing `Item` in implementation

error: cannot normalize `<&_ as Foo>::Item: '_`
--> $DIR/next-solver-region-resolution.rs:19:1
|
LL | / impl<'a, T> Foo for &T
LL | |
LL | | where
LL | | Self::Item: Baz,
| |____________________^

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

For more information about this error, try `rustc --explain E0391`.
Some errors have detailed explanations: E0046, E0283.
For more information about an error, try `rustc --explain E0046`.
Loading
Loading