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

Avoid stack overflow when checking struct/enum representability #11839

Merged
merged 3 commits into from
Jan 30, 2014

Conversation

isabelmu
Copy link
Contributor

It was possible to trigger a stack overflow in rustc because the routine used to verify enum representability,
type_structurally_contains, would recurse on inner types until hitting the original type. The overflow condition was when a different structurally recursive type (enum or struct) was contained in the type being checked.

I suspect my solution isn't as efficient as it could be. I pondered adding a cache of previously-seen types to avoid duplicating work (if enums A and B both contain type C, my code goes through C twice), but I didn't want to do anything that may not be necessary.

I'm a new contributor, so please pay particular attention to any unidiomatic code, misuse of terminology, bad naming of tests, or similar horribleness :)

Updated to verify struct representability as well.

Fixes #3008.
Fixes #3779.

let this_item = ReprItem { did: *did, tps: *tps };
for (i, &ref item) in seen.iter().enumerate() {
if this_item == *item {
if i == 0 {
Copy link
Member

Choose a reason for hiding this comment

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

This could be return if i == 0 { SelfRecursive } else { ContainsRecursive }. (Also, the ; at the end of the if here is not required.)

@huonw
Copy link
Member

huonw commented Jan 27, 2014

Looks reasonable to me; this is a cool first contribution!

I'd guess this fixes struct Foo { x: Option<Foo> } too? In which case, you can add that test case and "fixes #3779" to a commit message or the PR text.

(Btw you had "fixes issue #3008" before, but github doesn't recognise that as a special command. It has to be "fixes #3008" without "issue".)

@isabelmu
Copy link
Contributor Author

Oh! Didn't realize #3779 existed. I thought structs were somehow OK (and say as much in a comment somewhere) already. Clearly they aren't. I will add an is_type_representable call for structs, and a test case.

@isabelmu
Copy link
Contributor Author

I'm actually still able to trigger a stack overflow in fun cases like:

enum E1 { V1(E2<E1>), }
enum E2<T> { V2(E2<E1>), }

I'll try to work out what's going on later today. (This stack overflow happens later, like the one in #3779).

I suppose #[deriving(Eq)] is totally wrong for ReprItem?

@brson
Copy link
Contributor

brson commented Jan 27, 2014

Woo! Nice 1.0 fix.

@isabelmu
Copy link
Contributor Author

This should be OK now. @huonw what next?

@@ -3451,6 +3454,28 @@ pub fn check_const_with_ty(fcx: @FnCtxt,
writeback::resolve_type_vars_in_expr(fcx, e);
}

pub fn check_representable(tcx: ty::ctxt,
Copy link
Member

Choose a reason for hiding this comment

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

Do you reckon you could put a comment above this like check_instantiable has below?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would this be OK?

/// Checks whether a type can be represented in memory. In particular, it
/// identifies types that contain themselves without indirection through a
/// pointer, which would mean their size is unbounded. This is different from
/// the question of whether a type can be instantiated. See the definition of
/// `check_instantiable`.

Copy link
Member

Choose a reason for hiding this comment

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

Yes

@isabelmu
Copy link
Contributor Author

(thanks for all the feedback!)

@isabelmu
Copy link
Contributor Author

@huonw I just realized that struct Foo { x: [Foo, .. 0] } and enum Foo { A([Foo, .. 0]) } still present a problem after this patch. The struct gives a stack overflow on use (let y = Foo { x: [] };) and the enum gives a stack overflow on declaration (not sure where in the code the stack overflow happens).

Right now, is_type_representable considers fixed-length-zero vectors as representable--is this OK? Are these Foo definitions supposed to be legal and usable (a new issue)?

@huonw
Copy link
Member

huonw commented Jan 30, 2014

Hm, good catch... I'm not sure the semantics of that corner case are decided.

I'd recommend removing the len == 0 special case (so that those Foo you've given there are not representable), filing an issue about it and leaving a FIXME #<issue number> at the relevant place in the code.

@isabelmu
Copy link
Contributor Author

For the struct and enum both, the debug output loops on "Representing: Foo", printed from trans::adt::represent_type--maybe a zero-length check is all that is needed?

@isabelmu
Copy link
Contributor Author

OK, will do.

@huonw
Copy link
Member

huonw commented Jan 30, 2014

Fixing the downstream code would be acceptable too. However, I do think filling an issue so that the precise semantics are decided upon (rather than just "this is what the compiler happens to do") would be good. :)

@isabelmu
Copy link
Contributor Author

@huonw Removed the if len != 0 check as discussed.

bors added a commit that referenced this pull request Jan 30, 2014
It was possible to trigger a stack overflow in rustc because the routine used to verify enum representability,
type_structurally_contains, would recurse on inner types until hitting the original type. The overflow condition was when a different structurally recursive type (enum or struct) was contained in the type being checked.

I suspect my solution isn't as efficient as it could be. I pondered adding a cache of previously-seen types to avoid duplicating work (if enums A and B both contain type C, my code goes through C twice), but I didn't want to do anything that may not be necessary.

I'm a new contributor, so please pay particular attention to any unidiomatic code, misuse of terminology, bad naming of tests, or similar horribleness :)

Updated to verify struct representability as well.

Fixes #3008.
Fixes #3779.
@bors bors closed this Jan 30, 2014
@bors bors merged commit 8d097b3 into rust-lang:master Jan 30, 2014
@isabelmu isabelmu mentioned this pull request Feb 1, 2014
bors added a commit to rust-lang-ci/rust that referenced this pull request Jul 25, 2022
…nment, r=flodiebold

feat: implement destructuring assignment

This is an attempt to implement destructuring assignments, or more specifically, type inference for [assignee expressions](https://doc.rust-lang.org/reference/expressions.html#place-expressions-and-value-expressions).

I'm not sure if this is the right approach, so I don't even expect this to be merged (hence the branch name 😉) but rather want to propose one direction we could choose. I don't mind getting merged if this is good enough though!

Some notes on the implementation choices:

- Assignee expressions are **not** desugared on HIR level unlike rustc, but are inferred directly along with other expressions. This matches the processing of other syntaxes that are desugared in rustc but not in r-a. I find this reasonable because r-a only needs to infer types and it's easier to relate AST nodes and HIR nodes, so I followed it.
- Assignee expressions obviously resemble patterns, so type inference for each kind of pattern and its corresponding assignee expressions share a significant amount of logic. I tried to reuse the type inference functions for patterns by introducing `PatLike` trait which generalizes assignee expressions and patterns.
  - This is not the most elegant solution I suspect (and I really don't like the name of the trait!), but it's cleaner and the change is smaller than other ways I experimented, like making the functions generic without such trait, or making them take `Either<ExprId, PatId>` in place of `PatId`.

in case this is merged:
Closes rust-lang#11532
Closes rust-lang#11839
Closes rust-lang#12322
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants