introduce enter_forall
This commit is contained in:
@@ -971,7 +971,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> {
|
||||
|
||||
let Goal { param_env, predicate } = goal.goal();
|
||||
|
||||
// For bound predicates we simply call `infcx.instantiate_binder_with_placeholders`
|
||||
// For bound predicates we simply call `infcx.enter_forall`
|
||||
// and then prove the resulting predicate as a nested goal.
|
||||
let trait_ref = match predicate.kind().no_bound_vars() {
|
||||
Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(tr))) => tr.trait_ref,
|
||||
|
||||
@@ -21,51 +21,62 @@ pub fn recompute_applicable_impls<'tcx>(
|
||||
|
||||
let impl_may_apply = |impl_def_id| {
|
||||
let ocx = ObligationCtxt::new(infcx);
|
||||
let placeholder_obligation =
|
||||
infcx.instantiate_binder_with_placeholders(obligation.predicate);
|
||||
let obligation_trait_ref =
|
||||
ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref);
|
||||
infcx.enter_forall(obligation.predicate, |placeholder_obligation| {
|
||||
let obligation_trait_ref = ocx.normalize(
|
||||
&ObligationCause::dummy(),
|
||||
param_env,
|
||||
placeholder_obligation.trait_ref,
|
||||
);
|
||||
|
||||
let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);
|
||||
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(tcx, impl_args);
|
||||
let impl_trait_ref = ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref);
|
||||
let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);
|
||||
let impl_trait_ref =
|
||||
tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(tcx, impl_args);
|
||||
let impl_trait_ref =
|
||||
ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref);
|
||||
|
||||
if let Err(_) =
|
||||
ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, impl_trait_ref)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if let Err(_) =
|
||||
ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, impl_trait_ref)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
let impl_predicates = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_args);
|
||||
ocx.register_obligations(impl_predicates.predicates.iter().map(|&predicate| {
|
||||
Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate)
|
||||
}));
|
||||
let impl_predicates = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_args);
|
||||
ocx.register_obligations(impl_predicates.predicates.iter().map(|&predicate| {
|
||||
Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate)
|
||||
}));
|
||||
|
||||
ocx.select_where_possible().is_empty()
|
||||
ocx.select_where_possible().is_empty()
|
||||
})
|
||||
};
|
||||
|
||||
let param_env_candidate_may_apply = |poly_trait_predicate: ty::PolyTraitPredicate<'tcx>| {
|
||||
let ocx = ObligationCtxt::new(infcx);
|
||||
let placeholder_obligation =
|
||||
infcx.instantiate_binder_with_placeholders(obligation.predicate);
|
||||
let obligation_trait_ref =
|
||||
ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref);
|
||||
infcx.enter_forall(obligation.predicate, |placeholder_obligation| {
|
||||
let obligation_trait_ref = ocx.normalize(
|
||||
&ObligationCause::dummy(),
|
||||
param_env,
|
||||
placeholder_obligation.trait_ref,
|
||||
);
|
||||
|
||||
let param_env_predicate = infcx.instantiate_binder_with_fresh_vars(
|
||||
DUMMY_SP,
|
||||
BoundRegionConversionTime::HigherRankedType,
|
||||
poly_trait_predicate,
|
||||
);
|
||||
let param_env_trait_ref =
|
||||
ocx.normalize(&ObligationCause::dummy(), param_env, param_env_predicate.trait_ref);
|
||||
let param_env_predicate = infcx.instantiate_binder_with_fresh_vars(
|
||||
DUMMY_SP,
|
||||
BoundRegionConversionTime::HigherRankedType,
|
||||
poly_trait_predicate,
|
||||
);
|
||||
let param_env_trait_ref =
|
||||
ocx.normalize(&ObligationCause::dummy(), param_env, param_env_predicate.trait_ref);
|
||||
|
||||
if let Err(_) =
|
||||
ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, param_env_trait_ref)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if let Err(_) = ocx.eq(
|
||||
&ObligationCause::dummy(),
|
||||
param_env,
|
||||
obligation_trait_ref,
|
||||
param_env_trait_ref,
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ocx.select_where_possible().is_empty()
|
||||
ocx.select_where_possible().is_empty()
|
||||
})
|
||||
};
|
||||
|
||||
let mut ambiguities = Vec::new();
|
||||
|
||||
@@ -64,39 +64,44 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
) -> Option<(DefId, GenericArgsRef<'tcx>)> {
|
||||
let tcx = self.tcx;
|
||||
let param_env = obligation.param_env;
|
||||
let trait_ref = self.instantiate_binder_with_placeholders(trait_ref);
|
||||
let trait_self_ty = trait_ref.self_ty();
|
||||
self.enter_forall(trait_ref, |trait_ref| {
|
||||
let trait_self_ty = trait_ref.self_ty();
|
||||
|
||||
let mut self_match_impls = vec![];
|
||||
let mut fuzzy_match_impls = vec![];
|
||||
let mut self_match_impls = vec![];
|
||||
let mut fuzzy_match_impls = vec![];
|
||||
|
||||
self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| {
|
||||
let impl_args = self.fresh_args_for_item(obligation.cause.span, def_id);
|
||||
let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().instantiate(tcx, impl_args);
|
||||
self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| {
|
||||
let impl_args = self.fresh_args_for_item(obligation.cause.span, def_id);
|
||||
let impl_trait_ref =
|
||||
tcx.impl_trait_ref(def_id).unwrap().instantiate(tcx, impl_args);
|
||||
|
||||
let impl_self_ty = impl_trait_ref.self_ty();
|
||||
let impl_self_ty = impl_trait_ref.self_ty();
|
||||
|
||||
if self.can_eq(param_env, trait_self_ty, impl_self_ty) {
|
||||
self_match_impls.push((def_id, impl_args));
|
||||
if self.can_eq(param_env, trait_self_ty, impl_self_ty) {
|
||||
self_match_impls.push((def_id, impl_args));
|
||||
|
||||
if iter::zip(trait_ref.args.types().skip(1), impl_trait_ref.args.types().skip(1))
|
||||
if iter::zip(
|
||||
trait_ref.args.types().skip(1),
|
||||
impl_trait_ref.args.types().skip(1),
|
||||
)
|
||||
.all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some())
|
||||
{
|
||||
fuzzy_match_impls.push((def_id, impl_args));
|
||||
{
|
||||
fuzzy_match_impls.push((def_id, impl_args));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let impl_def_id_and_args = if self_match_impls.len() == 1 {
|
||||
self_match_impls[0]
|
||||
} else if fuzzy_match_impls.len() == 1 {
|
||||
fuzzy_match_impls[0]
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
let impl_def_id_and_args = if self_match_impls.len() == 1 {
|
||||
self_match_impls[0]
|
||||
} else if fuzzy_match_impls.len() == 1 {
|
||||
fuzzy_match_impls[0]
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
tcx.has_attr(impl_def_id_and_args.0, sym::rustc_on_unimplemented)
|
||||
.then_some(impl_def_id_and_args)
|
||||
tcx.has_attr(impl_def_id_and_args.0, sym::rustc_on_unimplemented)
|
||||
.then_some(impl_def_id_and_args)
|
||||
})
|
||||
}
|
||||
|
||||
/// Used to set on_unimplemented's `ItemContext`
|
||||
|
||||
@@ -1248,52 +1248,55 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
) -> bool {
|
||||
let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
|
||||
let ty = self.instantiate_binder_with_placeholders(self_ty);
|
||||
let Some(generics) = self.tcx.hir().get_generics(obligation.cause.body_id) else {
|
||||
return false;
|
||||
};
|
||||
let ty::Ref(_, inner_ty, hir::Mutability::Not) = ty.kind() else { return false };
|
||||
let ty::Param(param) = inner_ty.kind() else { return false };
|
||||
let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } =
|
||||
obligation.cause.code()
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
let arg_node = self.tcx.hir_node(*arg_hir_id);
|
||||
let Node::Expr(Expr { kind: hir::ExprKind::Path(_), .. }) = arg_node else { return false };
|
||||
self.enter_forall(self_ty, |ty: Ty<'_>| {
|
||||
let Some(generics) = self.tcx.hir().get_generics(obligation.cause.body_id) else {
|
||||
return false;
|
||||
};
|
||||
let ty::Ref(_, inner_ty, hir::Mutability::Not) = ty.kind() else { return false };
|
||||
let ty::Param(param) = inner_ty.kind() else { return false };
|
||||
let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } =
|
||||
obligation.cause.code()
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
let arg_node = self.tcx.hir_node(*arg_hir_id);
|
||||
let Node::Expr(Expr { kind: hir::ExprKind::Path(_), .. }) = arg_node else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let clone_trait = self.tcx.require_lang_item(LangItem::Clone, None);
|
||||
let has_clone = |ty| {
|
||||
self.type_implements_trait(clone_trait, [ty], obligation.param_env)
|
||||
.must_apply_modulo_regions()
|
||||
};
|
||||
let clone_trait = self.tcx.require_lang_item(LangItem::Clone, None);
|
||||
let has_clone = |ty| {
|
||||
self.type_implements_trait(clone_trait, [ty], obligation.param_env)
|
||||
.must_apply_modulo_regions()
|
||||
};
|
||||
|
||||
let new_obligation = self.mk_trait_obligation_with_new_self_ty(
|
||||
obligation.param_env,
|
||||
trait_pred.map_bound(|trait_pred| (trait_pred, *inner_ty)),
|
||||
);
|
||||
|
||||
if self.predicate_may_hold(&new_obligation) && has_clone(ty) {
|
||||
if !has_clone(param.to_ty(self.tcx)) {
|
||||
suggest_constraining_type_param(
|
||||
self.tcx,
|
||||
generics,
|
||||
err,
|
||||
param.name.as_str(),
|
||||
"Clone",
|
||||
Some(clone_trait),
|
||||
None,
|
||||
);
|
||||
}
|
||||
err.span_suggestion_verbose(
|
||||
obligation.cause.span.shrink_to_hi(),
|
||||
"consider using clone here",
|
||||
".clone()",
|
||||
Applicability::MaybeIncorrect,
|
||||
let new_obligation = self.mk_trait_obligation_with_new_self_ty(
|
||||
obligation.param_env,
|
||||
trait_pred.map_bound(|trait_pred| (trait_pred, *inner_ty)),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
false
|
||||
|
||||
if self.predicate_may_hold(&new_obligation) && has_clone(ty) {
|
||||
if !has_clone(param.to_ty(self.tcx)) {
|
||||
suggest_constraining_type_param(
|
||||
self.tcx,
|
||||
generics,
|
||||
err,
|
||||
param.name.as_str(),
|
||||
"Clone",
|
||||
Some(clone_trait),
|
||||
None,
|
||||
);
|
||||
}
|
||||
err.span_suggestion_verbose(
|
||||
obligation.cause.span.shrink_to_hi(),
|
||||
"consider using clone here",
|
||||
".clone()".to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
false
|
||||
})
|
||||
}
|
||||
|
||||
/// Extracts information about a callable type for diagnostics. This is a
|
||||
@@ -4038,26 +4041,27 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
if let Some(where_pred) = where_pred.as_trait_clause()
|
||||
&& let Some(failed_pred) = failed_pred.to_opt_poly_trait_pred()
|
||||
{
|
||||
let where_pred = self.instantiate_binder_with_placeholders(where_pred);
|
||||
let failed_pred = self.instantiate_binder_with_fresh_vars(
|
||||
expr.span,
|
||||
BoundRegionConversionTime::FnCall,
|
||||
failed_pred,
|
||||
);
|
||||
self.enter_forall(where_pred, |where_pred| {
|
||||
let failed_pred = self.instantiate_binder_with_fresh_vars(
|
||||
expr.span,
|
||||
BoundRegionConversionTime::FnCall,
|
||||
failed_pred,
|
||||
);
|
||||
|
||||
let zipped = iter::zip(where_pred.trait_ref.args, failed_pred.trait_ref.args);
|
||||
for (expected, actual) in zipped {
|
||||
self.probe(|_| {
|
||||
match self.at(&ObligationCause::misc(expr.span, body_id), param_env).eq(
|
||||
DefineOpaqueTypes::No,
|
||||
expected,
|
||||
actual,
|
||||
) {
|
||||
Ok(_) => (), // We ignore nested obligations here for now.
|
||||
Err(err) => type_diffs.push(err),
|
||||
}
|
||||
})
|
||||
}
|
||||
let zipped =
|
||||
iter::zip(where_pred.trait_ref.args, failed_pred.trait_ref.args);
|
||||
for (expected, actual) in zipped {
|
||||
self.probe(|_| {
|
||||
match self
|
||||
.at(&ObligationCause::misc(expr.span, body_id), param_env)
|
||||
.eq(DefineOpaqueTypes::No, expected, actual)
|
||||
{
|
||||
Ok(_) => (), // We ignore nested obligations here for now.
|
||||
Err(err) => type_diffs.push(err),
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
} else if let Some(where_pred) = where_pred.as_projection_clause()
|
||||
&& let Some(failed_pred) = failed_pred.to_opt_poly_projection_pred()
|
||||
&& let Some(found) = failed_pred.skip_binder().term.ty()
|
||||
@@ -4615,14 +4619,16 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
{
|
||||
self.probe(|_| {
|
||||
let ocx = ObligationCtxt::new(self);
|
||||
let pred = self.instantiate_binder_with_placeholders(pred);
|
||||
let pred = ocx.normalize(&ObligationCause::dummy(), param_env, pred);
|
||||
ocx.register_obligation(Obligation::new(
|
||||
self.tcx,
|
||||
ObligationCause::dummy(),
|
||||
param_env,
|
||||
pred,
|
||||
));
|
||||
self.enter_forall(pred, |pred| {
|
||||
let pred = ocx.normalize(&ObligationCause::dummy(), param_env, pred);
|
||||
// FIXME(tree_universes): universe leakage
|
||||
ocx.register_obligation(Obligation::new(
|
||||
self.tcx,
|
||||
ObligationCause::dummy(),
|
||||
param_env,
|
||||
pred,
|
||||
));
|
||||
});
|
||||
if !ocx.select_where_possible().is_empty() {
|
||||
// encountered errors.
|
||||
return;
|
||||
@@ -4769,13 +4775,13 @@ fn hint_missing_borrow<'tcx>(
|
||||
}
|
||||
|
||||
let found_args = match found.kind() {
|
||||
ty::FnPtr(f) => infcx.instantiate_binder_with_placeholders(*f).inputs().iter(),
|
||||
ty::FnPtr(f) => infcx.enter_forall(*f, |f| f.inputs().iter()),
|
||||
kind => {
|
||||
span_bug!(span, "found was converted to a FnPtr above but is now {:?}", kind)
|
||||
}
|
||||
};
|
||||
let expected_args = match expected.kind() {
|
||||
ty::FnPtr(f) => infcx.instantiate_binder_with_placeholders(*f).inputs().iter(),
|
||||
ty::FnPtr(f) => infcx.enter_forall(*f, |f| f.inputs().iter()),
|
||||
kind => {
|
||||
span_bug!(span, "expected was converted to a FnPtr above but is now {:?}", kind)
|
||||
}
|
||||
|
||||
@@ -1307,12 +1307,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
let mut pred = obligation.predicate.to_opt_poly_trait_pred();
|
||||
while let Some((next_code, next_pred)) = code.parent() {
|
||||
if let Some(pred) = pred {
|
||||
let pred = self.instantiate_binder_with_placeholders(pred);
|
||||
diag.note(format!(
|
||||
"`{}` must implement `{}`, but it does not",
|
||||
pred.self_ty(),
|
||||
pred.print_modifiers_and_trait_path()
|
||||
));
|
||||
self.enter_forall(pred, |pred| {
|
||||
diag.note(format!(
|
||||
"`{}` must implement `{}`, but it does not",
|
||||
pred.self_ty(),
|
||||
pred.print_modifiers_and_trait_path()
|
||||
));
|
||||
})
|
||||
}
|
||||
code = next_code;
|
||||
pred = next_pred;
|
||||
@@ -2017,70 +2018,79 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
if let [single] = &impl_candidates {
|
||||
if self.probe(|_| {
|
||||
let ocx = ObligationCtxt::new(self);
|
||||
let obligation_trait_ref = self.instantiate_binder_with_placeholders(trait_ref);
|
||||
let impl_args = self.fresh_args_for_item(DUMMY_SP, single.impl_def_id);
|
||||
let impl_trait_ref = ocx.normalize(
|
||||
&ObligationCause::dummy(),
|
||||
param_env,
|
||||
ty::EarlyBinder::bind(single.trait_ref).instantiate(self.tcx, impl_args),
|
||||
);
|
||||
|
||||
ocx.register_obligations(
|
||||
self.tcx
|
||||
.predicates_of(single.impl_def_id)
|
||||
.instantiate(self.tcx, impl_args)
|
||||
.into_iter()
|
||||
.map(|(clause, _)| {
|
||||
Obligation::new(self.tcx, ObligationCause::dummy(), param_env, clause)
|
||||
}),
|
||||
);
|
||||
if !ocx.select_where_possible().is_empty() {
|
||||
return false;
|
||||
}
|
||||
self.enter_forall(trait_ref, |obligation_trait_ref| {
|
||||
let impl_args = self.fresh_args_for_item(DUMMY_SP, single.impl_def_id);
|
||||
let impl_trait_ref = ocx.normalize(
|
||||
&ObligationCause::dummy(),
|
||||
param_env,
|
||||
ty::EarlyBinder::bind(single.trait_ref).instantiate(self.tcx, impl_args),
|
||||
);
|
||||
|
||||
let mut terrs = vec![];
|
||||
for (obligation_arg, impl_arg) in
|
||||
std::iter::zip(obligation_trait_ref.args, impl_trait_ref.args)
|
||||
{
|
||||
if let Err(terr) =
|
||||
ocx.eq(&ObligationCause::dummy(), param_env, impl_arg, obligation_arg)
|
||||
{
|
||||
terrs.push(terr);
|
||||
}
|
||||
ocx.register_obligations(
|
||||
self.tcx
|
||||
.predicates_of(single.impl_def_id)
|
||||
.instantiate(self.tcx, impl_args)
|
||||
.into_iter()
|
||||
.map(|(clause, _)| {
|
||||
Obligation::new(
|
||||
self.tcx,
|
||||
ObligationCause::dummy(),
|
||||
param_env,
|
||||
clause,
|
||||
)
|
||||
}),
|
||||
);
|
||||
if !ocx.select_where_possible().is_empty() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Literally nothing unified, just give up.
|
||||
if terrs.len() == impl_trait_ref.args.len() {
|
||||
return false;
|
||||
}
|
||||
let mut terrs = vec![];
|
||||
for (obligation_arg, impl_arg) in
|
||||
std::iter::zip(obligation_trait_ref.args, impl_trait_ref.args)
|
||||
{
|
||||
// FIXME(tree_universes): universe leakage
|
||||
if let Err(terr) =
|
||||
ocx.eq(&ObligationCause::dummy(), param_env, impl_arg, obligation_arg)
|
||||
{
|
||||
terrs.push(terr);
|
||||
}
|
||||
if !ocx.select_where_possible().is_empty() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
let cand =
|
||||
self.resolve_vars_if_possible(impl_trait_ref).fold_with(&mut BottomUpFolder {
|
||||
tcx: self.tcx,
|
||||
ty_op: |ty| ty,
|
||||
lt_op: |lt| lt,
|
||||
ct_op: |ct| ct.normalize(self.tcx, ty::ParamEnv::empty()),
|
||||
});
|
||||
err.highlighted_help(vec![
|
||||
StringPart::normal(format!("the trait `{}` ", cand.print_trait_sugared())),
|
||||
StringPart::highlighted("is"),
|
||||
StringPart::normal(" implemented for `"),
|
||||
StringPart::highlighted(cand.self_ty().to_string()),
|
||||
StringPart::normal("`"),
|
||||
]);
|
||||
// Literally nothing unified, just give up.
|
||||
if terrs.len() == impl_trait_ref.args.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let [TypeError::Sorts(exp_found)] = &terrs[..] {
|
||||
let exp_found = self.resolve_vars_if_possible(*exp_found);
|
||||
err.help(format!(
|
||||
"for that trait implementation, expected `{}`, found `{}`",
|
||||
exp_found.expected, exp_found.found
|
||||
));
|
||||
}
|
||||
let cand = self.resolve_vars_if_possible(impl_trait_ref).fold_with(
|
||||
&mut BottomUpFolder {
|
||||
tcx: self.tcx,
|
||||
ty_op: |ty| ty,
|
||||
lt_op: |lt| lt,
|
||||
ct_op: |ct| ct.normalize(self.tcx, ty::ParamEnv::empty()),
|
||||
},
|
||||
);
|
||||
err.highlighted_help(vec![
|
||||
StringPart::normal(format!("the trait `{}` ", cand.print_trait_sugared())),
|
||||
StringPart::highlighted("is"),
|
||||
StringPart::normal(" implemented for `"),
|
||||
StringPart::highlighted(cand.self_ty().to_string()),
|
||||
StringPart::normal("`"),
|
||||
]);
|
||||
|
||||
true
|
||||
if let [TypeError::Sorts(exp_found)] = &terrs[..] {
|
||||
let exp_found = self.resolve_vars_if_possible(*exp_found);
|
||||
err.help(format!(
|
||||
"for that trait implementation, expected `{}`, found `{}`",
|
||||
exp_found.expected, exp_found.found
|
||||
));
|
||||
}
|
||||
|
||||
true
|
||||
})
|
||||
}) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user