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

typeck: use Pattern obligation cause more for better diagnostics #69452

Merged
merged 7 commits into from
Feb 28, 2020
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
26 changes: 14 additions & 12 deletions src/librustc/hir/map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,17 +153,13 @@ pub struct Map<'hir> {
hir_to_node_id: FxHashMap<HirId, NodeId>,
}

struct ParentHirIterator<'map, 'hir> {
/// An iterator that walks up the ancestor tree of a given `HirId`.
/// Constructed using `tcx.hir().parent_iter(hir_id)`.
pub struct ParentHirIterator<'map, 'hir> {
current_id: HirId,
map: &'map Map<'hir>,
}

impl<'map, 'hir> ParentHirIterator<'map, 'hir> {
fn new(current_id: HirId, map: &'map Map<'hir>) -> Self {
Self { current_id, map }
}
}

impl<'hir> Iterator for ParentHirIterator<'_, 'hir> {
type Item = (HirId, Node<'hir>);

Expand Down Expand Up @@ -618,6 +614,12 @@ impl<'hir> Map<'hir> {
self.find_entry(hir_id).and_then(|x| x.parent_node()).unwrap_or(hir_id)
}

/// Returns an iterator for the nodes in the ancestor tree of the `current_id`
/// until the crate root is reached. Prefer this over your own loop using `get_parent_node`.
pub fn parent_iter(&self, current_id: HirId) -> ParentHirIterator<'_, 'hir> {
ParentHirIterator { current_id, map: self }
}

/// Checks if the node is an argument. An argument is a local variable whose
/// immediate parent is an item or a closure.
pub fn is_argument(&self, id: HirId) -> bool {
Expand Down Expand Up @@ -684,7 +686,7 @@ impl<'hir> Map<'hir> {
/// }
/// ```
pub fn get_return_block(&self, id: HirId) -> Option<HirId> {
let mut iter = ParentHirIterator::new(id, &self).peekable();
let mut iter = self.parent_iter(id).peekable();
let mut ignore_tail = false;
if let Some(entry) = self.find_entry(id) {
if let Node::Expr(Expr { kind: ExprKind::Ret(_), .. }) = entry.node {
Expand Down Expand Up @@ -731,7 +733,7 @@ impl<'hir> Map<'hir> {
/// in the HIR which is recorded by the map and is an item, either an item
/// in a module, trait, or impl.
pub fn get_parent_item(&self, hir_id: HirId) -> HirId {
for (hir_id, node) in ParentHirIterator::new(hir_id, &self) {
for (hir_id, node) in self.parent_iter(hir_id) {
match node {
Node::Crate
| Node::Item(_)
Expand All @@ -753,7 +755,7 @@ impl<'hir> Map<'hir> {
/// Returns the `HirId` of `id`'s nearest module parent, or `id` itself if no
/// module parent is in this map.
pub fn get_module_parent_node(&self, hir_id: HirId) -> HirId {
for (hir_id, node) in ParentHirIterator::new(hir_id, &self) {
for (hir_id, node) in self.parent_iter(hir_id) {
if let Node::Item(&Item { kind: ItemKind::Mod(_), .. }) = node {
return hir_id;
}
Expand All @@ -767,7 +769,7 @@ impl<'hir> Map<'hir> {
/// Used by error reporting when there's a type error in a match arm caused by the `match`
/// expression needing to be unit.
pub fn get_match_if_cause(&self, hir_id: HirId) -> Option<&'hir Expr<'hir>> {
for (_, node) in ParentHirIterator::new(hir_id, &self) {
for (_, node) in self.parent_iter(hir_id) {
match node {
Node::Item(_) | Node::ForeignItem(_) | Node::TraitItem(_) | Node::ImplItem(_) => {
break;
Expand All @@ -788,7 +790,7 @@ impl<'hir> Map<'hir> {

/// Returns the nearest enclosing scope. A scope is roughly an item or block.
pub fn get_enclosing_scope(&self, hir_id: HirId) -> Option<HirId> {
for (hir_id, node) in ParentHirIterator::new(hir_id, &self) {
for (hir_id, node) in self.parent_iter(hir_id) {
if match node {
Node::Item(i) => match i.kind {
ItemKind::Fn(..)
Expand Down
10 changes: 9 additions & 1 deletion src/librustc_typeck/check/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expected: Ty<'tcx>,
actual: Ty<'tcx>,
) -> Option<DiagnosticBuilder<'tcx>> {
let cause = &self.misc(sp);
self.demand_suptype_with_origin(&self.misc(sp), expected, actual)
}

pub fn demand_suptype_with_origin(
&self,
cause: &ObligationCause<'tcx>,
expected: Ty<'tcx>,
actual: Ty<'tcx>,
) -> Option<DiagnosticBuilder<'tcx>> {
match self.at(cause, self.param_env).sup(expected, actual) {
Ok(InferOk { obligations, value: () }) => {
self.register_predicates(obligations);
Expand Down
61 changes: 39 additions & 22 deletions src/librustc_typeck/check/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ use rustc_hir::pat_util::EnumerateAndAdjustIterator;
use rustc_hir::{HirId, Pat, PatKind};
use rustc_infer::infer;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::traits::Pattern;
use rustc_infer::traits::{ObligationCause, Pattern};
use rustc_span::hygiene::DesugaringKind;
use rustc_span::Span;
use rustc_span::source_map::{Span, Spanned};
use syntax::ast;
use syntax::util::lev_distance::find_best_match_for_name;

Expand Down Expand Up @@ -66,16 +66,19 @@ struct TopInfo<'tcx> {
}

impl<'tcx> FnCtxt<'_, 'tcx> {
fn pattern_cause(&self, ti: TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> {
let code = Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr };
self.cause(cause_span, code)
}

fn demand_eqtype_pat_diag(
&self,
cause_span: Span,
expected: Ty<'tcx>,
actual: Ty<'tcx>,
ti: TopInfo<'tcx>,
) -> Option<DiagnosticBuilder<'tcx>> {
let code = Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr };
let cause = self.cause(cause_span, code);
self.demand_eqtype_with_origin(&cause, expected, actual)
self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)
}

fn demand_eqtype_pat(
Expand Down Expand Up @@ -152,7 +155,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, def_bm, ti)
}
PatKind::Path(ref qpath) => {
self.check_pat_path(pat, path_res.unwrap(), qpath, expected)
self.check_pat_path(pat, path_res.unwrap(), qpath, expected, ti)
}
PatKind::Struct(ref qpath, fields, etc) => {
self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm, ti)
Expand Down Expand Up @@ -361,16 +364,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Byte string patterns behave the same way as array patterns
// They can denote both statically and dynamically-sized byte arrays.
let mut pat_ty = ty;
if let hir::ExprKind::Lit(ref lt) = lt.kind {
if let ast::LitKind::ByteStr(_) = lt.node {
let expected_ty = self.structurally_resolved_type(span, expected);
if let ty::Ref(_, r_ty, _) = expected_ty.kind {
if let ty::Slice(_) = r_ty.kind {
let tcx = self.tcx;
pat_ty =
tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_slice(tcx.types.u8));
}
}
if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::ByteStr(_), .. }) = lt.kind {
let expected = self.structurally_resolved_type(span, expected);
if let ty::Ref(_, ty::TyS { kind: ty::Slice(_), .. }, _) = expected.kind {
let tcx = self.tcx;
pat_ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_slice(tcx.types.u8));
}
}

Expand All @@ -384,7 +382,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// &'static str <: expected
//
// then that's equivalent to there existing a LUB.
if let Some(mut err) = self.demand_suptype_diag(span, expected, pat_ty) {
let cause = self.pattern_cause(ti, span);
if let Some(mut err) = self.demand_suptype_with_origin(&cause, expected, pat_ty) {
err.emit_unless(
ti.span
.filter(|&s| {
Expand Down Expand Up @@ -543,8 +542,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// If there are multiple arms, make sure they all agree on
// what the type of the binding `x` ought to be.
if var_id != pat.hir_id {
let vt = self.local_ty(pat.span, var_id).decl_ty;
self.demand_eqtype_pat(pat.span, vt, local_ty, ti);
self.check_binding_alt_eq_ty(pat.span, var_id, local_ty, ti);
}

if let Some(p) = sub {
Expand All @@ -554,6 +552,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
local_ty
}

fn check_binding_alt_eq_ty(&self, span: Span, var_id: HirId, ty: Ty<'tcx>, ti: TopInfo<'tcx>) {
let var_ty = self.local_ty(span, var_id).decl_ty;
if let Some(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) {
let hir = self.tcx.hir();
let var_ty = self.resolve_vars_with_obligations(var_ty);
let msg = format!("first introduced with type `{}` here", var_ty);
err.span_label(hir.span(var_id), msg);
let in_arm = hir.parent_iter(var_id).any(|(_, n)| matches!(n, hir::Node::Arm(..)));
let pre = if in_arm { "in the same arm, " } else { "" };
err.note(&format!("{}a binding must have the same type in all alternatives", pre));
err.emit();
}
}

fn borrow_pat_suggestion(
&self,
err: &mut DiagnosticBuilder<'_>,
Expand Down Expand Up @@ -659,6 +671,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
path_resolution: (Res, Option<Ty<'tcx>>, &'b [hir::PathSegment<'b>]),
qpath: &hir::QPath<'_>,
expected: Ty<'tcx>,
ti: TopInfo<'tcx>,
) -> Ty<'tcx> {
let tcx = self.tcx;

Expand All @@ -684,7 +697,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

// Type-check the path.
let pat_ty = self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id).0;
self.demand_suptype(pat.span, expected, pat_ty);
if let Some(mut err) =
self.demand_suptype_with_origin(&self.pattern_cause(ti, pat.span), expected, pat_ty)
{
err.emit();
}
pat_ty
}

Expand Down Expand Up @@ -901,7 +918,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
});
let element_tys = tcx.mk_substs(element_tys_iter);
let pat_ty = tcx.mk_ty(ty::Tuple(element_tys));
if let Some(mut err) = self.demand_eqtype_diag(span, expected, pat_ty) {
if let Some(mut err) = self.demand_eqtype_pat_diag(span, expected, pat_ty, ti) {
err.emit();
// Walk subpatterns with an expected type of `err` in this case to silence
// further errors being emitted when using the bindings. #50333
Expand Down Expand Up @@ -1205,7 +1222,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
});
let rptr_ty = self.new_ref_ty(pat.span, mutbl, inner_ty);
debug!("check_pat_ref: demanding {:?} = {:?}", expected, rptr_ty);
let err = self.demand_eqtype_diag(pat.span, expected, rptr_ty);
let err = self.demand_eqtype_pat_diag(pat.span, expected, rptr_ty, ti);

// Look for a case like `fn foo(&foo: u32)` and suggest
// `fn foo(foo: &u32)`
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/destructure-trait-ref.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ error[E0308]: mismatched types
--> $DIR/destructure-trait-ref.rs:32:10
|
LL | let &&x = &1isize as &dyn T;
| ^^
| ^^ ----------------- this expression has type `&dyn T`
| |
| expected trait object `dyn T`, found reference
| help: you can probably remove the explicit borrow: `x`
Expand All @@ -32,7 +32,7 @@ error[E0308]: mismatched types
--> $DIR/destructure-trait-ref.rs:36:11
|
LL | let &&&x = &(&1isize as &dyn T);
| ^^
| ^^ -------------------- this expression has type `&&dyn T`
| |
| expected trait object `dyn T`, found reference
| help: you can probably remove the explicit borrow: `x`
Expand Down
4 changes: 3 additions & 1 deletion src/test/ui/elide-errors-on-mismatched-tuple.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ error[E0308]: mismatched types
--> $DIR/elide-errors-on-mismatched-tuple.rs:14:9
|
LL | let (a, b, c) = (A::new(), A::new()); // This tuple is 2 elements, should be three
| ^^^^^^^^^ expected a tuple with 2 elements, found one with 3 elements
| ^^^^^^^^^ -------------------- this expression has type `(A, A)`
| |
| expected a tuple with 2 elements, found one with 3 elements
|
= note: expected tuple `(A, A)`
found tuple `(_, _, _)`
Expand Down
3 changes: 3 additions & 0 deletions src/test/ui/issues/issue-12552.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ LL | Some(k) => match k {
error[E0308]: mismatched types
--> $DIR/issue-12552.rs:9:5
|
LL | match t {
| - this expression has type `std::result::Result<_, {integer}>`
...
LL | None => ()
| ^^^^ expected enum `std::result::Result`, found enum `std::option::Option`
|
Expand Down
4 changes: 3 additions & 1 deletion src/test/ui/issues/issue-37026.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ error[E0308]: mismatched types
--> $DIR/issue-37026.rs:6:9
|
LL | let empty_struct::XEmpty2 = ();
| ^^^^^^^^^^^^^^^^^^^^^ expected `()`, found struct `empty_struct::XEmpty2`
| ^^^^^^^^^^^^^^^^^^^^^ -- this expression has type `()`
| |
| expected `()`, found struct `empty_struct::XEmpty2`

error[E0308]: mismatched types
--> $DIR/issue-37026.rs:7:9
Expand Down
8 changes: 8 additions & 0 deletions src/test/ui/issues/issue-5100.stderr
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
error[E0308]: mismatched types
--> $DIR/issue-5100.rs:8:9
|
LL | match (true, false) {
| ------------- this expression has type `(bool, bool)`
LL | A::B => (),
| ^^^^ expected tuple, found enum `A`
|
Expand All @@ -10,6 +12,8 @@ LL | A::B => (),
error[E0308]: mismatched types
--> $DIR/issue-5100.rs:17:9
|
LL | match (true, false) {
| ------------- this expression has type `(bool, bool)`
LL | (true, false, false) => ()
| ^^^^^^^^^^^^^^^^^^^^ expected a tuple with 2 elements, found one with 3 elements
|
Expand All @@ -19,6 +23,8 @@ LL | (true, false, false) => ()
error[E0308]: mismatched types
--> $DIR/issue-5100.rs:25:9
|
LL | match (true, false) {
| ------------- this expression has type `(bool, bool)`
LL | (true, false, false) => ()
| ^^^^^^^^^^^^^^^^^^^^ expected a tuple with 2 elements, found one with 3 elements
|
Expand All @@ -39,6 +45,8 @@ LL | box (true, false) => ()
error[E0308]: mismatched types
--> $DIR/issue-5100.rs:40:9
|
LL | match (true, false) {
| ------------- this expression has type `(bool, bool)`
LL | &(true, false) => ()
| ^^^^^^^^^^^^^^ expected tuple, found reference
|
Expand Down
2 changes: 2 additions & 0 deletions src/test/ui/issues/issue-7867.stderr
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
error[E0308]: mismatched types
--> $DIR/issue-7867.rs:7:9
|
LL | match (true, false) {
| ------------- this expression has type `(bool, bool)`
LL | A::B => (),
| ^^^^ expected tuple, found enum `A`
|
Expand Down
3 changes: 3 additions & 0 deletions src/test/ui/match/match-ill-type2.stderr
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
error[E0308]: mismatched types
--> $DIR/match-ill-type2.rs:4:9
|
LL | match 1i32 {
| ---- this expression has type `i32`
LL | 1i32 => 1,
LL | 2u32 => 1,
| ^^^^ expected `i32`, found `u32`

Expand Down
4 changes: 3 additions & 1 deletion src/test/ui/match/match-tag-nullary.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ error[E0308]: mismatched types
--> $DIR/match-tag-nullary.rs:4:40
|
LL | fn main() { let x: A = A::A; match x { B::B => { } } }
| ^^^^ expected enum `A`, found enum `B`
| - ^^^^ expected enum `A`, found enum `B`
| |
| this expression has type `A`

error: aborting due to previous error

Expand Down
6 changes: 5 additions & 1 deletion src/test/ui/mismatched_types/E0409.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ error[E0308]: mismatched types
LL | match x {
| - this expression has type `({integer}, {integer})`
LL | (0, ref y) | (y, 0) => {}
| ^ expected `&{integer}`, found integer
| ----- ^ expected `&{integer}`, found integer
| |
| first introduced with type `&{integer}` here
|
= note: in the same arm, a binding must have the same type in all alternatives

error: aborting due to 2 previous errors

Expand Down
Loading