2022-12-04 03:19:10 +00:00
|
|
|
//! The new trait solver, currently still WIP.
|
|
|
|
//!
|
|
|
|
//! As a user of the trait system, you can use `TyCtxt::evaluate_goal` to
|
|
|
|
//! interact with this solver.
|
|
|
|
//!
|
|
|
|
//! For a high-level overview of how this solver works, check out the relevant
|
|
|
|
//! section of the rustc-dev-guide.
|
|
|
|
//!
|
|
|
|
//! FIXME(@lcnr): Write that section. If you read this before then ask me
|
|
|
|
//! about it on zulip.
|
|
|
|
|
|
|
|
// FIXME: Instead of using `infcx.canonicalize_query` we have to add a new routine which
|
|
|
|
// preserves universes and creates a unique var (in the highest universe) for each
|
|
|
|
// appearance of a region.
|
|
|
|
|
|
|
|
// FIXME: uses of `infcx.at` need to enable deferred projection equality once that's implemented.
|
|
|
|
|
2023-01-20 18:38:33 +00:00
|
|
|
use rustc_hir::def_id::DefId;
|
2023-01-26 20:33:34 +00:00
|
|
|
use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues};
|
2023-03-17 15:02:24 +00:00
|
|
|
use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
|
2022-12-04 03:19:10 +00:00
|
|
|
use rustc_infer::traits::query::NoSolution;
|
2023-02-15 02:08:05 +00:00
|
|
|
use rustc_middle::traits::solve::{
|
|
|
|
CanonicalGoal, CanonicalResponse, Certainty, ExternalConstraints, ExternalConstraintsData,
|
2023-03-17 15:02:24 +00:00
|
|
|
Goal, QueryResult, Response,
|
2023-02-15 02:08:05 +00:00
|
|
|
};
|
2023-02-17 09:32:33 +00:00
|
|
|
use rustc_middle::ty::{self, Ty, TyCtxt};
|
2023-01-20 03:05:06 +00:00
|
|
|
use rustc_middle::ty::{
|
2023-02-15 02:08:05 +00:00
|
|
|
CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate,
|
2023-01-20 03:05:06 +00:00
|
|
|
};
|
2022-12-04 03:19:10 +00:00
|
|
|
|
2023-01-10 20:24:10 +00:00
|
|
|
use crate::traits::ObligationCause;
|
|
|
|
|
2022-12-19 07:01:38 +00:00
|
|
|
mod assembly;
|
2023-02-20 12:37:28 +01:00
|
|
|
mod canonical;
|
2023-02-16 02:28:10 +00:00
|
|
|
mod eval_ctxt;
|
2022-12-04 03:19:10 +00:00
|
|
|
mod fulfill;
|
|
|
|
mod project_goals;
|
2023-01-17 10:21:30 +01:00
|
|
|
mod search_graph;
|
2022-12-04 03:19:10 +00:00
|
|
|
mod trait_goals;
|
|
|
|
|
2023-03-17 15:02:24 +00:00
|
|
|
pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt};
|
2022-12-04 03:19:10 +00:00
|
|
|
pub use fulfill::FulfillmentCtxt;
|
|
|
|
|
2023-02-10 14:54:50 +00:00
|
|
|
trait CanonicalResponseExt {
|
|
|
|
fn has_no_inference_or_external_constraints(&self) -> bool;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> {
|
|
|
|
fn has_no_inference_or_external_constraints(&self) -> bool {
|
2023-02-20 12:37:28 +01:00
|
|
|
self.value.external_constraints.region_constraints.is_empty()
|
|
|
|
&& self.value.var_values.is_identity()
|
2023-02-10 14:54:50 +00:00
|
|
|
&& self.value.external_constraints.opaque_types.is_empty()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-17 10:21:30 +01:00
|
|
|
impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
2023-03-16 14:58:26 +00:00
|
|
|
#[instrument(level = "debug", skip(self))]
|
2022-12-04 03:19:10 +00:00
|
|
|
fn compute_type_outlives_goal(
|
|
|
|
&mut self,
|
2023-02-20 12:37:28 +01:00
|
|
|
goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>,
|
2022-12-04 03:19:10 +00:00
|
|
|
) -> QueryResult<'tcx> {
|
2023-02-20 12:37:28 +01:00
|
|
|
let ty::OutlivesPredicate(ty, lt) = goal.predicate;
|
|
|
|
self.infcx.register_region_obligation_with_cause(ty, lt, &ObligationCause::dummy());
|
2023-03-16 14:58:26 +00:00
|
|
|
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
2022-12-04 03:19:10 +00:00
|
|
|
}
|
|
|
|
|
2023-03-16 14:58:26 +00:00
|
|
|
#[instrument(level = "debug", skip(self))]
|
2022-12-04 03:19:10 +00:00
|
|
|
fn compute_region_outlives_goal(
|
|
|
|
&mut self,
|
2023-02-20 12:37:28 +01:00
|
|
|
goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>,
|
2022-12-04 03:19:10 +00:00
|
|
|
) -> QueryResult<'tcx> {
|
2023-02-20 12:37:28 +01:00
|
|
|
self.infcx.region_outlives_predicate(
|
|
|
|
&ObligationCause::dummy(),
|
|
|
|
ty::Binder::dummy(goal.predicate),
|
|
|
|
);
|
2023-03-16 14:58:26 +00:00
|
|
|
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
2022-12-04 03:19:10 +00:00
|
|
|
}
|
2023-01-20 03:05:06 +00:00
|
|
|
|
2023-03-16 14:58:26 +00:00
|
|
|
#[instrument(level = "debug", skip(self))]
|
2023-01-20 18:38:33 +00:00
|
|
|
fn compute_coerce_goal(
|
|
|
|
&mut self,
|
|
|
|
goal: Goal<'tcx, CoercePredicate<'tcx>>,
|
|
|
|
) -> QueryResult<'tcx> {
|
|
|
|
self.compute_subtype_goal(Goal {
|
|
|
|
param_env: goal.param_env,
|
|
|
|
predicate: SubtypePredicate {
|
|
|
|
a_is_expected: false,
|
|
|
|
a: goal.predicate.a,
|
|
|
|
b: goal.predicate.b,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-03-16 14:58:26 +00:00
|
|
|
#[instrument(level = "debug", skip(self))]
|
2023-01-20 03:05:06 +00:00
|
|
|
fn compute_subtype_goal(
|
|
|
|
&mut self,
|
|
|
|
goal: Goal<'tcx, SubtypePredicate<'tcx>>,
|
|
|
|
) -> QueryResult<'tcx> {
|
2023-01-20 18:38:33 +00:00
|
|
|
if goal.predicate.a.is_ty_var() && goal.predicate.b.is_ty_var() {
|
|
|
|
// FIXME: Do we want to register a subtype relation between these vars?
|
|
|
|
// That won't actually reflect in the query response, so it seems moot.
|
2023-03-16 14:58:26 +00:00
|
|
|
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
|
2023-01-20 18:38:33 +00:00
|
|
|
} else {
|
2023-01-27 04:00:37 +00:00
|
|
|
let InferOk { value: (), obligations } = self
|
|
|
|
.infcx
|
|
|
|
.at(&ObligationCause::dummy(), goal.param_env)
|
2023-03-15 14:00:15 +01:00
|
|
|
.sub(DefineOpaqueTypes::No, goal.predicate.a, goal.predicate.b)?;
|
2023-03-16 14:58:26 +00:00
|
|
|
self.add_goals(obligations.into_iter().map(|pred| pred.into()));
|
|
|
|
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
2023-01-20 18:38:33 +00:00
|
|
|
}
|
2023-01-20 03:05:06 +00:00
|
|
|
}
|
|
|
|
|
2023-03-16 14:58:26 +00:00
|
|
|
#[instrument(level = "debug", skip(self))]
|
2023-01-20 03:05:06 +00:00
|
|
|
fn compute_closure_kind_goal(
|
|
|
|
&mut self,
|
2023-01-20 18:38:33 +00:00
|
|
|
goal: Goal<'tcx, (DefId, ty::SubstsRef<'tcx>, ty::ClosureKind)>,
|
2023-01-20 03:05:06 +00:00
|
|
|
) -> QueryResult<'tcx> {
|
2023-01-20 18:38:33 +00:00
|
|
|
let (_, substs, expected_kind) = goal.predicate;
|
|
|
|
let found_kind = substs.as_closure().kind_ty().to_opt_closure_kind();
|
|
|
|
|
2023-01-20 03:05:06 +00:00
|
|
|
let Some(found_kind) = found_kind else {
|
2023-03-16 14:58:26 +00:00
|
|
|
return self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
|
2023-01-20 03:05:06 +00:00
|
|
|
};
|
|
|
|
if found_kind.extends(expected_kind) {
|
2023-03-16 14:58:26 +00:00
|
|
|
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
2023-01-20 03:05:06 +00:00
|
|
|
} else {
|
|
|
|
Err(NoSolution)
|
|
|
|
}
|
|
|
|
}
|
2023-01-24 18:48:15 +00:00
|
|
|
|
2023-03-16 14:58:26 +00:00
|
|
|
#[instrument(level = "debug", skip(self))]
|
2023-01-24 18:48:15 +00:00
|
|
|
fn compute_object_safe_goal(&mut self, trait_def_id: DefId) -> QueryResult<'tcx> {
|
2023-01-28 15:07:21 +00:00
|
|
|
if self.tcx().check_is_object_safe(trait_def_id) {
|
2023-03-16 14:58:26 +00:00
|
|
|
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
2023-01-24 18:48:15 +00:00
|
|
|
} else {
|
|
|
|
Err(NoSolution)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-16 14:58:26 +00:00
|
|
|
#[instrument(level = "debug", skip(self))]
|
2023-01-24 18:48:15 +00:00
|
|
|
fn compute_well_formed_goal(
|
|
|
|
&mut self,
|
|
|
|
goal: Goal<'tcx, ty::GenericArg<'tcx>>,
|
|
|
|
) -> QueryResult<'tcx> {
|
2023-01-27 04:00:37 +00:00
|
|
|
match crate::traits::wf::unnormalized_obligations(
|
|
|
|
self.infcx,
|
|
|
|
goal.param_env,
|
|
|
|
goal.predicate,
|
|
|
|
) {
|
2023-03-16 14:58:26 +00:00
|
|
|
Some(obligations) => {
|
|
|
|
self.add_goals(obligations.into_iter().map(|o| o.into()));
|
|
|
|
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
|
|
}
|
|
|
|
None => self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS),
|
2023-01-27 04:00:37 +00:00
|
|
|
}
|
2023-01-24 18:48:15 +00:00
|
|
|
}
|
2023-02-10 14:54:50 +00:00
|
|
|
|
|
|
|
#[instrument(level = "debug", skip(self), ret)]
|
2023-03-21 22:11:40 +00:00
|
|
|
fn compute_alias_relate_goal(
|
2023-02-10 14:54:50 +00:00
|
|
|
&mut self,
|
2023-03-21 21:50:16 +00:00
|
|
|
goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>,
|
2023-02-10 14:54:50 +00:00
|
|
|
) -> QueryResult<'tcx> {
|
|
|
|
let tcx = self.tcx();
|
|
|
|
|
2023-03-21 21:50:16 +00:00
|
|
|
let evaluate_normalizes_to = |ecx: &mut EvalCtxt<'_, 'tcx>, alias, other, direction| {
|
2023-02-10 14:54:50 +00:00
|
|
|
debug!("evaluate_normalizes_to(alias={:?}, other={:?})", alias, other);
|
2023-03-21 21:50:16 +00:00
|
|
|
let result = ecx.probe(|ecx| {
|
|
|
|
let other = match direction {
|
|
|
|
// This is purely an optimization.
|
|
|
|
ty::AliasRelationDirection::Equate => other,
|
|
|
|
|
|
|
|
ty::AliasRelationDirection::Subtype | ty::AliasRelationDirection::Supertype => {
|
|
|
|
let fresh = ecx.next_term_infer_of_kind(other);
|
|
|
|
let (sub, sup) = if direction == ty::AliasRelationDirection::Subtype {
|
|
|
|
(fresh, other)
|
|
|
|
} else {
|
|
|
|
(other, fresh)
|
|
|
|
};
|
|
|
|
ecx.add_goals(
|
|
|
|
ecx.infcx
|
|
|
|
.at(&ObligationCause::dummy(), goal.param_env)
|
|
|
|
.sub(DefineOpaqueTypes::No, sub, sup)?
|
|
|
|
.into_obligations()
|
|
|
|
.into_iter()
|
|
|
|
.map(|o| o.into()),
|
|
|
|
);
|
|
|
|
fresh
|
|
|
|
}
|
|
|
|
};
|
2023-03-16 14:58:26 +00:00
|
|
|
ecx.add_goal(goal.with(
|
2023-02-10 14:54:50 +00:00
|
|
|
tcx,
|
|
|
|
ty::Binder::dummy(ty::ProjectionPredicate {
|
|
|
|
projection_ty: alias,
|
|
|
|
term: other,
|
|
|
|
}),
|
2023-03-16 14:58:26 +00:00
|
|
|
));
|
|
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
2023-02-10 14:54:50 +00:00
|
|
|
});
|
2023-03-21 21:50:16 +00:00
|
|
|
debug!("evaluate_normalizes_to({alias}, {other}, {direction:?}) -> {result:?}");
|
|
|
|
result
|
2023-02-10 14:54:50 +00:00
|
|
|
};
|
|
|
|
|
2023-03-21 21:50:16 +00:00
|
|
|
let (lhs, rhs, direction) = goal.predicate;
|
|
|
|
|
|
|
|
if lhs.is_infer() || rhs.is_infer() {
|
2023-02-10 14:54:50 +00:00
|
|
|
bug!(
|
2023-03-21 22:11:40 +00:00
|
|
|
"`AliasRelate` goal with an infer var on lhs or rhs which should have been instantiated"
|
2023-02-10 14:54:50 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-03-21 21:50:16 +00:00
|
|
|
match (lhs.to_projection_term(tcx), rhs.to_projection_term(tcx)) {
|
2023-03-21 22:11:40 +00:00
|
|
|
(None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"),
|
2023-03-21 21:50:16 +00:00
|
|
|
|
|
|
|
// RHS is not a projection, only way this is true is if LHS normalizes-to RHS
|
|
|
|
(Some(alias_lhs), None) => evaluate_normalizes_to(self, alias_lhs, rhs, direction),
|
|
|
|
|
|
|
|
// LHS is not a projection, only way this is true is if RHS normalizes-to LHS
|
|
|
|
(None, Some(alias_rhs)) => {
|
|
|
|
evaluate_normalizes_to(self, alias_rhs, lhs, direction.invert())
|
|
|
|
}
|
|
|
|
|
2023-02-10 14:54:50 +00:00
|
|
|
(Some(alias_lhs), Some(alias_rhs)) => {
|
2023-03-21 22:11:40 +00:00
|
|
|
debug!("compute_alias_relate_goal: both sides are aliases");
|
2023-02-10 14:54:50 +00:00
|
|
|
|
2023-03-21 21:50:16 +00:00
|
|
|
let candidates = vec![
|
|
|
|
// LHS normalizes-to RHS
|
|
|
|
evaluate_normalizes_to(self, alias_lhs, rhs, direction),
|
|
|
|
// RHS normalizes-to RHS
|
|
|
|
evaluate_normalizes_to(self, alias_rhs, lhs, direction.invert()),
|
|
|
|
// Relate via substs
|
|
|
|
self.probe(|ecx| {
|
2023-03-21 22:11:40 +00:00
|
|
|
debug!(
|
|
|
|
"compute_alias_relate_goal: alias defids are equal, equating substs"
|
|
|
|
);
|
2023-02-10 14:54:50 +00:00
|
|
|
|
2023-03-21 21:50:16 +00:00
|
|
|
ecx.add_goals(
|
|
|
|
match direction {
|
|
|
|
ty::AliasRelationDirection::Equate => ecx
|
|
|
|
.infcx
|
|
|
|
.at(&ObligationCause::dummy(), goal.param_env)
|
|
|
|
.eq(DefineOpaqueTypes::No, alias_lhs, alias_rhs),
|
|
|
|
ty::AliasRelationDirection::Subtype => ecx
|
|
|
|
.infcx
|
|
|
|
.at(&ObligationCause::dummy(), goal.param_env)
|
|
|
|
.sub(DefineOpaqueTypes::No, alias_lhs, alias_rhs),
|
|
|
|
ty::AliasRelationDirection::Supertype => ecx
|
|
|
|
.infcx
|
|
|
|
.at(&ObligationCause::dummy(), goal.param_env)
|
|
|
|
.sup(DefineOpaqueTypes::No, alias_lhs, alias_rhs),
|
|
|
|
}?
|
|
|
|
.into_obligations()
|
|
|
|
.into_iter()
|
|
|
|
.map(|o| o.into()),
|
|
|
|
);
|
2023-02-10 14:54:50 +00:00
|
|
|
|
2023-03-21 21:50:16 +00:00
|
|
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
|
|
}),
|
|
|
|
];
|
2023-02-10 14:54:50 +00:00
|
|
|
debug!(?candidates);
|
|
|
|
|
|
|
|
self.try_merge_responses(candidates.into_iter())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-02-17 09:32:33 +00:00
|
|
|
|
|
|
|
#[instrument(level = "debug", skip(self), ret)]
|
|
|
|
fn compute_const_arg_has_type_goal(
|
|
|
|
&mut self,
|
|
|
|
goal: Goal<'tcx, (ty::Const<'tcx>, Ty<'tcx>)>,
|
|
|
|
) -> QueryResult<'tcx> {
|
|
|
|
let (ct, ty) = goal.predicate;
|
2023-03-16 14:58:26 +00:00
|
|
|
self.eq(goal.param_env, ct.ty(), ty)?;
|
|
|
|
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
2023-02-17 09:32:33 +00:00
|
|
|
}
|
2022-12-04 03:19:10 +00:00
|
|
|
}
|
|
|
|
|
2023-01-17 10:21:30 +01:00
|
|
|
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
2023-03-16 14:58:26 +00:00
|
|
|
#[instrument(level = "debug", skip(self))]
|
2023-03-17 14:04:39 +00:00
|
|
|
fn set_normalizes_to_hack_goal(&mut self, goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>) {
|
2023-03-16 14:58:26 +00:00
|
|
|
assert!(
|
2023-03-17 14:04:39 +00:00
|
|
|
self.nested_goals.normalizes_to_hack_goal.is_none(),
|
2023-03-16 14:58:26 +00:00
|
|
|
"attempted to set the projection eq hack goal when one already exists"
|
|
|
|
);
|
2023-03-17 14:04:39 +00:00
|
|
|
self.nested_goals.normalizes_to_hack_goal = Some(goal);
|
2023-03-16 14:58:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[instrument(level = "debug", skip(self))]
|
|
|
|
fn add_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) {
|
|
|
|
self.nested_goals.goals.push(goal);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[instrument(level = "debug", skip(self, goals))]
|
|
|
|
fn add_goals(&mut self, goals: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>) {
|
|
|
|
let current_len = self.nested_goals.goals.len();
|
|
|
|
self.nested_goals.goals.extend(goals);
|
|
|
|
debug!("added_goals={:?}", &self.nested_goals.goals[current_len..]);
|
|
|
|
}
|
|
|
|
|
2023-02-10 14:54:50 +00:00
|
|
|
fn try_merge_responses(
|
|
|
|
&mut self,
|
|
|
|
responses: impl Iterator<Item = QueryResult<'tcx>>,
|
|
|
|
) -> QueryResult<'tcx> {
|
|
|
|
let candidates = responses.into_iter().flatten().collect::<Box<[_]>>();
|
|
|
|
|
|
|
|
if candidates.is_empty() {
|
|
|
|
return Err(NoSolution);
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME(-Ztreat-solver=next): We should instead try to find a `Certainty::Yes` response with
|
|
|
|
// a subset of the constraints that all the other responses have.
|
|
|
|
let one = candidates[0];
|
|
|
|
if candidates[1..].iter().all(|resp| resp == &one) {
|
|
|
|
return Ok(one);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(response) = candidates.iter().find(|response| {
|
|
|
|
response.value.certainty == Certainty::Yes
|
|
|
|
&& response.has_no_inference_or_external_constraints()
|
|
|
|
}) {
|
2023-02-15 23:18:40 +01:00
|
|
|
return Ok(*response);
|
2023-02-10 14:54:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let certainty = candidates.iter().fold(Certainty::AMBIGUOUS, |certainty, response| {
|
|
|
|
certainty.unify_and(response.value.certainty)
|
|
|
|
});
|
|
|
|
// FIXME(-Ztrait-solver=next): We should take the intersection of the constraints on all the
|
|
|
|
// responses and use that for the constraints of this ambiguous response.
|
2023-03-16 14:58:26 +00:00
|
|
|
let response = self.evaluate_added_goals_and_make_canonical_response(certainty);
|
2023-02-10 14:54:50 +00:00
|
|
|
if let Ok(response) = &response {
|
|
|
|
assert!(response.has_no_inference_or_external_constraints());
|
|
|
|
}
|
|
|
|
|
|
|
|
response
|
|
|
|
}
|
2022-12-04 03:19:10 +00:00
|
|
|
}
|
|
|
|
|
2023-01-17 10:21:30 +01:00
|
|
|
pub(super) fn response_no_constraints<'tcx>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
goal: Canonical<'tcx, impl Sized>,
|
|
|
|
certainty: Certainty,
|
|
|
|
) -> QueryResult<'tcx> {
|
|
|
|
Ok(Canonical {
|
|
|
|
max_universe: goal.max_universe,
|
|
|
|
variables: goal.variables,
|
|
|
|
value: Response {
|
2023-01-26 20:33:34 +00:00
|
|
|
var_values: CanonicalVarValues::make_identity(tcx, goal.variables),
|
2023-02-03 02:29:52 +00:00
|
|
|
// FIXME: maybe we should store the "no response" version in tcx, like
|
|
|
|
// we do for tcx.types and stuff.
|
2023-02-17 14:33:08 +11:00
|
|
|
external_constraints: tcx.mk_external_constraints(ExternalConstraintsData::default()),
|
2023-01-17 10:21:30 +01:00
|
|
|
certainty,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|