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

Fix handling of empty types in patterns. #38069

Merged
merged 22 commits into from
Jan 6, 2017

Conversation

canndrew
Copy link
Contributor

Fix for #12609.

@rust-highfive
Copy link
Collaborator

r? @nikomatsakis

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

@canndrew canndrew changed the title Fix handling of empty types in patterns. [WIP] Fix handling of empty types in patterns. Nov 29, 2016
ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]),
LeaveOutWitness => Useful,
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Style: usually else is on the same line as the closing bracket.

@@ -28,7 +28,7 @@ For example, the following `match` block has too many arms:
```compile_fail,E0001
match Some(0) {
Some(bar) => {/* ... */}
None => {/* ... */}
x => {/* ... */} // This handles the `None` case
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is it necessary to change this example?

Copy link
Contributor

Choose a reason for hiding this comment

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

ah nevermind, because this triggers for E0001 and not for unreachable_patterns

@bluss
Copy link
Member

bluss commented Nov 30, 2016

cc @brson because this would be a new (insta-stable?) feature

"unreachable pattern");
err.span_label(pat.span, &"this is an unreachable pattern");
// if we had a catchall pattern, hint at that
// if we had a catchall pattern, raise an error.
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we keep this as an error even if there is an _ pattern? Is there a strong reason?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure, it was @eddyb's suggestion. If that becomes a lint then we don't get to use E0001 at all though.

Copy link
Contributor

Choose a reason for hiding this comment

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

Don't use E0001 at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@eddyb Opinion?

Copy link
Member

Choose a reason for hiding this comment

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

The annoying thing is that a lint loses the help message, but if everyone else is fine with it, then sure, you can remove this special case.

@nikomatsakis
Copy link
Contributor

Seems like we need some more test -- for example, I don't see any tests covering the private field cases, nor the "don't need to match variants" code, right? (is that because this is a WIP?)

@canndrew
Copy link
Contributor Author

canndrew commented Dec 1, 2016

Seems like we need some more test -- for example, I don't see any tests covering the private field cases, nor the "don't need to match variants" code, right? (is that because this is a WIP?)

Yep, it should all be there now though.

@canndrew canndrew changed the title [WIP] Fix handling of empty types in patterns. Fix handling of empty types in patterns. Dec 1, 2016
@canndrew
Copy link
Contributor Author

canndrew commented Dec 1, 2016

It might be worth @arielb1 having a look at this too, seeing as they wrote the match checking code that I've fiddled with.

@canndrew
Copy link
Contributor Author

canndrew commented Dec 1, 2016

So a quick summary of these changes:

(Most) unreachable patterns now generate warnings instead of errors. I've added a new unreachable_patterns lint rather than reuse unreachable_code just so people can be slightly more specific about {en,dis}abling errors.

Unreachable patterns that used to sneak past the pattern-match checks no longer produce an unreachable_code warning later on.

I've fixed is_uninhabited for enums. Before it used to think that the fields of enum variants were private and so, for example, it would treat Result<!, !> as being inhabited on the assumption that the user can't see the !s inside. I did this by making FieldDefData::is_uninhabited_recurse take an is_enum: bool flag which overrides the field's visibility flag. Is this the best way to do this? Maybe the visibility of enum variant fields should be correctly set in the first place.

is_useful no longer exits early with Useful if it gets a matrix with zero rows. This is incorrect behaviour when empty types are involved. It would be possible to add this check back and short-circuit the rest of the algorithm so long as we also check for empty types and return NotUseful if we find one. But for now I've kept the algorithm simple.

all_constructors(Ty) only returns constructors that are actually possible. eg. for Result<u32, !> it will only return Ok, for &[!] it will only return &[], for empty types it will not return anything etc.

Removed the special-casing that made match uninhabited_expr {} compile before. This should be handled naturally by the pattern-matching algs now.

Correct type information is carried on dummy wildcard patterns. This was necessary to make the above change but I suspect it's also a more general bugfix. It used to be possible for the pattern-match algs to be handling a dummy TyError when they should really know the actual type, even for non-empty matrices.

is_useful produces fewer, more general witnesses using wildcards when it can. eg. instead of returning (Ok(_), Some(_)) and (Err(_), Some(_)) it will just return (_, Some(_)).

PatternKind::Variant now carries Substs info.

rustc_mir::build::Builder::simplify_match_pair can now handle enums that have multiple variants but only one statically-possible variant. This allows code like let x: Result<u32, !> = ...; let Ok(y) = x; to compile by downcasting x to its inner u32.

@bors
Copy link
Contributor

bors commented Dec 2, 2016

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

@canndrew
Copy link
Contributor Author

canndrew commented Dec 6, 2016

@nikomatsakis this is ready now btw.

@brson any chance I could get a crater run on this?

@nikomatsakis
Copy link
Contributor

@canndrew sorry for delay. Mozilla all hands didn't leave much time for reviewing. I'm going to be on vacation next week but will try to find time, else perhaps someone else can/should review.

@canndrew
Copy link
Contributor Author

@nikomatsakis That's alright. I was just a bit angsty that this would get ignored for a month or so and eventually bit-rot like the last PR for this did. I understand you guys have a very full plate though.

Probably @arielb1 would be the best person to review this?

@@ -20,7 +20,7 @@ fn tail(source_list: &IntList) -> IntList {
match source_list {
&IntList::Cons(val, box ref next_list) => tail(next_list),
&IntList::Cons(val, box Nil) => IntList::Cons(val, box Nil),
//~^ ERROR unreachable pattern
//~^ ERROR cannot move out of borrowed content
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be #[deny(unreachable_patterns)]

ty::TyAdt(def, substs) if def.is_enum() && def.variants.len() != 1 => {
def.variants.iter().filter_map(|v| {
let mut visited = FxHashSet::default();
if v.is_uninhabited_recurse(&mut visited,
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 worried that the recursion here might cause things to be slow. Could you try adding a cache somewhere?

@canndrew
Copy link
Contributor Author

canndrew commented Dec 11, 2016

I've refactored is_uninhabited_recurse to use a cache. It's now called uninhabited_from and it calculates the full forest of nodes that a type is visibly uninhabited from and caches that. Eg. if we have this:

mod mod_a {
    pub struct SecretlyUninhabited {
        _priv: !,
    }
}
mod mod_b {
    pub mod mod_c {
        pub struct SecretlyUninhabited {
            _priv: !,
        }
        mod mod_d {
            ...
        }
    }
}
struct MyType {
    foo: mod_a::SecretlyUninhabited,
    bar: mod_b::SecretlyUninhabited,
}

Then uninhabited_from(MyType) will give the set containing mod_a, mod_c and all their descendants (represented as [mod_a, mod_c]). We can then check whether that set is non-empty to see whether a type is uninhabited or we can check whether the set contains a given node to see whether the type is visibly uninhabited from that node.

@canndrew
Copy link
Contributor Author

Anyone know what happened to travis there?

@eddyb
Copy link
Member

eddyb commented Dec 11, 2016

@canndrew I think Travis has been busted for almost a week now.

@canndrew
Copy link
Contributor Author

Is there anything more I can do to help get this reviewed?

@nikomatsakis
Copy link
Contributor

@canndrew back now, sorry about that.

@nikomatsakis
Copy link
Contributor

Will review.

@nikomatsakis
Copy link
Contributor

Sorry for delay. I am doing a bit of reading up on the general algorithm since I'm feeling ill-equipped to judge this patch. =)

@canndrew
Copy link
Contributor Author

That's cool.

is_useful no longer exits early with Useful if it gets a matrix with zero rows. This is incorrect behaviour when empty types are involved. It would be possible to add this check back and short-circuit the rest of the algorithm so long as we also check for empty types and return NotUseful if we find one. But for now I've kept the algorithm simple.

all_constructors(Ty) only returns constructors that are actually possible. eg. for Result<u32, !> it will only return Ok, for &[!] it will only return &[], for empty types it will not return anything etc.

Algorithm-wise these should be the only relevant changes and should be enough to make it correctly handle empty types.

@bors
Copy link
Contributor

bors commented Jan 5, 2017

📌 Commit 291c84a has been approved by nikomatsakis

@bors
Copy link
Contributor

bors commented Jan 5, 2017

⌛ Testing commit 291c84a with merge 85c76a0...

@eddyb
Copy link
Member

eddyb commented Jan 5, 2017

@bors r=nikomatsakis force

@eddyb
Copy link
Member

eddyb commented Jan 5, 2017

@bors retry

@eddyb
Copy link
Member

eddyb commented Jan 5, 2017

@bors r=nikomatsakis

@bors
Copy link
Contributor

bors commented Jan 5, 2017

📌 Commit 275c19d has been approved by nikomatsakis

@bors
Copy link
Contributor

bors commented Jan 6, 2017

⌛ Testing commit 275c19d with merge 6f1ae66...

bors added a commit that referenced this pull request Jan 6, 2017
Fix handling of empty types in patterns.

Fix for #12609.
@bors
Copy link
Contributor

bors commented Jan 6, 2017

☀️ Test successful - status-appveyor, status-travis
Approved by: nikomatsakis
Pushing 6f1ae66 to master...

@tomaka
Copy link
Contributor

tomaka commented Jan 11, 2017

I suggest a relnotes tag for this PR.

@GuillaumeGomez GuillaumeGomez added the relnotes Marks issues that should be documented in the release notes of the next release. label Jan 11, 2017
@GuillaumeGomez
Copy link
Member

Done.

jsonn pushed a commit to jsonn/pkgsrc that referenced this pull request Mar 20, 2017
Version 1.16.0 (2017-03-16)
===========================

Language
--------

* Lifetimes in statics and consts default to `'static`. [RFC 1623]
* [The compiler's `dead_code` lint now accounts for type aliases][38051].
* [Uninhabitable enums (those without any variants) no longer permit wildcard
  match patterns][38069]
* [Clean up semantics of `self` in an import list][38313]
* [`Self` may appear in `impl` headers][38920]
* [`Self` may appear in struct expressions][39282]

Compiler
--------

* [`rustc` now supports `--emit=metadata`, which causes rustc to emit
  a `.rmeta` file containing only crate metadata][38571]. This can be
  used by tools like the Rust Language Service to perform
  metadata-only builds.
* [Levenshtein based typo suggestions now work in most places, while
  previously they worked only for fields and sometimes for local
  variables][38927]. Together with the overhaul of "no
  resolution"/"unexpected resolution" errors (#[38154]) they result in
  large and systematic improvement in resolution diagnostics.
* [Fix `transmute::<T, U>` where `T` requires a bigger alignment than
  `U`][38670]
* [rustc: use -Xlinker when specifying an rpath with ',' in it][38798]
* [`rustc` no longer attempts to provide "consider using an explicit
  lifetime" suggestions][37057]. They were inaccurate.

Stabilized APIs
---------------

* [`VecDeque::truncate`]
* [`VecDeque::resize`]
* [`String::insert_str`]
* [`Duration::checked_add`]
* [`Duration::checked_sub`]
* [`Duration::checked_div`]
* [`Duration::checked_mul`]
* [`str::replacen`]
* [`str::repeat`]
* [`SocketAddr::is_ipv4`]
* [`SocketAddr::is_ipv6`]
* [`IpAddr::is_ipv4`]
* [`IpAddr::is_ipv6`]
* [`Vec::dedup_by`]
* [`Vec::dedup_by_key`]
* [`Result::unwrap_or_default`]
* [`<*const T>::wrapping_offset`]
* [`<*mut T>::wrapping_offset`]
* `CommandExt::creation_flags`
* [`File::set_permissions`]
* [`String::split_off`]

Libraries
---------

* [`[T]::binary_search` and `[T]::binary_search_by_key` now take
  their argument by `Borrow` parameter][37761]
* [All public types in std implement `Debug`][38006]
* [`IpAddr` implements `From<Ipv4Addr>` and `From<Ipv6Addr>`][38327]
* [`Ipv6Addr` implements `From<[u16; 8]>`][38131]
* [Ctrl-Z returns from `Stdin.read()` when reading from the console on
  Windows][38274]
* [std: Fix partial writes in `LineWriter`][38062]
* [std: Clamp max read/write sizes on Unix][38062]
* [Use more specific panic message for `&str` slicing errors][38066]
* [`TcpListener::set_only_v6` is deprecated][38304]. This
  functionality cannot be achieved in std currently.
* [`writeln!`, like `println!`, now accepts a form with no string
  or formatting arguments, to just print a newline][38469]
* [Implement `iter::Sum` and `iter::Product` for `Result`][38580]
* [Reduce the size of static data in `std_unicode::tables`][38781]
* [`char::EscapeDebug`, `EscapeDefault`, `EscapeUnicode`,
  `CaseMappingIter`, `ToLowercase`, `ToUppercase`, implement
  `Display`][38909]
* [`Duration` implements `Sum`][38712]
* [`String` implements `ToSocketAddrs`][39048]

Cargo
-----

* [The `cargo check` command does a type check of a project without
  building it][cargo/3296]
* [crates.io will display CI badges from Travis and AppVeyor, if
  specified in Cargo.toml][cargo/3546]
* [crates.io will display categories listed in Cargo.toml][cargo/3301]
* [Compilation profiles accept integer values for `debug`, in addition
  to `true` and `false`. These are passed to `rustc` as the value to
  `-C debuginfo`][cargo/3534]
* [Implement `cargo --version --verbose`][cargo/3604]
* [All builds now output 'dep-info' build dependencies compatible with
  make and ninja][cargo/3557]
* [Build all workspace members with `build --all`][cargo/3511]
* [Document all workspace members with `doc --all`][cargo/3515]
* [Path deps outside workspace are not members][cargo/3443]

Misc
----

* [`rustdoc` has a `--sysroot` argument that, like `rustc`, specifies
  the path to the Rust implementation][38589]
* [The `armv7-linux-androideabi` target no longer enables NEON
  extensions, per Google's ABI guide][38413]
* [The stock standard library can be compiled for Redox OS][38401]
* [Rust has initial SPARC support][38726]. Tier 3. No builds
  available.
* [Rust has experimental support for Nvidia PTX][38559]. Tier 3. No
  builds available.
* [Fix backtraces on i686-pc-windows-gnu by disabling FPO][39379]

Compatibility Notes
-------------------

* [Uninhabitable enums (those without any variants) no longer permit wildcard
  match patterns][38069]
* In this release, references to uninhabited types can not be
  pattern-matched. This was accidentally allowed in 1.15.
* [The compiler's `dead_code` lint now accounts for type aliases][38051].
* [Ctrl-Z returns from `Stdin.read()` when reading from the console on
  Windows][38274]
* [Clean up semantics of `self` in an import list][38313]

[37057]: rust-lang/rust#37057
[37761]: rust-lang/rust#37761
[38006]: rust-lang/rust#38006
[38051]: rust-lang/rust#38051
[38062]: rust-lang/rust#38062
[38062]: rust-lang/rust#38622
[38066]: rust-lang/rust#38066
[38069]: rust-lang/rust#38069
[38131]: rust-lang/rust#38131
[38154]: rust-lang/rust#38154
[38274]: rust-lang/rust#38274
[38304]: rust-lang/rust#38304
[38313]: rust-lang/rust#38313
[38314]: rust-lang/rust#38314
[38327]: rust-lang/rust#38327
[38401]: rust-lang/rust#38401
[38413]: rust-lang/rust#38413
[38469]: rust-lang/rust#38469
[38559]: rust-lang/rust#38559
[38571]: rust-lang/rust#38571
[38580]: rust-lang/rust#38580
[38589]: rust-lang/rust#38589
[38670]: rust-lang/rust#38670
[38712]: rust-lang/rust#38712
[38726]: rust-lang/rust#38726
[38781]: rust-lang/rust#38781
[38798]: rust-lang/rust#38798
[38909]: rust-lang/rust#38909
[38920]: rust-lang/rust#38920
[38927]: rust-lang/rust#38927
[39048]: rust-lang/rust#39048
[39282]: rust-lang/rust#39282
[39379]: rust-lang/rust#39379
[`<*const T>::wrapping_offset`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.wrapping_offset
[`<*mut T>::wrapping_offset`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.wrapping_offset
[`Duration::checked_add`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.checked_add
[`Duration::checked_div`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.checked_div
[`Duration::checked_mul`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.checked_mul
[`Duration::checked_sub`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.checked_sub
[`File::set_permissions`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.set_permissions
[`IpAddr::is_ipv4`]: https://doc.rust-lang.org/std/net/enum.IpAddr.html#method.is_ipv4
[`IpAddr::is_ipv6`]: https://doc.rust-lang.org/std/net/enum.IpAddr.html#method.is_ipv6
[`Result::unwrap_or_default`]: https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap_or_default
[`SocketAddr::is_ipv4`]: https://doc.rust-lang.org/std/net/enum.SocketAddr.html#method.is_ipv4
[`SocketAddr::is_ipv6`]: https://doc.rust-lang.org/std/net/enum.SocketAddr.html#method.is_ipv6
[`String::insert_str`]: https://doc.rust-lang.org/std/string/struct.String.html#method.insert_str
[`String::split_off`]: https://doc.rust-lang.org/std/string/struct.String.html#method.split_off
[`Vec::dedup_by_key`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.dedup_by_key
[`Vec::dedup_by`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.dedup_by
[`VecDeque::resize`]:  https://doc.rust-lang.org/std/collections/vec_deque/struct.VecDeque.html#method.resize
[`VecDeque::truncate`]: https://doc.rust-lang.org/std/collections/vec_deque/struct.VecDeque.html#method.truncate
[`str::repeat`]: https://doc.rust-lang.org/std/primitive.str.html#method.repeat
[`str::replacen`]: https://doc.rust-lang.org/std/primitive.str.html#method.replacen
[cargo/3296]: rust-lang/cargo#3296
[cargo/3301]: rust-lang/cargo#3301
[cargo/3443]: rust-lang/cargo#3443
[cargo/3511]: rust-lang/cargo#3511
[cargo/3515]: rust-lang/cargo#3515
[cargo/3534]: rust-lang/cargo#3534
[cargo/3546]: rust-lang/cargo#3546
[cargo/3557]: rust-lang/cargo#3557
[cargo/3604]: rust-lang/cargo#3604
[RFC 1623]: https://github.com/rust-lang/rfcs/blob/master/text/1623-static.md
arielb1 added a commit to arielb1/rust that referenced this pull request Jan 13, 2018
the match-checking code used to use TyErr for signaling "unknown,
inhabited" types for a long time. It had been switched to using the
exact type in rust-lang#38069, to handle uninhabited types.

However, in rust-lang#39980, we discovered that we still needed the "unknown
inhabited" logic, but I used `()` instead of `TyErr` to handle that.
Revert to using `TyErr` to fix that problem.
bors added a commit that referenced this pull request Jan 21, 2018
check_match: fix handling of privately uninhabited types

the match-checking code used to use TyErr for signaling "unknown,
inhabited" types for a long time. It had been switched to using the
exact type in #38069, to handle uninhabited types.

However, in #39980, we discovered that we still needed the "unknown
inhabited" logic, but I used `()` instead of `TyErr` to handle that.
Revert to using `TyErr` to fix that problem.

Fixes #46964.

r? @nikomatsakis
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
relnotes Marks issues that should be documented in the release notes of the next release.
Projects
None yet
Development

Successfully merging this pull request may close these issues.