Pull out logic from #111131, plus some new logic in EvalCtxt::normalize_opaque_type
Co-authored-by: lcnr <rust@lcnr.de>
This commit is contained in:
parent
7664dfe433
commit
4d80b8090c
@ -113,10 +113,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||||||
bug!()
|
bug!()
|
||||||
}
|
}
|
||||||
|
|
||||||
(_, ty::Alias(AliasKind::Projection | AliasKind::Inherent, _))
|
(_, ty::Alias(..)) | (ty::Alias(..), _) if self.tcx.trait_solver_next() => {
|
||||||
| (ty::Alias(AliasKind::Projection | AliasKind::Inherent, _), _)
|
|
||||||
if self.tcx.trait_solver_next() =>
|
|
||||||
{
|
|
||||||
relation.register_type_relate_obligation(a, b);
|
relation.register_type_relate_obligation(a, b);
|
||||||
Ok(a)
|
Ok(a)
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,8 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> {
|
|||||||
(&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
|
(&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
|
||||||
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
|
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
|
||||||
if self.fields.define_opaque_types == DefineOpaqueTypes::Yes
|
if self.fields.define_opaque_types == DefineOpaqueTypes::Yes
|
||||||
&& def_id.is_local() =>
|
&& def_id.is_local()
|
||||||
|
&& !self.tcx().trait_solver_next() =>
|
||||||
{
|
{
|
||||||
self.fields.obligations.extend(
|
self.fields.obligations.extend(
|
||||||
infcx
|
infcx
|
||||||
|
@ -108,9 +108,12 @@ where
|
|||||||
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
|
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
|
||||||
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
|
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
|
||||||
) if a_def_id == b_def_id => infcx.super_combine_tys(this, a, b),
|
) if a_def_id == b_def_id => infcx.super_combine_tys(this, a, b),
|
||||||
|
|
||||||
(&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
|
(&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
|
||||||
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
|
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
|
||||||
if this.define_opaque_types() == DefineOpaqueTypes::Yes && def_id.is_local() =>
|
if this.define_opaque_types() == DefineOpaqueTypes::Yes
|
||||||
|
&& def_id.is_local()
|
||||||
|
&& !this.tcx().trait_solver_next() =>
|
||||||
{
|
{
|
||||||
this.register_obligations(
|
this.register_obligations(
|
||||||
infcx
|
infcx
|
||||||
|
@ -491,16 +491,22 @@ where
|
|||||||
(
|
(
|
||||||
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
|
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
|
||||||
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
|
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
|
||||||
) if a_def_id == b_def_id => infcx.super_combine_tys(self, a, b).or_else(|err| {
|
) if a_def_id == b_def_id || infcx.tcx.trait_solver_next() => {
|
||||||
self.tcx().sess.delay_span_bug(
|
infcx.super_combine_tys(self, a, b).or_else(|err| {
|
||||||
self.delegate.span(),
|
// This behavior is only there for the old solver, the new solver
|
||||||
"failure to relate an opaque to itself should result in an error later on",
|
// shouldn't ever fail. Instead, it unconditionally emits an
|
||||||
);
|
// alias-relate goal.
|
||||||
if a_def_id.is_local() { self.relate_opaques(a, b) } else { Err(err) }
|
assert!(!self.tcx().trait_solver_next());
|
||||||
}),
|
self.tcx().sess.delay_span_bug(
|
||||||
|
self.delegate.span(),
|
||||||
|
"failure to relate an opaque to itself should result in an error later on",
|
||||||
|
);
|
||||||
|
if a_def_id.is_local() { self.relate_opaques(a, b) } else { Err(err) }
|
||||||
|
})
|
||||||
|
}
|
||||||
(&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
|
(&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
|
||||||
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
|
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
|
||||||
if def_id.is_local() =>
|
if def_id.is_local() && !self.tcx().trait_solver_next() =>
|
||||||
{
|
{
|
||||||
self.relate_opaques(a, b)
|
self.relate_opaques(a, b)
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
|||||||
use super::{DefineOpaqueTypes, InferResult};
|
use super::{DefineOpaqueTypes, InferResult};
|
||||||
use crate::errors::OpaqueHiddenTypeDiag;
|
use crate::errors::OpaqueHiddenTypeDiag;
|
||||||
use crate::infer::{DefiningAnchor, InferCtxt, InferOk};
|
use crate::infer::{DefiningAnchor, InferCtxt, InferOk};
|
||||||
use crate::traits;
|
use crate::traits::{self, PredicateObligation};
|
||||||
use hir::def_id::{DefId, LocalDefId};
|
use hir::def_id::{DefId, LocalDefId};
|
||||||
use hir::OpaqueTyOrigin;
|
use hir::OpaqueTyOrigin;
|
||||||
use rustc_data_structures::fx::FxIndexMap;
|
use rustc_data_structures::fx::FxIndexMap;
|
||||||
@ -48,9 +48,15 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||||||
span: Span,
|
span: Span,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
) -> InferOk<'tcx, T> {
|
) -> InferOk<'tcx, T> {
|
||||||
|
// We handle opaque types differently in the new solver.
|
||||||
|
if self.tcx.trait_solver_next() {
|
||||||
|
return InferOk { value, obligations: vec![] };
|
||||||
|
}
|
||||||
|
|
||||||
if !value.has_opaque_types() {
|
if !value.has_opaque_types() {
|
||||||
return InferOk { value, obligations: vec![] };
|
return InferOk { value, obligations: vec![] };
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut obligations = vec![];
|
let mut obligations = vec![];
|
||||||
let replace_opaque_type = |def_id: DefId| {
|
let replace_opaque_type = |def_id: DefId| {
|
||||||
def_id.as_local().is_some_and(|def_id| self.opaque_type_origin(def_id).is_some())
|
def_id.as_local().is_some_and(|def_id| self.opaque_type_origin(def_id).is_some())
|
||||||
@ -521,9 +527,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||||||
origin: hir::OpaqueTyOrigin,
|
origin: hir::OpaqueTyOrigin,
|
||||||
a_is_expected: bool,
|
a_is_expected: bool,
|
||||||
) -> InferResult<'tcx, ()> {
|
) -> InferResult<'tcx, ()> {
|
||||||
let tcx = self.tcx;
|
|
||||||
let OpaqueTypeKey { def_id, substs } = opaque_type_key;
|
|
||||||
|
|
||||||
// Ideally, we'd get the span where *this specific `ty` came
|
// Ideally, we'd get the span where *this specific `ty` came
|
||||||
// from*, but right now we just use the span from the overall
|
// from*, but right now we just use the span from the overall
|
||||||
// value being folded. In simple cases like `-> impl Foo`,
|
// value being folded. In simple cases like `-> impl Foo`,
|
||||||
@ -531,7 +534,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||||||
// Foo, impl Bar)`.
|
// Foo, impl Bar)`.
|
||||||
let span = cause.span;
|
let span = cause.span;
|
||||||
let prev = self.inner.borrow_mut().opaque_types().register(
|
let prev = self.inner.borrow_mut().opaque_types().register(
|
||||||
OpaqueTypeKey { def_id, substs },
|
opaque_type_key,
|
||||||
OpaqueHiddenType { ty: hidden_ty, span },
|
OpaqueHiddenType { ty: hidden_ty, span },
|
||||||
origin,
|
origin,
|
||||||
);
|
);
|
||||||
@ -543,6 +546,26 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||||||
Vec::new()
|
Vec::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.add_item_bounds_for_hidden_type(
|
||||||
|
opaque_type_key,
|
||||||
|
cause,
|
||||||
|
param_env,
|
||||||
|
hidden_ty,
|
||||||
|
&mut obligations,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(InferOk { value: (), obligations })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_item_bounds_for_hidden_type(
|
||||||
|
&self,
|
||||||
|
OpaqueTypeKey { def_id, substs }: OpaqueTypeKey<'tcx>,
|
||||||
|
cause: ObligationCause<'tcx>,
|
||||||
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
hidden_ty: Ty<'tcx>,
|
||||||
|
obligations: &mut Vec<PredicateObligation<'tcx>>,
|
||||||
|
) {
|
||||||
|
let tcx = self.tcx;
|
||||||
let item_bounds = tcx.explicit_item_bounds(def_id);
|
let item_bounds = tcx.explicit_item_bounds(def_id);
|
||||||
|
|
||||||
for (predicate, _) in item_bounds.subst_iter_copied(tcx, substs) {
|
for (predicate, _) in item_bounds.subst_iter_copied(tcx, substs) {
|
||||||
@ -555,14 +578,15 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||||||
// FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too.
|
// FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too.
|
||||||
ty::Alias(ty::Projection, projection_ty)
|
ty::Alias(ty::Projection, projection_ty)
|
||||||
if !projection_ty.has_escaping_bound_vars()
|
if !projection_ty.has_escaping_bound_vars()
|
||||||
&& !tcx.is_impl_trait_in_trait(projection_ty.def_id) =>
|
&& !tcx.is_impl_trait_in_trait(projection_ty.def_id)
|
||||||
|
&& !tcx.trait_solver_next() =>
|
||||||
{
|
{
|
||||||
self.infer_projection(
|
self.infer_projection(
|
||||||
param_env,
|
param_env,
|
||||||
projection_ty,
|
projection_ty,
|
||||||
cause.clone(),
|
cause.clone(),
|
||||||
0,
|
0,
|
||||||
&mut obligations,
|
obligations,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Replace all other mentions of the same opaque type with the hidden type,
|
// Replace all other mentions of the same opaque type with the hidden type,
|
||||||
@ -588,10 +612,10 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||||||
predicate.kind().skip_binder()
|
predicate.kind().skip_binder()
|
||||||
{
|
{
|
||||||
if projection.term.references_error() {
|
if projection.term.references_error() {
|
||||||
// No point on adding these obligations since there's a type error involved.
|
// No point on adding any obligations since there's a type error involved.
|
||||||
return Ok(InferOk { value: (), obligations: vec![] });
|
obligations.clear();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
trace!("{:#?}", projection.term);
|
|
||||||
}
|
}
|
||||||
// Require that the predicate holds for the concrete type.
|
// Require that the predicate holds for the concrete type.
|
||||||
debug!(?predicate);
|
debug!(?predicate);
|
||||||
@ -602,7 +626,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||||||
predicate,
|
predicate,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Ok(InferOk { value: (), obligations })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +131,8 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> {
|
|||||||
(&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
|
(&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
|
||||||
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
|
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
|
||||||
if self.fields.define_opaque_types == DefineOpaqueTypes::Yes
|
if self.fields.define_opaque_types == DefineOpaqueTypes::Yes
|
||||||
&& def_id.is_local() =>
|
&& def_id.is_local()
|
||||||
|
&& !self.tcx().trait_solver_next() =>
|
||||||
{
|
{
|
||||||
self.fields.obligations.extend(
|
self.fields.obligations.extend(
|
||||||
infcx
|
infcx
|
||||||
|
@ -996,17 +996,11 @@ impl<'tcx> Term<'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function returns the inner `AliasTy` if this term is a projection.
|
/// This function returns the inner `AliasTy` for a `ty::Alias` or `ConstKind::Unevaluated`.
|
||||||
///
|
pub fn to_alias_ty(&self, tcx: TyCtxt<'tcx>) -> Option<AliasTy<'tcx>> {
|
||||||
/// FIXME: rename `AliasTy` to `AliasTerm` and make sure we correctly
|
|
||||||
/// deal with constants.
|
|
||||||
pub fn to_projection_term(&self, tcx: TyCtxt<'tcx>) -> Option<AliasTy<'tcx>> {
|
|
||||||
match self.unpack() {
|
match self.unpack() {
|
||||||
TermKind::Ty(ty) => match ty.kind() {
|
TermKind::Ty(ty) => match *ty.kind() {
|
||||||
ty::Alias(kind, alias_ty) => match kind {
|
ty::Alias(_kind, alias_ty) => Some(alias_ty),
|
||||||
AliasKind::Projection | AliasKind::Inherent => Some(*alias_ty),
|
|
||||||
AliasKind::Opaque => None,
|
|
||||||
},
|
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
TermKind::Const(ct) => match ct.kind() {
|
TermKind::Const(ct) => match ct.kind() {
|
||||||
|
@ -133,12 +133,14 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
|
|||||||
| ty::PredicateKind::ObjectSafe(_)
|
| ty::PredicateKind::ObjectSafe(_)
|
||||||
| ty::PredicateKind::ClosureKind(_, _, _)
|
| ty::PredicateKind::ClosureKind(_, _, _)
|
||||||
| ty::PredicateKind::ConstEvaluatable(_)
|
| ty::PredicateKind::ConstEvaluatable(_)
|
||||||
| ty::PredicateKind::TypeWellFormedFromEnv(_)
|
|
||||||
| ty::PredicateKind::Ambiguous => {
|
| ty::PredicateKind::Ambiguous => {
|
||||||
FulfillmentErrorCode::CodeSelectionError(
|
FulfillmentErrorCode::CodeSelectionError(
|
||||||
SelectionError::Unimplemented,
|
SelectionError::Unimplemented,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
ty::PredicateKind::TypeWellFormedFromEnv(_) => {
|
||||||
|
bug!("unexpected goal: {goal:?}")
|
||||||
|
}
|
||||||
},
|
},
|
||||||
root_obligation: obligation,
|
root_obligation: obligation,
|
||||||
});
|
});
|
||||||
|
@ -24,6 +24,7 @@ mod assembly;
|
|||||||
mod canonicalize;
|
mod canonicalize;
|
||||||
mod eval_ctxt;
|
mod eval_ctxt;
|
||||||
mod fulfill;
|
mod fulfill;
|
||||||
|
mod opaques;
|
||||||
mod project_goals;
|
mod project_goals;
|
||||||
mod search_graph;
|
mod search_graph;
|
||||||
mod trait_goals;
|
mod trait_goals;
|
||||||
@ -212,7 +213,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
match (lhs.to_projection_term(tcx), rhs.to_projection_term(tcx)) {
|
match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) {
|
||||||
(None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"),
|
(None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"),
|
||||||
|
|
||||||
// RHS is not a projection, only way this is true is if LHS normalizes-to RHS
|
// RHS is not a projection, only way this is true is if LHS normalizes-to RHS
|
||||||
|
44
compiler/rustc_trait_selection/src/solve/opaques.rs
Normal file
44
compiler/rustc_trait_selection/src/solve/opaques.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
use rustc_infer::infer::InferOk;
|
||||||
|
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
|
||||||
|
use rustc_middle::traits::{ObligationCause, Reveal};
|
||||||
|
use rustc_middle::ty::ProjectionPredicate;
|
||||||
|
|
||||||
|
use super::{EvalCtxt, SolverMode};
|
||||||
|
|
||||||
|
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
|
pub(super) fn normalize_opaque_type(
|
||||||
|
&mut self,
|
||||||
|
goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
|
||||||
|
) -> QueryResult<'tcx> {
|
||||||
|
let tcx = self.tcx();
|
||||||
|
let opaque_ty = goal.predicate.projection_ty;
|
||||||
|
let expected = goal.predicate.term.ty().expect("no such thing as an opaque const");
|
||||||
|
|
||||||
|
match goal.param_env.reveal() {
|
||||||
|
Reveal::UserFacing => match self.solver_mode() {
|
||||||
|
SolverMode::Normal => self.probe(|ecx| {
|
||||||
|
// FIXME: Check that the usage is "defining" (all free params), otherwise bail.
|
||||||
|
// FIXME: This should probably just check the anchor directly
|
||||||
|
let InferOk { value: (), obligations } = self.infcx.handle_opaque_type(
|
||||||
|
expected,
|
||||||
|
tcx.mk_opaque(opaque_ty.def_id, opaque_ty.substs),
|
||||||
|
true,
|
||||||
|
&ObligationCause::dummy(),
|
||||||
|
goal.param_env,
|
||||||
|
)?;
|
||||||
|
// FIXME: Need to fold these to replace the opaque ty with the expected ty.
|
||||||
|
ecx.add_goals(obligations.into_iter().map(Into::into));
|
||||||
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
|
}),
|
||||||
|
SolverMode::Coherence => {
|
||||||
|
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Reveal::All => self.probe(|ecx| {
|
||||||
|
let actual = tcx.type_of(opaque_ty.def_id).subst(tcx, opaque_ty.substs);
|
||||||
|
ecx.eq(goal.param_env, expected, actual)?;
|
||||||
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -30,8 +30,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||||||
// `U` and equate it with `u32`. This means that we don't need a separate
|
// `U` and equate it with `u32`. This means that we don't need a separate
|
||||||
// projection cache in the solver.
|
// projection cache in the solver.
|
||||||
if self.term_is_fully_unconstrained(goal) {
|
if self.term_is_fully_unconstrained(goal) {
|
||||||
let candidates = self.assemble_and_evaluate_candidates(goal);
|
match goal.predicate.projection_ty.kind(self.tcx()) {
|
||||||
self.merge_candidates(candidates)
|
ty::AliasKind::Projection => {
|
||||||
|
let candidates = self.assemble_and_evaluate_candidates(goal);
|
||||||
|
self.merge_candidates(candidates)
|
||||||
|
}
|
||||||
|
ty::AliasKind::Opaque => self.normalize_opaque_type(goal),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.set_normalizes_to_hack_goal(goal);
|
self.set_normalizes_to_hack_goal(goal);
|
||||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
|
@ -834,8 +834,10 @@ impl<'tcx> AutoTraitFinder<'tcx> {
|
|||||||
| ty::PredicateKind::Subtype(..)
|
| ty::PredicateKind::Subtype(..)
|
||||||
// FIXME(generic_const_exprs): you can absolutely add this as a where clauses
|
// FIXME(generic_const_exprs): you can absolutely add this as a where clauses
|
||||||
| ty::PredicateKind::ConstEvaluatable(..)
|
| ty::PredicateKind::ConstEvaluatable(..)
|
||||||
| ty::PredicateKind::Coerce(..)
|
| ty::PredicateKind::Coerce(..) => {}
|
||||||
| ty::PredicateKind::TypeWellFormedFromEnv(..) => {}
|
ty::PredicateKind::TypeWellFormedFromEnv(..) => {
|
||||||
|
bug!("predicate should only exist in the environment: {bound_predicate:?}")
|
||||||
|
}
|
||||||
ty::PredicateKind::Ambiguous => return false,
|
ty::PredicateKind::Ambiguous => return false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -679,7 +679,7 @@ impl<'tcx> LowerInto<'tcx, Option<chalk_ir::QuantifiedWhereClause<RustInterner<'
|
|||||||
| ty::PredicateKind::ConstEquate(..)
|
| ty::PredicateKind::ConstEquate(..)
|
||||||
| ty::PredicateKind::Ambiguous
|
| ty::PredicateKind::Ambiguous
|
||||||
| ty::PredicateKind::TypeWellFormedFromEnv(..) => {
|
| ty::PredicateKind::TypeWellFormedFromEnv(..) => {
|
||||||
bug!("unexpected predicate {}", &self)
|
bug!("unexpected predicate {self}")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
value.map(|value| chalk_ir::Binders::new(binders, value))
|
value.map(|value| chalk_ir::Binders::new(binders, value))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user