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

Clearer error diagnostic on type mismatch within a range #67214

Closed
wants to merge 2 commits into from

Conversation

kevgrasso
Copy link
Contributor

closes #57389.
I tried implementing the expected because of this note as shown in the example for the original issue, but I decided it would be easier and more useful to just annotate the type for the other endpoint of the range, no matter what.

@rust-highfive
Copy link
Contributor

r? @matthewjasper

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Dec 11, 2019
@kevgrasso kevgrasso changed the title E0308range Clearer error diagnostic on type mismatch within a range Dec 12, 2019
@Centril
Copy link
Contributor

Centril commented Dec 13, 2019

r? @estebank

/// Computing common supertype in the pattern guard for the arms of a match expression
MatchExpressionArmPattern { span: Span, ty: Ty<'tcx> },
/// Computing common supertype in a pattern guard
PatternGuard(Box<PatternGuardCause<'tcx>>),
Copy link
Contributor

Choose a reason for hiding this comment

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

PatternGuard feels misleading as the usage in demand_eqtype_pat_diag is about patterns. (There's also no such thing as a pattern guard. Only arms have guards.)

Comment on lines +384 to +445
use rustc::traits::PredicateObligations;
use infer::InferOk;

let at_cause_eq = |
expect_ty: Ty<'tcx>, actual_type: Ty<'tcx>, cause_span: Span,
other_expr: Option<(Span, Ty<'tcx>)>
| {
if !actual_type.references_error() {
let other_expr = other_expr.filter(|(_, ty)| !ty.references_error());
let cause = self.pat_guard_cause(
other_expr, cause_span, expected, discrim_span
);

let result = self.at(&cause, self.param_env).eq(expect_ty, actual_type);
result.map_err(|terr| (cause, terr))
} else {
Ok(InferOk {obligations: PredicateObligations::new(), value: ()})
}
};

// test if sides both sides of range have the compatible types
let lhs_result = at_cause_eq(expected, lhs_ty, begin.span, Some((end.span, rhs_ty)));
let rhs_result = at_cause_eq(expected, rhs_ty, end.span, Some((begin.span, lhs_ty)));

let joined_result = match (lhs_result, rhs_result) {
// full success, move forwards
(
Ok(InferOk { obligations: lhs_obligation, value: () }),
Ok(InferOk { obligations: rhs_obligation, value: () })
) => {
self.register_predicates(lhs_obligation);
self.register_predicates(rhs_obligation);

// Now that we know the types can be unified we find the unified type and use
// it to type the entire expression.
Ok(self.resolve_vars_if_possible(&lhs_ty))
},
// only lhs is wrong
(Err((cause, terr)), Ok(_)) =>
Err(self.report_mismatched_types(&cause, expected, lhs_ty, terr)),
// only rhs is wrong
(Ok(_), Err((cause, terr))) =>
Err(self.report_mismatched_types(&cause, expected, rhs_ty, terr)),
// both sides are wrong
(Err(_), Err((rhs_cause, terr))) => {
if let Ok(_) = at_cause_eq(lhs_ty, rhs_ty, Span::default(), None) {
let cause = self.pat_guard_cause(None, span, expected, discrim_span);
Err(self.report_mismatched_types(&cause, expected, rhs_ty, terr))
} else {
Err(self.report_mismatched_types(&rhs_cause, expected, rhs_ty, terr))
}
}
};

// Subtyping doesn't matter here, as the value is some kind of scalar.
self.demand_eqtype_pat(span, expected, lhs_ty, discrim_span);
self.demand_eqtype_pat(span, expected, rhs_ty, discrim_span);
Some(common_type)
match joined_result {
Ok(common_type) => Some(common_type),
Err(mut err) => {
err.emit();
None
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not happy about the amount of special casing, rawness, and the fragility of this type checking logic.
Looking right now to see if there's a less complicated way to get near the same results.

}
}
}
if let Some((span, ty)) = other_range_expr {
err.span_label(span, format!("other endpoint has type `{}`", ty));
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like "this endpoint has type will lend itself to less confusion in the output.

@@ -355,8 +355,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expected: Ty<'tcx>,
discrim_span: Option<Span>,
) -> Option<Ty<'tcx>> {
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel that if you change the return type to Result<Ty<'tcx>, DiagnosticBuilder<'tcx>> it will let you clean up a few lines below. Just make sure that in the single check_pat_range caller you emit the error.

@@ -371,17 +378,71 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.emit_err_pat_range(
span, begin.span, end.span, lhs_fail, rhs_fail, lhs_ty, rhs_ty
);
Copy link
Contributor

Choose a reason for hiding this comment

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

You'll have to change emit_err_pat_range to return the error instead of emitting it.

All of this then becomes

            return self.emit_err_pat_range(
                span, begin.span, end.span, lhs_fail, rhs_fail, lhs_ty, rhs_ty
                span, begin.span, end.span, lhs_fail, rhs_fail, lhs_ty, rhs_ty
            );

which lets you have the else clause implicit, removing one indentation level.

@bors
Copy link
Collaborator

bors commented Dec 22, 2019

☔ The latest upstream changes (presumably #67532) made this pull request unmergeable. Please resolve the merge conflicts.

Mark-Simulacrum added a commit to Mark-Simulacrum/rust that referenced this pull request Dec 26, 2019
…, r=estebank

typeck: note other end-point when checking range pats

Fixes rust-lang#57389, alternative to rust-lang#67214 that should be less invasive to type checking logic.

r? @estebank
Mark-Simulacrum added a commit to Mark-Simulacrum/rust that referenced this pull request Dec 26, 2019
…, r=estebank

typeck: note other end-point when checking range pats

Fixes rust-lang#57389, alternative to rust-lang#67214 that should be less invasive to type checking logic.

r? @estebank
JohnTitor added a commit to JohnTitor/rust that referenced this pull request Dec 30, 2019
…, r=estebank

typeck: note other end-point when checking range pats

Fixes rust-lang#57389, alternative to rust-lang#67214 that should be less invasive to type checking logic.

r? @estebank
@Dylan-DPC-zz
Copy link

@kevgrasso can you rebase this? thanks :)

@Dylan-DPC-zz Dylan-DPC-zz added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 15, 2020
@JohnCSimon JohnCSimon added the S-inactive Status: Inactive and waiting on the author. This is often applied to closed PRs. label Jan 25, 2020
@JohnCSimon
Copy link
Member

Pinging again from triage -
@kevgrasso - Unfortunately, I'm closing this out as inactive, when you have time to address the merge conflicts again please feel free to reopen this PR.
Note: please don't push to the PR while it is closed as that prevents it from being reopened.

@JohnCSimon JohnCSimon closed this Jan 25, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-inactive Status: Inactive and waiting on the author. This is often applied to closed PRs. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Be clearer on type mismatch within a range
8 participants