Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Method chain nitpicks #105985

Merged
merged 5 commits into from
Dec 22, 2022
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,27 @@ impl<'a, 'tcx> TypeRelation<'tcx> for CollectAllMismatches<'a, 'tcx> {
fn tag(&self) -> &'static str {
"CollectAllMismatches"
}

fn tcx(&self) -> TyCtxt<'tcx> {
self.infcx.tcx
}

fn intercrate(&self) -> bool {
false
}

fn param_env(&self) -> ty::ParamEnv<'tcx> {
self.param_env
}

fn a_is_expected(&self) -> bool {
true
} // irrelevant
}

fn mark_ambiguous(&mut self) {
bug!()
}

fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
_: ty::Variance,
Expand All @@ -38,22 +44,28 @@ impl<'a, 'tcx> TypeRelation<'tcx> for CollectAllMismatches<'a, 'tcx> {
) -> RelateResult<'tcx, T> {
self.relate(a, b)
}

fn regions(
&mut self,
a: ty::Region<'tcx>,
_b: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
Ok(a)
}

fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
if a == b || matches!(a.kind(), ty::Infer(_)) || matches!(b.kind(), ty::Infer(_)) {
return Ok(a);
}
relate::super_relate_tys(self, a, b).or_else(|e| {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code will treat int/float infer variables equal to non-int/floats, which is probably incorrect.

self.errors.push(e);
Ok(a)
self.infcx.probe(|_| {
if a.is_ty_infer() || b.is_ty_infer() {
Ok(a)
} else {
self.infcx.super_combine_tys(self, a, b).or_else(|e| {
self.errors.push(e);
Ok(a)
})
}
})
}

fn consts(
&mut self,
a: ty::Const<'tcx>,
Expand All @@ -64,6 +76,7 @@ impl<'a, 'tcx> TypeRelation<'tcx> for CollectAllMismatches<'a, 'tcx> {
}
relate::super_relate_consts(self, a, b) // could do something similar here for constants!
}

fn binders<T: Relate<'tcx>>(
&mut self,
a: ty::Binder<'tcx, T>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ pub trait TypeErrCtxtExt<'tcx> {
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
);
fn function_argument_obligation(
fn note_function_argument_obligation(
&self,
arg_hir_id: HirId,
err: &mut Diagnostic,
Expand Down Expand Up @@ -2909,7 +2909,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ref parent_code,
..
} => {
self.function_argument_obligation(
self.note_function_argument_obligation(
arg_hir_id,
err,
parent_code,
Expand Down Expand Up @@ -3141,23 +3141,20 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
);
}
}
fn function_argument_obligation(
fn note_function_argument_obligation(
&self,
arg_hir_id: HirId,
err: &mut Diagnostic,
parent_code: &ObligationCauseCode<'tcx>,
param_env: ty::ParamEnv<'tcx>,
predicate: ty::Predicate<'tcx>,
failed_pred: ty::Predicate<'tcx>,
call_hir_id: HirId,
) {
let tcx = self.tcx;
let hir = tcx.hir();
if let Some(Node::Expr(expr)) = hir.find(arg_hir_id) {
let parent_id = hir.get_parent_item(arg_hir_id);
let typeck_results: &TypeckResults<'tcx> = match &self.typeck_results {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very likely to cause a cycle if used incorrectly, since these function argument obligations are always going to come from the same body as we're checking.

Some(t) if t.hir_owner == parent_id => t,
_ => self.tcx.typeck(parent_id.def_id),
};
if let Some(Node::Expr(expr)) = hir.find(arg_hir_id)
&& let Some(typeck_results) = &self.typeck_results
{
if let hir::Expr { kind: hir::ExprKind::Block(..), .. } = expr {
let expr = expr.peel_blocks();
let ty = typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error());
Expand All @@ -3182,37 +3179,29 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut type_diffs = vec![];

if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = parent_code.deref()
&& let predicates = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instantiating identity substitutions will cause us to collect unnecesary type errors that wouldn't show up after substitution -- see the UI test that changed.

&& let Some(pred) = predicates.predicates.get(*idx)
&& let Some(node_substs) = typeck_results.node_substs_opt(call_hir_id)
&& let where_clauses = self.tcx.predicates_of(def_id).instantiate(self.tcx, node_substs)
&& let Some(where_pred) = where_clauses.predicates.get(*idx)
{
if let Ok(trait_pred) = pred.kind().try_map_bound(|pred| match pred {
ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) => Ok(trait_pred),
_ => Err(()),
})
&& let Ok(trait_predicate) = predicate.kind().try_map_bound(|pred| match pred {
ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) => Ok(trait_pred),
_ => Err(()),
})
if let Some(where_pred) = where_pred.to_opt_poly_trait_pred()
&& let Some(failed_pred) = failed_pred.to_opt_poly_trait_pred()
{
let mut c = CollectAllMismatches {
infcx: self.infcx,
param_env,
errors: vec![],
};
if let Ok(_) = c.relate(trait_pred, trait_predicate) {
if let Ok(_) = c.relate(where_pred, failed_pred) {
type_diffs = c.errors;
}
} else if let ty::PredicateKind::Clause(
ty::Clause::Projection(proj)
) = pred.kind().skip_binder()
&& let ty::PredicateKind::Clause(
ty::Clause::Projection(projection)
) = predicate.kind().skip_binder()
} else if let Some(where_pred) = where_pred.to_opt_poly_projection_pred()
&& let Some(failed_pred) = failed_pred.to_opt_poly_projection_pred()
&& let Some(found) = failed_pred.skip_binder().term.ty()
{
type_diffs = vec![
Sorts(ty::error::ExpectedFound {
expected: self.tcx.mk_ty(ty::Alias(ty::Projection, proj.projection_ty)),
found: projection.term.ty().unwrap(),
expected: self.tcx.mk_ty(ty::Alias(ty::Projection, where_pred.skip_binder().projection_ty)),
found,
}),
];
}
Expand All @@ -3227,9 +3216,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// If the expression we're calling on is a binding, we want to point at the
// `let` when talking about the type. Otherwise we'll point at every part
// of the method chain with the type.
self.point_at_chain(binding_expr, typeck_results, type_diffs, param_env, err);
self.point_at_chain(binding_expr, &typeck_results, type_diffs, param_env, err);
} else {
self.point_at_chain(expr, typeck_results, type_diffs, param_env, err);
self.point_at_chain(expr, &typeck_results, type_diffs, param_env, err);
}
}
let call_node = hir.find(call_hir_id);
Expand Down
8 changes: 4 additions & 4 deletions src/test/ui/iterators/invalid-iterator-chain.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ LL | .sum::<i32>(),
<i32 as Sum<&'a i32>>
<i32 as Sum>
note: the method call chain might not have had the expected associated types
--> $DIR/invalid-iterator-chain.rs:20:14
--> $DIR/invalid-iterator-chain.rs:25:14
|
LL | vec![0, 1]
| ---------- this expression has type `Vec<{integer}>`
LL | .iter()
| ------ `Iterator::Item` is `&{integer}` here
LL | .map(|x| x * 2)
| ^^^^^^^^^^^^^^ `Iterator::Item` changed to `{integer}` here
| -------------- `Iterator::Item` changed to `{integer}` here
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This span (correctly) shouldn't be a primary span, I think.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea behind these where "this is where the type was correct, and this is where it wasn't" for the primary spans, but it works either way.

Copy link
Member Author

@compiler-errors compiler-errors Dec 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but I'm not sure if I understand why it was counting this as an incorrect span in the first place 😅

Or perhaps I just misunderstood the logic -- from what I read, it seemed to only count something as a primary span if it was the "source" of a type error...

LL | .map(|x| x as f64)
| ----------------- `Iterator::Item` changed to `f64` here
LL | .map(|x| x as i64)
Expand All @@ -84,14 +84,14 @@ LL | .sum::<i32>(),
<i32 as Sum<&'a i32>>
<i32 as Sum>
note: the method call chain might not have had the expected associated types
--> $DIR/invalid-iterator-chain.rs:32:14
--> $DIR/invalid-iterator-chain.rs:33:14
|
LL | vec![0, 1]
| ---------- this expression has type `Vec<{integer}>`
LL | .iter()
| ------ `Iterator::Item` is `&{integer}` here
LL | .map(|x| x * 2)
| ^^^^^^^^^^^^^^ `Iterator::Item` changed to `{integer}` here
| -------------- `Iterator::Item` changed to `{integer}` here
LL | .map(|x| x as f64)
| ^^^^^^^^^^^^^^^^^ `Iterator::Item` changed to `f64` here
LL | .filter(|x| *x > 0.0)
Expand Down