Skip to content

Fix handling of empty types in patterns. - #38069

Merged
bors merged 22 commits into
rust-lang:masterfrom
canndrew:empty-sub-patterns-again
Jan 6, 2017
Merged

Fix handling of empty types in patterns.#38069
bors merged 22 commits into
rust-lang:masterfrom
canndrew:empty-sub-patterns-again

Conversation

@canndrew

Copy link
Copy Markdown
Contributor

Fix for #12609.

@rust-highfive

Copy link
Copy Markdown
Contributor

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
Comment thread src/librustc_const_eval/_match.rs Outdated

Copy link
Copy Markdown
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.

Comment thread src/librustc_const_eval/diagnostics.rs Outdated

Copy link
Copy Markdown
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
Copy Markdown
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

bluss commented Nov 30, 2016

Copy link
Copy Markdown
Contributor

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

Comment thread src/librustc_const_eval/check_match.rs Outdated

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Contributor

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
Copy Markdown
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
canndrew force-pushed the empty-sub-patterns-again branch from 603dff6 to e885c88 Compare December 1, 2016 03:42
@canndrew

canndrew commented Dec 1, 2016

Copy link
Copy Markdown
Contributor Author

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

canndrew commented Dec 1, 2016

Copy link
Copy Markdown
Contributor Author

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

canndrew commented Dec 1, 2016

Copy link
Copy Markdown
Contributor Author

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

bors commented Dec 2, 2016

Copy link
Copy Markdown
Collaborator

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

@canndrew
canndrew force-pushed the empty-sub-patterns-again branch from 8a09338 to b9e51f9 Compare December 3, 2016 04:56
@canndrew

canndrew commented Dec 6, 2016

Copy link
Copy Markdown
Contributor Author

@nikomatsakis this is ready now btw.

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

@nikomatsakis

Copy link
Copy Markdown
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
Copy Markdown
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?

Comment thread src/test/compile-fail/issue-12116.rs Outdated

Copy link
Copy Markdown
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)]

Comment thread src/librustc_const_eval/_match.rs Outdated

Copy link
Copy Markdown
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

canndrew commented Dec 11, 2016

Copy link
Copy Markdown
Contributor Author

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
Copy Markdown
Contributor Author

Anyone know what happened to travis there?

@eddyb

eddyb commented Dec 11, 2016

Copy link
Copy Markdown
Contributor

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

@canndrew

Copy link
Copy Markdown
Contributor Author

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

@nikomatsakis

Copy link
Copy Markdown
Contributor

@canndrew back now, sorry about that.

@nikomatsakis

Copy link
Copy Markdown
Contributor

Will review.

@nikomatsakis

Copy link
Copy Markdown
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
Copy Markdown
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.

@eddyb

eddyb commented Jan 5, 2017

Copy link
Copy Markdown
Contributor

@bors r=nikomatsakis force

@eddyb

eddyb commented Jan 5, 2017

Copy link
Copy Markdown
Contributor

@bors retry

@eddyb

eddyb commented Jan 5, 2017

Copy link
Copy Markdown
Contributor

@bors r=nikomatsakis

@bors

bors commented Jan 5, 2017

Copy link
Copy Markdown
Collaborator

📌 Commit 275c19d has been approved by nikomatsakis

@bors

bors commented Jan 6, 2017

Copy link
Copy Markdown
Collaborator

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

@bors

bors commented Jan 6, 2017

Copy link
Copy Markdown
Collaborator

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

@tomaka

tomaka commented Jan 11, 2017

Copy link
Copy Markdown
Contributor

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
Copy Markdown
Member

Done.

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.