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

RFC 2008: Uninhabitedness fixes for enum variants and tests #60529

Merged
merged 5 commits into from May 10, 2019

Conversation

Projects
None yet
5 participants
@davidtwco
Copy link
Member

commented May 4, 2019

Part of #44109.

At the request of @Centril, this PR adds tests asserting that uninhabited non-exhaustive types are considered inhabited in extern crates. In adding these tests, I fixed an oversight in the implementation of RFC 2008 on enum variants that resulted in non-exhaustive enum variants being considered uninhabited in extern crates.

Before this PR, these lines would error:

// extern crate
pub enum UninhabitedVariants {
    #[non_exhaustive] Tuple(!),
    #[non_exhaustive] Struct { x: ! }
}

pub enum PartiallyInhabitedVariants {
    Tuple(u8),
    #[non_exhaustive] Struct { x: ! }
}

// current crate
match uninhabited_variant() /* fn() -> Option<UninhabitedVariants> */ {
    Some(_x) => (), //~ ERROR unreachable pattern
    None => (),
}

while let PartiallyInhabitedVariants::Struct { x, .. } = partially_inhabited_variant() /* fn() -> PartiallyInhabitedVariants */ {
    //~^ ERROR unreachable pattern
}

cc @Centril
r? @petrochenkov

Show resolved Hide resolved src/librustc/ty/inhabitedness/mod.rs Outdated
Show resolved Hide resolved src/test/ui/rfc-2008-non-exhaustive/uninhabited.rs Outdated

@davidtwco davidtwco force-pushed the davidtwco:rfc-2008-uninhabited branch 2 times, most recently from 805a891 to 2121cc7 May 4, 2019

@davidtwco davidtwco force-pushed the davidtwco:rfc-2008-uninhabited branch from 2121cc7 to 5aa1f64 May 4, 2019

@rust-highfive

This comment was marked as resolved.

Copy link
Collaborator

commented May 4, 2019

The job x86_64-gnu-llvm-6.0 of your PR failed on Travis (raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
travis_time:end:04662d46:start=1556968104256625746,finish=1556968188989629572,duration=84733003826
$ git checkout -qf FETCH_HEAD
travis_fold:end:git.checkout

Encrypted environment variables have been removed for security reasons.
See https://docs.travis-ci.com/user/pull-requests/#pull-requests-and-security-restrictions
$ export SCCACHE_BUCKET=rust-lang-ci-sccache2
$ export SCCACHE_REGION=us-west-1
$ export GCP_CACHE_BUCKET=rust-lang-ci-cache
$ export AWS_ACCESS_KEY_ID=AKIA46X5W6CZEJZ6XT55
---

[00:03:56] travis_fold:start:tidy
travis_time:start:tidy
tidy check
[00:03:57] tidy error: /checkout/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs: leading newline
[00:04:01] some tidy checks failed
[00:04:01] 
[00:04:01] 
[00:04:01] command did not execute successfully: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-tools-bin/tidy" "/checkout/src" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "--no-vendor" "--quiet"
[00:04:01] 
[00:04:01] 
[00:04:01] failed to run: /checkout/obj/build/bootstrap/debug/bootstrap test src/tools/tidy
[00:04:01] Build completed unsuccessfully in 0:01:04
[00:04:01] Build completed unsuccessfully in 0:01:04
[00:04:01] Makefile:67: recipe for target 'tidy' failed
[00:04:01] make: *** [tidy] Error 1
The command "stamp sh -x -c "$RUN_SCRIPT"" exited with 2.
travis_time:start:10105968
$ date && (curl -fs --head https://google.com | grep ^Date: | sed 's/Date: //g' || true)
Sat May  4 11:13:59 UTC 2019
---
travis_time:end:03c55834:start=1556968440252678166,finish=1556968440257504463,duration=4826297
travis_fold:end:after_failure.3
travis_fold:start:after_failure.4
travis_time:start:1d5d466c
$ ln -s . checkout && for CORE in obj/cores/core.*; do EXE=$(echo $CORE | sed 's|obj/cores/core\.[0-9]*\.!checkout!\(.*\)|\1|;y|!|/|'); if [ -f "$EXE" ]; then printf travis_fold":start:crashlog\n\033[31;1m%s\033[0m\n" "$CORE"; gdb --batch -q -c "$CORE" "$EXE" -iex 'set auto-load off' -iex 'dir src/' -iex 'set sysroot .' -ex bt -ex q; echo travis_fold":"end:crashlog; fi; done || true
travis_fold:end:after_failure.4
travis_fold:start:after_failure.5
travis_time:start:17243bf4
travis_time:start:17243bf4
$ cat ./obj/build/x86_64-unknown-linux-gnu/native/asan/build/lib/asan/clang_rt.asan-dynamic-i386.vers || true
cat: ./obj/build/x86_64-unknown-linux-gnu/native/asan/build/lib/asan/clang_rt.asan-dynamic-i386.vers: No such file or directory
travis_fold:end:after_failure.5
travis_fold:start:after_failure.6
travis_time:start:05f2cfa0
$ dmesg | grep -i kill

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @TimNN. (Feature Requests)

@davidtwco davidtwco force-pushed the davidtwco:rfc-2008-uninhabited branch from 5aa1f64 to ca6e9c6 May 4, 2019

@davidtwco davidtwco force-pushed the davidtwco:rfc-2008-uninhabited branch from ca6e9c6 to 09e3e43 May 4, 2019

@Centril

Centril approved these changes May 4, 2019

Copy link
Member

left a comment

Love the barrage of tests! :)

@petrochenkov

This comment has been minimized.

Copy link
Contributor

commented May 4, 2019

I don't think I understand the examples.

pub enum UninhabitedVariants {
    #[non_exhaustive] Struct { x: ! }
}

The type already has a public uninhabited field, so no amount of added fields will make it inhabited, so it's publicly uninhabited for this crate or for other crates, doesn't matter.

#[non_exhaustive] on enums can affect inhabitedness, because new variants can make the enum inhabited, but for structs/variants that's not the case.

@Centril

This comment has been minimized.

Copy link
Member

commented May 5, 2019

but for structs/variants that's not the case.

If you have exhaustive_patterns, then the type system would let you match x: UninhabitedStruct {} on an otherwise uninhabited struct or variant (at least with rust-lang/rfcs#2593 for the latter). However, until we have collectively decided otherwise, I think #[non_exhaustive] should prevent that in other crates. It might be the case that we use a different mechanism in the end, but let's be conservative for now and allow exhaustive pattern matching deliberately in these cases. The use case for this is when you have something like a struct TcpStream(!); on some platforms where it cannot be constructed and where it can be constructed on other platforms.

@petrochenkov

This comment has been minimized.

Copy link
Contributor

commented May 5, 2019

#[non_exhaustive] implies that new fields can be added to a struct, so here are the possible cases of new fields affecting the struct inhabitedness as it's seen from other crates:

// The struct is externally inhabited, the field addition doesn't change that
struct S { a: PrivDoesntmatter } -> struct S { a: PrivDoesntmatter, b: PrivDoesntmatter }

// The struct is externally inhabited, the field addition doesn't change that
struct S { a: PrivDoesntmatter } -> struct S { a: PrivDoesntmatter, pub b: PubInhabited }

// The struct is externally inhabited, the field addition changes that
struct S { a: PrivDoesntmatter } -> struct S { a: PrivDoesntmatter, pub b: PubUninhabited }

---

// The struct is externally inhabited, the field addition doesn't change that
struct S { pub a: PubInhabited } -> struct S { pub a: PubInhabited, b: PrivDoesntmatter }

// The struct is externally inhabited, the field addition doesn't change that
struct S { pub a: PubInhabited } -> struct S { pub a: PubInhabited, pub b: PubInhabited }

// The struct is externally inhabited, the field addition changes that
struct S { pub a: PubInhabited } -> struct S { pub a: PubInhabited, pub b: PubUninhabited }

---

// The struct is externally uninhabited, the field addition doesn't change that
struct S { pub a: PubUninhabited } -> struct S { pub a: PubInhabited, b: PrivDoesntmatter }

// The struct is externally uninhabited, the field addition doesn't change that
struct S { pub a: PubUninhabited } -> struct S { pub a: PubInhabited, pub b: PubInhabited }

// The struct is externally uninhabited, the field addition doesn't change that
struct S { pub a: PubUninhabited } -> struct S { pub a: PubInhabited, pub b: PubUninhabited }

The situation with variants is a bit simpler since they don't have private fields:

// The variant is externally inhabited, the field addition doesn't change that
variant S { pub a: PubInhabited } -> variant S { pub a: PubInhabited, pub b: PubInhabited }

// The variant is externally inhabited, the field addition changes that
variant S { pub a: PubInhabited } -> variant S { pub a: PubInhabited, pub b: PubUninhabited }

---

// The variant is externally uninhabited, the field addition doesn't change that
variant S { pub a: PubUninhabited } -> variant S { pub a: PubInhabited, pub b: PubInhabited }

// The variant is externally uninhabited, the field addition doesn't change that
variant S { pub a: PubUninhabited } -> variant S { pub a: PubInhabited, pub b: PubUninhabited }

So, if #[non_exhaustive] does nothing with inhabitedness, then adding a new public uninhabited field to a previously inhabited struct or variant is a breaking change (kind of, unreachable_patterns is only a lint).

Is adding a new public uninhabited field to something previously inhabited a realistic scenario?

(The struct TcpStream(!); is already covered by privacy, doesn't need #[non_exhaustive].)

@petrochenkov

This comment has been minimized.

Copy link
Contributor

commented May 5, 2019

Any other properties that can change if a new field is added?
Something Drop-related perhaps?

@petrochenkov

This comment has been minimized.

Copy link
Contributor

commented May 9, 2019

Ok, I had some time to digest this and I agree that if addition of a new public uninhabited field can break code, then #[non_exhaustive] should prevent that.

@davidtwco davidtwco force-pushed the davidtwco:rfc-2008-uninhabited branch from 09e3e43 to f9e4da0 May 9, 2019

@davidtwco

This comment has been minimized.

Copy link
Member Author

commented May 9, 2019

@petrochenkov I've fixed all of your suggestions. I can't relabel from S-waiting-on-author to S-waiting-on-review.

@petrochenkov

This comment has been minimized.

Copy link
Contributor

commented May 10, 2019

@bors r+

@bors

This comment has been minimized.

Copy link
Contributor

commented May 10, 2019

📌 Commit f9e4da0 has been approved by petrochenkov

@bors

This comment has been minimized.

Copy link
Contributor

commented May 10, 2019

⌛️ Testing commit f9e4da0 with merge 1865e9b...

bors added a commit that referenced this pull request May 10, 2019

Auto merge of #60529 - davidtwco:rfc-2008-uninhabited, r=petrochenkov
RFC 2008: Uninhabitedness fixes for enum variants and tests

Part of #44109.

At the request of @Centril, this PR adds tests asserting that uninhabited non-exhaustive types are considered inhabited in extern crates. In adding these tests, I fixed an oversight in the implementation of RFC 2008 on enum variants that resulted in non-exhaustive enum variants being considered uninhabited in extern crates.

Before this PR, these lines would error:

```rust
// extern crate
pub enum UninhabitedVariants {
    #[non_exhaustive] Tuple(!),
    #[non_exhaustive] Struct { x: ! }
}

pub enum PartiallyInhabitedVariants {
    Tuple(u8),
    #[non_exhaustive] Struct { x: ! }
}

// current crate
match uninhabited_variant() /* fn() -> Option<UninhabitedVariants> */ {
    Some(_x) => (), //~ ERROR unreachable pattern
    None => (),
}

while let PartiallyInhabitedVariants::Struct { x, .. } = partially_inhabited_variant() /* fn() -> PartiallyInhabitedVariants */ {
    //~^ ERROR unreachable pattern
}
```

cc @Centril
r? @petrochenkov
@bors

This comment has been minimized.

Copy link
Contributor

commented May 10, 2019

💔 Test failed - status-appveyor

davidtwco added some commits May 4, 2019

Add uninhabitedness tests w/ `#[non_exhaustive]`.
This commit adds tests checking that uninhabited non-exhaustive types
are considered inhabited when used in another crate.
Fix inhabitedness of non-exhaustive variants.
This commit ensures that non-exhaustive variants are considered
inhabited when used in extern crates.
Fix uninhabitedness of non-exhaustive enums.
This commit ensures that non-exhaustive enums are considered inhabited
when used in extern crates.
Move uninhabited tests into subdirectory.
This commit just tries to tidy up a little.

@davidtwco davidtwco force-pushed the davidtwco:rfc-2008-uninhabited branch from f9e4da0 to 1f0fb03 May 10, 2019

@davidtwco

This comment has been minimized.

Copy link
Member Author

commented May 10, 2019

I've rebased this and updated the tests with longer names to use the same fix as #60648 to avoid spurious failures on Windows.

@petrochenkov

This comment has been minimized.

Copy link
Contributor

commented May 10, 2019

Legitimate error - file paths are too long (ui\rfc-2008-non-exhaustive\uninhabited\indirect_match_with_exhaustive_patterns_same_crate.rs).

@petrochenkov

This comment has been minimized.

Copy link
Contributor

commented May 10, 2019

@bors r+

@bors

This comment has been minimized.

Copy link
Contributor

commented May 10, 2019

📌 Commit 1f0fb03 has been approved by petrochenkov

Centril added a commit to Centril/rust that referenced this pull request May 10, 2019

Rollup merge of rust-lang#60529 - davidtwco:rfc-2008-uninhabited, r=p…
…etrochenkov

RFC 2008: Uninhabitedness fixes for enum variants and tests

Part of rust-lang#44109.

At the request of @Centril, this PR adds tests asserting that uninhabited non-exhaustive types are considered inhabited in extern crates. In adding these tests, I fixed an oversight in the implementation of RFC 2008 on enum variants that resulted in non-exhaustive enum variants being considered uninhabited in extern crates.

Before this PR, these lines would error:

```rust
// extern crate
pub enum UninhabitedVariants {
    #[non_exhaustive] Tuple(!),
    #[non_exhaustive] Struct { x: ! }
}

pub enum PartiallyInhabitedVariants {
    Tuple(u8),
    #[non_exhaustive] Struct { x: ! }
}

// current crate
match uninhabited_variant() /* fn() -> Option<UninhabitedVariants> */ {
    Some(_x) => (), //~ ERROR unreachable pattern
    None => (),
}

while let PartiallyInhabitedVariants::Struct { x, .. } = partially_inhabited_variant() /* fn() -> PartiallyInhabitedVariants */ {
    //~^ ERROR unreachable pattern
}
```

cc @Centril
r? @petrochenkov

bors added a commit that referenced this pull request May 10, 2019

Auto merge of #60708 - Centril:rollup-j5smdo0, r=Centril
Rollup of 6 pull requests

Successful merges:

 - #60529 (RFC 2008: Uninhabitedness fixes for enum variants and tests)
 - #60620 (Fix a couple of FIXMEs in ext::tt::transcribe)
 - #60659 (Tweak `Symbol` and `InternedString`)
 - #60692 (Extend #60676 test for nested mut patterns.)
 - #60697 (add regression test for #60629)
 - #60701 (Update mailmap for mati865)

Failed merges:

r? @ghost

@bors bors merged commit 1f0fb03 into rust-lang:master May 10, 2019

1 check passed

Travis CI - Pull Request Build Passed
Details

@davidtwco davidtwco deleted the davidtwco:rfc-2008-uninhabited branch May 12, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.