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

Don't allow static items to have types with destructors #11979

Merged
merged 8 commits into from
Feb 28, 2014

Conversation

flaper87
Copy link
Contributor

@flaper87 flaper87 commented Feb 1, 2014

This pull request partially addresses the 2 issues listed before. As part of the work required for this PR, NonCopyable was completely removed.

This PR also replaces the content of type_is_pod with TypeContents::is_pod, although type_is_content is currently not being used anywhere. I kept it for consistency with the other functions that exist in this module.

cc #10834
cc #10577

Proposed static restrictions

Taken from this comment.

I expect some code that, at a high-level, works like this:

  • For each mutable static item, check that the type:
    • cannot own any value whose type has a dtor
    • cannot own any values whose type is an owned pointer
  • For each immutable static item, check that the value:
    • does not contain any ~ or box expressions (including ~[1, 2, 3] sort of things, for now)
    • does not contain a struct literal or call to an enum variant / struct constructor where
      • the type of the struct/enum is freeze
      • the type of the struct/enum has a dtor

@flaper87
Copy link
Contributor Author

flaper87 commented Feb 1, 2014

@nikomatsakis r?

@flaper87
Copy link
Contributor Author

flaper87 commented Feb 1, 2014

I just realized the tests got lost somewhere. I'll add them right away.

let tcontents = ty::type_contents(cx.tcx, self_ty);
if tcontents.is_static(cx.tcx) && tcontents.has_dtor() {
cx.tcx.sess.span_err(self_type.span,
"Types with destructors can't be static items");
Copy link
Member

Choose a reason for hiding this comment

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

We're trying to have compiler errors start with lowercase letters.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Didn't know that, Consider it done!

@bill-myers
Copy link
Contributor

Why?

Process exit will tear down most resources just fine (otherwise the OS is broken), so there is usually no issue in allowing them and simply never running the destructor.

For things that aren't torn down by process exit, one could add a new Flush trait that gets called using the C++ global destructor mechanism, but only takes an &mut self, is idempotent and does not destroy the object (so that there are no safety issues related to accessing already destroyed global objects in global object destructors).

@alexcrichton
Copy link
Member

We have been very opposed to "life before main" in terms of static initialization, and we're similarly taking the stance of being opposed to "life after main" in terms of static destruction. For us, once you've made the decision to never run destructors on static variables, we figured it reasonable to just forbid it altogether.

@alexcrichton
Copy link
Member

Can you also add a test for an owned type? Some types I would not want in a static:

  • ~str
  • struct Foo { inner: ~str }
  • struct Foo { inner: ValueWithDestructor }

Just to make sure that it's not just checking that the type itself has a destructor but rather it requires a destructor in general.

@flaper87
Copy link
Contributor Author

flaper87 commented Feb 3, 2014

@alexcrichton done. Tests with ~str raise cannot allocate vectors in constant expressions so, I just added one with inner value with destructor.

@alexcrichton
Copy link
Member

Hm, "cannot allocate vectors in constant expressions" is a different error than this would be expecting, what happens if you try to make a static of type Option<~str>?

@flaper87
Copy link
Contributor Author

flaper87 commented Feb 3, 2014

@alexcrichton same error! I think something else is going on there! :/

EDIT: And it's not related to this patch, I'd say. That error is raised also with master.

@alexcrichton
Copy link
Member

This code compiles today, and I just want to make sure that this patch prevents it from compiling:

static foo: Option<~str> = None;

fn main() {}

ItemStatic(self_type, _, _) => {
let self_ty: ty::t = ty::node_id_to_type(cx.tcx, item.id);
let tcontents = ty::type_contents(cx.tcx, self_ty);
if tcontents.is_static(cx.tcx) && tcontents.has_dtor() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Per @alexcrichton's comment, I imagine the correct rule here is simply to check that all static things are POD. This would prohibit Option<~T>, destructors, etc., as well as managed data, which just seems like trouble waiting to happen.

Copy link
Contributor

Choose a reason for hiding this comment

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

This might not be good -- it would rule out static atomics, if we want those. I guess the real answer we want is something more like type_needs_drop but that is a codegen concept, not a language exposed thing (as of yet).

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 atomics need a destructor? Can they just be marked with NoPod now?

Copy link
Member

Choose a reason for hiding this comment

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

See #12016 (destructor => NoPod for atomics)

@nikomatsakis
Copy link
Contributor

I guess the right thing is just to define a category: "safe for static" or something, which is equivalent to !type_needs_drop today. That is, no owned data, no dtors, and no managed data.

@nikomatsakis
Copy link
Contributor

Can you add a case for putting Option<GC<T>> etc into a static?

@flaper87
Copy link
Contributor Author

flaper87 commented Feb 3, 2014

@nikomatsakis done! thanks!

@flaper87
Copy link
Contributor Author

flaper87 commented Feb 5, 2014

Something interesting came up.

The summary of the fix for this issue is that static items won't be allowed for types that own destructors, managed pointers or owned pointers. Although this rule seems reasonable, we've hit some cases where abusing of the lack of this rule was actually useful.

The first case, and perhaps the least invasive one, is the DUMMY_SP static item defined in codemap. It has a field that contains a managed pointer, hence it can't be the type of a static item. This case was replaced with a function DUMMY_SP() and all the references to that static item where changed.

The second case is for the static items defined in rustc::middle::ty . Although this case is very similar to the previous one, it's different in terms of the values being stored in the static item. In this case the enum type sty has a ty_tuple(~[T]) variant, however the static items created don't store that variant.

This last case is an example that suggest enforcing static restrictions on the values as opposed of types as a better solution.

After chatting with @nikomatsakis on IRC, it seems that enforcing these rules on the values is not especially hard. There are some cases to consider for static mut items. We could loosen the rules for immutable static items and make them harder for mutable static items.

I've pushed the latest version of the work. As already mentioned, DUMMY_SP was replaced with DUMMY_SP() whereas the static items in rustc::middle::ty haven't been changed.

Thoughts?

@brson
Copy link
Contributor

brson commented Feb 11, 2014

Is this mergeable now, with potentially more conservative rules than we might want later?

@flaper87
Copy link
Contributor Author

@nikomatsakis r?

let mutable = match mutability {
ast::MutImmutable => {false}
ast::MutMutable => {true}
};
Copy link
Member

Choose a reason for hiding this comment

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

Why not pass around mutability instead of converting it to a bool? It's self-documenting when you match on it vs having to name it mutable

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had an implementation that passed around mutability, I don't recall why I changed it, TBH. I can change it back

@nikomatsakis
Copy link
Contributor

This isn't quite setup how I expect.

I expect some code that, at a high-level, works like this:

  • For each static mut item, check that the type:
    • cannot own any value whose type has a dtor
    • cannot own any values whose type is an owned pointer
    • anything else?
  • For each static item, recurse over the expression and check that it:
    • does not contain any ~ or box expressions (including ~[1, 2, 3] sort of things, for now)
    • does not contain a struct literal or call to an enum variant / struct ctor where:
      • the type of the struct/enum is not freeze
      • the type of the struct/enum has a dtor

Note that we do not examine the type of static imm items at all. The necessary restrictions are all implied by checking the initializer expression instead.

@flaper87
Copy link
Contributor Author

@nikomatsakis thanks for your comment. I now realized that I could've implemented this in other way. I'll revamp this patch.

@nikomatsakis
Copy link
Contributor

@flaper87 ping me when I should re-review

} else {return None;}
}
_ => {}
}
Copy link
Member

Choose a reason for hiding this comment

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

This seems... odd. Structs with destructors are allowed, but enums aren't?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Structs destructors are checked in line L46. The reason we can't use type contents for enums is that it will also consider the variant constructors types and whether those types have a destructor, which is not good for things like this

Copy link
Member

Choose a reason for hiding this comment

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

That sounds like for immutable statics you want to not use the type contents, but for mutable statics you do indeed want to check the type contents.

With this in mind, can you add a test for this:

enum A { A1(~str), B }
static mut foo: A = B; //~error: `A` has a destructor

ast::ExprCall(..) => {
visit::walk_expr(self, e, is_const);
// We also want to check if the enum
// has a destructor
Copy link
Member

Choose a reason for hiding this comment

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

I think tuple struct constructors are also calls, so the comment shouldn't just single out enums

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@nikomatsakis
Copy link
Contributor

ok, I left some comments -- much nicer, almost there but still a bit more refactoring to go I think :)

- For each *mutable* static item, check that the **type**:
    - cannot own any value whose type has a dtor
    - cannot own any values whose type is an owned pointer

- For each *immutable* static item, check that the **value**:
    - does not contain any ~ or box expressions
        (including ~[1, 2, 3] sort of things)
    - does not contain a struct literal or call to an enum
        variant / struct constructor where
        - the type of the struct/enum has a dtor
bors added a commit that referenced this pull request Feb 27, 2014
This pull request partially addresses the 2 issues listed before. As part of the work required for this PR, `NonCopyable` was completely removed.

This PR also replaces the content of `type_is_pod` with `TypeContents::is_pod`, although `type_is_content` is currently not being used anywhere. I kept it for consistency with the other functions that exist in this module.

cc #10834
cc #10577

Proposed static restrictions
=====================

Taken from [this](#11979 (comment)) comment.

I expect some code that, at a high-level, works like this:

- For each *mutable* static item, check that the **type**:
    - cannot own any value whose type has a dtor
    - cannot own any values whose type is an owned pointer
- For each *immutable* static item, check that the **value**:
      - does not contain any ~ or box expressions (including ~[1, 2, 3] sort of things, for now)
      - does not contain a struct literal or call to an enum variant / struct constructor where
          - the type of the struct/enum is freeze
          - the type of the struct/enum has a dtor
bors added a commit that referenced this pull request Feb 28, 2014
This pull request partially addresses the 2 issues listed before. As part of the work required for this PR, `NonCopyable` was completely removed.

This PR also replaces the content of `type_is_pod` with `TypeContents::is_pod`, although `type_is_content` is currently not being used anywhere. I kept it for consistency with the other functions that exist in this module.

cc #10834
cc #10577

Proposed static restrictions
=====================

Taken from [this](#11979 (comment)) comment.

I expect some code that, at a high-level, works like this:

- For each *mutable* static item, check that the **type**:
    - cannot own any value whose type has a dtor
    - cannot own any values whose type is an owned pointer
- For each *immutable* static item, check that the **value**:
      - does not contain any ~ or box expressions (including ~[1, 2, 3] sort of things, for now)
      - does not contain a struct literal or call to an enum variant / struct constructor where
          - the type of the struct/enum is freeze
          - the type of the struct/enum has a dtor
@bors bors merged commit 59a04f5 into rust-lang:master Feb 28, 2014
@huonw
Copy link
Member

huonw commented Feb 28, 2014

Does the infrastructure added here make addressing #5244 easier?

(Awesome work implementing this, BTW.)

@flaper87 flaper87 deleted the static branch March 11, 2014 09:15
flip1995 pushed a commit to flip1995/rust that referenced this pull request Feb 8, 2024
add configuration for [`wildcard_imports`] to ignore certain imports

fixes: rust-lang#11428

changelog: add configuration `ignored-wildcard-imports` for lint [`wildcard_imports`]
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.

8 participants