Bug 18598 – cyclic constructor calls have undefined behavior but are accepted in @safe code

Status
NEW
Severity
normal
Priority
P3
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2018-03-12T10:29:36Z
Last change time
2024-12-13T18:57:49Z
Keywords
safe
Assigned to
No Owner
Creator
ag0aep6g
Moved to GitHub: dmd#19408 →

Comments

Comment #0 by ag0aep6g — 2018-03-12T10:29:36Z
On class constructors, the spec says [1]: > It is illegal for constructors to mutually call each other, although > the compiler is not required to detect it. It will result in undefined > behavior. But DMD accepts this: ---- class C { this() @safe { this(1); } this(int i) @safe { this(); } } void main() @safe { auto c = new C; } ---- According to the spec, the code has undefined behavior, so it shouldn't be accepted with the @safe attribute. Also according to the spec, "the compiler is not required to detect" this, but that can't apply to @safe code, because the compiler is required to ensure that there is "no possibility of undefined behavior" in @safe code [2]. (As always, this can be fixed by letting DMD reject the code, or by changing the spec to give the code defined behavior.) [1] https://dlang.org/spec/class.html#constructors [2] https://dlang.org/spec/function.html#function-safety
Comment #1 by bugzilla — 2018-03-20T07:03:43Z
I don't know a way to assign defined behavior to overflowing the stack :-( This can, however, be statically detected by the compiler by keeping track of the calls between constructors. (They can't be virtual, so that'll work as long as all the constructor bodies are visible to the compiler.)
Comment #2 by ag0aep6g — 2018-03-20T12:25:27Z
(In reply to Walter Bright from comment #1) > I don't know a way to assign defined behavior to overflowing the stack :-( Is that a problem for normal (non-constructor) functions as well? ---- void f() @safe { g(); } void g() @safe { f(); } void main() @safe { f(); } /* Undefined behavior? */ ----
Comment #3 by bugzilla — 2018-03-21T07:11:09Z
(In reply to ag0aep6g from comment #2) > Is that a problem for normal (non-constructor) functions as well? Yes. I'm open to advice on what to do about it.
Comment #4 by ag0aep6g — 2018-03-25T14:16:27Z
(In reply to Walter Bright from comment #3) > I'm open to advice on what to do about it. I'm by no means an expert here, but don't we have a guaranteed guard page beyond the stack? If we have, stack overflow is guaranteed to fail with a segfault, as long as the access doesn't jump over guard page (cf. issue 17566). The situation is very similar to null dereferences then. A null dereference also hits a guard page, as long as the offset from null isn't too large (cf. issue 5176). In both cases, the compiler has to detect offsets that are so large that they would jump over the guard page, and then it has to inject code that makes an earlier access to trigger the segfault. If I got the term right, for the stack that's called "stack probing".
Comment #5 by razvan.nitu1305 — 2023-05-09T15:01:37Z
Well, infinite recursion also leads to a segfault due to stack overflow however it is still accepted in @safe code [1]. @safe typically refers to undefined behavior caused by memory corruption. I don't see any memory corruption happening here, since the OS guards against that, rather a programming mistake that is beyond of what the @safe checking mechanism can discover. [1] void fun() @safe { fun();
Comment #6 by robert.schadek — 2024-12-13T18:57:49Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/19408 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB