Difference in the end of lifetime rules?
up vote
31
down vote
favorite
https://en.cppreference.com/w/cpp/language/lifetime in Notes section has this code, reproduced here:
struct A {
int* p;
~A() { std::cout << *p; } // if n outlives a, prints 123
};
void f() {
A a;
int n = 123; // if n does not outlive a, this is optimized out (dead store)
a.p = &n;
}
What is it trying to say in this Notes section?
From what I understand, the code is UB (or is it) as it's clear that n
does not outlive a
.
What does it mean by:
difference in the end of lifetime rules between non-class objects (end
of storage duration) and class objects (reverse order of construction)
matters
But it does not say matter how.
I am very confused by this entire section.
c++ language-lawyer undefined-behavior
add a comment |
up vote
31
down vote
favorite
https://en.cppreference.com/w/cpp/language/lifetime in Notes section has this code, reproduced here:
struct A {
int* p;
~A() { std::cout << *p; } // if n outlives a, prints 123
};
void f() {
A a;
int n = 123; // if n does not outlive a, this is optimized out (dead store)
a.p = &n;
}
What is it trying to say in this Notes section?
From what I understand, the code is UB (or is it) as it's clear that n
does not outlive a
.
What does it mean by:
difference in the end of lifetime rules between non-class objects (end
of storage duration) and class objects (reverse order of construction)
matters
But it does not say matter how.
I am very confused by this entire section.
c++ language-lawyer undefined-behavior
2
I'm not sure what they are trying to say in the notes section. AFAIK this is UB per the standard.
– NathanOliver
Dec 14 at 19:34
@NathanOliver I think it might be insinuating thatn
does outlivea
becausea
is destroyed just before the closing brace andn
is destroyed at the closing brace. I'm not convinced that this is true, though.
– Brian
Dec 14 at 19:37
I thought reverse order destruction would mean thatn
is destroyed beforea
leading toUB
.
– Galik
Dec 14 at 19:54
2
This is core issue 2256. We took this example (which Clang "miscompiles" according to the current rules) from one of the core reflector posts on this subject.
– T.C.
Dec 14 at 20:49
add a comment |
up vote
31
down vote
favorite
up vote
31
down vote
favorite
https://en.cppreference.com/w/cpp/language/lifetime in Notes section has this code, reproduced here:
struct A {
int* p;
~A() { std::cout << *p; } // if n outlives a, prints 123
};
void f() {
A a;
int n = 123; // if n does not outlive a, this is optimized out (dead store)
a.p = &n;
}
What is it trying to say in this Notes section?
From what I understand, the code is UB (or is it) as it's clear that n
does not outlive a
.
What does it mean by:
difference in the end of lifetime rules between non-class objects (end
of storage duration) and class objects (reverse order of construction)
matters
But it does not say matter how.
I am very confused by this entire section.
c++ language-lawyer undefined-behavior
https://en.cppreference.com/w/cpp/language/lifetime in Notes section has this code, reproduced here:
struct A {
int* p;
~A() { std::cout << *p; } // if n outlives a, prints 123
};
void f() {
A a;
int n = 123; // if n does not outlive a, this is optimized out (dead store)
a.p = &n;
}
What is it trying to say in this Notes section?
From what I understand, the code is UB (or is it) as it's clear that n
does not outlive a
.
What does it mean by:
difference in the end of lifetime rules between non-class objects (end
of storage duration) and class objects (reverse order of construction)
matters
But it does not say matter how.
I am very confused by this entire section.
c++ language-lawyer undefined-behavior
c++ language-lawyer undefined-behavior
asked Dec 14 at 19:26
PoweredByRice
1,2101320
1,2101320
2
I'm not sure what they are trying to say in the notes section. AFAIK this is UB per the standard.
– NathanOliver
Dec 14 at 19:34
@NathanOliver I think it might be insinuating thatn
does outlivea
becausea
is destroyed just before the closing brace andn
is destroyed at the closing brace. I'm not convinced that this is true, though.
– Brian
Dec 14 at 19:37
I thought reverse order destruction would mean thatn
is destroyed beforea
leading toUB
.
– Galik
Dec 14 at 19:54
2
This is core issue 2256. We took this example (which Clang "miscompiles" according to the current rules) from one of the core reflector posts on this subject.
– T.C.
Dec 14 at 20:49
add a comment |
2
I'm not sure what they are trying to say in the notes section. AFAIK this is UB per the standard.
– NathanOliver
Dec 14 at 19:34
@NathanOliver I think it might be insinuating thatn
does outlivea
becausea
is destroyed just before the closing brace andn
is destroyed at the closing brace. I'm not convinced that this is true, though.
– Brian
Dec 14 at 19:37
I thought reverse order destruction would mean thatn
is destroyed beforea
leading toUB
.
– Galik
Dec 14 at 19:54
2
This is core issue 2256. We took this example (which Clang "miscompiles" according to the current rules) from one of the core reflector posts on this subject.
– T.C.
Dec 14 at 20:49
2
2
I'm not sure what they are trying to say in the notes section. AFAIK this is UB per the standard.
– NathanOliver
Dec 14 at 19:34
I'm not sure what they are trying to say in the notes section. AFAIK this is UB per the standard.
– NathanOliver
Dec 14 at 19:34
@NathanOliver I think it might be insinuating that
n
does outlive a
because a
is destroyed just before the closing brace and n
is destroyed at the closing brace. I'm not convinced that this is true, though.– Brian
Dec 14 at 19:37
@NathanOliver I think it might be insinuating that
n
does outlive a
because a
is destroyed just before the closing brace and n
is destroyed at the closing brace. I'm not convinced that this is true, though.– Brian
Dec 14 at 19:37
I thought reverse order destruction would mean that
n
is destroyed before a
leading to UB
.– Galik
Dec 14 at 19:54
I thought reverse order destruction would mean that
n
is destroyed before a
leading to UB
.– Galik
Dec 14 at 19:54
2
2
This is core issue 2256. We took this example (which Clang "miscompiles" according to the current rules) from one of the core reflector posts on this subject.
– T.C.
Dec 14 at 20:49
This is core issue 2256. We took this example (which Clang "miscompiles" according to the current rules) from one of the core reflector posts on this subject.
– T.C.
Dec 14 at 20:49
add a comment |
2 Answers
2
active
oldest
votes
up vote
27
down vote
This is an odd aspect of C++'s lifetime rules. [basic.life]/1 tells us that an object's lifetime ends:
- if
T
is a class type with a non-trivial destructor ([class.dtor]), the destructor call starts, or
- the storage which the object occupies is released, or is reused by an object that is not nested within o ([intro.object]).
Emphasis added. int
is not a "class type with a non-trivial destructor", so its lifetime only ends when the storage it occupies is released. By contrast, A
is a class type with a non-trivial destructor", so its lifetime ends when the destructor gets called.
The storage for a scope is released when the scope exits, pursuant to [basic.stc.auto]/1:
The storage for [variables with automatic storage duration] lasts until the block in which they are created exits.
But automatic variables are destroyed in accord with [stmt.jump]/2:
On exit from a scope (however accomplished), objects with automatic storage duration that have been constructed in that scope are destroyed in the reverse order of their construction.
Notice that the order of destruction is specified, but the order of automatic storage release is not specified. This means that the implementation could release the storage right after each variable is destroyed, or release it all at once later, or in some arbitrary other order.
Now, the fact that it uses the singular for storage ("the storage for ... lasts") rather than talking about each variable individually may suggest that the intent is for the storage as a whole to be released at once for that scope. But there is no explicit statement of this in the standard. So as long as a variable is destroyed before its storage is released, any ordering of destruction vs. release appears to be legal.
This means it is entirely possible for the code to work, for n
to outlive a
. But it is unspecified whether it does work.
1
@SergeyA: Upon further investigation, things aren't quite that simple. My answer has been revised accordingly.
– Nicol Bolas
Dec 14 at 20:07
2
@Galik: "I still (from that wording) don't see why each object won't be destructed then deallocated in turn." I'm not saying that it couldn't; I'm saying that it is not required. Which is why the cppreference page speaks in hypothetical terms about whether it works or not. Because it is unspecified.
– Nicol Bolas
Dec 14 at 20:13
3
@Brian: Who is "we"? Can the committee? Sure; there have been some discussion on that. Can random users on SO? No.
– Nicol Bolas
Dec 14 at 20:23
3
By "we" I meant the C++ community, by proposing to the committee that such a change be made. By "can", I meant whether or not this would have a decent chance at getting accepted.
– Brian
Dec 14 at 20:24
3
@SoronelHaetir That's covered by as-if.
– T.C.
Dec 14 at 21:21
|
show 12 more comments
up vote
10
down vote
This example is borrowed from Core Language Issue 2256:
Section: 6.8 [basic.life] Status: drafting Submitter: Richard Smith Date: 2016-03-30
According to 6.4 [basic.lookup] bullet 1.4, the following example has defined behavior because the lifetime of
n
extends until its storage is released, which is aftera
's destructor runs:
void f() {
struct A { int *p; ~A() { *p = 0; } } a;
int n;
a.p = &n;
}
It would be more consistent if the end of the lifetime of all objects, regardless of whether they have a non-trivial destructor, were treated the same.
Notes from the March, 2018 meeting:
CWG agreed with the suggested direction.
The key idea is, whether the lifetime of an object ends at its destruction or at the time its memory is released may affect the semantic of a program. In the example,
if the lifetime of
n
ends at the destruction ofn
, the program is undefined;if the lifetime of
n
ends until the memory is released, the program has defined behavior1.
Hence it requires further discussion to determine when the lifetime of an object ends.
1 This is because Core Language Issue 2115:
Section: 9.6 [stmt.jump] Status: drafting Submitter: Richard Smith Date: 2015-04-16
The relative ordering between destruction of automatic variables on exit from a block and the release of the variables' storage is not specified by the Standard: are all the destructors executed first and then the storage released, or are they interleaved?
Notes from the February, 2016 meeting:
CWG agreed that the storage should persist until all destructions are complete, although the “as-if” rule would allow for unobservable optimizations of this ordering.
The intent is that the memory release of automatic variables happens after all destructions are complete.
Is there any real need for plain-old-data objects have a lifetime separate from that of the storage they occupy? If the goal is to allow aliasing-related optimization, it would seem both simpler and more effective to simply say that loads and stores of seemingly-unrelated lvalues are generally unsequenced, and specify situations where lvaues must be recognized as related, or operations upon them recognized as sequenced.
– supercat
Dec 14 at 22:19
@supercat: "Is there any real need for plain-old-data objects have a lifetime separate from that of the storage they occupy?" Yes. Scalar lifetime analysis can catch people using uninitialized memory and other similar errors. But that requires that scalars have lifetimes, that you can point to a piece of memory and know exactly the (nested set of) objects that live there.
– Nicol Bolas
Dec 14 at 22:22
Just to be clear: this means that CWG has agreed that this code snippet should have undefined behaviour, and the standard should be updated to make it so?
– Brian
Dec 14 at 22:35
@NicolBolas: Having explicit directives to either say that a certain region of storage should be treated as holding Indeterminate Value, or that any bytes within a region of storage that hold Indeterminate Value should be changed to hold Unspecified values, would seem more useful, for purposes of diagnostics and optimization, than trying to use lifetime rules for that purpose.
– supercat
Dec 14 at 22:37
@supercat: But there are "explicit directives" to do just that. They're called "destroying the object" and "creating an object without initializing it". They already exist and already have useful semantic meanings.
– Nicol Bolas
Dec 15 at 0:24
|
show 4 more comments
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53785791%2fdifference-in-the-end-of-lifetime-rules%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
27
down vote
This is an odd aspect of C++'s lifetime rules. [basic.life]/1 tells us that an object's lifetime ends:
- if
T
is a class type with a non-trivial destructor ([class.dtor]), the destructor call starts, or
- the storage which the object occupies is released, or is reused by an object that is not nested within o ([intro.object]).
Emphasis added. int
is not a "class type with a non-trivial destructor", so its lifetime only ends when the storage it occupies is released. By contrast, A
is a class type with a non-trivial destructor", so its lifetime ends when the destructor gets called.
The storage for a scope is released when the scope exits, pursuant to [basic.stc.auto]/1:
The storage for [variables with automatic storage duration] lasts until the block in which they are created exits.
But automatic variables are destroyed in accord with [stmt.jump]/2:
On exit from a scope (however accomplished), objects with automatic storage duration that have been constructed in that scope are destroyed in the reverse order of their construction.
Notice that the order of destruction is specified, but the order of automatic storage release is not specified. This means that the implementation could release the storage right after each variable is destroyed, or release it all at once later, or in some arbitrary other order.
Now, the fact that it uses the singular for storage ("the storage for ... lasts") rather than talking about each variable individually may suggest that the intent is for the storage as a whole to be released at once for that scope. But there is no explicit statement of this in the standard. So as long as a variable is destroyed before its storage is released, any ordering of destruction vs. release appears to be legal.
This means it is entirely possible for the code to work, for n
to outlive a
. But it is unspecified whether it does work.
1
@SergeyA: Upon further investigation, things aren't quite that simple. My answer has been revised accordingly.
– Nicol Bolas
Dec 14 at 20:07
2
@Galik: "I still (from that wording) don't see why each object won't be destructed then deallocated in turn." I'm not saying that it couldn't; I'm saying that it is not required. Which is why the cppreference page speaks in hypothetical terms about whether it works or not. Because it is unspecified.
– Nicol Bolas
Dec 14 at 20:13
3
@Brian: Who is "we"? Can the committee? Sure; there have been some discussion on that. Can random users on SO? No.
– Nicol Bolas
Dec 14 at 20:23
3
By "we" I meant the C++ community, by proposing to the committee that such a change be made. By "can", I meant whether or not this would have a decent chance at getting accepted.
– Brian
Dec 14 at 20:24
3
@SoronelHaetir That's covered by as-if.
– T.C.
Dec 14 at 21:21
|
show 12 more comments
up vote
27
down vote
This is an odd aspect of C++'s lifetime rules. [basic.life]/1 tells us that an object's lifetime ends:
- if
T
is a class type with a non-trivial destructor ([class.dtor]), the destructor call starts, or
- the storage which the object occupies is released, or is reused by an object that is not nested within o ([intro.object]).
Emphasis added. int
is not a "class type with a non-trivial destructor", so its lifetime only ends when the storage it occupies is released. By contrast, A
is a class type with a non-trivial destructor", so its lifetime ends when the destructor gets called.
The storage for a scope is released when the scope exits, pursuant to [basic.stc.auto]/1:
The storage for [variables with automatic storage duration] lasts until the block in which they are created exits.
But automatic variables are destroyed in accord with [stmt.jump]/2:
On exit from a scope (however accomplished), objects with automatic storage duration that have been constructed in that scope are destroyed in the reverse order of their construction.
Notice that the order of destruction is specified, but the order of automatic storage release is not specified. This means that the implementation could release the storage right after each variable is destroyed, or release it all at once later, or in some arbitrary other order.
Now, the fact that it uses the singular for storage ("the storage for ... lasts") rather than talking about each variable individually may suggest that the intent is for the storage as a whole to be released at once for that scope. But there is no explicit statement of this in the standard. So as long as a variable is destroyed before its storage is released, any ordering of destruction vs. release appears to be legal.
This means it is entirely possible for the code to work, for n
to outlive a
. But it is unspecified whether it does work.
1
@SergeyA: Upon further investigation, things aren't quite that simple. My answer has been revised accordingly.
– Nicol Bolas
Dec 14 at 20:07
2
@Galik: "I still (from that wording) don't see why each object won't be destructed then deallocated in turn." I'm not saying that it couldn't; I'm saying that it is not required. Which is why the cppreference page speaks in hypothetical terms about whether it works or not. Because it is unspecified.
– Nicol Bolas
Dec 14 at 20:13
3
@Brian: Who is "we"? Can the committee? Sure; there have been some discussion on that. Can random users on SO? No.
– Nicol Bolas
Dec 14 at 20:23
3
By "we" I meant the C++ community, by proposing to the committee that such a change be made. By "can", I meant whether or not this would have a decent chance at getting accepted.
– Brian
Dec 14 at 20:24
3
@SoronelHaetir That's covered by as-if.
– T.C.
Dec 14 at 21:21
|
show 12 more comments
up vote
27
down vote
up vote
27
down vote
This is an odd aspect of C++'s lifetime rules. [basic.life]/1 tells us that an object's lifetime ends:
- if
T
is a class type with a non-trivial destructor ([class.dtor]), the destructor call starts, or
- the storage which the object occupies is released, or is reused by an object that is not nested within o ([intro.object]).
Emphasis added. int
is not a "class type with a non-trivial destructor", so its lifetime only ends when the storage it occupies is released. By contrast, A
is a class type with a non-trivial destructor", so its lifetime ends when the destructor gets called.
The storage for a scope is released when the scope exits, pursuant to [basic.stc.auto]/1:
The storage for [variables with automatic storage duration] lasts until the block in which they are created exits.
But automatic variables are destroyed in accord with [stmt.jump]/2:
On exit from a scope (however accomplished), objects with automatic storage duration that have been constructed in that scope are destroyed in the reverse order of their construction.
Notice that the order of destruction is specified, but the order of automatic storage release is not specified. This means that the implementation could release the storage right after each variable is destroyed, or release it all at once later, or in some arbitrary other order.
Now, the fact that it uses the singular for storage ("the storage for ... lasts") rather than talking about each variable individually may suggest that the intent is for the storage as a whole to be released at once for that scope. But there is no explicit statement of this in the standard. So as long as a variable is destroyed before its storage is released, any ordering of destruction vs. release appears to be legal.
This means it is entirely possible for the code to work, for n
to outlive a
. But it is unspecified whether it does work.
This is an odd aspect of C++'s lifetime rules. [basic.life]/1 tells us that an object's lifetime ends:
- if
T
is a class type with a non-trivial destructor ([class.dtor]), the destructor call starts, or
- the storage which the object occupies is released, or is reused by an object that is not nested within o ([intro.object]).
Emphasis added. int
is not a "class type with a non-trivial destructor", so its lifetime only ends when the storage it occupies is released. By contrast, A
is a class type with a non-trivial destructor", so its lifetime ends when the destructor gets called.
The storage for a scope is released when the scope exits, pursuant to [basic.stc.auto]/1:
The storage for [variables with automatic storage duration] lasts until the block in which they are created exits.
But automatic variables are destroyed in accord with [stmt.jump]/2:
On exit from a scope (however accomplished), objects with automatic storage duration that have been constructed in that scope are destroyed in the reverse order of their construction.
Notice that the order of destruction is specified, but the order of automatic storage release is not specified. This means that the implementation could release the storage right after each variable is destroyed, or release it all at once later, or in some arbitrary other order.
Now, the fact that it uses the singular for storage ("the storage for ... lasts") rather than talking about each variable individually may suggest that the intent is for the storage as a whole to be released at once for that scope. But there is no explicit statement of this in the standard. So as long as a variable is destroyed before its storage is released, any ordering of destruction vs. release appears to be legal.
This means it is entirely possible for the code to work, for n
to outlive a
. But it is unspecified whether it does work.
edited Dec 14 at 20:14
answered Dec 14 at 19:43
Nicol Bolas
281k33462637
281k33462637
1
@SergeyA: Upon further investigation, things aren't quite that simple. My answer has been revised accordingly.
– Nicol Bolas
Dec 14 at 20:07
2
@Galik: "I still (from that wording) don't see why each object won't be destructed then deallocated in turn." I'm not saying that it couldn't; I'm saying that it is not required. Which is why the cppreference page speaks in hypothetical terms about whether it works or not. Because it is unspecified.
– Nicol Bolas
Dec 14 at 20:13
3
@Brian: Who is "we"? Can the committee? Sure; there have been some discussion on that. Can random users on SO? No.
– Nicol Bolas
Dec 14 at 20:23
3
By "we" I meant the C++ community, by proposing to the committee that such a change be made. By "can", I meant whether or not this would have a decent chance at getting accepted.
– Brian
Dec 14 at 20:24
3
@SoronelHaetir That's covered by as-if.
– T.C.
Dec 14 at 21:21
|
show 12 more comments
1
@SergeyA: Upon further investigation, things aren't quite that simple. My answer has been revised accordingly.
– Nicol Bolas
Dec 14 at 20:07
2
@Galik: "I still (from that wording) don't see why each object won't be destructed then deallocated in turn." I'm not saying that it couldn't; I'm saying that it is not required. Which is why the cppreference page speaks in hypothetical terms about whether it works or not. Because it is unspecified.
– Nicol Bolas
Dec 14 at 20:13
3
@Brian: Who is "we"? Can the committee? Sure; there have been some discussion on that. Can random users on SO? No.
– Nicol Bolas
Dec 14 at 20:23
3
By "we" I meant the C++ community, by proposing to the committee that such a change be made. By "can", I meant whether or not this would have a decent chance at getting accepted.
– Brian
Dec 14 at 20:24
3
@SoronelHaetir That's covered by as-if.
– T.C.
Dec 14 at 21:21
1
1
@SergeyA: Upon further investigation, things aren't quite that simple. My answer has been revised accordingly.
– Nicol Bolas
Dec 14 at 20:07
@SergeyA: Upon further investigation, things aren't quite that simple. My answer has been revised accordingly.
– Nicol Bolas
Dec 14 at 20:07
2
2
@Galik: "I still (from that wording) don't see why each object won't be destructed then deallocated in turn." I'm not saying that it couldn't; I'm saying that it is not required. Which is why the cppreference page speaks in hypothetical terms about whether it works or not. Because it is unspecified.
– Nicol Bolas
Dec 14 at 20:13
@Galik: "I still (from that wording) don't see why each object won't be destructed then deallocated in turn." I'm not saying that it couldn't; I'm saying that it is not required. Which is why the cppreference page speaks in hypothetical terms about whether it works or not. Because it is unspecified.
– Nicol Bolas
Dec 14 at 20:13
3
3
@Brian: Who is "we"? Can the committee? Sure; there have been some discussion on that. Can random users on SO? No.
– Nicol Bolas
Dec 14 at 20:23
@Brian: Who is "we"? Can the committee? Sure; there have been some discussion on that. Can random users on SO? No.
– Nicol Bolas
Dec 14 at 20:23
3
3
By "we" I meant the C++ community, by proposing to the committee that such a change be made. By "can", I meant whether or not this would have a decent chance at getting accepted.
– Brian
Dec 14 at 20:24
By "we" I meant the C++ community, by proposing to the committee that such a change be made. By "can", I meant whether or not this would have a decent chance at getting accepted.
– Brian
Dec 14 at 20:24
3
3
@SoronelHaetir That's covered by as-if.
– T.C.
Dec 14 at 21:21
@SoronelHaetir That's covered by as-if.
– T.C.
Dec 14 at 21:21
|
show 12 more comments
up vote
10
down vote
This example is borrowed from Core Language Issue 2256:
Section: 6.8 [basic.life] Status: drafting Submitter: Richard Smith Date: 2016-03-30
According to 6.4 [basic.lookup] bullet 1.4, the following example has defined behavior because the lifetime of
n
extends until its storage is released, which is aftera
's destructor runs:
void f() {
struct A { int *p; ~A() { *p = 0; } } a;
int n;
a.p = &n;
}
It would be more consistent if the end of the lifetime of all objects, regardless of whether they have a non-trivial destructor, were treated the same.
Notes from the March, 2018 meeting:
CWG agreed with the suggested direction.
The key idea is, whether the lifetime of an object ends at its destruction or at the time its memory is released may affect the semantic of a program. In the example,
if the lifetime of
n
ends at the destruction ofn
, the program is undefined;if the lifetime of
n
ends until the memory is released, the program has defined behavior1.
Hence it requires further discussion to determine when the lifetime of an object ends.
1 This is because Core Language Issue 2115:
Section: 9.6 [stmt.jump] Status: drafting Submitter: Richard Smith Date: 2015-04-16
The relative ordering between destruction of automatic variables on exit from a block and the release of the variables' storage is not specified by the Standard: are all the destructors executed first and then the storage released, or are they interleaved?
Notes from the February, 2016 meeting:
CWG agreed that the storage should persist until all destructions are complete, although the “as-if” rule would allow for unobservable optimizations of this ordering.
The intent is that the memory release of automatic variables happens after all destructions are complete.
Is there any real need for plain-old-data objects have a lifetime separate from that of the storage they occupy? If the goal is to allow aliasing-related optimization, it would seem both simpler and more effective to simply say that loads and stores of seemingly-unrelated lvalues are generally unsequenced, and specify situations where lvaues must be recognized as related, or operations upon them recognized as sequenced.
– supercat
Dec 14 at 22:19
@supercat: "Is there any real need for plain-old-data objects have a lifetime separate from that of the storage they occupy?" Yes. Scalar lifetime analysis can catch people using uninitialized memory and other similar errors. But that requires that scalars have lifetimes, that you can point to a piece of memory and know exactly the (nested set of) objects that live there.
– Nicol Bolas
Dec 14 at 22:22
Just to be clear: this means that CWG has agreed that this code snippet should have undefined behaviour, and the standard should be updated to make it so?
– Brian
Dec 14 at 22:35
@NicolBolas: Having explicit directives to either say that a certain region of storage should be treated as holding Indeterminate Value, or that any bytes within a region of storage that hold Indeterminate Value should be changed to hold Unspecified values, would seem more useful, for purposes of diagnostics and optimization, than trying to use lifetime rules for that purpose.
– supercat
Dec 14 at 22:37
@supercat: But there are "explicit directives" to do just that. They're called "destroying the object" and "creating an object without initializing it". They already exist and already have useful semantic meanings.
– Nicol Bolas
Dec 15 at 0:24
|
show 4 more comments
up vote
10
down vote
This example is borrowed from Core Language Issue 2256:
Section: 6.8 [basic.life] Status: drafting Submitter: Richard Smith Date: 2016-03-30
According to 6.4 [basic.lookup] bullet 1.4, the following example has defined behavior because the lifetime of
n
extends until its storage is released, which is aftera
's destructor runs:
void f() {
struct A { int *p; ~A() { *p = 0; } } a;
int n;
a.p = &n;
}
It would be more consistent if the end of the lifetime of all objects, regardless of whether they have a non-trivial destructor, were treated the same.
Notes from the March, 2018 meeting:
CWG agreed with the suggested direction.
The key idea is, whether the lifetime of an object ends at its destruction or at the time its memory is released may affect the semantic of a program. In the example,
if the lifetime of
n
ends at the destruction ofn
, the program is undefined;if the lifetime of
n
ends until the memory is released, the program has defined behavior1.
Hence it requires further discussion to determine when the lifetime of an object ends.
1 This is because Core Language Issue 2115:
Section: 9.6 [stmt.jump] Status: drafting Submitter: Richard Smith Date: 2015-04-16
The relative ordering between destruction of automatic variables on exit from a block and the release of the variables' storage is not specified by the Standard: are all the destructors executed first and then the storage released, or are they interleaved?
Notes from the February, 2016 meeting:
CWG agreed that the storage should persist until all destructions are complete, although the “as-if” rule would allow for unobservable optimizations of this ordering.
The intent is that the memory release of automatic variables happens after all destructions are complete.
Is there any real need for plain-old-data objects have a lifetime separate from that of the storage they occupy? If the goal is to allow aliasing-related optimization, it would seem both simpler and more effective to simply say that loads and stores of seemingly-unrelated lvalues are generally unsequenced, and specify situations where lvaues must be recognized as related, or operations upon them recognized as sequenced.
– supercat
Dec 14 at 22:19
@supercat: "Is there any real need for plain-old-data objects have a lifetime separate from that of the storage they occupy?" Yes. Scalar lifetime analysis can catch people using uninitialized memory and other similar errors. But that requires that scalars have lifetimes, that you can point to a piece of memory and know exactly the (nested set of) objects that live there.
– Nicol Bolas
Dec 14 at 22:22
Just to be clear: this means that CWG has agreed that this code snippet should have undefined behaviour, and the standard should be updated to make it so?
– Brian
Dec 14 at 22:35
@NicolBolas: Having explicit directives to either say that a certain region of storage should be treated as holding Indeterminate Value, or that any bytes within a region of storage that hold Indeterminate Value should be changed to hold Unspecified values, would seem more useful, for purposes of diagnostics and optimization, than trying to use lifetime rules for that purpose.
– supercat
Dec 14 at 22:37
@supercat: But there are "explicit directives" to do just that. They're called "destroying the object" and "creating an object without initializing it". They already exist and already have useful semantic meanings.
– Nicol Bolas
Dec 15 at 0:24
|
show 4 more comments
up vote
10
down vote
up vote
10
down vote
This example is borrowed from Core Language Issue 2256:
Section: 6.8 [basic.life] Status: drafting Submitter: Richard Smith Date: 2016-03-30
According to 6.4 [basic.lookup] bullet 1.4, the following example has defined behavior because the lifetime of
n
extends until its storage is released, which is aftera
's destructor runs:
void f() {
struct A { int *p; ~A() { *p = 0; } } a;
int n;
a.p = &n;
}
It would be more consistent if the end of the lifetime of all objects, regardless of whether they have a non-trivial destructor, were treated the same.
Notes from the March, 2018 meeting:
CWG agreed with the suggested direction.
The key idea is, whether the lifetime of an object ends at its destruction or at the time its memory is released may affect the semantic of a program. In the example,
if the lifetime of
n
ends at the destruction ofn
, the program is undefined;if the lifetime of
n
ends until the memory is released, the program has defined behavior1.
Hence it requires further discussion to determine when the lifetime of an object ends.
1 This is because Core Language Issue 2115:
Section: 9.6 [stmt.jump] Status: drafting Submitter: Richard Smith Date: 2015-04-16
The relative ordering between destruction of automatic variables on exit from a block and the release of the variables' storage is not specified by the Standard: are all the destructors executed first and then the storage released, or are they interleaved?
Notes from the February, 2016 meeting:
CWG agreed that the storage should persist until all destructions are complete, although the “as-if” rule would allow for unobservable optimizations of this ordering.
The intent is that the memory release of automatic variables happens after all destructions are complete.
This example is borrowed from Core Language Issue 2256:
Section: 6.8 [basic.life] Status: drafting Submitter: Richard Smith Date: 2016-03-30
According to 6.4 [basic.lookup] bullet 1.4, the following example has defined behavior because the lifetime of
n
extends until its storage is released, which is aftera
's destructor runs:
void f() {
struct A { int *p; ~A() { *p = 0; } } a;
int n;
a.p = &n;
}
It would be more consistent if the end of the lifetime of all objects, regardless of whether they have a non-trivial destructor, were treated the same.
Notes from the March, 2018 meeting:
CWG agreed with the suggested direction.
The key idea is, whether the lifetime of an object ends at its destruction or at the time its memory is released may affect the semantic of a program. In the example,
if the lifetime of
n
ends at the destruction ofn
, the program is undefined;if the lifetime of
n
ends until the memory is released, the program has defined behavior1.
Hence it requires further discussion to determine when the lifetime of an object ends.
1 This is because Core Language Issue 2115:
Section: 9.6 [stmt.jump] Status: drafting Submitter: Richard Smith Date: 2015-04-16
The relative ordering between destruction of automatic variables on exit from a block and the release of the variables' storage is not specified by the Standard: are all the destructors executed first and then the storage released, or are they interleaved?
Notes from the February, 2016 meeting:
CWG agreed that the storage should persist until all destructions are complete, although the “as-if” rule would allow for unobservable optimizations of this ordering.
The intent is that the memory release of automatic variables happens after all destructions are complete.
edited Dec 15 at 3:28
answered Dec 14 at 21:04
xskxzr
6,02182052
6,02182052
Is there any real need for plain-old-data objects have a lifetime separate from that of the storage they occupy? If the goal is to allow aliasing-related optimization, it would seem both simpler and more effective to simply say that loads and stores of seemingly-unrelated lvalues are generally unsequenced, and specify situations where lvaues must be recognized as related, or operations upon them recognized as sequenced.
– supercat
Dec 14 at 22:19
@supercat: "Is there any real need for plain-old-data objects have a lifetime separate from that of the storage they occupy?" Yes. Scalar lifetime analysis can catch people using uninitialized memory and other similar errors. But that requires that scalars have lifetimes, that you can point to a piece of memory and know exactly the (nested set of) objects that live there.
– Nicol Bolas
Dec 14 at 22:22
Just to be clear: this means that CWG has agreed that this code snippet should have undefined behaviour, and the standard should be updated to make it so?
– Brian
Dec 14 at 22:35
@NicolBolas: Having explicit directives to either say that a certain region of storage should be treated as holding Indeterminate Value, or that any bytes within a region of storage that hold Indeterminate Value should be changed to hold Unspecified values, would seem more useful, for purposes of diagnostics and optimization, than trying to use lifetime rules for that purpose.
– supercat
Dec 14 at 22:37
@supercat: But there are "explicit directives" to do just that. They're called "destroying the object" and "creating an object without initializing it". They already exist and already have useful semantic meanings.
– Nicol Bolas
Dec 15 at 0:24
|
show 4 more comments
Is there any real need for plain-old-data objects have a lifetime separate from that of the storage they occupy? If the goal is to allow aliasing-related optimization, it would seem both simpler and more effective to simply say that loads and stores of seemingly-unrelated lvalues are generally unsequenced, and specify situations where lvaues must be recognized as related, or operations upon them recognized as sequenced.
– supercat
Dec 14 at 22:19
@supercat: "Is there any real need for plain-old-data objects have a lifetime separate from that of the storage they occupy?" Yes. Scalar lifetime analysis can catch people using uninitialized memory and other similar errors. But that requires that scalars have lifetimes, that you can point to a piece of memory and know exactly the (nested set of) objects that live there.
– Nicol Bolas
Dec 14 at 22:22
Just to be clear: this means that CWG has agreed that this code snippet should have undefined behaviour, and the standard should be updated to make it so?
– Brian
Dec 14 at 22:35
@NicolBolas: Having explicit directives to either say that a certain region of storage should be treated as holding Indeterminate Value, or that any bytes within a region of storage that hold Indeterminate Value should be changed to hold Unspecified values, would seem more useful, for purposes of diagnostics and optimization, than trying to use lifetime rules for that purpose.
– supercat
Dec 14 at 22:37
@supercat: But there are "explicit directives" to do just that. They're called "destroying the object" and "creating an object without initializing it". They already exist and already have useful semantic meanings.
– Nicol Bolas
Dec 15 at 0:24
Is there any real need for plain-old-data objects have a lifetime separate from that of the storage they occupy? If the goal is to allow aliasing-related optimization, it would seem both simpler and more effective to simply say that loads and stores of seemingly-unrelated lvalues are generally unsequenced, and specify situations where lvaues must be recognized as related, or operations upon them recognized as sequenced.
– supercat
Dec 14 at 22:19
Is there any real need for plain-old-data objects have a lifetime separate from that of the storage they occupy? If the goal is to allow aliasing-related optimization, it would seem both simpler and more effective to simply say that loads and stores of seemingly-unrelated lvalues are generally unsequenced, and specify situations where lvaues must be recognized as related, or operations upon them recognized as sequenced.
– supercat
Dec 14 at 22:19
@supercat: "Is there any real need for plain-old-data objects have a lifetime separate from that of the storage they occupy?" Yes. Scalar lifetime analysis can catch people using uninitialized memory and other similar errors. But that requires that scalars have lifetimes, that you can point to a piece of memory and know exactly the (nested set of) objects that live there.
– Nicol Bolas
Dec 14 at 22:22
@supercat: "Is there any real need for plain-old-data objects have a lifetime separate from that of the storage they occupy?" Yes. Scalar lifetime analysis can catch people using uninitialized memory and other similar errors. But that requires that scalars have lifetimes, that you can point to a piece of memory and know exactly the (nested set of) objects that live there.
– Nicol Bolas
Dec 14 at 22:22
Just to be clear: this means that CWG has agreed that this code snippet should have undefined behaviour, and the standard should be updated to make it so?
– Brian
Dec 14 at 22:35
Just to be clear: this means that CWG has agreed that this code snippet should have undefined behaviour, and the standard should be updated to make it so?
– Brian
Dec 14 at 22:35
@NicolBolas: Having explicit directives to either say that a certain region of storage should be treated as holding Indeterminate Value, or that any bytes within a region of storage that hold Indeterminate Value should be changed to hold Unspecified values, would seem more useful, for purposes of diagnostics and optimization, than trying to use lifetime rules for that purpose.
– supercat
Dec 14 at 22:37
@NicolBolas: Having explicit directives to either say that a certain region of storage should be treated as holding Indeterminate Value, or that any bytes within a region of storage that hold Indeterminate Value should be changed to hold Unspecified values, would seem more useful, for purposes of diagnostics and optimization, than trying to use lifetime rules for that purpose.
– supercat
Dec 14 at 22:37
@supercat: But there are "explicit directives" to do just that. They're called "destroying the object" and "creating an object without initializing it". They already exist and already have useful semantic meanings.
– Nicol Bolas
Dec 15 at 0:24
@supercat: But there are "explicit directives" to do just that. They're called "destroying the object" and "creating an object without initializing it". They already exist and already have useful semantic meanings.
– Nicol Bolas
Dec 15 at 0:24
|
show 4 more comments
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53785791%2fdifference-in-the-end-of-lifetime-rules%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2
I'm not sure what they are trying to say in the notes section. AFAIK this is UB per the standard.
– NathanOliver
Dec 14 at 19:34
@NathanOliver I think it might be insinuating that
n
does outlivea
becausea
is destroyed just before the closing brace andn
is destroyed at the closing brace. I'm not convinced that this is true, though.– Brian
Dec 14 at 19:37
I thought reverse order destruction would mean that
n
is destroyed beforea
leading toUB
.– Galik
Dec 14 at 19:54
2
This is core issue 2256. We took this example (which Clang "miscompiles" according to the current rules) from one of the core reflector posts on this subject.
– T.C.
Dec 14 at 20:49