Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upPattern reachability algorithm fails in the presence of uninhabited types #12609
Comments
glaebhoerl
referenced this issue
Feb 27, 2014
Closed
What are semantics of empty enums that have been artificially fabricated? #4499
This comment has been minimized.
This comment has been minimized.
|
cc @brson @nikomatsakis @pcwalton (not really sure who I should be cc-ing): I think this is a backwards compatibility issue in the respect that fixing this bug will cause fewer programs to be accepted. We should make it clear that pattern matching uninhabited types with wildcard patterns is not legal according to the language spec, that it being currently accepted is a compiler bug, and that we reserve the right to fix the bug in the future (if/when we figure out how to). See examples above if the rationale isn't clear. |
This comment has been minimized.
This comment has been minimized.
ghost
commented
Jul 1, 2014
|
Well, if anything, this would require extending the definition of an empty type to include sum types with all members being empty and product types with at least one empty operand. |
This comment has been minimized.
This comment has been minimized.
|
Yes, this is implied by the term "uninhabited type". (Or do you mean that there's a definition of an "empty type" in some official spec that needs updating?) |
This comment has been minimized.
This comment has been minimized.
|
I agree with @glaebhoerl, though this is clearly a corner case. I wonder if we should remove disallow uninhabited enums and make our lives easier? |
This comment has been minimized.
This comment has been minimized.
|
/me cannot help but point at #4499 (comment) (though arguably feature-gating is quite different from outright removal...) |
This comment has been minimized.
This comment has been minimized.
|
Nominating, P-backcompat-lang. See #12609 (comment) for the basis. |
pnkfelix
added
the
I-nominated
label
Jul 3, 2014
This comment has been minimized.
This comment has been minimized.
|
Assigning P-backcompat-lang, 1.0 milestone. (There are many interesting ways to resolve this, and some not-so-interesting ways.) |
pnkfelix
added
the
P-backcompat-lang
label
Jul 3, 2014
pnkfelix
added this to the 1.0 milestone
Jul 3, 2014
pnkfelix
removed
the
I-nominated
label
Jul 3, 2014
This comment has been minimized.
This comment has been minimized.
|
Uninhabited enums are useful as phantom type parameters. |
This comment has been minimized.
This comment has been minimized.
|
OCaml has the same behavior as Rust today:
|
This comment has been minimized.
This comment has been minimized.
|
SML/NJ ICE's:
|
This comment has been minimized.
This comment has been minimized.
|
@pcwalton that's hilarious |
This comment has been minimized.
This comment has been minimized.
|
I was going to try Scala and Haskell but there is no such thing as an uninhabited type in Scala because of Given this exciting romp through languages, I'm going to vote that we go with the only other working compiler that handles this case, and close this as not a bug. Nominating for closure. |
pcwalton
added
the
I-nominated
label
Jul 4, 2014
This comment has been minimized.
This comment has been minimized.
|
Why is that preferable to saying that it is a bug that we may, at our option, fix in the future? I mean, we can even change our minds later if we convince ourselves that it's impossible. But not vice versa. |
This comment has been minimized.
This comment has been minimized.
ghost
commented
Jul 4, 2014
|
GHC does check exhaustiveness but it produces a warning. And it indeed doesn't handle this properly: import Unsafe.Coerce
data Void
f :: Maybe Void -> Int
f Nothing = 0
main :: IO ()
main = putStr . show . f . unsafeCoerce $ 0complains with:
|
This comment has been minimized.
This comment has been minimized.
|
@jakub- Only when you compile with |
This comment has been minimized.
This comment has been minimized.
|
This survey of other languages is very interesting, but I don't see why it's very relevant. The correct behavior is not in question. Just because other implementations also have the bug, that doesn't mean it's not a bug. |
This comment has been minimized.
This comment has been minimized.
|
@pcwalton I'd rather feature-guard uninhabited enums than commit forever (or at least for 1.x) to our current implementation of how they are handled in |
This comment has been minimized.
This comment has been minimized.
|
As a middle ground: would it be reasonable to just feature gate |
This comment has been minimized.
This comment has been minimized.
|
@huonw depending on how one interprets "whenever |x: Option<Uninhabited>| match x { ... }is caught by the net.) |
This comment has been minimized.
This comment has been minimized.
ghost
commented
Jul 4, 2014
|
@pnkfelix Feature-gating uninhabited enums will not be sufficient as the same problem applies to !.
I think feature gating |
This comment has been minimized.
This comment has been minimized.
|
@jakub- I do not think In the example you gave, bottom is unified with If you revise your example to avoid unifying |
This comment has been minimized.
This comment has been minimized.
ghost
commented
Jul 4, 2014
|
Right, thanks! In that case, I don't have a strong opinion other than that
|
This comment has been minimized.
This comment has been minimized.
|
To pose the question once more: It's safe to assume that the 1.0 compiler will have bugs. I hope it's also safe to assume that we won't hesitate to fix those bugs, even if doing so has the side effect of breaking programs which relied on the buggy behavior. Why isn't the easiest and most appropriate solution to simply note that this is a bug, and move on? We don't have to fix it before 1.0. We don't have to fix it next year. All we have to do is to say that it's a bug. |
This comment has been minimized.
This comment has been minimized.
|
Following similar reasoning to what @glaebhoerl presented above: We are not going to block 1.0 on resolving this issue. It is a P-backcompat-lang issue, but it is one that we feel prepared to handle (or explicitly not handle, i.e. leave things as they are), post 1.0, in some manner. P-backcompat-lang, not 1.0 milestone. |
This comment has been minimized.
This comment has been minimized.
|
Triage: nominating for retriage. Seems like a serious issue without updates in a long time. cc @rust-lang/lang @rust-lang/compiler . |
brson
added
the
T-compiler
label
Jul 14, 2016
This comment has been minimized.
This comment has been minimized.
|
Lang team discussed, and we don't feel that this is a very serious issue -- in particular, this only comes up when matching against an empty type, which is very rare to begin with, and the lack of exhaustiveness checking there is unlikely to hide any serious bugs. P-low. |
aturon
added
P-low
and removed
I-nominated
P-medium
labels
Jul 14, 2016
This comment has been minimized.
This comment has been minimized.
|
Not just matching directly on an empty (uninhabited) type, but also on a composite type involving one. In particular types like (Not disputing the priority, which I have no opinion on, only the characterization.) |
This comment has been minimized.
This comment has been minimized.
|
@glaebhoerl Yes, my summary was a little too brief -- the match against an empty type could occur in the context of another type. It is a good point that the introduction of |
This was referenced Aug 22, 2016
This comment has been minimized.
This comment has been minimized.
|
I think this should be fixed before |
This comment has been minimized.
This comment has been minimized.
|
Some concrete motivation for that: for user-defined uninhabited |
This comment has been minimized.
This comment has been minimized.
|
@canndrew I added this as a note to the |
canndrew
added a commit
to canndrew/rust
that referenced
this issue
Sep 14, 2016
This comment has been minimized.
This comment has been minimized.
|
I've had a crack at implementing this here: #36476 Also, the change I've implemented allows people to omit patterns that are unreachable due to uninhabited types but doesn't force them to. This is important for backwards-compatibility but also because I think code like this: let res: Result<T, E> = ...;
match res {
Ok(t) => ...,
Err(e) => ...,
}should be allowed to compile even when |
canndrew
added a commit
to canndrew/rust
that referenced
this issue
Sep 14, 2016
canndrew
added a commit
to canndrew/rust
that referenced
this issue
Sep 26, 2016
canndrew
added a commit
to canndrew/rust
that referenced
this issue
Oct 6, 2016
This was referenced Nov 28, 2016
bors
added a commit
that referenced
this issue
Nov 30, 2016
bors
added a commit
that referenced
this issue
Jan 5, 2017
bors
added a commit
that referenced
this issue
Jan 5, 2017
bors
added a commit
that referenced
this issue
Jan 6, 2017
This comment has been minimized.
This comment has been minimized.
|
So should this be closed, or do we wait for it to hit stable? |
This comment has been minimized.
This comment has been minimized.
|
We can close. |
glaebhoerl commentedFeb 27, 2014
From
check_match.rs:This results in, most prominently (#4499), a
matchon an empty enum with a wildcard pattern being accepted:while if you add a variant to the enum and a corresponding arm to the match:
rustc rejects the program with
error: unreachable pattern.Clearly a trailing wildcard pattern after you've already matched all possible values in preceding arms should always be considered unreachable, and this is true even when the number of potential values, and hence preceding arms, are both zero.
As the comment notes the algorithm we use relies on "no empty types" as an assumption, so this may not be an easy bug, but it's still a bug.