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

enum size lint #14300

Merged
merged 4 commits into from
May 26, 2014
Merged

enum size lint #14300

merged 4 commits into from
May 26, 2014

Conversation

emberian
Copy link
Member

See commits for details.

}

let mut sizes = sizes.move_iter().enumerate().collect::<Vec<(uint, u64)>>();
sizes.sort_by(|&(_, a), &(_, b)| b.cmp(&a));
Copy link
Contributor

Choose a reason for hiding this comment

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

A full sort is unnecessary. You can just iterate the sizes once and track the two largest values.

let (a,b) = sizes.iter().fold((0, 0), |(a,b), &(_,sz)| if sz > a { (sz,a) } else if sz > b { (a,sz) } else { (a,b) });
if b > 0 && a > b*3 {
    // ...

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, but that's, like, work!

@lilyball
Copy link
Contributor

I want to write a lint that needs information from trans, similar to what you're doing here. Would it be possible to add support for post-trans lints as @huonw suggested in #10362, instead of trying to teach trans special behavior for specific lints?


/// Level of EnumSizeVariance lint for each enum, stored here because the
/// body of the lint needs to run in trans.
enum_levels: HashMap<ast::NodeId, (level, LintSource)>,
Copy link
Member

Choose a reason for hiding this comment

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

Maybe this could be something like

node_levels: HashMap<(ast::NodeId, Lint), (level, LintSource)>

to allow other trans/post-trans lints naturally. (This could easily be a case of YAGNI though.)

@emberian
Copy link
Member Author

@kballard @huonw My first iteration of this PR was a separate lint pass that re-walked the AST, but with the trans context. It ended up being pretty gnarly and with lots of duplication. With the suggested node_levels, which nodes are you going to track, and for which lints? There are over 150000 nodes in a given build of rustc, and you need to decide what you are going to track. Of course, it could be done per-lint... it might not be awful.

@huonw
Copy link
Member

huonw commented May 20, 2014

Yeah, I was thinking only those lints that trans is actually interested in (so ATM it would just be storing (node_id, EnumSizeVariance)).

@lilyball
Copy link
Contributor

@cmr I'm interested in having a lint that enforces that a given enum (marked with an attribute) always benefits from the null pointer optimization. And by that I mean it's an enum that takes a type parameter and I want to flag any uses of the enum that result in a concrete type that's not null-pointer-optimized. I would then add this attribute to libc::Nullable, and promote that type as the right type to use for nullable functions in extern blocks. We're currently recommending using Option, but this use only works if we can guarantee that it's null-pointer-optimized, and right now we make no such guarantee.

More specifically, I'd want to ensure that it maps to RawNullablePointer (as opposed to StructWrappedNullablePointer), although that part shouldn't really be in question (as it's a function of the number of fields of the non-nullary variant), but it is part of the guarantee we need to make for correct FFI.

Note that this lint also requires knowing which enums to consider. Implementing this on top of your current hacky work (assuming node_levels instead of enum_levels) could be done by only recording the level in the map if the enum is marked with the attribute, and otherwise ignoring it. That way trans would consider any non-annotated enums to be Allow even though the enum itself would default to either warn or error.

@lilyball
Copy link
Contributor

Something to consider is that, if we ever get pluggable lints (which I'd really like to have), then special-casing the specific lints that need post-trans info won't work very well (i.e. the pluggable lints won't be able to be special-cased).

i: &mut uint) {
let mut sizes = Vec::new(); // does no allocation if no pushes, thankfully

let care_about_size = ccx.tcx.enum_lint_levels.borrow().find(&id).is_some();
Copy link
Contributor

Choose a reason for hiding this comment

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

You seem to be doing the work of testing every single enum even if the level is allow, and you only check to see if it's actually allow down on line 1587. You should make sure it's not allow right here.

@emberian
Copy link
Member Author

@kballard it's true that pluggable lints are going to have a painful life. But, they can always do a separate walk through the AST. This is really just an optimization.

("enum_size_variance",
LintSpec {
lint: EnumSizeVariance,
desc: "detects enum swith widely varying variant sizes",
Copy link
Member

Choose a reason for hiding this comment

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

s/swith/with/

@huonw
Copy link
Member

huonw commented May 20, 2014

Does this handle generic enums? At the very least, it shouldn't ICE (i.e. testcase please).

@emberian
Copy link
Member Author

It runs on all of the current rust source (it was warn by default) runs
with it. I'll add tests.

On Mon, May 19, 2014 at 10:53 PM, Huon Wilson notifications@github.comwrote:

Does this handle generic enums? At the very least, it shouldn't ICE (i.e.
testcase please).


Reply to this email directly or view it on GitHubhttps://github.com//pull/14300#issuecomment-43588753
.

http://octayn.net/

@emberian
Copy link
Member Author

I believe I've addressed the concerns above, re-review please?


let care_about_size = match ccx.tcx.node_lint_levels.borrow()
.find(&(id, lint::VariantSizeDifference)) {
None => true,
Copy link
Contributor

Choose a reason for hiding this comment

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

The default is allow, so why are you assuming that no entry at all means you should care about the size? You're just going to end up deciding it's allow later and not emitting the lint.

@lilyball
Copy link
Contributor

You're still sorting when you could just be folding to find the two largest sizes. I even gave you the code for it.

You're also still inserting an entry in the node map for every enum. That just seems like a waste of memory, when you could only insert non-allow nodes.

@emberian
Copy link
Member Author

Ah yes, sorry.

@emberian
Copy link
Member Author

@kballard updated


// we only warn if the largest variant is at least thrice as large as
// the second-largest.
if sizes.iter().count(|&x| x != 0) > 2 {
Copy link
Contributor

Choose a reason for hiding this comment

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

You don't need this count. If slargest > 0 then you obviously have at least 2 non-zero sizes.

@huonw
Copy link
Member

huonw commented May 21, 2014

BTW, there's actually already min_max for iterators. (The return value has the .into_option helper too.)

@emberian
Copy link
Member Author

I want the index of the largest element, so that doesn't seem super useful?

On Wed, May 21, 2014 at 6:22 AM, Huon Wilson notifications@github.comwrote:

BTW, there's actually already min_max for iteratorshttp://static.rust-lang.org/doc/master/core/iter/trait.OrdIterator.html#tymethod.min_max.
(The return value has the .into_option helper too.)


Reply to this email directly or view it on GitHubhttps://github.com//pull/14300#issuecomment-43753052
.

http://octayn.net/

@lilyball
Copy link
Contributor

Not just the index, you also want the two largest values, not the largest and smallest.

It can be easy to accidentally bloat the size of an enum by making one variant
larger than the others. When this happens, it usually goes unnoticed. This
commit adds a lint that can warn when the largest variant in an enum is more
than 3 times larger than the second-largest variant. This requires a little
bit of rejiggering, because size information is only available in trans, but
lint levels are only available in the lint context.

It is allow by default because it's pretty noisy, and isn't really *that*
undesirable.

Closes #10362
bors added a commit that referenced this pull request May 25, 2014
The compiler now tracks which attributes were actually looked at during the compilation process and warns for those that were unused.

Some things of note:

* The tracking is done via thread locals, as it made the implementation more straightforward. Note that this shouldn't hamper any future parallelization as each task can have its own thread local state which can be merged for the lint pass. If there are serious objections to this, I can restructure things to explicitly pass the state around.
* There are a number of attributes that have to be special-cased and globally whitelisted. This happens for four reasons:
  * The `doc` and `automatically_derived` attributes are used by rustdoc, but not by the compiler.
  * The crate-level attributes `license`, `desc` and `comment` aren't currently used by anything.
  * Stability attributes as well as `must_use` are checked only when the tagged item is used, so we can't guarantee that the compiler's looked at them.
  * 12 attributes are used only in trans, which happens after the lint pass.

#14300 is adding infrastructure to track lint state through trans, which this lint should also be able to use to handle the last case. For the other attributes, the right solution would probably involve a specific pass to mark uses that occur in the correct context. For example, a `doc` attribute attached to a match arm should generate a warning, but will not currently.

RFC: 0002-attribute-usage
bors added a commit that referenced this pull request May 26, 2014
@bors bors closed this May 26, 2014
@bors bors merged commit d8467e2 into rust-lang:master May 26, 2014
lnicola pushed a commit to lnicola/rust that referenced this pull request Mar 13, 2023
fix: Watch both stdout and stderr in flycheck

Fixes rust-lang#14217

This isn't great because it un-mixes the messages from the two streams, but maybe it's not such a big problem?
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.

None yet

5 participants