-
Notifications
You must be signed in to change notification settings - Fork 12.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #85012 - FabianWolff:struct-rec, r=davidtwco
Fix stack overflow when checking for structural recursion This pull request aims to fix #74224 and fix #84611. The current logic for detecting ADTs with structural recursion is flawed because it only looks at the root type, and then for exact matches. What I mean by this is that for examples such as: ```rust struct A<T> { x: T, y: A<A<T>>, } struct B { z: A<usize> } fn main() {} ``` When checking `A`, the compiler correctly determines that it has an infinite size (because the "root" type is `A`, and `A` occurs, albeit with different type arguments, as a nested type in `A`). However, when checking `B`, it also recurses into `A`, but now `B` is the root type, and it only checks for _exact_ matches of `A`, but since `A` never precisely contains itself (only `A<A<T>>`, `A<A<A<T>>>`, etc.), an endless recursion ensues until the stack overflows. In this PR, I have attempted to fix this behavior by implementing a two-phase checking: When checking `B`, my code first checks `A` _separately_ and stops if `A` already turns out to be infinite. If not (such as for `Option<T>`), the second phase checks whether the root type (`B`) is ever nested inside itself, e.g.: ```rust struct Foo { x: Option<Option<Foo>> } ``` Special care needs to be taken for mutually recursive types, e.g.: ```rust struct A<T> { z: T, x: B<T>, } struct B<T> { y: A<T> } ``` Here, both `A` and `B` both _are_ `SelfRecursive` and _contain_ a recursive type. The current behavior, which I have maintained, is to treat both `A` and `B` as `SelfRecursive`, and accordingly report errors for both.
- Loading branch information
Showing
7 changed files
with
338 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
struct A<T> { | ||
//~^ ERROR recursive type `A` has infinite size | ||
x: T, | ||
y: A<A<T>>, | ||
} | ||
|
||
struct B { | ||
z: A<usize> | ||
} | ||
|
||
fn main() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
error[E0072]: recursive type `A` has infinite size | ||
--> $DIR/issue-74224.rs:1:1 | ||
| | ||
LL | struct A<T> { | ||
| ^^^^^^^^^^^ recursive type has infinite size | ||
... | ||
LL | y: A<A<T>>, | ||
| ------- recursive without indirection | ||
| | ||
help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `A` representable | ||
| | ||
LL | y: Box<A<A<T>>>, | ||
| ^^^^ ^ | ||
|
||
error: aborting due to previous error | ||
|
||
For more information about this error, try `rustc --explain E0072`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
struct Foo<T> { | ||
//~^ ERROR recursive type `Foo` has infinite size | ||
x: Foo<[T; 1]>, | ||
y: T, | ||
} | ||
|
||
struct Bar { | ||
x: Foo<Bar>, | ||
} | ||
|
||
fn main() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
error[E0072]: recursive type `Foo` has infinite size | ||
--> $DIR/issue-84611.rs:1:1 | ||
| | ||
LL | struct Foo<T> { | ||
| ^^^^^^^^^^^^^ recursive type has infinite size | ||
LL | | ||
LL | x: Foo<[T; 1]>, | ||
| ----------- recursive without indirection | ||
| | ||
help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable | ||
| | ||
LL | x: Box<Foo<[T; 1]>>, | ||
| ^^^^ ^ | ||
|
||
error: aborting due to previous error | ||
|
||
For more information about this error, try `rustc --explain E0072`. |
23 changes: 23 additions & 0 deletions
23
src/test/ui/structs-enums/struct-rec/mutual-struct-recursion.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
struct A<T> { | ||
//~^ ERROR recursive type `A` has infinite size | ||
x: T, | ||
y: B<T>, | ||
} | ||
|
||
struct B<T> { | ||
//~^ ERROR recursive type `B` has infinite size | ||
z: A<T> | ||
} | ||
|
||
struct C<T> { | ||
//~^ ERROR recursive type `C` has infinite size | ||
x: T, | ||
y: Option<Option<D<T>>>, | ||
} | ||
|
||
struct D<T> { | ||
//~^ ERROR recursive type `D` has infinite size | ||
z: Option<Option<C<T>>>, | ||
} | ||
|
||
fn main() {} |
Oops, something went wrong.