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

Improve diagnostic messages for range patterns. #25743

Merged
merged 1 commit into from
Jun 4, 2015
Merged

Improve diagnostic messages for range patterns. #25743

merged 1 commit into from
Jun 4, 2015

Conversation

michaelsproul
Copy link
Contributor

Part of #24407.

Currently the diagnostics for range patterns are a bit wrong:

fn main() {
    match 5u32 {
        0 ... 10 => (),
        'a' ... 10 => (),
        10 ... 'z' => (),
        "what" ... 10 => (),
        "what" ... "well" => (),
        10 ... "what" => ()
    }
}
range.rs:4:9: 4:19 error: mismatched types in range:
 expected integral variable,
    found char [E0211]
range.rs:4         'a' ... 10 => (),
                   ^~~~~~~~~~
range.rs:4:9: 4:16 error: only char and numeric types are allowed in range [E0029]
range.rs:4         'a' ... 10 => (),
                   ^~~~~~~
range.rs:4:9: 4:19 error: mismatched types:
 expected `u32`,
    found `char`
(expected u32,
    found char) [E0308]
range.rs:4         'a' ... 10 => (),
                   ^~~~~~~~~~
range.rs:5:9: 5:19 error: mismatched types in range:
 expected char,
    found integral variable [E0211]
range.rs:5         10 ... 'z' => (),
                   ^~~~~~~~~~
range.rs:5:9: 5:15 error: only char and numeric types are allowed in range [E0029]
range.rs:5         10 ... 'z' => (),
                   ^~~~~~
range.rs:6:9: 6:22 error: mismatched types in range:
 expected integral variable,
    found &-ptr [E0211]
range.rs:6         "what" ... 10 => (),
                   ^~~~~~~~~~~~~
range.rs:6:9: 6:19 error: only char and numeric types are allowed in range [E0029]
range.rs:6         "what" ... 10 => (),
                   ^~~~~~~~~~
range.rs:6:9: 6:22 error: mismatched types:
 expected `u32`,
    found `&'static str`
(expected u32,
    found &-ptr) [E0308]
range.rs:6         "what" ... 10 => (),
                   ^~~~~~~~~~~~~
range.rs:7:9: 7:19 error: only char and numeric types are allowed in range [E0029]
range.rs:7         "what" ... "well" => (),
                   ^~~~~~~~~~
range.rs:7:9: 7:26 error: mismatched types:
 expected `u32`,
    found `&'static str`
(expected u32,
    found &-ptr) [E0308]
range.rs:7         "what" ... "well" => (),
                   ^~~~~~~~~~~~~~~~~
range.rs:8:9: 8:22 error: mismatched types in range:
 expected &-ptr,
    found integral variable [E0211]
range.rs:8         10 ... "what" => ()
                   ^~~~~~~~~~~~~
range.rs:8:9: 8:15 error: only char and numeric types are allowed in range [E0029]
range.rs:8         10 ... "what" => ()
                   ^~~~~~
error: aborting due to 12 previous errors

The problems here are:

  1. The type of the end of the range is used to predict the type of the start (only mildly counter intuitive).
  2. E0029 is erroneously generated for char ... num and num ... char.
  3. u32 is mentioned.
  4. Errors which are essentially the same are reported multiple times.

I've attempted to fix this by checking the requirements in a different order. The output I've achieved for the above example is:

/home/michael/Temp/range.rs:4:17: 4:22 error: mismatched types in range:
 expected char,
    found integral variable [E0211]
/home/michael/Temp/range.rs:4         'a' ... 10 => (),
                                              ^~~~~
/home/michael/Temp/range.rs:5:16: 5:22 error: mismatched types in range:
 expected integral variable,
    found char [E0211]
/home/michael/Temp/range.rs:5         10 ... 'z' => (),
                                             ^~~~~~
/home/michael/Temp/range.rs:6:9: 6:19 error: only char and numeric types are allowed in range [E0029]
/home/michael/Temp/range.rs:6         "what" ... 10 => (),
                                      ^~~~~~~~~~
/home/michael/Temp/range.rs:6:9: 6:19 help: run `rustc --explain E0029` to see a detailed explanation
/home/michael/Temp/range.rs:6:9: 6:19 note: Start type: &'static str
End type: _
/home/michael/Temp/range.rs:6         "what" ... 10 => (),
                                      ^~~~~~~~~~
/home/michael/Temp/range.rs:7:9: 7:26 error: only char and numeric types are allowed in range [E0029]
/home/michael/Temp/range.rs:7         "what" ... "well" => (),
                                      ^~~~~~~~~~~~~~~~~
/home/michael/Temp/range.rs:7:9: 7:26 help: run `rustc --explain E0029` to see a detailed explanation
/home/michael/Temp/range.rs:7:9: 7:26 note: Start type: &'static str
End type: &'static str
/home/michael/Temp/range.rs:7         "what" ... "well" => (),
                                      ^~~~~~~~~~~~~~~~~
/home/michael/Temp/range.rs:8:16: 8:25 error: only char and numeric types are allowed in range [E0029]
/home/michael/Temp/range.rs:8         10 ... "what" => ()
                                             ^~~~~~~~~
/home/michael/Temp/range.rs:8:16: 8:25 help: run `rustc --explain E0029` to see a detailed explanation
/home/michael/Temp/range.rs:8:16: 8:25 note: Start type: _
End type: &'static str
/home/michael/Temp/range.rs:8         10 ... "what" => ()
                                             ^~~~~~~~~
error: aborting due to 5 previous errors

I think this is already tonnes better, but the Start type/End type stuff could be neater. I don't think there's really any need to start a note: block but I wanted to get some feedback on this. I'd also appreciate advice on how to print the integer types as something other than _.

@rust-highfive
Copy link
Collaborator

r? @nrc

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

@bors
Copy link
Contributor

bors commented May 26, 2015

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

@nrc
Copy link
Member

nrc commented May 28, 2015

I agree there is no need to start a 'note' block here.

As far as I know, you can't get a better representation of the int inference var than _, if the compiler can't infer the exact type, it is just a inference variable (well, type checking distinguishes between int vars and other inference vars, but error reporting doesn't).

This PR could do with some tests to make sure the errors don't regress.

(Also, needs a rebase)

@@ -89,30 +89,46 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
let lhs_ty = fcx.expr_ty(&**begin);
let rhs_ty = fcx.expr_ty(&**end);

let lhs_eq_rhs =
let compat = |t| ty::type_is_numeric(t) || ty::type_is_char(t);
Copy link
Member

Choose a reason for hiding this comment

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

numeric_or_char is a better name than compat

@michaelsproul
Copy link
Contributor Author

I've addressed all your comments and pushed the commit, but I just noticed a failure in the compile-test results.

src/test/compile-fail/match-ill-type1.rs:13:9: 13:13 error: internal compiler error: error comparing literals in range pattern
src/test/compile-fail/match-ill-type1.rs:13         1...2_usize => 1, //~ ERROR mismatched types in range

Don't merge until I've fixed this...

// 1234
// Should have 4 spaces: see issue 18946
line.starts_with("(")
line.starts_with(" ") || line.starts_with("(")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this change was required to support start type: and end type: (each with a preceding space).

@michaelsproul
Copy link
Contributor Author

@nrc: I got it working! By computing a least-upper-bound for the begin and end types of the range and using it as a hint when evaluating and comparing the constants we get inference of the least-complete type from the most-complete one! As a bonus, this also removes the asymmetry whereby lhs_ty was used to type the whole pattern, because we can use the LUB instead. To reflect this change I moved the match-ill-typed example out of compile-fail and into run-pass, with a few added inference tests.

This is the output for the example in the PR description:

range.rs:4:9: 4:19 error: mismatched types in range:
 expected char,
    found integral variable [E0211]
range.rs:4         'a' ... 10 => (),
                   ^~~~~~~~~~
range.rs:5:9: 5:19 error: mismatched types in range:
 expected integral variable,
    found char [E0211]
range.rs:5         10 ... 'z' => (),
                   ^~~~~~~~~~
range.rs:6:9: 6:19 error: only char and numeric types are allowed in range patterns
 start type: &'static str
 end type: _ [E0029]
range.rs:6         "what" ... 10 => (),
                   ^~~~~~~~~~
range.rs:6:9: 6:19 help: run `rustc --explain E0029` to see a detailed explanation
range.rs:7:9: 7:26 error: only char and numeric types are allowed in range patterns
 start type: &'static str
 end type: &'static str [E0029]
range.rs:7         "what" ... "well" => (),
                   ^~~~~~~~~~~~~~~~~
range.rs:7:9: 7:26 help: run `rustc --explain E0029` to see a detailed explanation
range.rs:8:16: 8:25 error: only char and numeric types are allowed in range patterns
 start type: _
 end type: &'static str [E0029]
range.rs:8         10 ... "what" => ()
                          ^~~~~~~~~
range.rs:8:16: 8:25 help: run `rustc --explain E0029` to see a detailed explanation
error: aborting due to 5 previous errors

@@ -8,9 +8,17 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

Copy link
Member

Choose a reason for hiding this comment

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

Could you add a comment explaining what is being tested please?

@nrc
Copy link
Member

nrc commented Jun 2, 2015

Other than the small nits, this looks good. I wanted to ask around a few more people that unifying the type here is OK (I'm pretty sure it is). I'll try and get this r+'ed tomorrow.

@michaelsproul
Copy link
Contributor Author

Ok great, cheers.

// guarantee that the types are concrete and identical. We need to use the most
// specific type as a type hint.
// For example if we have 0 ... 5usize, we need to infer 0 : usize.
let common_type = infer::common_supertype(
Copy link
Contributor

Choose a reason for hiding this comment

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

given that we've just unified the two types, computing a common supertype seems harmless but inefficient. If you just want to remove type variables from the type, resolve_type_vars_if_possible or some similar method seems preferable.

fcx.write_ty(pat.id, lhs_ty);
// Now that we know the types can be unified we find the unified type and use
// it to type the entire expression.
let common_type = fcx.infcx().resolve_type_vars_if_possible(&lhs_ty);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@nikomatsakis, this works... there's no need to call it again with rhs_ty is there?

Copy link
Contributor

Choose a reason for hiding this comment

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

@michaelsproul nope, that looks good to me. (calling it rhs_ty would yield the same result)

@nrc
Copy link
Member

nrc commented Jun 3, 2015

@bors r+

@bors
Copy link
Contributor

bors commented Jun 3, 2015

📌 Commit 25d0ef3 has been approved by nrc

bors added a commit that referenced this pull request Jun 4, 2015
Part of #24407.

Currently the diagnostics for range patterns are a bit wrong:

```rust
fn main() {
    match 5u32 {
        0 ... 10 => (),
        'a' ... 10 => (),
        10 ... 'z' => (),
        "what" ... 10 => (),
        "what" ... "well" => (),
        10 ... "what" => ()
    }
}
```

```
range.rs:4:9: 4:19 error: mismatched types in range:
 expected integral variable,
    found char [E0211]
range.rs:4         'a' ... 10 => (),
                   ^~~~~~~~~~
range.rs:4:9: 4:16 error: only char and numeric types are allowed in range [E0029]
range.rs:4         'a' ... 10 => (),
                   ^~~~~~~
range.rs:4:9: 4:19 error: mismatched types:
 expected `u32`,
    found `char`
(expected u32,
    found char) [E0308]
range.rs:4         'a' ... 10 => (),
                   ^~~~~~~~~~
range.rs:5:9: 5:19 error: mismatched types in range:
 expected char,
    found integral variable [E0211]
range.rs:5         10 ... 'z' => (),
                   ^~~~~~~~~~
range.rs:5:9: 5:15 error: only char and numeric types are allowed in range [E0029]
range.rs:5         10 ... 'z' => (),
                   ^~~~~~
range.rs:6:9: 6:22 error: mismatched types in range:
 expected integral variable,
    found &-ptr [E0211]
range.rs:6         "what" ... 10 => (),
                   ^~~~~~~~~~~~~
range.rs:6:9: 6:19 error: only char and numeric types are allowed in range [E0029]
range.rs:6         "what" ... 10 => (),
                   ^~~~~~~~~~
range.rs:6:9: 6:22 error: mismatched types:
 expected `u32`,
    found `&'static str`
(expected u32,
    found &-ptr) [E0308]
range.rs:6         "what" ... 10 => (),
                   ^~~~~~~~~~~~~
range.rs:7:9: 7:19 error: only char and numeric types are allowed in range [E0029]
range.rs:7         "what" ... "well" => (),
                   ^~~~~~~~~~
range.rs:7:9: 7:26 error: mismatched types:
 expected `u32`,
    found `&'static str`
(expected u32,
    found &-ptr) [E0308]
range.rs:7         "what" ... "well" => (),
                   ^~~~~~~~~~~~~~~~~
range.rs:8:9: 8:22 error: mismatched types in range:
 expected &-ptr,
    found integral variable [E0211]
range.rs:8         10 ... "what" => ()
                   ^~~~~~~~~~~~~
range.rs:8:9: 8:15 error: only char and numeric types are allowed in range [E0029]
range.rs:8         10 ... "what" => ()
                   ^~~~~~
error: aborting due to 12 previous errors
```

The problems here are:

1. The type of the end of the range is used to predict the type of the start (only mildly counter intuitive).
2. E0029 is erroneously generated for `char ... num` and `num ... char`.
2. `u32` is mentioned.
3. Errors which are essentially the same are reported multiple times.

I've attempted to fix this by checking the requirements in a different order. The output I've achieved for the above example is:

```
/home/michael/Temp/range.rs:4:17: 4:22 error: mismatched types in range:
 expected char,
    found integral variable [E0211]
/home/michael/Temp/range.rs:4         'a' ... 10 => (),
                                              ^~~~~
/home/michael/Temp/range.rs:5:16: 5:22 error: mismatched types in range:
 expected integral variable,
    found char [E0211]
/home/michael/Temp/range.rs:5         10 ... 'z' => (),
                                             ^~~~~~
/home/michael/Temp/range.rs:6:9: 6:19 error: only char and numeric types are allowed in range [E0029]
/home/michael/Temp/range.rs:6         "what" ... 10 => (),
                                      ^~~~~~~~~~
/home/michael/Temp/range.rs:6:9: 6:19 help: run `rustc --explain E0029` to see a detailed explanation
/home/michael/Temp/range.rs:6:9: 6:19 note: Start type: &'static str
End type: _
/home/michael/Temp/range.rs:6         "what" ... 10 => (),
                                      ^~~~~~~~~~
/home/michael/Temp/range.rs:7:9: 7:26 error: only char and numeric types are allowed in range [E0029]
/home/michael/Temp/range.rs:7         "what" ... "well" => (),
                                      ^~~~~~~~~~~~~~~~~
/home/michael/Temp/range.rs:7:9: 7:26 help: run `rustc --explain E0029` to see a detailed explanation
/home/michael/Temp/range.rs:7:9: 7:26 note: Start type: &'static str
End type: &'static str
/home/michael/Temp/range.rs:7         "what" ... "well" => (),
                                      ^~~~~~~~~~~~~~~~~
/home/michael/Temp/range.rs:8:16: 8:25 error: only char and numeric types are allowed in range [E0029]
/home/michael/Temp/range.rs:8         10 ... "what" => ()
                                             ^~~~~~~~~
/home/michael/Temp/range.rs:8:16: 8:25 help: run `rustc --explain E0029` to see a detailed explanation
/home/michael/Temp/range.rs:8:16: 8:25 note: Start type: _
End type: &'static str
/home/michael/Temp/range.rs:8         10 ... "what" => ()
                                             ^~~~~~~~~
error: aborting due to 5 previous errors
```

I think this is already tonnes better, but the `Start type/End type` stuff could be neater. I don't think there's really any need to start a `note:` block but I wanted to get some feedback on this. I'd also appreciate advice on how to print the integer types as something other than `_`.
@bors
Copy link
Contributor

bors commented Jun 4, 2015

⌛ Testing commit 25d0ef3 with merge 06c6b3c...

@bors bors merged commit 25d0ef3 into rust-lang:master Jun 4, 2015
@michaelsproul michaelsproul deleted the match-diagnostics branch June 7, 2015 01:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants