<?xml version="1.0" encoding="us-ascii"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.0//EN"
	"http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us">

<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
<title>WG21/N1944: A finer-grained alternative to sequence points</title>
</head>

<body>

<table summary="This table provides identifying information for this document.">
	<tr>
		<th>Doc. No.:</th>
		<td>WG21/N1944<br />
		J16/06-0014</td>
	</tr>
	<tr>
		<th>Date:</th>
		<td>2006-02-17</td>
	</tr>
	<tr>
		<th>Reply to:</th>
		<td>Clark Nelson</td>
	</tr>
	<tr>
		<th>Phone:</th>
		<td>+1-503-712-8433</td>
	</tr>
	<tr>
		<th>Email:</th>
		<td><a href="mailto:clark.nelson@intel.com">clark.nelson@intel.com</a></td>
	</tr>
</table>
<h1>A finer-grained alternative to sequence points</h1>
<p>For some years it has been known and acknowledged that the words describing the 
sequencing of operations in the C and C++ standards, mainly using the term &quot;sequence 
point&quot;, are really not adequate. The C committee has more than once tried to find 
a better description, but those attempts have come to little.</p>
<p>According to my best guesses, there are at least two factors which have inhibited 
progress in the C committee:</p>
<ul>
	<li>WG14 seems collectively to believe that the problem is not urgent or compelling; 
	that most implementers and experts understand the situation well enough to agree 
	on the essentials of the requirements, and that most of the confusion can be 
	treated as an education problem (not within the committee&#39;s scope).</li>
	<li>Most of the proposals to clarify matters are so radical that they are (rightly) 
	met with considerable suspicion, and (accurately) judged to require much deep 
	consideration to ensure that they don&#39;t constitute an unacceptable change to 
	the status quo.</li>
</ul>
<p>For at least two reasons, the problem is more urgent for C++.</p>
<ul>
	<li>In the first place, we are planning to support concurrency in our upcoming 
	revision. A rigorous definition of sequencing in the context of a single thread 
	is an essential prerequisite for any definition of concurrent semantics.</li>
	<li>Even without concurrency, C++ has more sequencing issues than C. For example, 
	core issue 222 is a case where C++ needs to say more about sequencing than C 
	does, because an assignment-expression yields an lvalue instead of an rvalue.</li>
</ul>
<p>Although this is the sort of issue for which WG21 has generally deferred to WG14, 
if WG14 does not make progress in this area soon, WG21 may have no alternative but 
to take the initiative.</p>
<p>This paper outlines my thoughts about the problems with sequence points, and 
how they might be solved. The purpose is not to make any technical changes, but 
to achieve a better description (and understanding) of the status quo. This is of 
course based on my own understanding of the status quo. If anyone thinks I am proposing 
unacceptable technical changes, that would be a reflection on the lack of clarity 
and/or specificity of the existing words. The perspective is principally that of 
the C++ standard, but most of my proposals apply almost equally well to C.</p>
<p>Each section below begins with a citation from the standard, and ends with an 
indication of the direction I think should be taken. At this stage, I make no pretense 
of having finely polished standardese; first I want to get reactions to the direction 
I am suggesting. However, I do believe this paper presents a fairly complete and 
cohesive proposal.</p>
<h2>1.9p7: The definition of &quot;sequence point&quot;</h2>
<blockquote>
	<p>Accessing an object designated by a <code>volatile</code> lvalue (3.10), 
	modifying an object, calling a library I/O function, or calling a function that 
	does any of those operations are all <dfn>side effects</dfn>, which are changes 
	in the state of the execution environment. Evaluation of an expression might 
	produce side effects. <strong>At certain specified points in the execution sequence 
	called <dfn>sequence points</dfn>, all side effects of previous evaluations 
	shall be complete and no side effects of subsequent evaluations shall have taken 
	place.</strong></p>
</blockquote>
<!--
	<dt>C 5.1.2.3p2:</dt>
	<dd>Accessing a volatile object, modifying an object, modifying a file, or calling 
	a function that does any of those operations are all <dfn>side effects</dfn>, 
	which are changes in the state of the execution environment. Evaluation of an 
	expression may produce side effects. At certain specified points in the execution 
	sequence called <dfn>sequence points</dfn>, all side effects of previous evaluations 
	shall be complete and no side effects of subsequent evaluations shall have taken 
	place. (A summary of the sequence points is given in annex C.)</dd>
-->
<p>When reading this statement, two qualifications must be kept in mind:</p>
<ul>
	<li>
	<p>Not every side effect or evaluation can be authoritatively determined to 
	be either previous to or subsequent to a given sequence point. For example, 
	given</p>
	<pre>a = 0;
b = (a = 1, 2*a) + 3*a;</pre>
	<p>The evaluation of <code>3*a</code> is not ordered with respect to the sequence 
	point introduced by the comma operator. It may be previous or subsequent; the 
	standard simply doesn&#39;t say. Therefore, that evaluation may or may not be separated 
	from the assignment by a sequence point.</p>
	<p>It is clear from the C++ standard (specifically, from the last sentence of 
	5p4, q.v. below) that this example has undefined behavior. However, the C standard lacks 
	any similar statement, so the intention is far less clear. But it is crucially 
	important to have unambiguous rules by which we can determine whether a given 
	example is well-defined or undefined.</p>
	<p>In any event, because of the partially-ordering nature of sequence points, 
	any reference to the operations falling between the previous and next sequence 
	point is ambiguous &#8212; it&#39;s not always possible to tell what is and is not included 
	in that set. In fact, because sequence points are themselves only partially 
	ordered, any reference at all to &quot;the&quot; previous or next sequence point is ambiguous. 
	Unfortunately, the C standard contains several such references. Fortunately, 
	there are fewer in the C++ standard.</p>
	</li>
	<li>
	<p>The statement is talking about the abstract machine, and is therefore subject 
	to the as-if rule. For example, given</p>
	<pre>int a = 0, b = 0;
a = 1;
b = 2;</pre>
	<p>There need not be any point in the execution of the &quot;concrete&quot; machine at 
	which <code>a</code> can be observed to be 1 and <code>b</code> can be observed 
	to be 0, because in this example <code>a</code> and <code>b</code> are not both
	<code>volatile</code>. In other words, a state corresponding to the sequence 
	point need not actually occur within the &quot;concrete&quot; machine. If <code>a</code> 
	and <code>b</code> were both <code>volatile</code>, there would have to be an 
	interval between <code>(a = 1)</code> and <code>(b = 2)</code> &#8212; but that interval, 
	however small, would still have duration, and therefore would not actually be 
	a &quot;point&quot;. One could, of course, choose a point in that interval, and think 
	of it as &quot;the&quot; sequence point; but the selection would necessarily be arbitrary, 
	most especially if any machine instructions are executed between the instructions 
	implementing the volatile assignments.</p>
	</li>
</ul>
<p>For these reasons, I do not think of a sequence point as an event (in the 
concrete machine). Events in the concrete machine are such things as loads 
(evaluations) and stores (side effects) &#8212; transactions which are observable on 
some machine bus. A sequence 
point is certainly not an event in that sense.</p>
<p>I think that some (possibly much) 
of the confusion about sequence points is caused by thinking of them as events. 
Considering the example under the first bullet above, if the sequence point is considered 
to be a concrete event, then it must occur either before or after (or possibly concurrent 
with) other events in the concrete machine, and therefore may wind up between two 
events which the user wants to be separated by a sequence point, thereby preventing 
undefined behavior. The logic seems valid, but the conclusion is not, because the 
first example above is really supposed to have undefined behavior. So there must 
be a problem with a premise. QED</p>
<p>Instead, I think sequence points are nothing more than an editorial mechanism 
for sequencing constraints on events. And I think that those constraints could be 
expressed more precisely &#8212; and possibly even more clearly &#8212; without that specific 
editorial mechanism.</p>
<p>So for starters, I propose simply to delete the sentence defining the term &quot;sequence 
point&quot;. I propose instead to explicitly spell out where necessary the meaning of 
&quot;sequence point&quot;: that side effects and evaluations associated with one expression 
or subexpression are constrained to be sequenced before those associated with another 
expression.</p>
<p>Note that sequencing is an asymmetric pair-wise relation between program events; 
an event A may be sequenced before event B, or B may be sequenced before A, or they 
may not be relatively sequenced at all.</p>
<h2>1.9p9: Asynchronous signals</h2>
<blockquote>
	<p>When the processing of the abstract machine is interrupted by receipt of 
	a signal, the values of objects with type other than <code>volatile std::sig_atomic_t</code> 
	are unspecified, and the value of any object not of <code>volatile std::sig_atomic_t</code> 
	that is modified by the handler becomes undefined.</p>
</blockquote>
<p>First, let&#39;s consider the corresponding statement from the C standard (5.1.2.3p4):</p>
<blockquote>
	<p>When the processing of the abstract machine is interrupted by receipt of 
	a signal, only the values of objects as of the previous sequence point may be 
	relied on. Objects that may be modified between the previous sequence point 
	and the next sequence point need not have received their correct values yet.</p>
</blockquote>
<p>This statement explicitly refers to the abstract machine. From that fact it might 
be possible to conclude that no actual requirement is imposed on the concrete machine; 
in other words, that this statement is vacuous. Additionally, this statement is 
in conflict with 7.14.1.1p5, which basically states that a signal handler can&#39;t 
even access any object (except to assign to a <code>volatile sig_atomic_t</code> 
object), much less rely on its value. In any event, the references to &quot;the&quot; previous 
and &quot;the&quot; next sequence point are ambiguous, because of the partially-ordered nature 
of sequence points. Bottom line: the statement from the C standard is misleading 
at best, nonsensical at worst.</p>
<p>The corresponding statement from the C++ standard, however, is correct. It&#39;s 
even correct of the concrete machine, and so needn&#39;t refer specifically to the abstract 
machine. Technically, there is also a conflict with 7.14.1.1p5 from the C standard, 
in the implication that an asynchronous signal handler may use the value of a
<code>volatile sig_atomic_t</code> object, and not just assign to it. However, I 
believe that this should be considered well-defined, and the standard should say 
so explicitly.</p>
<p>(This is included primarily as an example of how bad the sequence point situation 
really is in the C standard.)</p>
<h2>1.9p11: The &quot;least requirements&quot;</h2>
<blockquote>
	<p>The least requirements on a conforming implementation are:</p>
	<ul>
		<li><strong>At sequence points, volatile objects are stable in the sense 
		that previous evaluations are complete and subsequent evaluations have not 
		yet occurred.</strong></li>
	</ul>
</blockquote>
<!--
	<dt>C 5.1.2.3p5:</dt>
	<dd>The least requirements on a conforming implementation are:
	<ul>
		<li>At sequence points, volatile objects are stable in the sense that previous 
		accesses are complete and subsequent accesses have not yet occurred.</li>
	</ul>
	</dd>
-->
<p>The sense of this constraint is simply that, in the &quot;concrete&quot; machine, the sequence 
of accesses to volatile objects is constrained exactly as in the abstract machine. 
It should be possible to say so in basically those words, without reference to sequence 
points. Consider these words from the description of <code>volatile</code> in the 
C standard (6.7.3p6):</p>
<blockquote>
	<p>An object that has volatile-qualified type may be modified in ways unknown 
	to the implementation or have other unknown side effects. Therefore any expression 
	referring to such an object shall be evaluated strictly according to the rules 
	of the abstract machine, as described in 5.1.2.3. Furthermore, at every sequence 
	point the value last stored in the object shall agree with that prescribed by 
	the abstract machine, except as modified by the unknown factors mentioned previously. 
	...</p>
</blockquote>
<p>I propose this replacement:</p>
<blockquote>
	<p>The least requirements on a conforming implementation are:</p>
	<ul>
		<li>Accesses to volatile objects occur strictly according to the rules of 
		the abstract machine.</li>
	</ul>
</blockquote>
<h2>1.9p12: The definition of &quot;full-expression&quot;</h2>
<blockquote>
	<p>A <dfn>full-expression</dfn> is an expression that is not a subexpression 
	of another expression. If a language construct is defined to produce an implicit 
	call of a function, a use of the language construct is considered to be an expression 
	for the purposes of this definition. Conversions applied to the result of an 
	expression in order to satisfy the requirements of the language construct in 
	which the expression appears are also considered to be part of the full-expression.</p>
</blockquote>
<p>I think we will also want to add something like this:</p>
<blockquote>
	<p>A call to a destructor generated at the end of the lifetime of an object 
	other than a temporary object is an implicit full-expression.</p>
</blockquote>
<p>This will give us a guarantee that the destruction of non-temporary object does 
not interleave with anything else.
<!--Destruction of a temporary (whose 
lifetime is not extended by reference-binding) --></p>
<h2>1.9p15: Full-expressions</h2>
<blockquote>
	<p><strong>There is a sequence point at the completion of evaluation of each 
	full-expression.</strong></p>
	<p><em>[Footnote:</em> As specified in 12.2, <strong>after the &quot;end-of-full-expression&quot; 
	sequence point,</strong> a sequence of zero or more invocations of destructor 
	functions for temporary objects takes place, usually in reverse order of the 
	construction of each temporary object. <em>&#8212; end footnote]</em></p>
</blockquote>
<!--
	<dt>C 6.8p4:</dt>
	<dd>A <dfn>full expression</dfn> is an expression that is not part of another 
	expression or of a declarator. Each of the following is a full expression: an 
	initializer; the expression in an expression statement; the controlling expression 
	of a selection statement (<code>if</code> or <code>switch</code>); the controlling 
	expression of a <code>while</code> or <code>do</code> statement; each of the 
	(optional) expressions of a <code>for</code> statement; the (optional) expression 
	in a <code>return</code> statement. The end of a full expression is a sequence 
	point.</dd>
-->
<p>The proposed change here is to spell out what sequence point means:</p>
<blockquote>
	<p>All evaluations and side effects associated with a full-expression are sequenced 
	before all evaluations and side effects associated with the next full-expression 
	to be evaluated.</p>
	<p><em>[Footnote:</em> As specified in 12.2, after a full-expression is evaluated, 
	a sequence of zero or more invocations of destructor functions for temporary 
	objects takes place, usually in reverse order of the construction of each temporary 
	object. <em>&#8212; end footnote]</em></p>
</blockquote>
<p>It may be felt that it is hand-waving to refer to &quot;the next full-expression to 
be evaluated&quot;, when what we are mainly trying to do is to nail down sequencing requirements 
(i.e. what is required/allowed to be &quot;next&quot;). On the other hand, my proposed wording 
certainly does no more hand-waving than the previous wording. But more importantly, 
I think that the specification of the sequence of evaluations of full-expressions 
should be considered a &quot;higher-level protocol&quot;, and specified elsewhere &#8212; which 
for the most part, it already is. The important thing here is to specify that evaluations 
of different full-expressions do not interleave.</p>
<p>These are the only contexts in which a full-expression (other than a constant-expression) 
can appear:</p>
<dl>
	<dt><var>statement</var></dt>
	<dd>Sequencing constraints are already described in chapter 6.</dd>
	<dt><var>parameter-declaration</var> (default argument)</dt>
	<dd>For evaluation and sequencing purposes, a default argument expression is 
	considered to be part of the expression that triggers its evaluation; see 1.9p13.</dd>
	<dt><var>initializer</var> (parenthesized list)</dt>
	<dd>For evaluation and sequencing purposes, all expressions in a parenthesized 
	initializer are considered to be part of a larger full-expression, which performs 
	the overall initialization; see 1.9p12.</dd>
	<dt><var>initializer-clause</var> (brace-enclosed list)</dt>
	<dd>I am unable to find any words in 8.5 which define or constrain the sequencing 
	of evaluations or side effects of expressions in an aggregate initializer. This 
	is almost certainly an oversight which needs to be fixed.</dd>
	<dd>It is interesting to note that in C99, side effects of the various expressions 
	of an aggregate initializer occur in unspecified order. This is presumably motivated 
	by designated initializers, which enable sub-object initialization order to 
	differ from lexical order.</dd>
	<dd>Because of uncertainty as to what the constraints should be, and because 
	it&#39;s not obvious in what paragraph they should be expressed, I leave this as 
	an exercise for the future.</dd>
	<dt><var>mem-initializer</var></dt>
	<dd>Again, the various expressions of a <var>mem-initializer</var> are combined 
	into a larger full-expression to initialize the member or base class. The sequencing 
	of those full-expressions is specified in 12.6.2p5.</dd>
	<dt><var>template-argument</var></dt>
	<dd>I am inclined to believe that this is a mistake &#8212; that a <var>template-argument</var> 
	should really be a constant-expression &#8212; so for the time being I ignore this 
	possibility.</dd>
</dl>
<p>It should also be noted that 3.6.2 has much to say about the order of initialization 
of namespace-scope objects, and uses the terms &quot;ordered&quot; and &quot;unordered&quot;. The concept 
of unordered initialization should not be confused with the concept of unsequenced 
evaluation. Unsequenced evaluations may interleave, and result in undefined behavior 
if, for example, a single scalar object is modified in both. Unordered initializations 
may not interleave; the result may be unspecified, but is not undefined, even if 
a single scalar object is modified in both. Unordered initializations are relatively 
sequenced, but the sequence is unspecified.</p>
<h2>1.9p16 et al.: Function calls</h2>
<blockquote>
	<p><strong>When calling a function (whether or not the function is inline), 
	there is a sequence point after the evaluation of all function arguments (if 
	any) which takes place before execution of any expressions or statements in 
	the function body. There is also a sequence point after the copying of a returned 
	value and before the execution of any expressions outside the function.</strong> 
	Several contexts in C++ cause evaluation of a function call, even though no 
	corresponding function call syntax appears in the translation unit. <em>[Example:</em> 
	evaluation of a <code>new</code> expression invokes one or more allocation and 
	constructor functions; see 5.3.4. For another example, invocation of a conversion 
	function (12.3.2) can arise in contexts in which no function call syntax appears.
	<em>&#8212;end example]</em> <strong>The sequence points at function-entry and function-exit 
	(as described above) are features of the function calls as evaluated, whatever 
	the syntax of the expression that calls the function might be.</strong></p>
</blockquote>
<p>Also 1.9p8:</p>
<blockquote>
	<p><strong>Once the execution of a function begins, no expressions from the 
	calling function are evaluated until execution of the called function has completed.</strong></p>
	<p><strong><em>[Footnote:</em> In other words, function executions do not &quot;interleave&quot; 
	with each other. <em>&#8212; end footnote]</em></strong></p>
</blockquote>
<p>Also 5.2.2p10:</p>
<blockquote>
	<p><strong>The order of evaluation of arguments is unspecified. All side effects 
	of argument expression evaluations take effect before the function is entered. 
	The order of evaluation of the postfix expression and the argument expression 
	list is unspecified.</strong></p>
</blockquote>
<!--
	<dt>C 6.5.2.2p10:</dt>
	<dd>The order of evaluation of the function designator, the actual arguments, 
	and subexpressions within the actual arguments is unspecified, but there is 
	a sequence point before the actual call.</dd>
-->
<p>There is obviously considerable redundancy among these paragraphs. I&#39;m not going 
to bother here to untangle editorially exactly what should be said where, but I 
propose to formulate the sum of the sequencing constraints expressed in them along 
these lines:</p>
<blockquote>
	<p>All evaluations and side effects associated with the postfix expression designating 
	the function to call, and with any argument expressions, are sequenced before 
	the execution of the called function (whether or not the function is inline).
	<em>[Note:</em> Side effects and evaluations from different argument expressions 
	are not sequenced relative to one another. <em>&#8212;end note]</em> The execution 
	of the called function is sequenced before any further operation of the calling 
	function. <em>[Note:</em> In other words, function executions do not &quot;interleave&quot; 
	with each other. <em>&#8212;end note]</em> ... The sequencing constraints on the execution 
	of the function are features of the function calls as evaluated, regardless 
	of the syntax of the expression that calls the function.</p>
</blockquote>
<h2>1.9p17: Sequence points within expressions</h2>
<blockquote>
	<p>In the evaluation of each of the expressions</p>
	<blockquote>
		<pre>a &amp;&amp; b
a || b
a ? b : c
a , b</pre>
	</blockquote>
	<p>using the built-in meaning of the operators in these expressions (5.14, 5.15, 
	5.16, 5.18), there is a sequence point after the evaluation of the first expression.</p>
</blockquote>
<p>I&#39;m not quite sure why this paragraph is here; it is (or ought to be) redundant 
with the forward-referenced descriptions of the operators in question. Here I simply 
call this out as an editorial question, rather than ignoring it completely.</p>
<h2>5p4: Undefined behavior</h2>
<blockquote>
	<p><strong>Except where noted, the order of evaluation of operands of individual 
	operators and subexpressions of individual expressions, and the order in which 
	side effects take place, is unspecified. Between the previous and next sequence 
	point a scalar object shall have its stored value modified at most once by the 
	evaluation of an expression. Furthermore, the prior value shall be accessed 
	only to determine the value to be stored. The requirements of this paragraph 
	shall be met for each allowable ordering of the subexpressions of a full expression; 
	otherwise the behavior is undefined.</strong></p>
</blockquote>
<!--
	<dt>C 6.5p2-3:</dt>
	<dd>
	<p>Between the previous and next sequence point an object shall have its stored 
	value modified at most once by the evaluation of an expression. Furthermore, 
	the prior value shall be read only to determine the value to be stored.</p>
	<p>The grouping of operators and operands is indicated by the syntax. Except 
	as specified later (for the function-call <code>()</code>, <code>&amp;&amp;</code>,
	<code>||</code>, <code>?:</code>, and comma operators), the order of evaluation 
	of subexpressions and the order in which side effects take place are both unspecified.</p>
	</dd>
-->
<p>Again, because sequence points are only partially ordered, the references to 
&quot;the&quot; previous and next sequence points are ambiguous at best. I suggest that this 
statement would be better expressed along the following lines:</p>
<blockquote>
	<p>Except where noted, the order of evaluation of operands of individual operators 
	and sub-expressions of individual expressions, and the order in which side effects 
	take place, are not relatively sequenced. If two modifications of a single scalar 
	object are not relatively sequenced, the behavior is undefined. If a use of 
	the value of and a modification of a single scalar object are not relatively 
	sequenced, the behavior is undefined.</p>
</blockquote>
<p>With this formulation, I believe that the statement about &quot;each allowable ordering&quot; 
is no longer necessary &#8212; but of course opinions may differ.</p>
<p>Also, it should be noted that problems have been discovered with the formulation 
&quot;the prior value shall be accessed only to determine the value to be stored.&quot; The 
strictest interpretation of those words would outlaw any use of the value of a post-increment 
expression, since the prior value is also used to determine the value of the expression. 
There is also a problem with <code>(a[a[0]] = x)</code>, where the prior value of
<code>a[0]</code> is zero. In that case, the prior value is used to determine the 
identity of the object to be assigned, which the existing words do not allow.</p>
<p>I think I have discovered an alternate way to express the desired constraint; 
see the discussion of assignment below (5.17p1).</p>
<h2>5.2.6p1: Post-increment</h2>
<blockquote>
	<p><strong>The value obtained by applying a postfix <code>++</code> is the value 
	that the operand had before applying the operator.</strong> <em>[Note:</em> 
	the value obtained is a copy of the original value <em>&#8212;end note]</em> The operand 
	shall be a modifiable lvalue. The type of the operand shall be an arithmetic 
	type or a pointer to a complete object type. <strong>After the result is noted, 
	the value of the object is modified by adding 1 to it, unless the object is 
	of type <code>bool</code>, in which case it is set to <code>true</code>.</strong>
	<em>[Note:</em> this use is deprecated, see Annex D. <em>&#8212;end note]</em> The 
	result is an rvalue. The type of the result is the cv-unqualified version of 
	the type of the operand. See also 5.7 and 5.17.</p>
</blockquote>
<p>Note that there is here a necessary sequencing constraint, which is not specified 
in terms of a sequence point: the modification must follow the evaluation.. For 
editorial purposes it is instructive to compare the words in the C++ standard to 
the corresponding words from C (6.5.2.4p2):</p>
<blockquote>
	<p>The result of the postfix <code>++</code> operator is the value of the operand. 
	After the result is obtained, the value of the operand is incremented. ... The 
	side effect of updating the stored value of the operand shall occur between 
	the previous and the next sequence point.</p>
</blockquote>
<p>The formulation I would propose for the sequencing constraints goes something 
like this:</p>
<blockquote>
	<p>The value of the postfix <code>++</code> expression is the value of the operand. 
	... The value of the operand is incremented by 1, unless the object is of type
	<code>bool</code>, in which case it is set to <code>true</code>. The evaluation 
	of the <code>++</code> expression is sequenced before the modification of the 
	operand. ...</p>
</blockquote>
<p>(For the explanation of a subtle detail here, see the discussion of assignment 
(5.17p1) below.)</p>
<h2>5.14p1-2: Logical AND expression</h2>
<blockquote>
	<p>The <code>&amp;&amp;</code> operator groups left-to-right. The operands are both 
	implicitly converted to type <code>bool</code> (clause 4). The result is
	<code>true</code> if both operands are <code>true</code> and <code>false</code> 
	otherwise. Unlike <code>&amp;</code>, <code>&amp;&amp;</code> guarantees left-to-right evaluation: 
	the second operand is not evaluated if the first operand is <code>false</code>.</p>
	<p>The result is a <code>bool</code>. <strong>All side effects of the first 
	expression except for destruction of temporaries (12.2) happen before the second 
	expression is evaluated.</strong></p>
</blockquote>
<!--
	<dt>C 6.5.13p4:</dt>
	<dd>Unlike the bitwise binary <code>&amp;</code> operator, the <code>&amp;&amp;</code> operator 
	guarantees left-to-right evaluation; there is a sequence point after the evaluation 
	of the first operand. If the first operand compares equal to 0, the second operand 
	is not evaluated.</dd>
-->
<p>It should be noted that the term &quot;sequence point&quot; does not appear here; instead 
its definition is already (at least partially) spelled out. And paragraph 1 doesn&#39;t 
really talk about sequencing. So the only thing we need to do is to tighten up the 
definition in paragraph 2:</p>
<blockquote>
	<p>The result is a <code>bool</code>. If the second expression is evaluated, 
	all side effects and evaluations associated with the first expression are sequenced 
	before all side effects and evaluations associated with the second expression.</p>
</blockquote>
<p>Obviously, the changes for logical AND would be exactly analogous. See 12.2 below 
for details of the handling of temporaries.</p>
<h2>5.16p1: Conditional expression</h2>
<blockquote>
	<p>Conditional expressions group right-to-left. The first expression is implicitly 
	converted to <code>bool</code> (clause 4). It is evaluated and if it is
	<code>true</code>, the result of the conditional expression is the value of 
	the second expression, otherwise that of the third expression. <strong>All side 
	effects of the first expression except for destruction of temporaries (12.2) 
	happen before the second or third expression is evaluated. Only one of the second 
	and third expressions is evaluated.</strong></p>
</blockquote>
<!--
	<dt>C 6.5.15p4:</dt>
	<dd>The first operand is evaluated; there is a sequence point after its evaluation. 
	The second operand is evaluated only if the first compares unequal to 0; the 
	third operand is evaluated only if the first compares equal to 0; the result 
	is the value of the second or third operand (whichever is evaluated), converted 
	to the type described below. If an attempt is made to modify the result of a 
	conditional operator or to access it after the next sequence point, the behavior 
	is undefined.</dd>
-->
<p>The problem and solution here are very similar to the logical-and expression 
immediately above:</p>
<blockquote>
	<p>... Only one of the second or third expressions is evaluated. All side effects 
	and evaluations associated with the first expression are sequenced before all 
	side effects and evaluations associated with the second or third expression.</p>
</blockquote>
<h2>5.17p1: Assignment</h2>
<blockquote>
	<p>The assignment operator (<code>=</code>) and the compound assignment operators 
	all group right-to-left. <strong>All require a modifiable lvalue as their left 
	operand and return an lvalue with the type and value of the left operand after 
	the assignment has taken place.</strong> The result in all cases is a bit-field 
	if the left operand is a bit-field.</p>
</blockquote>
<!--
	<dt>C 6.5.16p3,4:</dt>
	<dd>
	<p>An assignment operator stores a value in the object designated by the left 
	operand. An assignment expression has the value of the left operand after the 
	assignment, but is not an lvalue. The type of an assignment expression is the 
	type of the left operand unless the left operand has qualified type, in which 
	case it is the unqualified version of the type of the left operand. The side 
	effect of updating the stored value of the left operand shall occur between 
	the previous and the next sequence point.</p>
	<p>The order of evaluation of the operands is unspecified. If an attempt is 
	made to modify the result of an assignment operator or to access it after the 
	next sequence point, the behavior is undefined.</p>
	</dd>
-->
<p>Here again is a sequencing constraint that is necessary, but is not expressed 
in terms of a sequence point. I propose:</p>
<blockquote>
	<p>The assignment operator(<code>=</code>) and the compound assignment operators 
	all group right-to-left. All require a modifiable lvalue as their left operand 
	and return the left operand as an lvalue. The result in all cases is a bit-field 
	if the left operand is a bit-field. In all cases, the assignment is sequenced 
	after the evaluation of the left and right operands, and before the evaluation 
	of the assignment expression.</p>
</blockquote>
<p>It is extremely important to note here that the assignment is <strong>not</strong> 
sequenced after any side effects of the operands, but only after their evaluation. 
That is why, when I propose words to spell out the definition of a sequence point, 
I tediously refer to both evaluations and side effects; so that it is also possible 
to express weaker sequencing constraints. I used the same trick above in my proposal 
for post-increment.</p>
<p>There is room here for a clarifying footnote along these lines:</p>
<blockquote>
	<p>Because the evaluation of the assignment expression follows the assignment, 
	if it is converted to an lvalue, the result is the value assigned.</p>
</blockquote>
<p>Also note that I have added a new constraint: the assignment can&#39;t be done until 
both operands are evaluated. Although it would seem to be pointless to say this 
(causality more or less requires it, after all), I believe that by saying so explicitly 
we can eliminate the need for the troublesome words from 5p4: &quot;the prior value shall 
be accessed only to determine the value to be stored.&quot; In the new formulation of 
5p4, the behavior is undefined only if a use and a modification of a single scalar 
object are not relatively sequenced. If we insist that the evaluation of both sides 
of the assignment are sequenced before the assignment, then any use of the prior 
value in either of those operands is sequenced before the assignment, so there is 
no undefined behavior.</p>
<h2>5.18p1: Comma expressions</h2>
<blockquote>
	<p>A pair of expressions separated by a comma is evaluated left-to-right and 
	the value of the left expression is discarded. The lvalue-to-rvalue (4.1), array-to-pointer 
	(4.2), and function-to-pointer (4.3) standard conversions are not applied to 
	the left expression. <strong>All side effects (1.9) of the left expression, 
	except for the destruction of temporaries (12.2), are performed before the evaluation 
	of the right expression.</strong> The type and value of the result are the type 
	and value of the right operand; the result is an lvalue if its right operand 
	is an lvalue, and is a bit-field if its right operand is an lvalue and a bit-field.</p>
</blockquote>
<!--
	<dt>C 6.5.17p2:</dt>
	<dd>The left operand of a comma operator is evaluated as a void expression; 
	there is a sequence point after its evaluation. Then the right operand is evaluated; 
	the result has its type and value. If an attempt is made to modify the result 
	of a comma operator or to access it after the next sequence point, the behavior 
	is undefined.</dd>
-->
<p>Again, all that is required here is to substitute the correct and complete definition 
of &quot;sequence point&quot;:</p>
<blockquote>
	<p>... All evaluations and side effects associated with the left operand are 
	sequenced before all evaluations and side effects associated with the right 
	operand. ...</p>
</blockquote>
<h2>12.2p3-5: Destruction of temporaries</h2>
<blockquote>
	<p>When an implementation introduces a temporary object of a class that has 
	a non-trivial constructor (12.1, 12.8), it shall ensure that a constructor is 
	called for the temporary object. Similarly, the destructor shall be called for 
	a temporary with a non-trivial destructor (12.4). <strong>Temporary objects 
	are destroyed as the last step in evaluating the full-expression (1.9) that 
	(lexically) contains the point where they were created.</strong> This is true 
	even if that evaluation ends in throwing an exception.</p>
	<p>There are two contexts in which temporaries are destroyed at a different 
	point than the end of the full-expression. The first context is when a default 
	constructor is called to initialize an element of an array. <strong>If the constructor 
	has one or more default arguments, any temporaries created in the default argument 
	expressions are destroyed immediately after return from the constructor.</strong></p>
	<p>The second context is when a reference is bound to a temporary. The temporary 
	to which the reference is bound or the temporary that is the complete object 
	of a subobject to which the reference is bound persists for the lifetime of 
	the reference except as specified below. ... <strong>In all these cases, the 
	temporaries created during the evaluation of the expression initializing the 
	reference, except the temporary to which the reference is bound, are destroyed 
	at the end of the full-expression in which they are created and in the reverse 
	order of the completion of their construction. </strong>If the lifetime of two 
	or more temporaries to which references are bound ends at the same point, these 
	temporaries are destroyed at that point in the reverse order of the completion 
	of their construction. In addition, the destruction of temporaries bound to 
	references shall take into account the ordering of destruction of objects with 
	static or automatic storage duration (3.7.1, 3.7.2); that is, if obj1 is an 
	object with static or automatic storage duration created before the temporary 
	is created, the temporary shall be destroyed before obj1 is destroyed; if obj2 
	is an object with static or automatic storage duration created after the temporary 
	is created, the temporary shall be destroyed after obj2 is destroyed.</p>
</blockquote>
<p>I think we need to add a statement like this, probably to paragraph 3:</p>
<blockquote>
	<p>The side effects and evaluations of destroying a temporary object are associated 
	only with the full-expression, not with any specific sub-expression.</p>
</blockquote>
<p>The words used for sequencing events of a full-expression, and of operands of 
a sequencing operator, refer to side effects and evaluations &quot;associated with&quot; an 
expression. This addition makes it clear that a temporary object is destroyed at 
the end of the full-expression, and no earlier, even if a sequencing operator is 
present.</p>
<p>In paragraph 4, we may want to say something like this:</p>
<blockquote>
	<p>If the constructor has one or more default arguments, the destruction of 
	any temporary created in a default argument expression is sequenced before the 
	construction of the next array element, if any.</p>
</blockquote>
<p>Because the destruction of temporaries is associated with the containing full-expression, 
any temporary created during the construction of the last array element will still 
be destroyed.</p>
<p>In paragraph 5, I would recommend words along these lines:</p>
<blockquote>
	<p>The destruction of a temporary whose lifetime is not extended by being bound 
	to a reference is sequenced before the destruction of any temporary which was 
	constructed earlier in the same full-expression.</p>
</blockquote>
<h2>12.6.2p3:</h2>
<blockquote>
	<p><strong>There is a sequence point (1.9) after the initialization of each 
	base and member. </strong>The <var>expression-list</var> of a <var>mem-initializer</var> 
	is evaluated as part of the initialization of the corresponding base or member.</p>
</blockquote>
<p>If the initialization is defined as a full-expression (which it surely should 
be), the necessary sequencing implications come along for free:</p>
<blockquote>
	<p>The initialization of each base and member constitutes a full-expression. 
	Any expression in a <var>mem-initializer</var> is evaluated as part of the full-expression 
	that performs the initialization.</p>
</blockquote>

</body>

</html>
