Skip to content
Open
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
4 changes: 4 additions & 0 deletions compiler/rustc_infer/src/infer/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,10 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
self.resolve_vars_if_possible(value)
}

fn commit_if_ok<T, E>(&self, f: impl FnOnce() -> Result<T, E>) -> Result<T, E> {
self.commit_if_ok(|_| f())
}

fn probe<T>(&self, probe: impl FnOnce() -> T) -> T {
self.probe(|_| probe())
}
Expand Down
66 changes: 50 additions & 16 deletions compiler/rustc_next_trait_solver/src/canonical/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use std::iter;

use canonicalizer::Canonicalizer;
use rustc_index::IndexVec;
use rustc_type_ir::error::TypeError;
use rustc_type_ir::inherent::*;
use rustc_type_ir::relate::{
self, Relate, RelateResult, TypeRelation, VarianceDiagInfo, relate_args_invariantly,
Expand All @@ -27,7 +28,7 @@ use crate::delegate::SolverDelegate;
use crate::resolve::eager_resolve_vars;
use crate::solve::{
CanonicalInput, CanonicalResponse, Certainty, ExternalConstraintsData,
ExternalRegionConstraints, Goal, NestedNormalizationGoals, QueryInput, Response,
ExternalRegionConstraints, Goal, NestedNormalizationGoals, NoSolution, QueryInput, Response,
VisibleForLeakCheck, inspect,
};

Expand Down Expand Up @@ -103,7 +104,7 @@ pub(super) fn instantiate_and_apply_query_response<D, I>(
original_values: &[I::GenericArg],
response: CanonicalResponse<I>,
span: I::Span,
) -> (NestedNormalizationGoals<I>, Certainty)
) -> Result<(NestedNormalizationGoals<I>, Certainty), NoSolution>
where
D: SolverDelegate<Interner = I>,
I: Interner,
Expand All @@ -114,7 +115,7 @@ where
let Response { var_values, external_constraints, certainty } =
delegate.instantiate_canonical(response, instantiation);

unify_query_var_values(delegate, param_env, &original_values, var_values, span);
unify_query_var_values(delegate, param_env, &original_values, var_values, span)?;

let ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } =
&*external_constraints;
Expand All @@ -139,7 +140,7 @@ where
};
register_new_opaque_types(delegate, opaque_types, span);

(normalization_nested_goals.clone(), certainty)
Ok((normalization_nested_goals.clone(), certainty))
}

/// This returns the canonical variable values to instantiate the bound variables of
Expand Down Expand Up @@ -320,10 +321,24 @@ where
}

(ty::Infer(ty::TyVar(a_vid)), _) => {
if !infcx
.universe_of_ty(a_vid)
.unwrap()
.can_name(ty::max_universe_of_non_region_placeholders(infcx, b))
{
return Err(TypeError::Mismatch);
}
infcx.instantiate_ty_var_raw(a_vid, b);
}

(_, ty::Infer(ty::TyVar(b_vid))) => {
if !infcx
.universe_of_ty(b_vid)
.unwrap()
.can_name(ty::max_universe_of_non_region_placeholders(infcx, a))
{
return Err(TypeError::Mismatch);
}
infcx.instantiate_ty_var_raw(b_vid, a);
}

Expand Down Expand Up @@ -407,10 +422,24 @@ where
}

(ty::ConstKind::Infer(ty::InferConst::Var(a_vid)), _) => {
if !infcx
.universe_of_ct(a_vid)
.unwrap()
.can_name(ty::max_universe_of_non_region_placeholders(infcx, b))
{
return Err(TypeError::Mismatch);
}
infcx.instantiate_const_var_raw(a_vid, b);
}

(_, ty::ConstKind::Infer(ty::InferConst::Var(b_vid))) => {
if !infcx
.universe_of_ct(b_vid)
.unwrap()
.can_name(ty::max_universe_of_non_region_placeholders(infcx, a))
{
return Err(TypeError::Mismatch);
}
infcx.instantiate_const_var_raw(b_vid, a);
}

Expand Down Expand Up @@ -443,10 +472,10 @@ where

/// Unify the `original_values` with the `var_values` returned by the canonical query..
///
/// This assumes that this unification will always succeed. This is the case when
/// applying a query response right away. However, calling a canonical query, doing any
/// other kind of trait solving, and only then instantiating the result of the query
/// can cause the instantiation to fail. This is not supported and we ICE in this case.
/// This unification can fail if an input inference variable cannot name a placeholder
/// in the response. Input canonicalization maps all universes to the root universe, so
/// the query itself cannot detect that mismatch. Treating the response as `NoSolution`
/// here prevents a higher-ranked placeholder from leaking into the caller.
///
/// We always structurally instantiate aliases. Relating aliases needs to be different
/// depending on whether the alias is *rigid* or not. We're only really able to tell
Expand All @@ -460,16 +489,21 @@ fn unify_query_var_values<D, I>(
original_values: &[I::GenericArg],
var_values: CanonicalVarValues<I>,
span: I::Span,
) where
) -> Result<(), NoSolution>
where
D: SolverDelegate<Interner = I>,
I: Interner,
{
assert_eq!(original_values.len(), var_values.len());

for (&orig, response) in iter::zip(original_values, var_values.var_values.iter()) {
let mut must_eq = ResponseRelating::new(&**delegate, span);
must_eq.relate(orig, response).unwrap();
}
delegate.commit_if_ok(|| {
for (&orig, response) in iter::zip(original_values, var_values.var_values.iter()) {
let mut must_eq = ResponseRelating::new(&**delegate, span);
must_eq.relate(orig, response).map_err(|_| NoSolution)?;
}

Ok(())
})
}

fn register_region_constraints<D, I>(
Expand Down Expand Up @@ -546,7 +580,7 @@ pub fn instantiate_canonical_state<D, I, T>(
param_env: I::ParamEnv,
orig_values: &mut Vec<I::GenericArg>,
state: inspect::CanonicalState<I, T>,
) -> T
) -> Result<T, NoSolution>
where
D: SolverDelegate<Interner = I>,
I: Interner,
Expand All @@ -565,8 +599,8 @@ where

let inspect::State { var_values, data } = delegate.instantiate_canonical(state, instantiation);

unify_query_var_values(delegate, param_env, orig_values, var_values, span);
data
unify_query_var_values(delegate, param_env, orig_values, var_values, span)?;
Ok(data)
}

pub fn response_no_constraints_raw<I: Interner>(
Expand Down
29 changes: 25 additions & 4 deletions compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ where
&orig_values,
response,
self.origin_span,
);
)?;

// FIXME: We previously had an assert here that checked that recomputing
// a goal after applying its constraints did not change its response.
Expand Down Expand Up @@ -1734,7 +1734,7 @@ pub(super) fn evaluate_root_goal_for_proof_tree<D: SolverDelegate<Interner = I>,
let (canonical_result, final_revision) =
delegate.cx().evaluate_root_goal_for_proof_tree_raw(canonical_goal);

let proof_tree = inspect::GoalEvaluation {
let mut proof_tree = inspect::GoalEvaluation {
uncanonicalized_goal: goal,
orig_values,
final_revision,
Expand All @@ -1746,13 +1746,34 @@ pub(super) fn evaluate_root_goal_for_proof_tree<D: SolverDelegate<Interner = I>,
Ok(response) => response,
};

let (normalization_nested_goals, _certainty) = instantiate_and_apply_query_response(
let Ok((normalization_nested_goals, _certainty)) = instantiate_and_apply_query_response(
delegate,
goal.param_env,
&proof_tree.orig_values,
response,
origin_span,
);
) else {
proof_tree.result = Err(NoSolution);
// The recorded states may contain the same constraints which made the response
// inapplicable in the caller, so they cannot be safely replayed by diagnostics.
let var_kinds = canonical_goal.canonical.var_kinds;
proof_tree.final_revision = delegate.cx().mk_probe(inspect::Probe {
steps: vec![],
kind: inspect::ProbeKind::Root { result: Err(NoSolution) },
// This failed proof has no state to replay. Keep an identity state in the
// solver query's canonical variables instead of response-canonicalizing
// caller-side values, which may contain parameters.
final_state: ty::Canonical {
value: inspect::State {
var_values: CanonicalVarValues::make_identity(delegate.cx(), var_kinds),
data: (),
},
max_universe: canonical_goal.canonical.max_universe,
var_kinds,
},
});
return (Err(NoSolution), proof_tree);
};

(Ok(normalization_nested_goals), proof_tree)
}
22 changes: 13 additions & 9 deletions compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,14 +229,16 @@ impl<'tcx> BestObligation<'tcx> {
if candidates.len() > 1 {
candidates.retain(|candidate| {
goal.infcx().probe(|_| {
candidate.instantiate_nested_goals(self.span()).iter().any(
|nested_goal| {
matches!(
nested_goal.source(),
GoalSource::ImplWhereBound
| GoalSource::AliasBoundConstCondition
| GoalSource::AliasWellFormed
) && nested_goal.result().is_err()
candidate.instantiate_nested_goals(self.span()).is_ok_and(
|nested_goals| {
nested_goals.iter().any(|nested_goal| {
matches!(
nested_goal.source(),
GoalSource::ImplWhereBound
| GoalSource::AliasBoundConstCondition
| GoalSource::AliasWellFormed
) && nested_goal.result().is_err()
})
},
)
})
Expand Down Expand Up @@ -470,7 +472,9 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
_ => ChildMode::PassThrough,
};

let nested_goals = candidate.instantiate_nested_goals(self.span());
let Ok(nested_goals) = candidate.instantiate_nested_goals(self.span()) else {
return self.detect_error_from_empty_candidates(goal);
};

// If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as
// an actual candidate, instead we should treat them as if the impl was never considered to
Expand Down
Loading
Loading